Table of Contents

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 at J0 + 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

cipher IBlockCipher

The 128-bit block cipher used by GCM.

nonce Nonce

The 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

cipher is null.

ArgumentException

cipher does not have a 16-byte block size, or nonce is 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

cipher IBlockCipher

The 128-bit block cipher used by GCM.

nonce byte[]

The 96-bit (12-byte) nonce. Must be unique per key.

Exceptions

ArgumentNullException

cipher or nonce is null.

ArgumentException

cipher does not have a 16-byte block size, or nonce is 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

cipher IBlockCipher

The 128-bit block cipher used by GCM.

nonce ReadOnlySpan<byte>

The 96-bit (12-byte) nonce. Must be unique per key.

Exceptions

ArgumentNullException

cipher is null.

ArgumentException

cipher does not have a 16-byte block size, or nonce is 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

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.

ObjectDisposedException

The instance has been disposed.

InvalidOperationException

The instance has already encrypted or decrypted a message.

CryptographicException

The implicit plaintext length (ciphertextWithTag.Length − 16) exceeds the SP 800-38D §5.2.1.1 ceiling. Unreachable through the public int-typed span surface today.

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

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.

ObjectDisposedException

The instance has been disposed.

InvalidOperationException

The instance has already encrypted or decrypted a message.

CryptographicException

plaintext length 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

associatedData ReadOnlySpan<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

associatedData length exceeds the SP 800-38D §5.2.1.1 ceiling. Unreachable through the public int-typed span surface today.

Applies to

ProductVersions
.NET8, 10

See Also