Namespace: WvdS.System.Security.Cryptography.KeyDerivation
Static class for Key Derivation Functions with Post-Quantum support. Supports HKDF, PBKDF2 and Argon2id.
Supported KDF algorithms:
| KDF | Standard | Usage |
|---|---|---|
| HKDF | RFC 5869 | Session keys from shared secrets |
| PBKDF2 | RFC 8018 | Password-based keys |
| Argon2id | RFC 9106 | Memory-hard KDF (passwords) |
Derives keys from a shared secret (HKDF-Extract-then-Expand).
// ML-KEM Shared Secret from Key Exchange byte[] sharedSecret = session.SharedSecret; // Standard derivation (SHA-256) byte[] aesKey = KeyDerivationExtensions.DeriveKey( sharedSecret, outputLength: 32); // 256 bit // With salt and context info byte[] salt = RandomNumberGenerator.GetBytes(32); byte[] info = Encoding.UTF8.GetBytes("MyApp-Session-Key"); byte[] sessionKey = KeyDerivationExtensions.DeriveKey( sharedSecret, outputLength: 32, salt: salt, info: info, hashAlgorithm: HashAlgorithmName.SHA384); // Optional
Extracts PRK (Pseudorandom Key) from Input Key Material.
byte[] inputKeyMaterial = GetSharedSecret(); byte[] salt = RandomNumberGenerator.GetBytes(32); byte[] prk = KeyDerivationExtensions.HkdfExtract( inputKeyMaterial, salt: salt, hashAlgorithm: HashAlgorithmName.SHA256); // PRK has same length as hash output (32 bytes for SHA-256)
Expands PRK to Output Key Material.
byte[] prk = GetPrk(); // Encryption Key byte[] encKey = KeyDerivationExtensions.HkdfExpand( prk, outputLength: 32, info: Encoding.UTF8.GetBytes("encryption")); // MAC Key byte[] macKey = KeyDerivationExtensions.HkdfExpand( prk, outputLength: 32, info: Encoding.UTF8.GetBytes("mac"));
Combines classical (ECDH/DH) and PQ (ML-KEM) shared secrets.
byte[] ecdhSecret = GetEcdhSharedSecret(); byte[] mlKemSecret = GetMlKemSharedSecret(); // Hybrid mode: Both secrets are combined byte[] hybridKey = KeyDerivationExtensions.DeriveHybridKey( classicSecret: ecdhSecret, pqSecret: mlKemSecret, outputLength: 32, mode: CryptoMode.Hybrid); // Classic only byte[] classicKey = KeyDerivationExtensions.DeriveHybridKey( classicSecret: ecdhSecret, pqSecret: null, outputLength: 32, mode: CryptoMode.Classic); // Post-Quantum only byte[] pqKey = KeyDerivationExtensions.DeriveHybridKey( classicSecret: null, pqSecret: mlKemSecret, outputLength: 32, mode: CryptoMode.PostQuantum); // Custom Info byte[] customKey = KeyDerivationExtensions.DeriveHybridKey( classicSecret: ecdhSecret, pqSecret: mlKemSecret, outputLength: 64, mode: CryptoMode.Hybrid, info: Encoding.UTF8.GetBytes("MyProtocol-v1"));
Key combination:
Hybrid Mode: IKM = classicSecret || pqSecret Key = HKDF-SHA256(IKM, info="WvdS-Hybrid-Key")
Derives multiple keys for different purposes.
using HybridKeyMaterial keyMaterial = KeyDerivationExtensions.DeriveHybridKeyMaterial( classicSecret: ecdhSecret, pqSecret: mlKemSecret, mode: CryptoMode.Hybrid); // Usage byte[] encKey = keyMaterial.EncryptionKey; // 32 bytes byte[] macKey = keyMaterial.MacKey; // 32 bytes byte[] iv = keyMaterial.Iv; // 16 bytes byte[] authKey = keyMaterial.AuthKey; // 32 bytes // IDisposable: Keys are securely erased
string password = "SecurePassword123!"; byte[] salt = RandomNumberGenerator.GetBytes(32); // Standard PBKDF2 byte[] key = KeyDerivationExtensions.Pbkdf2( password, salt, iterations: 100000, outputLength: 32); // With PQ entropy (additional protection) byte[] pqEntropy = GetPqEntropy(); byte[] enhancedKey = KeyDerivationExtensions.Pbkdf2( password, salt, iterations: 100000, outputLength: 32, pqEntropy: pqEntropy, // Combined with salt hashAlgorithm: HashAlgorithmName.SHA512);
PBKDF2 with PQ-enhanced salt (public key is included in salt calculation).
string password = "UserPassword"; byte[] baseSalt = RandomNumberGenerator.GetBytes(16); byte[] mlKemPublicKey = GetRecipientPublicKey(); // Salt = SHA256(baseSalt || pqPublicKey) byte[] key = KeyDerivationExtensions.Pbkdf2WithPqSalt( password, baseSalt, mlKemPublicKey, iterations: 100000, outputLength: 32);
Argon2id via OpenSSL 3.6 - resistant against GPU/ASIC attacks.
byte[] password = Encoding.UTF8.GetBytes("SecurePassword"); byte[] salt = RandomNumberGenerator.GetBytes(16); // Minimum 16 bytes byte[] key = KeyDerivationExtensions.Argon2id( password, salt, outputLength: 32, // Key length iterations: 3, // Time cost (t) memoryKiB: 65536, // Memory: 64 MB parallelism: 4); // Threads (p)
string password = "UserPassword123"; byte[] salt = RandomNumberGenerator.GetBytes(16); byte[] key = KeyDerivationExtensions.Argon2id( password, salt, outputLength: 32, iterations: 3, memoryKiB: 65536, parallelism: 4);
Recommended parameters:
| Application | Iterations (t) | Memory (m) | Parallelism (p) |
|---|---|---|---|
| Password hashing | 3 | 64 MB | 4 |
| High security | 4 | 256 MB | 4 |
| Low memory | 4 | 16 MB | 4 |
byte[] preMasterSecret = GetPreMasterSecret(); byte[] clientRandom = GetClientRandom(); byte[] serverRandom = GetServerRandom(); using TlsKeyMaterial keys = KeyDerivationExtensions.DeriveTlsKeys( preMasterSecret, clientRandom, serverRandom, mode: CryptoMode.Hybrid); // Usage var clientKey = keys.ClientWriteKey; // 32 bytes var serverKey = keys.ServerWriteKey; // 32 bytes var clientIv = keys.ClientWriteIv; // 12 bytes var serverIv = keys.ServerWriteIv; // 12 bytes
TLS 1.3 compatible key schedule.
byte[]? pskSecret = null; // Pre-Shared Key (optional) byte[] ecdhSecret = GetEcdhSecret(); byte[] pqSecret = GetMlKemSecret(); byte[] clientHello = GetClientHelloBytes(); byte[] serverHello = GetServerHelloBytes(); using Tls13KeySchedule schedule = KeyDerivationExtensions.DeriveTls13Keys( pskSecret, ecdhSecret, pqSecret, clientHello, serverHello, mode: CryptoMode.Hybrid); // Handshake Traffic Secrets var clientHsSecret = schedule.ClientHandshakeTrafficSecret; var serverHsSecret = schedule.ServerHandshakeTrafficSecret; // Application Traffic Secrets var clientAppSecret = schedule.ClientApplicationTrafficSecret; var serverAppSecret = schedule.ServerApplicationTrafficSecret; // Resumption Secret var resumptionSecret = schedule.ResumptionMasterSecret;
Container for derived keys with secure memory cleanup.
| Property | Type | Length | Description |
|---|---|---|---|
EncryptionKey | byte[] | 32 | AES key |
MacKey | byte[] | 32 | HMAC key |
Iv | byte[] | 16 | Initialization vector |
AuthKey | byte[] | 32 | Authentication key |
using HybridKeyMaterial keys = DeriveKeys(); // Keys are securely erased on Dispose() // (CryptographicOperations.ZeroMemory)
TLS 1.2 style key material.
| Property | Type | Description |
|---|---|---|
MasterSecret | byte[] | 48 bytes master secret |
ClientWriteKey | byte[] | Client-side encryption key |
ServerWriteKey | byte[] | Server-side encryption key |
ClientWriteIv | byte[] | Client-side IV |
ServerWriteIv | byte[] | Server-side IV |
ClientWriteMacKey | byte[] | Client MAC key (empty for GCM) |
ServerWriteMacKey | byte[] | Server MAC key (empty for GCM) |
TLS 1.3 key schedule.
| Property | Type | Description |
|---|---|---|
ClientHandshakeTrafficSecret | byte[]? | Client handshake traffic secret |
ServerHandshakeTrafficSecret | byte[]? | Server handshake traffic secret |
ClientApplicationTrafficSecret | byte[]? | Client application traffic secret |
ServerApplicationTrafficSecret | byte[]? | Server application traffic secret |
ResumptionMasterSecret | byte[]? | Session resumption secret |
| Method | Parameters | Return |
|---|---|---|
DeriveKey | byte[] sharedSecret, int outputLength, byte[]? salt, byte[]? info, HashAlgorithmName? | byte[] |
HkdfExtract | byte[] ikm, byte[]? salt, HashAlgorithmName? | byte[] |
HkdfExpand | byte[] prk, int outputLength, byte[]? info, HashAlgorithmName? | byte[] |
| Method | Parameters | Return |
|---|---|---|
DeriveHybridKey | byte[]? classicSecret, byte[]? pqSecret, int outputLength, CryptoMode, byte[]? info | byte[] |
DeriveHybridKeyMaterial | byte[]? classicSecret, byte[]? pqSecret, CryptoMode | HybridKeyMaterial |
| Method | Parameters | Return |
|---|---|---|
Pbkdf2 | string password, byte[] salt, int iterations, int outputLength, byte[]? pqEntropy, HashAlgorithmName? | byte[] |
Pbkdf2WithPqSalt | string password, byte[] baseSalt, byte[] pqPublicKey, int iterations, int outputLength | byte[] |
| Method | Parameters | Return |
|---|---|---|
Argon2id | byte[] password, byte[] salt, int outputLength, int iterations, int memoryKiB, int parallelism | byte[] |
Argon2id | string password, byte[] salt, int outputLength, int iterations, int memoryKiB, int parallelism | byte[] |
| Method | Parameters | Return |
|---|---|---|
DeriveTlsKeys | byte[] preMasterSecret, byte[] clientRandom, byte[] serverRandom, CryptoMode | TlsKeyMaterial |
DeriveTls13Keys | byte[]? psk, byte[]? ecdh, byte[]? pq, byte[] clientHello, byte[] serverHello, CryptoMode | Tls13KeySchedule |
using WvdS.System.Security.Cryptography; using WvdS.System.Security.Cryptography.KeyDerivation; using WvdS.System.Security.Cryptography.KeyExchange; // 1. Perform key exchange using var session = new KeyExchangeService(); await session.InitiateKeyExchangeAsync(recipientPublicKey, CryptoMode.Hybrid); // 2. Derive hybrid key material using HybridKeyMaterial keys = KeyDerivationExtensions.DeriveHybridKeyMaterial( classicSecret: session.ClassicSharedSecret, pqSecret: session.PqSharedSecret, mode: CryptoMode.Hybrid); // 3. Use the keys using var aes = Aes.Create(); aes.Key = keys.EncryptionKey; using var hmac = new HMACSHA256(keys.MacKey); // 4. Perform encryption // ... // 5. Keys are automatically securely erased
IDisposable classes implement CryptographicOperations.ZeroMemoryIn hybrid mode, the final key is only compromised if BOTH secrets (classical AND PQ) are broken. This provides protection against both classical and quantum attacks.
Wolfgang van der Stille @ EMSR DATA d.o.o. - Post-Quantum Cryptography Professional