Skip to main content

Crate bouncycastle_hkdf

Crate bouncycastle_hkdf 

Source
Expand description

HMAC-based Extract-and-Expand Key Derivation Function (HKDF) as per RFC5859, as allowed by NIST SP 800-56Cr2.

§Usage

Since HKDF uses HMAC<HASH> as its underlying primitive, most of what is said in the HMAC crate docs about instantiating HMAC objects applies here as well. Unlike HMAC, an HKDF object is created without an initial key, and will self-initialize the internal HMAC object as part of the HKDF::extract phase.

§Examples

§Constructing an object

HMAC objects can be constructed with any underlying hash function that implements Hash. Type aliases are provided for the common HKDF-HASH algorithms.

The following object instantiations are equivalent:

use bouncycastle_hkdf::HKDF_SHA256;

let hkdf = HKDF_SHA256::new();

and

use bouncycastle_hkdf::HKDF;
use bouncycastle_sha2::SHA256;

let hkdf = HKDF::<SHA256>::new();

§Deriving a key via the KDF trait

Being a Key Derivation Function (KDF), the objective of HKDF is to take input key material which is not directly usable for its intended purpose and transform into a suitable output key. Typically, this takes one or both of the following forms:

  • Starting with a seed and mixing in additional input to diversify the output key (ie make it unique). An example of this would be starting with a secret seed and mixing in a public ID or URL to generate keys which are unique per URL.
  • Starting with a full-entropy seed which is at the correct security level for the application, but which is not long enough. An example could be starting with a 128-bit seed and mixing it with the strings “read” and “write” to produce one AES-128 key for each of the two directions of a communication channel.

The simplest usage is via the one-shot functions provided by the KDF trait.

use bouncycastle_core_interface::key_material::{KeyMaterial256, KeyType};
use bouncycastle_core_interface::traits::{KDF };
use bouncycastle_hkdf::HKDF_SHA256;

let key = KeyMaterial256::from_bytes_as_type(
            b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
            KeyType::Seed).unwrap();

let hkdf = HKDF_SHA256::new();
let key = hkdf.derive_key(&key, b"extra input").unwrap();

KDF::derive_key will produce a key the same length as the underlying hash function. Longer output can be requested by instead using KDF::derive_key_out and providing a larger output buffer, which will be filled.

As with other uses of KeyMaterial, the KDF::derive_key function will track the entropy of the input key material, and will set the entropy of the output key material accordingly.

The KDF trait also provides the KDF::derive_key_from_multiple and KDF::derive_key_from_multiple_out functions, which allows for multiple inputs to be mixed into a single output key, and which allows for some advanced control of the underlying HKDF primitive.

§HKDF Extract-and-Expand

The HKDF algorithm defined in RFC5896 and SP 800-56Cr2 is a two-step KDF, broken into an Extract step which essentially absorbs entropy from the input key material, and an Expand step which produces the output key material of any requested size. This interface is essentially a pre-cursor to the XOF API which was introduced with SHA3; the main difference being that HKDF-Expand needs to be told up-front how much output to produce, whereas XOFs can stream output as needed.

Naturally, the full two-step HKDF-Extract and HKDF-Expand interface is provided by the HKDF struct, and exposes additional HKDF-specific parameters beyond what is exposed by the functions of the KDF trait.

The usage pattern here is flexible, but generally follows the pattern of first calling HKDF::extract with a salt and an input key material ikm, which produces a pseudorandom key prk. The prk will have a KeyType and SecurityStrength that results from combining the two provided input keys, The prk may be! used directly as a full-entropy cryptographic key.

Since the extract step may be called with any number of input keys, a streaming interface is provided whereby streaming mode in initialized with a call to HKDF::do_extract_init, and then repeated calls to HKDF::do_extract_update_key and HKDF::do_extract_update_bytes may be made. Entropy from the inputs keys provided via HKDF::do_extract_update_key are credited towards the output key, while bytes provided via HKDF::do_extract_update_bytes are not. One restriction here is that once you start provided un-credited bytes via HKDF::do_extract_update_bytes, no more calls to HKDF::do_extract_update_key may be made. The streaming API is completed with a call to either HKDF::do_extract_final or HKDF::do_extract_final_out.

The second stage, HKDF::expand_out stretches the prk into a longer output key, still of the same KeyType and SecurityStrength.

A typical flow looks like this:

use bouncycastle_core_interface::key_material::{KeyMaterial, KeyMaterial256, KeyMaterialInternal, KeyType};
use bouncycastle_core_interface::traits::KDF;
use bouncycastle_hkdf::{HKDF, HKDF_SHA256};
use bouncycastle_sha2::{SHA256};

// setup variables
let salt = KeyMaterial256::from_bytes_as_type(
            b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
            KeyType::MACKey).unwrap();

 let ikm = KeyMaterial256::from_bytes_as_type(
            b"\x0f\x0e\x0d\x0c\x0b\x0a\x09\x08\x07\x06\x05\x04\x03\x02\x01\x00",
            KeyType::MACKey).unwrap();

let info = b"some extra context info";

 // Use the streaming API to derive an output key of length 200 bytes.
 let mut okm = KeyMaterialInternal::<200>::new();
 let mut hkdf = HKDF::<SHA256>::default();
 hkdf.do_extract_init(&salt).unwrap();
 hkdf.do_extract_update_bytes(ikm.ref_to_bytes()).unwrap();
 let prk = hkdf.do_extract_final().unwrap();
 HKDF_SHA256::expand_out(&prk, info, 200, &mut okm).unwrap();

Various convenience wrapper functions are provided which can reduce the amount of boilerplate code for common cases. For example, the above code can be condensed to:

use bouncycastle_core_interface::key_material::{KeyMaterial, KeyMaterial256, KeyMaterialInternal, KeyType};
use bouncycastle_hkdf::{HKDF_SHA256};

// setup variables
let salt = KeyMaterial256::from_bytes_as_type(
            b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
            KeyType::MACKey).unwrap();

 let ikm = KeyMaterial256::from_bytes_as_type(
            b"\x0f\x0e\x0d\x0c\x0b\x0a\x09\x08\x07\x06\x05\x04\x03\x02\x01\x00",
            KeyType::MACKey).unwrap();

let info = b"some extra context info";

// Use the one-shot API to derive an output key of length 200 bytes.
let mut okm = KeyMaterialInternal::<200>::new();
let _bytes_written = HKDF_SHA256::extract_and_expand_out(&salt, &ikm, info, 200, &mut okm).unwrap();

Structs§

HKDF

Constants§

HKDF_SHA256_NAME
HKDF_SHA512_NAME

Type Aliases§

HKDF_SHA256
HKDF_SHA512