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
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
masterCipherIBlockCipherThe block cipher keyed with the master key. Used for per-message key derivation. Must have a 16-byte block size.
cipherFactoryFunc<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.
ivbyte[]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
ivlength does not equal the cipher block size.- InvalidOperationException
cipherFactoryreturned 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
masterCipherIBlockCipherThe block cipher keyed with the master key. Used for per-message key derivation. Must have a 16-byte block size.
cipherFactoryFunc<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.
ivbyte[]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.
keySizeintThe size, in bits, of the key-generating key
masterCipheris 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
ivlength does not equal the cipher block size.- ArgumentOutOfRangeException
keySizeis neither 128 nor 256.- InvalidOperationException
cipherFactoryreturned 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
ciphertextWithTagReadOnlySpan<byte>The ciphertext followed immediately by the TagSize / 8 byte authentication tag. Must be at least TagSize / 8 bytes long.
outputSpan<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
outputfirst 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 mismatchoutputis 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
ciphertextWithTagis shorter than TagSize / 8 bytes, oroutputis 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
plaintextReadOnlySpan<byte>The data to encrypt.
outputSpan<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
outputis 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
associatedDataReadOnlySpan<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
| Product | Versions |
|---|---|
| .NET | 8, 10 |