Table of Contents

GcmSivModeTransform Class

Definition

Namespace
Bodu.Security.Cryptography
Assembly
Bodu.Security.Cryptography.dll
Package
Bodu.Security.Cryptography 1.2.0
Source
GcmSivModeTransform.cs

Applies GCM-SIV mode to an underlying IBlockCipher, providing nonce-misuse resistant authenticated encryption per RFC 8452.

public sealed class GcmSivModeTransform : IAeadBlockCipherModeTransform, IAeadTransform, IDisposable
Inheritance
GcmSivModeTransform
Implements
Inherited Members
Extension Methods

Examples

using System.Security.Cryptography;
using Bodu.Security.Cryptography;
using Bodu.Security.Cryptography.Extensions;

using IBlockCipher master = new AesBlockCipher(masterKey);
byte[] iv = BuildSivIv(nonce); // 12-byte nonce padded to the cipher block size
using IAeadBlockCipherModeTransform sivlike = new GcmSivModeTransform(
    masterCipher: master,
    cipherFactory: derivedKey => new AesBlockCipher(derivedKey),
    iv: iv);
byte[] sealed_ = sivlike.Encrypt(plaintext, associatedData: header);

Remarks

Generic AEAD data flow - GCM-SIV runs a POLYVAL-based MAC over nonce, associated data, and plaintext to derive a synthetic tag, then uses that tag as the CTR initial counter.

GCM-SIV shares SIV's misuse-resistant ordering - the MAC pipeline runs before the keystream pipeline so that the tag doubles as the CTR counter base - but swaps GHASH for POLYVAL, which is GHASH composed with a byte/bit reflection that makes little-endian processing efficient on modern processors.

GCM-SIV derives per-message authentication and encryption keys from the master key and a 12-byte nonce by encrypting blocks that carry little-endian counters (RFC 8452 Section 4). The encryption key is as long as the master key, so a 128-bit master key takes four blocks and a 256-bit one six, encrypted in one multi-block call:

K_auth = E_K(LE32(0) || nonce)[0..7] || E_K(LE32(1) || nonce)[0..7]                      (16 bytes)
K_enc  = E_K(LE32(2) || nonce)[0..7] || E_K(LE32(3) || nonce)[0..7]                      (16 bytes, 128-bit K)
K_enc  = E_K(LE32(2) || nonce)[0..7] || ... || E_K(LE32(5) || nonce)[0..7]               (32 bytes, 256-bit K)

POLYVAL runs on the library's GHASH kernels through RFC 8452 Appendix A's isomorphism with GHASH, folding four blocks into each reduction on processors with a carry-less multiply (PCLMULQDQ on x64, PMULL on ARM64) and using a constant-time scalar multiply elsewhere. The associated data is hashed when it is supplied, so only the 16-byte POLYVAL state is kept until the message is processed, and the keystream is produced a 4 KiB run of counter blocks at a time.

Because GCM-SIV must create a fresh cipher instance keyed with the derived K_enc, a Func<T, TResult> cipher factory is required in the constructor alongside the master cipher. The factory is called once per transform instance.

Ciphertext is output as C || Tag (16-byte tag appended), consistent with the IAeadBlockCipherModeTransform convention.

When to use GCM-SIV. The right modern AEAD pick when nonce uniqueness cannot be guaranteed - distributed systems where a coordinator might re-issue the same nonce after a crash, key wrapping, deduplication, or any context where a fresh nonce per message is impractical. Under nonce reuse, GCM-SIV's only leak is that two identical (plaintext, AAD) pairs encrypt to identical ciphertexts - confidentiality and authenticity for distinct messages remain intact. Throughput is lower than GcmModeTransform because of the two-pass MAC-then-encrypt structure; for nonce-disciplined high-throughput contexts prefer GCM. SivModeTransform is the AES-SIV (RFC 5297) sibling - also misuse-resistant but with a different MAC (S2V) and key schedule.

Constructors

GcmSivModeTransform(IBlockCipher, Func<byte[], IBlockCipher>, byte[])

Initializes a new instance of the GcmSivModeTransform class, reading the key-generating key's size from masterCipher.

public GcmSivModeTransform(IBlockCipher masterCipher, Func<byte[], IBlockCipher> cipherFactory, byte[] iv)

Parameters

masterCipher IBlockCipher

The block cipher keyed with the master key. Used for per-message key derivation. Must have a 16-byte block size.

cipherFactory Func<byte[], IBlockCipher>

A factory that creates a fresh IBlockCipher instance keyed with the supplied byte array. Called once to produce the per-message encryption cipher.

iv byte[]

The initialization vector. The first 12 bytes are used as the GCM-SIV nonce. Must equal the master cipher block size. A defensive copy is taken.

Remarks

An AesBlockCipher created with a 256-bit key derives a 256-bit message-encryption key, as RFC 8452 specifies for AEAD_AES_256_GCM_SIV. Every other master cipher derives a 128-bit one, including AES-192, which RFC 8452 does not define. To use a 256-bit key-generating key through another IBlockCipher, call the overload that takes the key size.

Exceptions

ArgumentNullException

Any argument is null.

ArgumentException

iv length does not equal the cipher block size.

InvalidOperationException

cipherFactory returned null.

GcmSivModeTransform(IBlockCipher, Func<byte[], IBlockCipher>, byte[], int)

Initializes a new instance of the GcmSivModeTransform class for a key-generating key of the specified size.

public GcmSivModeTransform(IBlockCipher masterCipher, Func<byte[], IBlockCipher> cipherFactory, byte[] iv, int keySize)

Parameters

masterCipher IBlockCipher

The block cipher keyed with the master key. Used for per-message key derivation. Must have a 16-byte block size.

cipherFactory Func<byte[], IBlockCipher>

A factory that creates a fresh IBlockCipher instance keyed with the supplied byte array. Called once to produce the per-message encryption cipher.

iv byte[]

The initialization vector. The first 12 bytes are used as the GCM-SIV nonce. Must equal the master cipher block size. A defensive copy is taken.

keySize int

The size, in bits, of the key-generating key masterCipher is keyed with.

Remarks

RFC 8452 defines GCM-SIV for 128- and 256-bit key-generating keys, and derives a message-encryption key of the same size: four cipher blocks for 128 bits, six for 256. That key is what cipherFactory receives.

Exceptions

ArgumentNullException

Any argument is null.

ArgumentException

iv length does not equal the cipher block size.

ArgumentOutOfRangeException

keySize is neither 128 nor 256.

InvalidOperationException

cipherFactory returned null.

Properties

TagSize

Gets the authentication-tag size, in bits.

public int TagSize { get; }

Property Value

int

The authentication-tag size, in bits.

Methods

Decrypt(ReadOnlySpan<byte>, Span<byte>)

Decrypts ciphertextWithTag and verifies the authentication tag.

public int Decrypt(ReadOnlySpan<byte> ciphertextWithTag, Span<byte> output)

Parameters

ciphertextWithTag ReadOnlySpan<byte>

The ciphertext followed immediately by the TagSize / 8 byte authentication tag. Must be at least TagSize / 8 bytes long.

output Span<byte>

Receives the decrypted plaintext. Must be at least ciphertextWithTag.Length - (TagSize / 8) bytes long.

Returns

int

Bytes written: ciphertextWithTag.Length - (TagSize / 8).

Remarks

Authentication failure contract. All implementations honour the same observable guarantee on tag mismatch: CryptographicException is thrown and no plaintext is released to the caller. Implementations achieve this in one of two ways:

  • Verify-before-release. The tag is compared in constant time before any plaintext byte is written to output. Used by GCM, CCM, EAX, and OCB.
  • Write-then-clear. The candidate plaintext is streamed into output first because the algorithm's structure requires the transform to complete before the tag can be computed. The tag is then compared in constant time; on mismatch output is zeroed via ZeroMemory(Span<byte>) before the exception is thrown. Used by Ascon-AEAD-128 and GCM-SIV.

In both cases the API is strictly one-shot: a failed decryption invalidates the instance, and subsequent calls throw InvalidOperationException. Construct a fresh instance per message.

Exceptions

CryptographicException

The authentication tag did not match.

ArgumentException

ciphertextWithTag is shorter than TagSize / 8 bytes, or output is too small.

InvalidOperationException

The instance has already encrypted or decrypted a message, including after a previous tag-mismatch failure. AEAD transforms are single-use per message - construct a fresh instance.

Dispose()

Releases the resources used by this instance and clears the retained authentication key, nonce, and associated-data state, and disposes the derived encryption cipher.

public void Dispose()

Remarks

The supplied master cipher is not disposed by this type. Ownership of the master cipher remains with the caller. The derived encryption cipher created by the supplied factory is owned by this transform and is disposed here.

Encrypt(ReadOnlySpan<byte>, Span<byte>)

Encrypts plaintext and appends the authentication tag to output.

public int Encrypt(ReadOnlySpan<byte> plaintext, Span<byte> output)

Parameters

plaintext ReadOnlySpan<byte>

The data to encrypt.

output Span<byte>

Receives the ciphertext followed immediately by the TagSize / 8 byte tag. Must be at least plaintext.Length + (TagSize / 8) bytes long.

Returns

int

Total bytes written: plaintext.Length + (TagSize / 8).

Exceptions

ArgumentException

output is too small.

InvalidOperationException

The instance has already encrypted or decrypted a message. AEAD transforms are single-use per message - construct a fresh instance.

ProcessAssociatedData(ReadOnlySpan<byte>)

Processes associated data (AAD) that will be authenticated but not encrypted. Must be called before Encrypt(ReadOnlySpan<byte>, Span<byte>) or Decrypt(ReadOnlySpan<byte>, Span<byte>).

public void ProcessAssociatedData(ReadOnlySpan<byte> associatedData)

Parameters

associatedData ReadOnlySpan<byte>

The bytes to authenticate. May be empty to indicate no associated data.

Remarks

The associated data is folded into the POLYVAL state here, since POLYVAL takes it before the plaintext, so the transform keeps neither the data nor a copy of it.

Exceptions

InvalidOperationException

Associated data has already been processed on this instance, or the instance has already completed an Encrypt(ReadOnlySpan<byte>, Span<byte>) or Decrypt(ReadOnlySpan<byte>, Span<byte>) operation.

Applies to

ProductVersions
.NET8, 10

See Also