Using HKDF
Hkdf is the HMAC-based Extract-and-Expand Key Derivation Function of RFC 5869. It turns input keying material that is merely high-entropy - a Diffie-Hellman shared secret, a KEM output, a master key - into one or more cryptographically strong, fixed-length keys, each bound to an application-specific context. Reach for it whenever you have a strong but non-uniform secret and need usable symmetric key material; for low-entropy inputs such as passwords, use a memory-hard KDF instead (Argon2 or scrypt).
Note
The platform already ships HKDF, and its surface is interchangeable with this type - prefer the BCL implementation where it covers your need. Bodu's Hkdf exists to give the rest of the library a self-contained HKDF (it backs the HPKE labeled KDF). It is not independently audited and offers best-effort, not guaranteed, side-channel resistance.
The two stages
HKDF is two steps that you can call separately or together:
| Stage | Method | Purpose |
|---|---|---|
| Extract | Extract(HashAlgorithmName, ReadOnlySpan<byte>, ReadOnlySpan<byte>) | Condense the input keying material (with an optional salt) into a pseudorandom key (PRK) of one hash length. |
| Expand | Expand(HashAlgorithmName, ReadOnlySpan<byte>, int, ReadOnlySpan<byte>) | Stretch the PRK into output keying material of any length, bound to an optional info context. |
| Both | DeriveKey(HashAlgorithmName, ReadOnlySpan<byte>, int, ReadOnlySpan<byte>, ReadOnlySpan<byte>) | Extract then Expand in one call - the common case. |
The supported hash algorithms are SHA-1, SHA-256, SHA-384, and SHA-512; any other HashAlgorithmName throws ArgumentException. The PRK is one hash length long (32 bytes for SHA-256), and the maximum output of a single Expand is 255 × HashLen bytes - requesting more throws ArgumentOutOfRangeException. SHA-256 is the right default; SHA-1 is accepted only for interoperating with legacy schemes that mandate it, not for new designs.
Pattern 1 - derive a key in one call
DeriveKey is the method you want most of the time:
using System.Security.Cryptography;
using Bodu.Security.Cryptography;
byte[] sharedSecret = GetSharedSecret(); // high-entropy, but not uniform
byte[] sessionKey = Hkdf.DeriveKey(
HashAlgorithmName.SHA256,
inputKeyingMaterial: sharedSecret,
outputLength: 32,
salt: salt, // optional, non-secret; binds the derivation
info: "myapp v1 traffic key"u8); // optional context / label
CryptographicOperations.ZeroMemory(sharedSecret); // wipe the raw secret once stretched
The salt and info matter: a salt strengthens extraction when the input is structured, and a distinct info ensures the same secret yields different keys for different purposes (a key, a nonce, a MAC key) without re-running the agreement.
Pattern 2 - extract once, expand many
When you derive several independent keys from one secret, extract a single PRK and expand it repeatedly with different info labels:
using System.Security.Cryptography;
using Bodu.Security.Cryptography;
byte[] prk = Hkdf.Extract(HashAlgorithmName.SHA256, inputKeyingMaterial: sharedSecret, salt: salt);
byte[] encryptionKey = Hkdf.Expand(HashAlgorithmName.SHA256, prk, 32, info: "encrypt"u8);
byte[] macKey = Hkdf.Expand(HashAlgorithmName.SHA256, prk, 32, info: "mac"u8);
CryptographicOperations.ZeroMemory(prk);
Pattern 3 - span destinations (allocation-free)
Every operation has an overload that writes into a caller-supplied span, so a key can be derived into a pooled or stack buffer without an intermediate array:
Span<byte> key = stackalloc byte[32];
Hkdf.DeriveKey(HashAlgorithmName.SHA256, sharedSecret, key, salt, info: "traffic"u8);
// ... use key ...
key.Clear();
What HKDF is not
- Not a password hash. HKDF is fast by design. Never feed it a password or PIN - use Argon2id or scrypt, which are deliberately slow and memory-hard.
- Not an authenticator. HKDF derives keys; it does not protect integrity. Authenticate with an AEAD or a MAC.
- Not a source of entropy. It cannot manufacture randomness - the security of its output is bounded by the entropy of the input keying material.
Where to go next
- Key agreement with X25519 - the canonical source of a shared secret to feed into HKDF.
- Hybrid public key encryption with HPKE - builds the labeled HKDF of RFC 9180 on top of this primitive.
- Using Argon2 / Using scrypt - KDFs for low-entropy (password) inputs.
- Hkdf - API reference.
- Hashing & Cryptography guides - every guide in this topic.