dotnet add package HkdfDotNet --version 1.0.1
NuGet\Install-Package HkdfDotNet -Version 1.0.1
<PackageReference Include="HkdfDotNet" Version="1.0.1" />
paket add HkdfDotNet --version 1.0.1
#r "nuget: HkdfDotNet, 1.0.1"
// Install HkdfDotNet as a Cake Addin #addin nuget:?package=HkdfDotNet&version=1.0.1 // Install HkdfDotNet as a Cake Tool #tool nuget:?package=HkdfDotNet&version=1.0.1
A .NET implementation of HKDF, with support for SHA256, SHA384, and SHA512.
You can find the NuGet package here.
The easiest way to install this is via the NuGet Package Manager in Visual Studio, as explained here. JetBrains Rider also has a package manager, and instructions can be found here.
- Download the latest release.
- Move the downloaded
.dllfile into your project folder.
- Click on the
Add Project Reference...in Visual Studio.
- Go to
Browse, click the
Browsebutton, and select the downloaded
using HkdfDotNet;to the top of each code file that needs the library.
- HKDF is NOT suitable for passwords. You MUST use a password-based KDF algorithm, such as Argon2 or scrypt, for such purposes. Both algorithms are available in the libsodium library.
- Although the
infoparameter is optional according to the specification, it SHOULD always be used to insert some randomness into HKDF and to bind derived keys to application- and context-specific information. Inadequate context information CAN lead to subtle vulnerabilities. The info SHOULD be independent of the input keying material (IKM).
saltparameter SHOULD NOT be used. You SHOULD randomly generate one 128-bit or 256-bit salt that gets used for all derived subkeys but placed into the
infoparameter alongside other context information using concatenation. Make sure you AVOID canonicalization attacks when doing this.
HKDF consists of three different functions:
DeriveKey(): if the input keying material (IKM) is not distributed uniformly (e.g. a shared secret from a key exchange) and you want to derive one or more keys using the same context information for all of those keys, then you should use this function. This calls the
Extract(): if the input keying material (IKM) is not distributed uniformly (e.g. a shared secret from a key exchange) and you want to derive one or more keys using different context information for different keys, then you should call this function once to derive a pseudorandom key. Then you can call
Expand()on the output to derive multiple subkeys, as explained below.
Expand(): if the input keying material (IKM) is distributed uniformly (e.g. the output of
Extract()or randomly generated using a cryptographically secure random number generator), then you can call this function multiple times to derive separate subkeys using different context information but the same randomly generated salt.
Here are my recommendations:
HashAlgorithmName: use SHA512 in all cases.
inputKeyingMaterial: at least 256-bits long and MUST be high-entropy (e.g. a shared secret, NOT a password).
outputLength: at least 256-bits, which is equivalent to 32 bytes. If deriving multiple keys using one call to
DeriveKey(), then use an output length as long as the required key lengths added together (e.g. a 256-bit encryption key and a 512-bit MAC key means a 96 byte output length).
salt: leave this null to get the standard security definition for HKDF. Randomly generate one 128-bit or 256-bit salt for all subkeys and concatenate it to the
infoparameter instead, as explained below.
info: one randomly generated 128-bit or 256-bit salt used for all subkeys, followed by a different hardcoded, globally unique, application-specific string for each subkey converted into a byte array using UTF8 encoding. The default context string format should be
"[application] [date and time] [purpose]". You can also include things like protocol/version numbers, algorithm identifiers, and user identities. The salt and context information should be concatenated together using Array.Copy() in a way that is resistant to canonicalization attacks (please see that link and the code example below).
Here is an example of how to derive a 256-bit key for an AEAD, followed by how to derive two separate keys for Encrypt-then-MAC, using an X25519 shared secret as the input keying material (IKM), a random 128-bit salt, and hardcoded context strings:
const int saltLength = 16; const string context = "HKDF Demo 15/11/2021 21:52 Deriving an encryption key for ChaCha20-Poly1305"; const int outputLength = 32; // The high-entropy input keying material (NOT a password) that you want to derive subkeys from byte inputKeyingMaterial = GetSharedSecret(senderPrivateKey, recipientPublicKey); // A random 128-bit or 256-bit salt to feed into info byte salt = SecureRandom.GetBytes(saltLength); // The hardcoded, globally unique, and application-specific context information byte info = Encoding.UTF8.GetBytes(context); // Convert the length of each parameter to concatenate in info into bytes byte saltLength = BitConversion.GetBytes(salt.Length); byte infoLength = BitConversion.GetBytes(info.Length); // Concatenate the salt, info, and lengths info = Arrays.Concat(salt, info, saltLength, infoLength); // Derive a single subkey (e.g. for encryption with an AEAD, like ChaCha20-Poly1305 or AES-GCM) byte subkey = Hkdf.DeriveKey(HashAlgorithmName.SHA512, inputKeyingMaterial, outputLength, info); // Or derive multiple subkeys (e.g. for Encrypt-then-MAC) byte pseudorandomKey = Hkdf.Extract(HashAlgorithmName.SHA512, inputKeyingMaterial); byte encryptionInfo = Encoding.UTF8.GetBytes("HKDF Demo 15/11/2021 21:54 ChaCha20 encryption key"); byte encryptionInfoLength = BitConversion.GetBytes(encryptionInfo.Length); encryptionInfo = Arrays.Concat(salt, encryptionInfo, saltLength, encryptionInfoLength); byte encryptionKey = Hkdf.Expand(HashAlgorithmName.SHA512, pseudorandomKey, outputLength, encryptionInfo); byte macInfo = Encoding.UTF8.GetBytes("HKDF Demo 15/11/2021 21:55 BLAKE2b MAC key"); byte macInfoLength = BitConversion.GetBytes(macInfo.Length); macInfo = Arrays.Concat(salt, macInfo, saltLength, macInfoLength); byte macKey = Hkdf.Expand(HashAlgorithmName.SHA512, pseudorandomKey, outputLength * 2, macInfo);
Note that the
Arrays.Concat() function looks like this, and the
BitConversion.GetBytes() function looks like this.
|.NET||net5.0 net5.0-windows net6.0 net6.0-android net6.0-ios net6.0-maccatalyst net6.0-macos net6.0-tvos net6.0-windows net7.0 net7.0-android net7.0-ios net7.0-maccatalyst net7.0-macos net7.0-tvos net7.0-windows|
|.NET Core||netcoreapp2.0 netcoreapp2.1 netcoreapp2.2 netcoreapp3.0 netcoreapp3.1|
|.NET Standard||netstandard2.0 netstandard2.1|
|.NET Framework||net461 net462 net463 net47 net471 net472 net48 net481|
- No dependencies.
This package is not used by any NuGet packages.
This package is not used by any popular GitHub repositories.