MAC Algorithms Reference
This page provides detailed documentation for all Message Authentication Code (MAC) algorithms implemented in the CryptoHives.Foundation.Security.Cryptography package.
Namespace
using CryptoHives.Foundation.Security.Cryptography.Mac;
Overview
Message Authentication Codes (MACs) provide both data integrity and authenticity verification. Unlike simple hash functions, MACs require a secret key, ensuring that only parties with the key can generate or verify the authentication tag.
MAC vs Hash
| Feature | Hash | MAC |
|---|---|---|
| Secret key required | No | Yes |
| Data integrity | Yes | Yes |
| Data authenticity | No | Yes |
| Use case | Fingerprinting | Authentication |
KMAC128
KMAC128 is the Keccak Message Authentication Code with 128-bit security, defined in NIST SP 800-185.
Class Declaration
public sealed class KMac128 : HashAlgorithm
Properties
| Property | Value |
|---|---|
| Security | 128 bits |
| Output Size | Variable (default 32 bytes) |
| Key Size | Any length |
| Block Size | 168 bytes (rate) |
Constructor
public KMac128(byte[] key, int outputBytes = 32, string customization = "")
Parameters:
key- The secret key (any length, cannot be empty)outputBytes- Desired output size in bytes (default: 32)customization- Optional customization string for domain separation
Factory Method
public static KMac128 Create(byte[] key, int outputBytes = 32, string customization = "")
Usage Examples
byte[] key = new byte[32];
RandomNumberGenerator.Fill(key);
byte[] message = Encoding.UTF8.GetBytes("Hello, World!");
// Basic KMAC128
using var kmac = KMac128.Create(key, outputBytes: 32);
Span<byte> mac = stackalloc byte[32];
kmac.TryComputeHash(message, mac, out _);
// With customization string
using var kmacCustom = KMac128.Create(key, outputBytes: 32, customization: "MyApp v1.0");
Span<byte> macCustom = stackalloc byte[32];
kmacCustom.TryComputeHash(message, macCustom, out _);
// Variable output length
using var kmacLong = KMac128.Create(key, outputBytes: 64);
Span<byte> longMac = stackalloc byte[64];
kmacLong.TryComputeHash(message, longMac, out _);
Incremental Usage
using var kmac = KMac128.Create(key, outputBytes: 32, customization: "");
// Process data in chunks — zero allocations
kmac.AppendData(chunk1);
kmac.AppendData(chunk2);
kmac.AppendData(chunk3);
Span<byte> mac = stackalloc byte[32];
kmac.TryGetHashAndReset(mac, out _);
KMAC256
KMAC256 is the Keccak Message Authentication Code with 256-bit security, defined in NIST SP 800-185.
Class Declaration
public sealed class KMac256 : HashAlgorithm
Properties
| Property | Value |
|---|---|
| Security | 256 bits |
| Output Size | Variable (default 64 bytes) |
| Key Size | Any length |
| Block Size | 136 bytes (rate) |
Constructor
public KMac256(byte[] key, int outputBytes = 64, string customization = "")
Parameters:
key- The secret key (any length, cannot be empty)outputBytes- Desired output size in bytes (default: 64)customization- Optional customization string for domain separation
Factory Method
public static KMac256 Create(byte[] key, int outputBytes = 64, string customization = "")
Usage Examples
byte[] key = new byte[32];
RandomNumberGenerator.Fill(key);
byte[] message = Encoding.UTF8.GetBytes("Hello, World!");
// Basic KMAC256
using var kmac = KMac256.Create(key, outputBytes: 64);
Span<byte> mac = stackalloc byte[64];
kmac.TryComputeHash(message, mac, out _);
// With customization string for domain separation
using var kmacCustom = KMac256.Create(key, outputBytes: 64, customization: "Session Authentication");
Span<byte> macCustom = stackalloc byte[64];
kmacCustom.TryComputeHash(message, macCustom, out _);
HMAC (Hash-based Message Authentication Code)
HMAC is the most widely used MAC construction, defined in RFC 2104 and FIPS 198-1. It works with any cryptographic hash function and is used extensively in TLS, SSH, IPsec, and OAuth.
IMac Interface
All new MAC types implement the IMac interface for consistent API:
public interface IMac : IDisposable
{
string AlgorithmName { get; }
int MacSize { get; }
void Update(ReadOnlySpan<byte> input);
void Finalize(Span<byte> destination);
void Reset();
}
Available HMAC Variants
| Class | Hash | MAC Size | Security | Status |
|---|---|---|---|---|
HmacSha256 |
SHA-256 | 32 bytes | 256 bits | ✅ Recommended |
HmacSha384 |
SHA-384 | 48 bytes | 384 bits | ✅ Recommended |
HmacSha512 |
SHA-512 | 64 bytes | 512 bits | ✅ Recommended |
HmacSha3_256 |
SHA3-256 | 32 bytes | 256 bits | ✅ Cross-platform |
HmacSha1 |
SHA-1 | 20 bytes | 160 bits | ⚠️ Legacy |
HmacMd5 |
MD5 | 16 bytes | 128 bits | ⚠️ Legacy |
Constructor
public HmacSha256(byte[] key)
Parameters:
key- The secret key (any length; keys longer than the hash block size are hashed first)
Factory Method
public static HmacSha256 Create(byte[] key)
Static One-Shot
public static byte[] Hash(byte[] key, byte[] data)
Usage Examples
byte[] key = new byte[32];
RandomNumberGenerator.Fill(key);
byte[] message = Encoding.UTF8.GetBytes("Hello, World!");
// One-shot API
byte[] tag = HmacSha256.Hash(key, message);
// Instance-based API
using var hmac = HmacSha256.Create(key);
byte[] mac = hmac.ComputeHash(message);
// Streaming API (IMac interface)
using var hmac = HmacSha256.Create(key);
hmac.Update(chunk1);
hmac.Update(chunk2);
byte[] result = new byte[hmac.MacSize];
hmac.Finalize(result);
// Reuse with same key
hmac.Reset();
hmac.Update(newData);
hmac.Finalize(result);
HMAC-SHA3-256 (Cross-Platform)
Unlike .NET's built-in HMACSHA3_256 which requires Windows 11+ or OpenSSL 1.1.1+, the CryptoHives implementation works on all platforms:
using var hmac = HmacSha3_256.Create(key);
byte[] mac = hmac.ComputeHash(message);
Generic HMAC with Any Hash
The HmacCore base class works with any HashAlgorithm from the library. Create custom HMAC variants by subclassing:
public sealed class HmacSm3 : HmacCore
{
public HmacSm3(byte[] key) : base("HMAC-SM3", SM3.Create(), SM3.Create(), key) { }
}
AES-CMAC (Cipher-based MAC)
AES-CMAC is a MAC based on the AES block cipher, defined in NIST SP 800-38B and RFC 4493. Unlike HMAC, it computes the tag in a single pass and does not require a hash function.
Note: .NET does not provide a portable CMAC implementation. This is a CryptoHives differentiator.
Class Declaration
public sealed class AesCmac : IMac
Properties
| Property | Value |
|---|---|
| MAC Size | 128 bits (16 bytes) |
| Key Sizes | 128, 192, or 256 bits |
| Block Size | 128 bits (AES) |
| Hardware Accel. | AES-NI when available |
Constructor
public AesCmac(byte[] key)
public AesCmac(ReadOnlySpan<byte> key)
Parameters:
key- The secret key (16, 24, or 32 bytes for AES-128/192/256)
Factory Method
public static AesCmac Create(byte[] key)
Static One-Shot
public static byte[] Hash(byte[] key, byte[] data)
Usage Examples
byte[] key = new byte[16]; // AES-128
RandomNumberGenerator.Fill(key);
byte[] message = Encoding.UTF8.GetBytes("Hello, World!");
// One-shot API
byte[] tag = AesCmac.Hash(key, message);
// Instance-based API
using var cmac = AesCmac.Create(key);
byte[] mac = cmac.ComputeHash(message);
// Streaming API
using var cmac = AesCmac.Create(key);
cmac.Update(chunk1);
cmac.Update(chunk2);
byte[] result = new byte[cmac.MacSize];
cmac.Finalize(result);
AES-256 CMAC
byte[] key = new byte[32]; // AES-256
RandomNumberGenerator.Fill(key);
using var cmac = AesCmac.Create(key);
byte[] mac = cmac.ComputeHash(message);
AES-GMAC (Galois MAC)
AES-GMAC is the authentication-only mode of AES-GCM, defined in NIST SP 800-38D. It produces a 128-bit tag using the Galois field multiplication (GHASH) used in GCM.
Note: .NET does not provide a standalone GMAC class. This is a CryptoHives differentiator.
Class Declaration
public sealed class AesGmac : IDisposable
Properties
| Property | Value |
|---|---|
| MAC Size | 128 bits (16 bytes) |
| Key Sizes | 128, 192, or 256 bits |
| Nonce Size | 96 bits (12 bytes) |
| Hardware Accel. | AES-NI + PCLMULQDQ |
Constructor
public AesGmac(ReadOnlySpan<byte> key)
Parameters:
key- The secret key (16, 24, or 32 bytes)
Factory Method
public static AesGmac Create(byte[] key)
Usage Examples
byte[] key = new byte[16];
RandomNumberGenerator.Fill(key);
byte[] nonce = new byte[12]; // MUST be unique per message
RandomNumberGenerator.Fill(nonce);
byte[] data = Encoding.UTF8.GetBytes("authenticated data");
// Compute tag
using var gmac = AesGmac.Create(key);
byte[] tag = gmac.ComputeTag(nonce, data);
// Verify tag
bool valid = gmac.VerifyTag(nonce, data, tag);
Important: Nonce Requirements
Never reuse a nonce with the same key. Each GMAC invocation must use a unique 12-byte nonce. Nonce reuse completely compromises the authentication guarantee.
// CORRECT: Generate a fresh nonce for each message
byte[] nonce = new byte[12];
RandomNumberGenerator.Fill(nonce);
// WRONG: Reusing the same nonce with the same key
// byte[] nonce = new byte[12]; // reused - INSECURE!
Poly1305
Poly1305 is a high-speed one-time authenticator designed by Daniel J. Bernstein, defined in RFC 8439. It is the MAC component of ChaCha20-Poly1305 and XChaCha20-Poly1305 AEAD constructions.
Note: .NET does not provide a standalone Poly1305 class. This is a CryptoHives differentiator.
Class Declaration
public sealed class Poly1305Mac : IMac
Properties
| Property | Value |
|---|---|
| MAC Size | 128 bits (16 bytes) |
| Key Size | 256 bits (32 bytes) |
| Block Size | 128 bits (16 bytes) |
| One-Time Key | Yes — key must be unique per message |
Constructor
public Poly1305Mac(ReadOnlySpan<byte> key)
Parameters:
key- A 32-byte one-time key. Must be unique for every message.
Factory Method
public static Poly1305Mac Create(byte[] key)
public static Poly1305Mac Create(ReadOnlySpan<byte> key)
Static One-Shot
public static byte[] Hash(byte[] key, byte[] data)
Usage Examples
using CryptoHives.Foundation.Security.Cryptography.Mac;
byte[] key = new byte[32]; // Must be exactly 32 bytes, unique per message
RandomNumberGenerator.Fill(key);
byte[] message = Encoding.UTF8.GetBytes("Hello, World!");
// One-shot API
byte[] tag = Poly1305Mac.Hash(key, message);
// Instance-based API
using var mac = Poly1305Mac.Create(key);
byte[] result = mac.ComputeHash(message);
// Streaming API (IMac interface)
using var mac = Poly1305Mac.Create(key);
mac.Update(chunk1);
mac.Update(chunk2);
byte[] tag = new byte[mac.MacSize]; // 16 bytes
mac.Finalize(tag);
Important: Key Requirements
The 32-byte key must be used for exactly one message. Reusing a key across multiple messages allows an attacker to forge tags. In practice, the key is derived from a session key and nonce:
// Typical pattern: derive Poly1305 key from ChaCha20 key stream
// (This is done internally by ChaCha20-Poly1305)
byte[] sessionKey = ...; // 256-bit session key
byte[] nonce = ...; // 96-bit nonce
// Generate one-time key from ChaCha20 block 0
byte[] oneTimeKey = ChaCha20KeyStream(sessionKey, nonce, blockCounter: 0);
using var mac = Poly1305Mac.Create(oneTimeKey);
Reset Behavior
Calling Reset() restores the accumulator to the initial state with the same key. This allows computing multiple MACs with the same key (for testing or if the one-time key property is managed externally):
using var mac = Poly1305Mac.Create(key);
mac.Update(message1);
byte[] tag1 = new byte[16];
mac.Finalize(tag1);
mac.Reset(); // Resets accumulator, same key
mac.Update(message2);
byte[] tag2 = new byte[16];
mac.Finalize(tag2);
BLAKE2 MAC
BLAKE2b and BLAKE2s support built-in keyed hashing mode for message authentication.
Blake2b Keyed Mode
public static Blake2b Create(byte[]? key = null, int hashSize = 64)
Properties:
- Security: Up to 256 bits (depends on key and output size)
- Key Size: 1-64 bytes
- Output Size: 1-64 bytes
Usage:
byte[] key = new byte[32]; // Up to 64 bytes
RandomNumberGenerator.Fill(key);
using var blake2b = Blake2b.Create(key: key, hashSize: 32);
Span<byte> mac = stackalloc byte[32];
blake2b.TryComputeHash(message, mac, out _);
Blake2s Keyed Mode
public static Blake2s Create(byte[]? key = null, int hashSize = 32)
Properties:
- Security: Up to 128 bits (depends on key and output size)
- Key Size: 1-32 bytes
- Output Size: 1-32 bytes
Usage:
byte[] key = new byte[16]; // Up to 32 bytes
RandomNumberGenerator.Fill(key);
using var blake2s = Blake2s.Create(key: key, hashSize: 16);
Span<byte> mac = stackalloc byte[16];
blake2s.TryComputeHash(message, mac, out _);
BLAKE3 MAC
BLAKE3 provides a dedicated keyed hashing mode for message authentication.
Blake3 Keyed Mode
public static Blake3 CreateKeyed(byte[] key, int outputBytes = 32)
Properties:
- Security: 128 bits
- Key Size: Exactly 32 bytes
- Output Size: Variable (default 32 bytes)
Usage:
byte[] key = new byte[32]; // Must be exactly 32 bytes
RandomNumberGenerator.Fill(key);
// Standard 32-byte MAC
using var blake3 = Blake3.CreateKeyed(key);
Span<byte> mac = stackalloc byte[32];
blake3.TryComputeHash(message, mac, out _);
// Extended 64-byte MAC
using var blake3Long = Blake3.CreateKeyed(key, outputBytes: 64);
Span<byte> longMac = stackalloc byte[64];
blake3Long.TryComputeHash(message, longMac, out _);
BLAKE3 Key Derivation
BLAKE3 also supports key derivation from a context string and input key material:
public static Blake3 CreateDeriveKey(string context, int outputBytes = 32)
Usage:
string context = "MyApp 2025-01-01 encryption key";
byte[] inputKeyMaterial = ...; // Your master key or password-derived key
using var blake3 = Blake3.CreateDeriveKey(context);
Span<byte> derivedKey = stackalloc byte[32];
blake3.TryComputeHash(inputKeyMaterial, derivedKey, out _);
Algorithm Comparison
Security Levels
| Algorithm | Security Strength | Notes |
|---|---|---|
| HMAC-SHA-512 | 512 bits | Maximum HMAC security |
| HMAC-SHA-256 | 256 bits | Most widely used, recommended |
| KMAC256 | 256 bits | Highest security, NIST approved |
| HMAC-SHA3-256 | 256 bits | Cross-platform SHA-3 HMAC |
| Poly1305 | 128 bits | Ultra-fast one-time authenticator |
| AES-CMAC | 128 bits | Cipher-based, single-pass |
| AES-GMAC | 128 bits | Galois field, nonce-required |
| KMAC128 | 128 bits | Good security, NIST approved |
| BLAKE3 keyed | 128 bits | High performance |
| BLAKE2b keyed | Up to 256 bits | Depends on key/output size |
| BLAKE2s keyed | Up to 128 bits | Good for embedded systems |
Performance
| Algorithm | Relative Speed | Best For |
|---|---|---|
| Poly1305 | Fastest | AEAD constructions, one-time auth |
| BLAKE3 keyed | Very fast | High-throughput applications |
| BLAKE2b keyed | Very fast | General purpose on 64-bit |
| AES-GMAC | Very fast (AES-NI) | When nonce management is feasible |
| AES-CMAC | Fast (AES-NI) | Protocol compliance (EAP, 802.11i) |
| HMAC-SHA-256 | Moderate | General purpose, widest compatibility |
| HMAC-SHA-512 | Moderate | Maximum security on 64-bit |
| HMAC-SHA3-256 | Moderate | Cross-platform SHA-3 |
| BLAKE2s keyed | Fast | 32-bit and embedded systems |
| KMAC256 | Moderate | Maximum security |
| KMAC128 | Moderate | NIST compliance |
Feature Comparison
| Feature | HMAC | AES-CMAC | AES-GMAC | Poly1305 | KMAC | BLAKE2 | BLAKE3 |
|---|---|---|---|---|---|---|---|
| Variable output | ❌ | ❌ | ❌ | ❌ | ✅ | ✅ | ✅ |
| Customization string | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ |
| Nonce required | ❌ | ❌ | ✅ | ❌¹ | ❌ | ❌ | ❌ |
| One-time key required | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ |
| NIST approved | ✅ | ✅ | ✅ | ❌ | ✅ | ❌ | ❌ |
| .NET built-in | ✅ | ❌ | ❌ | ❌ | Partial | ❌ | ❌ |
| Key derivation | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ |
| Arbitrary key size | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | |
| Hardware accelerated | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ | ✅ |
¹ Poly1305 does not use a nonce directly, but its key must be unique per message (typically derived from a nonce via ChaCha20).
Best Practices
Key Generation
Always use cryptographically secure random number generators for key generation:
byte[] key = new byte[32];
RandomNumberGenerator.Fill(key);
Key Storage
- Never hardcode keys in source code
- Use secure key storage (Azure Key Vault, AWS KMS, etc.)
- Rotate keys periodically
Domain Separation
Use customization strings (KMAC) or different contexts (BLAKE3) to separate different uses of the same key:
// Authentication for different message types
using var kmacAuth = KMac256.Create(key, customization: "Auth");
using var kmacEncrypt = KMac256.Create(key, customization: "Encrypt");
// BLAKE3 key derivation with context
using var authKey = Blake3.CreateDeriveKey("MyApp Auth Key");
using var encKey = Blake3.CreateDeriveKey("MyApp Encryption Key");
Verification
When verifying MACs, use constant-time comparison to prevent timing attacks:
using System.Security.Cryptography;
bool VerifyMac(byte[] expected, byte[] actual)
{
return CryptographicOperations.FixedTimeEquals(expected, actual);
}
Common Patterns
Authenticated Encryption
public class AuthenticatedMessage
{
public byte[] Ciphertext { get; set; }
public byte[] Mac { get; set; }
public static AuthenticatedMessage Create(byte[] key, byte[] plaintext)
{
// Encrypt (using your preferred cipher)
byte[] ciphertext = Encrypt(plaintext);
// Authenticate
using var kmac = KMac256.Create(key, customization: "Auth");
Span<byte> mac = stackalloc byte[64];
kmac.TryComputeHash(ciphertext, mac, out _);
return new AuthenticatedMessage { Ciphertext = ciphertext, Mac = mac.ToArray() };
}
public bool Verify(byte[] key)
{
using var kmac = KMac256.Create(key, customization: "Auth");
Span<byte> expectedMac = stackalloc byte[64];
kmac.TryComputeHash(Ciphertext, expectedMac, out _);
return CryptographicOperations.FixedTimeEquals(Mac, expectedMac);
}
}
API Request Signing
public string SignRequest(byte[] key, string method, string path, string timestamp)
{
string message = $"{method}:{path}:{timestamp}";
byte[] messageBytes = Encoding.UTF8.GetBytes(message);
using var blake3 = Blake3.CreateKeyed(key);
Span<byte> signature = stackalloc byte[32];
blake3.TryComputeHash(messageBytes, signature, out _);
return Convert.ToBase64String(signature);
}
Session Token Generation
public byte[] GenerateSessionKey(byte[] masterKey, string userId, DateTime expiry)
{
string context = $"session:{userId}:{expiry:O}";
using var blake3 = Blake3.CreateDeriveKey(context);
Span<byte> sessionKey = stackalloc byte[32];
blake3.TryComputeHash(masterKey, sessionKey, out _);
return sessionKey.ToArray();
}
See Also
- Hash Algorithms
- Cipher Algorithms
- KDF Algorithms
- Cryptography Package Overview
- KMAC Specifications
- HMAC Specification (RFC 2104)
- CMAC Specification (SP 800-38B)
- HMAC Test Vectors
- CMAC Test Vectors
- Poly1305 / ChaCha20 Test Vectors
© 2026 The Keepers of the CryptoHives