📜 MAC Algorithms
Message Authentication Codes (MACs) provide integrity and authenticity for messages by allowing the recipient to verify that the data was produced by someone holding the secret key.
All code examples are written in the C# language and use LibSodium.Net’s allocation-free API.
Unlike encryption, authentication doesn't hide the contents of a message. It ensures that the message has not been tampered with and was created by someone who knows the shared secret key.
LibSodium.Net exposes three HMAC‑SHA‑2 variants and Poly1305 that cover the vast majority of real‑world requirements while staying allocation‑free and AOT‑friendly.
🧂 Based on libsodium’s HMAC‑SHA‑2
🧂 Based on libsodium’s One-time authentication
ℹ️ API Reference: CryptoHmacSha256
ℹ️ API Reference: CryptoHmacSha512
ℹ️ API Reference: CryptoHmacSha512_256
ℹ️ API Reference: CryptoOneTimeAuth
🌟 Features
- Keyed authentication with proven constructions (HMAC‑SHA‑2 & Poly1305).
- Allocation‑free
Span<T>API + streaming + async overloads. - Unified method names:
ComputeMac,VerifyMac,GenerateKey. - Safe size checks that throw early (
ArgumentException). - Deterministic output – same key + message ⇒ same tag.
- Incremental (Multi-Part) MAC construction.
⚖️ Algorithm Comparison (bytes)
| API | Hash / Primitive | MacLen | KeyLen | Recommended Use‑Case |
|---|---|---|---|---|
CryptoHmacSha256 |
SHA‑256 | 32 | 32 | Interop with systems expecting HMAC‑SHA‑256 (JWT, TLS, S3). |
CryptoHmacSha512 |
SHA‑512 | 64 | 32 | Larger tag & usually faster on 64‑bit CPUs. |
CryptoHmacSha512_256 |
SHA‑512/256 | 32 | 32 | 32‑byte tag with SHA‑512 speed & security margin. |
CryptoOneTimeAuth |
Poly1305 | 16 | 32 | One‑time MAC for single‑key usage (AEAD internals, per‑message keys, SecretBox). |
ℹ️ All lengths are in bytes
✨ HMAC‑SHA‑256
The CryptoHmacSha256 API implements HMAC-SHA-256 —built on the widely-deployed SHA-256 hash function— producing a 32-byte tag with a fixed 32-byte key. Choose this when you are interacting with external systems that require HMAC-SHA-256 (e.g., AWS S3 signatures, JWT HS256 tokens, many REST APIs).
📏 Constants
| Name | Value | Description |
|---|---|---|
MacLen |
32 | Length of the authentication tag (32). |
KeyLen |
32 | Length of the secret key (32). |
✨ HMAC‑SHA‑512
The CryptoHmacSha512 API implements HMAC-SHA-512 —producing a 64-byte tag with a fixed 32-byte key— and leverages SHA-512, which is typically faster than SHA-256 on modern 64-bit CPUs thanks to wider 64-bit arithmetic. Choose this variant when tag length isn’t constrained and you want the largest security margin (the 64-byte tag halves collision probability compared to 32-byte tags).
📏 Constants
| Name | Value | Description |
|---|---|---|
MacLen |
64 | Length of the authentication tag (64). |
KeyLen |
32 | Length of the secret key (32). |
✨ HMAC‑SHA‑512/256
The CryptoHmacSha512_256 API implements HMAC-SHA-512/256. It is not “SHA-256”; it re-uses SHA-512’s wide pipeline but truncates to 256 bits, producing a 32-byte tag with a fixed 32-byte key while retaining the performance of SHA-512. Use this variant when you need SHA-256-sized output yet can rely on SHA-512 acceleration in your environment (typically 64-bit CPUs).
📏 Constants
| Name | Value | Description |
|---|---|---|
MacLen |
32 | Length of the authentication tag (32). |
KeyLen |
32 | Length of the secret key (32). |
✨ Poly1305
The CryptoOneTimeAuth API implements Poly1305. It is an extremely fast, one-time authenticator designed by D. J. Bernstein, authenticating a message with a fixed single-use 32-byte key and producing a compact 16-byte tag.
Use cases include:
- AEAD constructions – e.g. ChaCha20‑Poly1305, XChaCha20‑Poly1305, XSalsa20‑Poly1305 (SecretBox)
- Authenticating short messages when you can derive a fresh key per message
- Implementing high‑speed, one‑shot integrity checks inside protocols
⚠️ Key reuse breaks security. Each key must be used to authenticate one message only. Derive a new random key (or sub‑key) per message.
📏 Constants
| Name | Value | Description |
|---|---|---|
MacLen |
16 | Length of the authentication tag (16). |
KeyLen |
32 | Length of the secret key (32). |
🗝️ Key Management
You can pass keys to MAC methods using:
SecureMemory<byte>– most secure, with memory protection and automatic zeroing.Span<byte>/ReadOnlySpan<byte>– fast and stack-allocated, but not supported in asynchronous methods.byte[]– works with both synchronous and asynchronous APIs.Memory<byte>/ReadOnlyMemory<byte>— used in async APIs. Can be converted to span if needed.
📝 Use
Span<T>when performance is critical, andSecureMemory<byte>when security is paramount.
Examples:
// SecureMemory<byte>
using var key = new SecureMemory<byte>(CryptoHmacSha256.KeyLen);
CryptoHmacSha256.GenerateKey(key);
key.ProtectReadOnly();
// Span<byte>
Span<byte> key = stackalloc byte[CryptoHmacSha256.KeyLen];
CryptoHmacSha256.GenerateKey(key);
// byte[]
var key = new byte[CryptoHmacSha256.KeyLen];
CryptoHmacSha256.GenerateKey(key);
Tips:
- Generate a fresh random key with
GenerateKeyand store it securely (e.g., Azure Key Vault, environment variable, HSM). - Never reuse keys across different algorithms or protocols. Even if the key size is compatible, doing so weakens isolation and security boundaries.
- Rotate keys periodically.
- Don't reuse keys in
CryptoOneTimeAuth.
đź“‹ Usage Example
using System.Diagnostics;
// Async overloads accept Memory<byte>/ReadOnlyMemory<byte>, not Span<byte>.
// We use byte[] because it implicitly converts to both Memory<byte> and Span<byte>.
byte[] mac = new byte[CryptoHmacSha256.MacLen];
// UTF‑8 string literal (.NET 8+)
ReadOnlySpan<byte> message = "hello"u8;
// Calculate MAC
CryptoHmacSha256.ComputeMac(key, message, mac);
// Verify MAC
bool ok = CryptoHmacSha256.VerifyMac(key, message, mac);
Debug.Assert(ok);
// Stream example (sync)
using var stream = File.OpenRead("file.bin");
CryptoHmacSha256.ComputeMac(key, stream, mac);
stream.Position = 0; // rewind
bool okStream = CryptoHmacSha256.VerifyMac(key, stream, mac);
Debug.Assert(okStream);
// Stream example (async)
stream.Position = 0;
await CryptoHmacSha256.ComputeMacAsync(key, stream, mac);
stream.Position = 0;
bool okStreamAsync = await CryptoHmacSha256.VerifyMacAsync(key, stream, mac);
Debug.Assert(okStreamAsync);
ℹ️ Same API across algorithms – swap the class name to change the MAC primitive.
✨ Incremental MAC
All MAC algorithms in LibSodium.Net support incremental MAC construction, allowing you to compute a tag over a sequence of message parts (e.g., MAC(key, a || b || c)) without allocating or copying them into a single buffer.
This is useful when authenticating structured messages, layered protocols, or data assembled in multiple stages.
The following APIs expose incremental MAC support via the ICryptoIncrementalHash interface:
CryptoHmacSha256CryptoHmacSha512CryptoHmacSha512_256CryptoOneTimeAuth
Each class provides CreateIncrementalMac(ReadOnlySpan<byte> key) and CreateIncrementalMac(SecureMemory<byte> key) methods.
Compute MAC over multiple parts:
Span<byte> mac = stackalloc byte[CryptoHmacSha512.MacLen];
ReadOnlySpan<byte> part1 = "hello "u8;
ReadOnlySpan<byte> part2 = "world"u8;
using var incMac = CryptoHmacSha512.CreateIncrementalMac(key);
incMac.Update(part1);
incMac.Update(part2);
incMac.Final(mac);
Poly1305 incremental MAC:
Span<byte> mac = stackalloc byte[CryptoOneTimeAuth.MacLen];
ReadOnlySpan<byte> part1 = "hello "u8;
ReadOnlySpan<byte> part2 = "world"u8;
using var incMac = CryptoOneTimeAuth.CreateIncrementalMac(key);
incMac.Update(part1);
incMac.Update(part2);
incMac.Final(mac);
⚠️ The
Final()method may only be called once. If you need to compute another MAC, create a new incremental instance withCreateIncrementalMac().
đź§ Choosing the Right MAC
| Scenario | Recommendation |
|---|---|
| Interop with JWT HS256 / Web APIs | CryptoHmacSha256 |
| Internal service‑to‑service messages on x64 servers | CryptoHmacSha512 |
| Need 32‑byte tag but favour SHA‑512 performance | CryptoHmacSha512_256 |
| Authenticating AEAD ciphertext / per‑packet tag with derived sub‑key | CryptoOneTimeAuth |
| Very short messages (< 64 B) & performance critical, one‑time setting | CryptoOneTimeAuth |
| Long‑term shared key, multi‑message scenario | Any HMAC variant (avoid Poly1305 key reuse) |
⚠️ Error Handling
| Condition | Exception |
|---|---|
| Key or MAC buffer wrong length | ArgumentException |
| Stream read fails / internal RC | LibSodiumException |
📝 Notes
- All APIs are deterministic: same key + message ⇒ same MAC.
- Verification is constant‑time to avoid timing attacks.
- The secret key length is fixed: 32 bytes.