Table of Contents

SivModeTransform Class

Definition

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

Applies Synthetic Initialization Vector (SIV) mode to two underlying IBlockCipher instances, providing deterministic authenticated encryption per RFC 5297 (AES-SIV).

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

Examples

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

// SIV uses a doubled key: first half drives the MAC, second half drives CTR encryption.
using IBlockCipher s2v = new AesBlockCipher(macKey);
using IBlockCipher ctr = new AesBlockCipher(encKey);
byte[] iv = new byte[s2v.BlockSize / 8]; // ignored by SIV - present for interface compatibility
using IAeadBlockCipherModeTransform siv = new SivModeTransform(s2v, ctr, iv);

byte[] sealed_ = siv.Encrypt(plaintext, associatedData: header);

Remarks

Generic AEAD data flow - SIV inverts the usual order by running the MAC pipeline first (S2V) to derive a synthetic IV, which is then used as the CTR counter.

SIV inverts the order shown in the generic AEAD diagram: the bottom pipeline runs first - S2V/CMAC over the associated data and plaintext produces the synthetic IV, which is both the tag and the CTR counter base - and only then does the top pipeline encrypt the plaintext under that derived counter. That reversal is what makes SIV misuse-resistant: re-encrypting the same message yields the same ciphertext, but confidentiality is not lost beyond confirming message equality.

SIV requires two independent ciphers keyed with different material:

  • s2vCipher (K₁) - used by CMAC and S2V to derive the synthetic IV.
  • ctrCipher (K₂) - used by AES-CTR to encrypt the plaintext.

The S2V algorithm (RFC 5297 Section 2.4) accumulates all associated data blocks and the plaintext into a single 128-bit tag using CMAC:

D ← CMAC(K₁, 0^128)
for each AD block Sᵢ (i < n): D ← dbl(D) ⊕ CMAC(K₁, Sᵢ)
if |Sₙ| ≥ 128: T ← CMAC(K₁, xorend(Sₙ, D))
else:            T ← CMAC(K₁, dbl(D) ⊕ pad(Sₙ))
SIV ← T

Ciphertext is output as C || SIV (ciphertext then tag), consistent with the IAeadBlockCipherModeTransform convention.

When to use SIV. Pick AES-SIV when deterministic authenticated encryption is wanted - key wrapping (RFC 5297 §6 / RFC 5649), envelope encryption schemes that need stable ciphertext for deduplication, or any context that cannot maintain a per-message nonce. SIV is two-pass and slower than GcmModeTransform on commodity hardware, but it has the strongest misuse-resistance profile in this library: re-encrypting the same (plaintext, AAD) tuple produces the same ciphertext, but distinct messages remain confidential and authentic. GcmSivModeTransform is the RFC 8452 alternative - same misuse-resistance category, different MAC (POLYVAL) and key schedule, typically faster on AES-NI/PCLMULQDQ hardware.

Constructors

SivModeTransform(IBlockCipher, IBlockCipher, byte[])

Initializes a new instance of the SivModeTransform class.

public SivModeTransform(IBlockCipher s2vCipher, IBlockCipher ctrCipher, byte[] iv)

Parameters

s2vCipher IBlockCipher

The cipher keyed with K₁, used for CMAC and S2V computation.

ctrCipher IBlockCipher

The cipher keyed with K₂, used for CTR encryption.

iv byte[]

Accepted for interface compatibility; not used by SIV because the synthetic IV is derived from the data.

Exceptions

ArgumentNullException

s2vCipher, ctrCipher, or iv is null.

ArgumentException

Either cipher does not have a 16-byte block size, or iv length does not equal the S2V cipher block size.

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 retained associated-data state from memory.

public void Dispose()

Remarks

The supplied IBlockCipher instances are not disposed by this type. Ownership remains with the caller.

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.

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