GcmModeTransform Class
Definition
- Namespace
- Bodu.Security.Cryptography
- Assembly
- Bodu.Security.Cryptography.dll
- Package
- Bodu.Security.Cryptography 1.2.0
- Source
- GcmModeTransform.cs
Applies Galois/Counter Mode (GCM) to a 128-bit block cipher, providing single-pass authenticated encryption with associated data (AEAD) per NIST SP 800-38D.
public sealed class GcmModeTransform : IAeadBlockCipherModeTransform, IAeadTransform, IDisposable
- Inheritance
-
GcmModeTransform
- Implements
- Inherited Members
- Extension Methods
Examples
using System.Security.Cryptography;
using Bodu.Security.Cryptography;
using Bodu.Security.Cryptography.Extensions;
using IBlockCipher cipher = new AesBlockCipher(key);
// GCM takes the 96-bit (12-byte) nonce directly - J0 is derived internally as nonce || 0x00000001.
using IAeadBlockCipherModeTransform gcm = new GcmModeTransform(cipher, nonce);
byte[] sealed_ = gcm.Encrypt(plaintext, associatedData: header);
using IAeadBlockCipherModeTransform dec = new GcmModeTransform(cipher, nonce);
byte[] recovered = dec.Decrypt(sealed_, associatedData: header);
Remarks
GCM combines counter-mode encryption with GHASH authentication over GF(2¹²⁸):
- Hash subkey:
H = E_K(0¹²⁸). - Initial counter
J0 = nonce ‖ 0x00000001; payload counter starts atJ0 + 1. - Ciphertext:
C_i = P_i ⊕ E_K(counter_i), counter incremented per block. - Tag:
T = GHASH_H(AAD ‖ C ‖ len(AAD)‖len(C)) ⊕ E_K(J0).
GF(2¹²⁸) multiplication uses the irreducible polynomial x¹²⁸ + x⁷ + x² + x + 1 with big-endian bit ordering
and the reduction constant 0xE1 in the most-significant byte.
Nonce length. This implementation accepts only the 96-bit (12-byte) nonce form, which is what every interoperable GCM consumer uses (TLS 1.2/1.3, IPsec ESP, SSH, QUIC). The SP 800-38D §7.1 GHASH-based derivation for other nonce lengths is intentionally not supported.
Lifecycle. Each instance encrypts or decrypts exactly one message. A second call to Encrypt(ReadOnlySpan<byte>, Span<byte>) or Decrypt(ReadOnlySpan<byte>, Span<byte>) throws InvalidOperationException. The instance must be disposed when finished; Dispose() clears the GHASH subkey, initial counter, running counter, and cached associated data. The supplied IBlockCipher is not disposed by this type - ownership remains with the caller.
Length limits. SP 800-38D §5.2.1.1 caps the plaintext at 2³⁹ − 256 bits and the associated
data at 2⁶⁴ bits per (key, nonce) pair. Encrypt(ReadOnlySpan<byte>, Span<byte>) / Decrypt(ReadOnlySpan<byte>, Span<byte>) /
ProcessAssociatedData(ReadOnlySpan<byte>) all defensively call ValidatePlaintextLength or ValidateAadLength
, which throw CryptographicException when the input length would breach the spec ceiling. Both checks
are dead code through the public ReadOnlySpan<T> surface today (the int-typed
length caps inputs at ≈ 2 GiB, far below either limit) but document the invariant at the call site and protect any
future surface that admits longer inputs. The 32-bit counter is separately guarded against wrapping past
0xFFFFFFFF - see Encrypt_WhenCounterWouldWrapPast0xFFFFFFFF.
When to use GCM. The default modern AEAD mode - single-pass, parallelisable, and
hardware-accelerated on AES-NI / PCLMULQDQ. The cost is fragility under nonce reuse: a single repeated
(key, nonce) pair leaks the GHASH subkey and forfeits authentication forever. For nonce-misuse resistance
prefer GcmSivModeTransform or SivModeTransform; for constrained environments prefer
CcmModeTransform; for a single-pass alternative without GCM's failure profile prefer
OcbModeTransform.
When to use GCM. The default modern AEAD mode - TLS 1.2/1.3, IPsec ESP, SSH, QUIC, and most
file-format AEAD layers all use AES-GCM. Single-pass, parallelisable, hardware-accelerated on AES-NI / PCLMULQDQ,
and the fastest AEAD on commodity x86/ARM. The cost is fragility under nonce reuse: a single repeated
(key, nonce) pair leaks the GHASH key and forfeits authentication forever. Use only when the caller can
guarantee nonce uniqueness - usually via a 96-bit counter or a random nonce drawn from a large enough space.
For nonce-misuse resistance prefer GcmSivModeTransform or SivModeTransform; for
constrained environments prefer CcmModeTransform; for a single-pass alternative without GCM's
fragility profile prefer OcbModeTransform.
Constructors
GcmModeTransform(IBlockCipher, Nonce)
Initializes a new instance of the GcmModeTransform class with a typed 96-bit GCM nonce.
public GcmModeTransform(IBlockCipher cipher, Nonce nonce)
Parameters
cipherIBlockCipherThe 128-bit block cipher used by GCM.
nonceNonceThe 96-bit (12-byte) nonce. Must be unique per key.
Remarks
Convenience overload over the span form for callers using the Nonce value type - typically produced by Random(int) - which keeps nonces distinct from keys and tags in calling code.
Exceptions
- ArgumentNullException
cipheris null.- ArgumentException
cipherdoes not have a 16-byte block size, ornonceis not exactly Bodu.Security.Cryptography.GcmModeTransform.NonceSize bytes.
GcmModeTransform(IBlockCipher, byte[])
Initializes a new instance of the GcmModeTransform class with a 96-bit GCM nonce.
public GcmModeTransform(IBlockCipher cipher, byte[] nonce)
Parameters
cipherIBlockCipherThe 128-bit block cipher used by GCM.
noncebyte[]The 96-bit (12-byte) nonce. Must be unique per key.
Exceptions
- ArgumentNullException
cipherornonceis null.- ArgumentException
cipherdoes not have a 16-byte block size, ornonceis not exactly Bodu.Security.Cryptography.GcmModeTransform.NonceSize bytes.
GcmModeTransform(IBlockCipher, ReadOnlySpan<byte>)
Initializes a new instance of the GcmModeTransform class with a 96-bit GCM nonce.
public GcmModeTransform(IBlockCipher cipher, ReadOnlySpan<byte> nonce)
Parameters
cipherIBlockCipherThe 128-bit block cipher used by GCM.
nonceReadOnlySpan<byte>The 96-bit (12-byte) nonce. Must be unique per key.
Exceptions
- ArgumentNullException
cipheris null.- ArgumentException
cipherdoes not have a 16-byte block size, ornonceis not exactly Bodu.Security.Cryptography.GcmModeTransform.NonceSize bytes.
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.
- ObjectDisposedException
The instance has been disposed.
- InvalidOperationException
The instance has already encrypted or decrypted a message.
- CryptographicException
The implicit plaintext length (
) exceeds the SP 800-38D §5.2.1.1 ceiling. Unreachable through the public int-typed span surface today.ciphertextWithTag.Length − 16
Dispose()
Releases all resources used by this instance and clears the GHASH subkey, initial counter, running counter, and cached associated data from memory. Idempotent. Does not dispose the supplied IBlockCipher - ownership remains with the caller.
public void Dispose()
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.
- ObjectDisposedException
The instance has been disposed.
- InvalidOperationException
The instance has already encrypted or decrypted a message.
- CryptographicException
plaintextlength exceeds the SP 800-38D §5.2.1.1 ceiling. Unreachable through the public int-typed span surface today.
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.
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.
- ObjectDisposedException
The instance has been disposed.
- InvalidOperationException
Associated data has already been processed, or the instance has already completed encryption or decryption.
- CryptographicException
associatedDatalength exceeds the SP 800-38D §5.2.1.1 ceiling. Unreachable through the public int-typed span surface today.
Applies to
| Product | Versions |
|---|---|
| .NET | 8, 10 |