Repository: chipsalliance/caliptra-sw
Pull request #372 (2023-06-09 00:23:36)

Implement a KDF in counter mode, compliant with SP 800-108.
Also provide a vector generator.


Patch:
diff --git a/Cargo.lock b/Cargo.lock
index 761044fd..bcdb1e9e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1724,6 +1724,13 @@ version = "0.2.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
 
+[[package]]
+name = "vector_gen"
+version = "0.1.0"
+dependencies = [
+ "openssl",
+]
+
 [[package]]
 name = "version_check"
 version = "0.9.4"
diff --git a/Cargo.toml b/Cargo.toml
index 2256c3fa..0ce8694e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -20,6 +20,7 @@ members = [
   "cpu",
   "drivers",
   "drivers/test-fw",
+  "drivers/test-fw/scripts/vector_gen",
   "fmc",
   "fmc/test-fw/test-rt",
   "hw-latest/verilated",
diff --git a/drivers/src/hmac384_kdf.rs b/drivers/src/hmac384_kdf.rs
new file mode 100644
index 00000000..b41380d9
--- /dev/null
+++ b/drivers/src/hmac384_kdf.rs
@@ -0,0 +1,51 @@
+/*++
+
+Licensed under the Apache-2.0 license.
+
+File Name:
+
+    hmac384_kdf.rs
+
+Abstract:
+
+    A KDF implementation that is compliant with SP 800-108.
+
+--*/
+
+use caliptra_error::CaliptraResult;
+
+use crate::{Hmac384, Hmac384Key, Hmac384Tag};
+
+/// Calculate HMAC-384-KDF
+///
+/// If the output is a KV slot, the slot is marked as a valid HMAC key /
+/// ECC keygen seed.
+///
+/// # Arguments
+///
+/// * `hmac` - HMAC384 context
+/// * `key` - HMAC384 key
+/// * `label` - Label for the KDF. If `context` is omitted, this is considered
+///             the fixed input data.
+/// * `context` - Context for KDF. If present, a NULL byte is included between
+///               the label and context.
+/// * `output` - Location to store the output
+pub fn hmac384_kdf(
+    hmac: &mut Hmac384,
+    key: Hmac384Key,
+    label: &[u8],
+    context: Option<&[u8]>,
+    output: Hmac384Tag,
+) -> CaliptraResult<()> {
+    let mut hmac_op = hmac.hmac_init(key, output)?;
+
+    hmac_op.update(&1_u32.to_be_bytes())?;
+    hmac_op.update(label)?;
+
+    if let Some(context) = context {
+        hmac_op.update(&[0x00])?;
+        hmac_op.update(context)?;
+    }
+
+    hmac_op.finalize()
+}
diff --git a/drivers/src/lib.rs b/drivers/src/lib.rs
index a8c9468d..4d269c5a 100644
--- a/drivers/src/lib.rs
+++ b/drivers/src/lib.rs
@@ -26,6 +26,7 @@ mod error_reporter;
 mod exit_ctrl;
 mod fuse_bank;
 mod hmac384;
+mod hmac384_kdf;
 mod key_vault;
 mod kv_access;
 mod lms;
@@ -59,6 +60,7 @@ pub use error_reporter::{
 pub use exit_ctrl::ExitCtrl;
 pub use fuse_bank::{FuseBank, IdevidCertAttr, VendorPubKeyRevocation, X509KeyIdAlgo};
 pub use hmac384::{Hmac384, Hmac384Data, Hmac384Key, Hmac384Op, Hmac384Tag};
+pub use hmac384_kdf::hmac384_kdf;
 pub use key_vault::{KeyId, KeyUsage, KeyVault};
 pub use kv_access::{KeyReadArgs, KeyWriteArgs};
 pub use lms::{
diff --git a/kat/src/hmac384_kat.rs b/kat/src/hmac384_kat.rs
index 8fb94bad..4b526091 100644
--- a/kat/src/hmac384_kat.rs
+++ b/kat/src/hmac384_kat.rs
@@ -12,17 +12,25 @@ Abstract:
 
 --*/
 
-use caliptra_drivers::{Array4x12, CaliptraError, CaliptraResult, Hmac384};
+use caliptra_drivers::{hmac384_kdf, Array4x12, CaliptraError, CaliptraResult, Hmac384};
 
 const KEY: Array4x12 = Array4x12::new([
-    0xb0b0b0b, 0xb0b0b0b, 0xb0b0b0b, 0xb0b0b0b, 0xb0b0b0b, 0xb0b0b0b, 0xb0b0b0b, 0xb0b0b0b,
-    0xb0b0b0b, 0xb0b0b0b, 0xb0b0b0b, 0xb0b0b0b,
+    0xb57dc523, 0x54afee11, 0xedb4c905, 0x2a528344, 0x348b2c6b, 0x6c39f321, 0x33ed3bb7, 0x2035a4ab,
+    0x55d6648c, 0x1529ef7a, 0x9170fec9, 0xef26a81e,
 ]);
 
-const EXPECTED_TAG: Array4x12 = Array4x12::new([
-    0xb93a3e87, 0xa1bc85c8, 0x7b54f81d, 0xabb499a5, 0xe1a66254, 0x9198594c, 0x9088733c, 0x8edd0068,
-    0x83e4d461, 0x823e6259, 0x8b07a904, 0x28f9add9,
-]);
+const LABEL: [u8; 60] = [
+    0x17, 0xe6, 0x41, 0x90, 0x9d, 0xed, 0xfe, 0xe4, 0x96, 0x8b, 0xb9, 0x5d, 0x7f, 0x77, 0x0e, 0x45,
+    0x57, 0xca, 0x34, 0x7a, 0x46, 0x61, 0x4c, 0xb3, 0x71, 0x42, 0x3f, 0x0d, 0x91, 0xdf, 0x3b, 0x58,
+    0xb5, 0x36, 0xed, 0x54, 0x53, 0x1f, 0xd2, 0xa2, 0xeb, 0x0b, 0x8b, 0x2a, 0x16, 0x34, 0xc2, 0x3c,
+    0x88, 0xfa, 0xd9, 0x70, 0x6c, 0x45, 0xdb, 0x44, 0x11, 0xa2, 0x3b, 0x89,
+];
+
+const EXPECTED_OUT: [u8; 40] = [
+    0x59, 0x49, 0xac, 0xf9, 0x63, 0x5a, 0x77, 0x29, 0x79, 0x28, 0xc1, 0xe1, 0x55, 0xd4, 0x3a, 0x4e,
+    0x4b, 0xca, 0x61, 0xb1, 0x36, 0x9a, 0x5e, 0xf5, 0x05, 0x30, 0x88, 0x85, 0x50, 0xba, 0x27, 0x0e,
+    0x26, 0xbe, 0x4a, 0x42, 0x1c, 0xdf, 0x80, 0xb7,
+];
 
 #[derive(Default, Debug)]
 pub struct Hmac384Kat {}
@@ -31,7 +39,7 @@ impl Hmac384Kat {
     /// This function executes the Known Answer Tests (aka KAT) for HMAC384.
     ///
     /// Test vector source:
-    /// Generated using MbedTLS library.
+    /// https://csrc.nist.gov/Projects/Cryptographic-Algorithm-Validation-Program/Key-Derivation
     ///
     /// # Arguments
     ///
@@ -41,11 +49,11 @@ impl Hmac384Kat {
     ///
     /// * `CaliptraResult` - Result denoting the KAT outcome.
     pub fn execute(&self, hmac: &mut Hmac384) -> CaliptraResult<()> {
-        self.kat_no_data(hmac)?;
+        self.kat_nist_vector(hmac)?;
         Ok(())
     }
 
-    /// Performs tag generation of a zero size buffer.
+    /// Performs KDF generation with a single fixed input data buffer.
     ///
     /// # Arguments
     ///
@@ -54,14 +62,13 @@ impl Hmac384Kat {
     /// # Returns
     ///
     /// * `CaliptraResult` - Result denoting the KAT outcome.
-    fn kat_no_data(&self, hmac: &mut Hmac384) -> CaliptraResult<()> {
-        let data = &[];
-        let mut tag = Array4x12::default();
+    fn kat_nist_vector(&self, hmac: &mut Hmac384) -> CaliptraResult<()> {
+        let mut out = Array4x12::default();
 
-        hmac.hmac((&KEY).into(), data.into(), (&mut tag).into())
+        hmac384_kdf(hmac, (&KEY).into(), &LABEL, None, (&mut out).into())
             .map_err(|_| CaliptraError::ROM_KAT_HMAC384_FAILURE)?;
 
-        if tag != EXPECTED_TAG {
+        if EXPECTED_OUT != <[u8; 48]>::from(out)[..EXPECTED_OUT.len()] {
             Err(CaliptraError::ROM_KAT_HMAC384_TAG_MISMATCH)?;
         }
 


Test patch:
diff --git a/drivers/test-fw/scripts/vector_gen/Cargo.toml b/drivers/test-fw/scripts/vector_gen/Cargo.toml
new file mode 100644
index 00000000..fda39c2a
--- /dev/null
+++ b/drivers/test-fw/scripts/vector_gen/Cargo.toml
@@ -0,0 +1,10 @@
+# Licensed under the Apache-2.0 license
+
+
+[package]
+name = "vector_gen"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+openssl = "0.10.48"
\ No newline at end of file
diff --git a/drivers/test-fw/scripts/vector_gen/src/hmac384_kdf_vector_gen.rs b/drivers/test-fw/scripts/vector_gen/src/hmac384_kdf_vector_gen.rs
new file mode 100644
index 00000000..b98e8bce
--- /dev/null
+++ b/drivers/test-fw/scripts/vector_gen/src/hmac384_kdf_vector_gen.rs
@@ -0,0 +1,92 @@
+/*++
+
+Licensed under the Apache-2.0 license.
+
+File Name:
+
+    hmac384_kdf_vector_gen.rs
+
+Abstract:
+
+    A vector generator for an SP 800-108-compliant KDF.
+
+--*/
+
+use openssl::{hash::MessageDigest, pkey::PKey, sign::Signer};
+
+pub struct Hmac384KdfVector {
+    pub key_0: [u8; 48],
+    pub msg_0: [u8; 48],
+    pub kdf_key: [u8; 48], // HMAC(key=key_0, msg=msg_0)
+    pub label: Vec<u8>,
+    pub context: Vec<u8>,
+    pub kdf_out: [u8; 48], // KDF(key=kdf_key, label=label, context=context)
+    pub msg_1: [u8; 48],
+    pub out: [u8; 48], // HMAC(key=kdf_out, msg=msg_1)
+}
+
+impl Default for Hmac384KdfVector {
+    fn default() -> Hmac384KdfVector {
+        Hmac384KdfVector {
+            key_0: [0; 48],
+            msg_0: [0; 48],
+            kdf_key: [0; 48],
+            label: Vec::<u8>::default(),
+            context: Vec::<u8>::default(),
+            kdf_out: [0; 48],
+            msg_1: [0; 48],
+            out: [0; 48],
+        }
+    }
+}
+
+fn rand_bytes(buf: &mut [u8]) {
+    openssl::rand::rand_bytes(buf).unwrap()
+}
+
+fn hmac(key: &[u8], msg: &[u8], tag: &mut [u8]) {
+    let pkey = PKey::hmac(key).unwrap();
+    let mut signer = Signer::new(MessageDigest::sha384(), &pkey).unwrap();
+    signer.update(msg).unwrap();
+    signer.sign(tag).unwrap();
+}
+
+fn kdf(key: &[u8], label: &[u8], context: Option<&[u8]>, output: &mut [u8; 48]) {
+    let ctr_be = 1_u32.to_be_bytes();
+
+    let mut msg = Vec::<u8>::default();
+    msg.extend_from_slice(&ctr_be);
+    msg.extend_from_slice(label);
+
+    if let Some(context) = context {
+        msg.push(0x00);
+        msg.extend_from_slice(context);
+    }
+
+    hmac(key, &msg, output);
+}
+
+pub fn gen_vector(label_len: usize, context_len: usize) -> Hmac384KdfVector {
+    let mut vec = Hmac384KdfVector::default();
+
+    vec.label.resize(label_len, 0);
+
+    rand_bytes(&mut vec.key_0);
+    rand_bytes(&mut vec.msg_0);
+    rand_bytes(&mut vec.label[..]);
+    rand_bytes(&mut vec.msg_1);
+
+    let context = if context_len == 0 {
+        None
+    } else {
+        vec.context.resize(context_len, 0);
+        rand_bytes(&mut vec.context[..]);
+        Some(&vec.context[..])
+    };
+
+    hmac(&vec.key_0, &vec.msg_0, &mut vec.kdf_key);
+    kdf(&vec.kdf_key, &vec.label[..], context, &mut vec.kdf_out);
+    hmac(&vec.kdf_out, &vec.msg_1, &mut vec.out);
+
+    vec
+}
diff --git a/drivers/test-fw/scripts/vector_gen/src/main.rs b/drivers/test-fw/scripts/vector_gen/src/main.rs
new file mode 100644
index 00000000..46152aa9
--- /dev/null
+++ b/drivers/test-fw/scripts/vector_gen/src/main.rs
@@ -0,0 +1,51 @@
+/*++
+
+Licensed under the Apache-2.0 license.
+
+File Name:
+
+    main.rs
+
+Abstract:
+
+    Driver for test vector generators.
+
+--*/
+
+use crate::hmac384_kdf_vector_gen::{gen_vector, Hmac384KdfVector};
+
+mod hmac384_kdf_vector_gen;
+
+fn hex_arr(bytes: &[u8]) -> String {
+    format!(
+        "[{}]",
+        bytes
+            .iter()
+            .map(|b| format!("0x{:02x}", b))
+            .collect::<Vec<String>>()
+            .join(", ")
+    )
+}
+
+fn main() {
+    let vec_c48: Hmac384KdfVector = gen_vector(4, 48);
+
+    println!("hmac384_kdf context_len=48");
+    println!("  let key_0 = {};", hex_arr(&vec_c48.key_0));
+    println!("  let msg_0 = {};", hex_arr(&vec_c48.msg_0));
+    println!("  let label = {};", hex_arr(&vec_c48.label));
+    println!("  let context = {};", hex_arr(&vec_c48.context));
+    println!("  let msg_1 = {};", hex_arr(&vec_c48.msg_1));
+    println!("  let out = {};", hex_arr(&vec_c48.out));
+    println!();
+
+    let vec_c0: Hmac384KdfVector = gen_vector(4, 0);
+
+    println!("hmac384_kdf context_len=0");
+    println!("  let key_0 = {};", hex_arr(&vec_c0.key_0));
+    println!("  let msg_0 = {};", hex_arr(&vec_c0.msg_0));
+    println!("  let label = {};", hex_arr(&vec_c0.label));
+    println!("  let msg_1 = {};", hex_arr(&vec_c0.msg_1));
+    println!("  let out = {};", hex_arr(&vec_c0.out));
+    println!();
+}
diff --git a/drivers/test-fw/src/bin/hmac384_tests.rs b/drivers/test-fw/src/bin/hmac384_tests.rs
index a2c272d0..c314c328 100755
--- a/drivers/test-fw/src/bin/hmac384_tests.rs
+++ b/drivers/test-fw/src/bin/hmac384_tests.rs
@@ -16,8 +16,8 @@ Abstract:
 #![no_main]
 
 use caliptra_drivers::{
-    Array4x12, Array4xN, Ecc384, Ecc384PrivKeyOut, Ecc384Scalar, Ecc384Seed, Hmac384, KeyId,
-    KeyReadArgs, KeyUsage, KeyWriteArgs,
+    hmac384_kdf, Array4x12, Array4xN, Ecc384, Ecc384PrivKeyOut, Ecc384Scalar, Ecc384Seed, Hmac384,
+    KeyId, KeyReadArgs, KeyUsage, KeyWriteArgs,
 };
 use caliptra_kat::Hmac384Kat;
 use caliptra_registers::ecc::EccReg;
@@ -308,6 +308,161 @@ fn test_hmac6() {
     assert_eq!(hmac_step_1, hmac_step_2);
 }
 
+fn test_kdf(
+    key_0: &[u8; 48],
+    msg_0: &[u8],
+    label: &[u8],
+    context: Option<&[u8]>,
+    msg_1: &[u8],
+    out_key_id: KeyId,
+    out_value: &[u8; 48],
+) {
+    let mut hmac384 = unsafe { Hmac384::new(HmacReg::new()) };
+
+    let key_0 = Array4x12::from(key_0);
+    let kdf_key_out = KeyWriteArgs::new(
+        KeyId::KeyId0,
+        KeyUsage::default().set_hmac_key_en().set_hmac_data_en(),
+    );
+    let kdf_key_in = KeyReadArgs::new(KeyId::KeyId0);
+
+    hmac384
+        .hmac((&key_0).into(), msg_0.into(), kdf_key_out.into())
+        .unwrap();
+
+    let kdf_out = KeyWriteArgs::new(out_key_id, KeyUsage::default().set_hmac_key_en());
+
+    hmac384_kdf(
+        &mut hmac384,
+        kdf_key_in.into(),
+        label,
+        context,
+        kdf_out.into(),
+    )
+    .unwrap();
+
+    let mut out_tag = Array4x12::default();
+    let hmac_key = KeyReadArgs::new(out_key_id);
+    hmac384
+        .hmac(hmac_key.into(), msg_1.into(), (&mut out_tag).into())
+        .unwrap();
+    assert_eq!(out_tag, Array4x12::from(out_value));
+}
+
+fn test_kdf0() {
+    let key_0 = [
+        0xe3, 0xb7, 0x20, 0xa1, 0x89, 0xb0, 0x5e, 0xd3, 0x07, 0x42, 0x68, 0x97, 0xb1, 0x16, 0x20,
+        0xd5, 0x8c, 0xee, 0xe5, 0x04, 0xb9, 0x52, 0xfc, 0xba, 0xb6, 0xe1, 0x93, 0x8a, 0xfe, 0x96,
+        0xb7, 0xba, 0x4d, 0x39, 0xb3, 0x38, 0x92, 0x2a, 0x5c, 0xcc, 0x7d, 0x5c, 0x6b, 0x37, 0x61,
+        0xfb, 0x54, 0xd5,
+    ];
+    let msg_0 = [
+        0x20, 0x04, 0x89, 0x82, 0x9b, 0x5b, 0x31, 0xfa, 0x3a, 0x4f, 0x6c, 0x02, 0x45, 0xe4, 0xc8,
+        0x76, 0xbb, 0x91, 0x8e, 0x7b, 0xae, 0xbb, 0x72, 0xd2, 0x4c, 0x0c, 0x10, 0x2f, 0x16, 0xaf,
+        0xe9, 0xea, 0x8c, 0x45, 0xfb, 0xb2, 0x2c, 0x4b, 0x1f, 0x8c, 0xde, 0x5b, 0x38, 0x00, 0xf5,
+        0x7b, 0x0b, 0x77,
+    ];
+    let label = [0x6c, 0x99, 0x02, 0x0e];
+    let context = [
+        0x24, 0xc4, 0x28, 0xc1, 0x34, 0x33, 0xb9, 0xf5, 0xeb, 0x02, 0xdb, 0x67, 0xa8, 0x0d, 0xba,
+        0x08, 0x80, 0x8b, 0xdb, 0xc5, 0xca, 0xe6, 0xb1, 0x16, 0x47, 0xef, 0xcd, 0x6e, 0x35, 0xb8,
+        0x38, 0x69, 0xb9, 0x01, 0x4a, 0x2a, 0xd8, 0x9b, 0xfc, 0xbf, 0x1c, 0x72, 0x9a, 0x11, 0xf3,
+        0x62, 0x7e, 0x90,
+    ];
+    let msg_1 = [
+        0xa9, 0x8c, 0xb4, 0xf6, 0x37, 0x61, 0x21, 0xd4, 0xf0, 0x06, 0xb1, 0x9e, 0x11, 0x9d, 0x2d,
+        0xe4, 0xe6, 0x96, 0x92, 0x90, 0xae, 0x2a, 0x5d, 0xad, 0x16, 0x8a, 0xf2, 0x96, 0xca, 0x7d,
+        0x94, 0x54, 0x34, 0x23, 0x46, 0xee, 0xd3, 0x10, 0x3c, 0x37, 0xec, 0xd3, 0x28, 0xcc, 0x6f,
+        0x6d, 0xeb, 0x0b,
+    ];
+    let out = [
+        0x4a, 0x98, 0x72, 0xb3, 0xe7, 0xb2, 0xec, 0xc0, 0xb5, 0x04, 0xdc, 0xe7, 0xaa, 0xbe, 0xc1,
+        0x94, 0xa9, 0x0b, 0xfa, 0x2c, 0x89, 0xcf, 0x6b, 0x66, 0x05, 0x18, 0x5e, 0x9a, 0xce, 0x83,
+        0x95, 0xf5, 0xdf, 0xf2, 0x24, 0x96, 0x77, 0xf2, 0xb2, 0x04, 0xa4, 0x15, 0xa9, 0xbf, 0x58,
+        0xb4, 0x79, 0x13,
+    ];
+
+    let out_key_id = KeyId::KeyId1;
+
+    test_kdf(
+        &key_0,
+        &msg_0,
+        &label,
+        Some(&context),
+        &msg_1,
+        out_key_id,
+        &out,
+    );
+}
+
+fn test_kdf1() {
+    let key_0 = [
+        0x14, 0xa5, 0xca, 0x00, 0x08, 0x8a, 0x6f, 0x52, 0xdc, 0x72, 0x60, 0xda, 0x6c, 0x97, 0xde,
+        0x83, 0x3b, 0x48, 0x34, 0xf8, 0x4f, 0xcc, 0x08, 0x78, 0x96, 0xd8, 0x62, 0xb7, 0xe4, 0xbd,
+        0xd6, 0x0e, 0xdb, 0x71, 0xea, 0xee, 0x6e, 0xf1, 0xf3, 0x2a, 0x36, 0x16, 0xf7, 0x07, 0x7f,
+        0xf8, 0x52, 0x85,
+    ];
+    let msg_0 = [
+        0x36, 0x75, 0x6e, 0xed, 0xa4, 0x06, 0x25, 0x18, 0xe2, 0x45, 0x4f, 0x7a, 0xc1, 0x15, 0xc7,
+        0x68, 0x12, 0x62, 0xa6, 0x15, 0xfe, 0x64, 0xc7, 0x4d, 0x69, 0x63, 0x5c, 0x69, 0xd0, 0x1c,
+        0xb6, 0x12, 0x16, 0x19, 0xf1, 0xe2, 0x9e, 0x3f, 0x2c, 0xa0, 0xd6, 0xc1, 0x47, 0x18, 0xcd,
+        0xf9, 0x79, 0x1e,
+    ];
+    let label = [0x9f, 0xf2, 0x5b, 0x47];
+    let msg_1 = [
+        0x6c, 0x98, 0x13, 0xcb, 0x0e, 0xaa, 0x04, 0x8f, 0xb7, 0x87, 0xfb, 0xf1, 0xe9, 0x4d, 0x27,
+        0xf8, 0x87, 0xbc, 0x38, 0x4a, 0x8a, 0xca, 0x26, 0xfc, 0x9c, 0xeb, 0xe9, 0xd5, 0x87, 0xb2,
+        0x4c, 0xfe, 0x5c, 0xf6, 0xbc, 0xad, 0xd4, 0x90, 0x3e, 0xa7, 0x2c, 0x96, 0x06, 0x4f, 0x6b,
+        0x37, 0xf8, 0xfc,
+    ];
+    let out = [
+        0xb8, 0x93, 0x71, 0x07, 0xd9, 0x25, 0xef, 0x6e, 0x6e, 0x01, 0xa9, 0x66, 0x80, 0x73, 0x2b,
+        0xcf, 0xa4, 0x9a, 0xa1, 0xce, 0xa1, 0x3e, 0x6e, 0x98, 0xfa, 0x8d, 0x25, 0xb7, 0xb3, 0xe6,
+        0x6c, 0x1a, 0x42, 0x54, 0x3a, 0x16, 0x26, 0xce, 0x29, 0x5c, 0x39, 0x68, 0x17, 0x8f, 0xbd,
+        0xb5, 0x27, 0x79,
+    ];
+
+    let out_key_id = KeyId::KeyId1;
+
+    test_kdf(&key_0, &msg_0, &label, None, &msg_1, out_key_id, &out);
+}
+
+// Test using a NIST vector.
+fn test_kdf2() {
+    let mut hmac384 = unsafe { Hmac384::new(HmacReg::new()) };
+
+    let key = [
+        0xb5, 0x7d, 0xc5, 0x23, 0x54, 0xaf, 0xee, 0x11, 0xed, 0xb4, 0xc9, 0x05, 0x2a, 0x52, 0x83,
+        0x44, 0x34, 0x8b, 0x2c, 0x6b, 0x6c, 0x39, 0xf3, 0x21, 0x33, 0xed, 0x3b, 0xb7, 0x20, 0x35,
+        0xa4, 0xab, 0x55, 0xd6, 0x64, 0x8c, 0x15, 0x29, 0xef, 0x7a, 0x91, 0x70, 0xfe, 0xc9, 0xef,
+        0x26, 0xa8, 0x1e,
+    ];
+    let label = [
+        0x17, 0xe6, 0x41, 0x90, 0x9d, 0xed, 0xfe, 0xe4, 0x96, 0x8b, 0xb9, 0x5d, 0x7f, 0x77, 0x0e,
+        0x45, 0x57, 0xca, 0x34, 0x7a, 0x46, 0x61, 0x4c, 0xb3, 0x71, 0x42, 0x3f, 0x0d, 0x91, 0xdf,
+        0x3b, 0x58, 0xb5, 0x36, 0xed, 0x54, 0x53, 0x1f, 0xd2, 0xa2, 0xeb, 0x0b, 0x8b, 0x2a, 0x16,
+        0x34, 0xc2, 0x3c, 0x88, 0xfa, 0xd9, 0x70, 0x6c, 0x45, 0xdb, 0x44, 0x11, 0xa2, 0x3b, 0x89,
+    ];
+    let out = [
+        0x59, 0x49, 0xac, 0xf9, 0x63, 0x5a, 0x77, 0x29, 0x79, 0x28, 0xc1, 0xe1, 0x55, 0xd4, 0x3a,
+        0x4e, 0x4b, 0xca, 0x61, 0xb1, 0x36, 0x9a, 0x5e, 0xf5, 0x05, 0x30, 0x88, 0x85, 0x50, 0xba,
+        0x27, 0x0e, 0x26, 0xbe, 0x4a, 0x42, 0x1c, 0xdf, 0x80, 0xb7,
+    ];
+
+    let mut out_buf = Array4x12::default();
+
+    hmac384_kdf(
+        &mut hmac384,
+        (&Array4x12::from(&key)).into(),
+        &label,
+        None,
+        (&mut out_buf).into(),
+    )
+    .unwrap();
+
+    assert_eq!(<[u8; 48]>::from(out_buf)[..out.len()], out);
+}
+
 fn test_hmac_multi_block() {
     let mut hmac384 = unsafe { Hmac384::new(HmacReg::new()) };
     let key: [u8; 48] = [
@@ -437,6 +592,9 @@ test_suite! {
     test_hmac4,
     test_hmac5,
     test_hmac6,
+    test_kdf0,
+    test_kdf1,
+    test_kdf2,
     test_hmac_multi_block,
     test_hmac_exact_single_block,
     test_hmac_multi_block_two_step,