HkdfDotNet 1.0.1

dotnet add package HkdfDotNet --version 1.0.1
NuGet\Install-Package HkdfDotNet -Version 1.0.1
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="HkdfDotNet" Version="1.0.1" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add HkdfDotNet --version 1.0.1
#r "nuget: HkdfDotNet, 1.0.1"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// 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

License: MIT CodeQL

HKDF.NET

A .NET implementation of HKDF, with support for SHA256, SHA384, and SHA512.

Installation

NuGet

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.

Manual

  1. Download the latest release.
  2. Move the downloaded .dll file into your project folder.
  3. Click on the Project tab and Add Project Reference... in Visual Studio.
  4. Go to Browse, click the Browse button, and select the downloaded .dll file.
  5. Add using HkdfDotNet; to the top of each code file that needs the library.

Usage

⚠️Warnings

  1. 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.
  2. Although the info parameter 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).
  3. The salt parameter 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 info parameter alongside other context information using concatenation. Make sure you AVOID canonicalization attacks when doing this.

Functions

HKDF consists of three different functions:

  1. 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() and Expand() functions internally.
  2. 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.
  3. 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.

Parameters

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 info parameter 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).

Example

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.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETStandard 2.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
1.0.1 328 1/18/2022