diff --git a/Cargo.toml b/Cargo.toml index 8a53d4a07..2bc9e45d3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,6 @@ members = [ "libcrux-intrinsics", "libcrux-kem", "libcrux-hmac", - "libcrux-hkdf", "libcrux-ecdh", "libcrux-psq", "macros", @@ -32,9 +31,11 @@ members = [ "secrets", "fstar-helpers/core-models", "test-utils", + "crates/algorithms/hkdf", "crates/primitives/aead", "crates/primitives/digest", "crates/testing/kats", + "hacl-rs", ] [workspace.package] @@ -88,7 +89,7 @@ libcrux-chacha20poly1305 = { version = "=0.0.3", path = "chacha20poly1305" } libcrux-hacl-rs = { version = "=0.0.3", path = "hacl-rs" } libcrux-hacl = { version = "=0.0.2", path = "sys/hacl" } libcrux-platform = { version = "=0.0.2", path = "sys/platform" } -libcrux-hkdf = { version = "=0.0.3", path = "libcrux-hkdf" } +libcrux-hkdf = { version = "=0.0.3", path = "crates/algorithms/hkdf" } libcrux-hmac = { version = "=0.0.3", path = "libcrux-hmac" } libcrux-sha2 = { version = "=0.0.3", path = "sha2" } libcrux-ed25519 = { version = "=0.0.3", path = "ed25519" } diff --git a/libcrux-hkdf/CHANGELOG.md b/crates/algorithms/hkdf/CHANGELOG.md similarity index 100% rename from libcrux-hkdf/CHANGELOG.md rename to crates/algorithms/hkdf/CHANGELOG.md diff --git a/libcrux-hkdf/Cargo.toml b/crates/algorithms/hkdf/Cargo.toml similarity index 52% rename from libcrux-hkdf/Cargo.toml rename to crates/algorithms/hkdf/Cargo.toml index ff72196c1..74aca25f1 100644 --- a/libcrux-hkdf/Cargo.toml +++ b/crates/algorithms/hkdf/Cargo.toml @@ -14,8 +14,11 @@ repository.workspace = true path = "src/hkdf.rs" [features] +check-secret-independence = ["libcrux-secrets/check-secret-independence"] + [dependencies] -libcrux-hmac = { version = "=0.0.3", path = "../libcrux-hmac", features = [ +libcrux-hmac = { version = "=0.0.3", path = "../../../libcrux-hmac", features = [ "expose-hacl", ] } -libcrux-hacl-rs = { version = "=0.0.3", path = "../hacl-rs/" } +libcrux-hacl-rs = { version = "=0.0.3", path = "../../../hacl-rs/" } +libcrux-secrets = { version = "0.0.3", path = "../../../secrets" } diff --git a/libcrux-hkdf/Readme.md b/crates/algorithms/hkdf/Readme.md similarity index 100% rename from libcrux-hkdf/Readme.md rename to crates/algorithms/hkdf/Readme.md diff --git a/libcrux-hkdf/src/hacl.rs b/crates/algorithms/hkdf/src/hacl.rs similarity index 100% rename from libcrux-hkdf/src/hacl.rs rename to crates/algorithms/hkdf/src/hacl.rs diff --git a/crates/algorithms/hkdf/src/hkdf.rs b/crates/algorithms/hkdf/src/hkdf.rs new file mode 100644 index 000000000..7b000123e --- /dev/null +++ b/crates/algorithms/hkdf/src/hkdf.rs @@ -0,0 +1,539 @@ +//! # HKDF +//! +//! This crate implements HKDF ([RFC 5869](https://tools.ietf.org/html/rfc5869)) on SHA2-256, SHA2-384, and SHA2-512. +//! The implementation is based on code extracted from verified crypto code from the [HACL* project](https://hacl-star.github.io). +//! +//! ## Examples +//! +//! ### Using the typed SHA2-256 API +//! +//! ``` +//! use libcrux_hkdf::{Hkdf, Sha2_256}; +//! use libcrux_secrets::{U8, Classify, ClassifyRef, DeclassifyRef}; +//! +//! // Input key material and salt +//! let ikm = &[0x0b; 22].classify(); // 22 bytes of 0x0b +//! let salt = b"salt".classify_ref(); +//! +//! // Extract phase: derive pseudorandom key +//! let mut prk = [0u8; 32].classify(); // SHA2-256 output length +//! Hkdf::::extract(&mut prk, salt, ikm).unwrap(); +//! +//! // Expand phase: derive keys for different purposes +//! let mut encrypt_key = [0u8; 16].classify(); +//! let mut mac_key = [0u8; 16].classify(); +//! +//! Hkdf::::expand(&mut encrypt_key, &prk, b"encrypt").unwrap(); +//! Hkdf::::expand(&mut mac_key, &prk, b"mac").unwrap(); +//! ``` +//! +//! ### Using the dynamic API +//! +//! ``` +//! use libcrux_hkdf::{extract, expand, Algorithm}; +//! use libcrux_secrets::{U8, Classify, ClassifyRef, DeclassifyRef}; +//! +//! // Input key material and salt +//! let ikm = &[0x0b; 22].classify(); +//! let salt = b"salt".classify_ref(); +//! +//! // Extract phase using SHA2-512 +//! let mut prk = [0u8; 64].classify(); // SHA2-512 output length +//! extract(Algorithm::Sha512, &mut prk, salt, ikm).unwrap(); +//! +//! // Expand phase: derive keys for different purposes +//! let mut encrypt_key = [0u8; 32].classify(); +//! let mut mac_key = [0u8; 32].classify(); +//! +//! expand(Algorithm::Sha512, &mut encrypt_key, &prk, b"encrypt").unwrap(); +//! expand(Algorithm::Sha512, &mut mac_key, &prk, b"mac").unwrap(); +//! ``` +#![no_std] + +use core::marker::PhantomData; + +use libcrux_secrets::{Classify, DeclassifyRef, DeclassifyRefMut, U8}; + +pub mod hacl; + +/// The HKDF algorithm defining the used hash function. Only needed for the functions with dynamic +/// algorithm selection. +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum Algorithm { + Sha256, + Sha384, + Sha512, +} + +/// HKDF extract using the `salt` and the input key material `ikm`. +/// The result is written to `prk`. +/// The `algo` argument is used for dynamic algorithm selection. +/// +/// Returns nothing on success. +/// Returns [`ExtractError::ArgumentTooLong`] if one of `ikm` or `salt` is longer than [`u32::MAX`] +/// bytes. +/// Returns [`ExtractError::PrkTooShort`] if `prk` is shorter than the hash length. +pub fn extract( + algo: Algorithm, + prk: &mut [U8], + salt: &[U8], + ikm: &[U8], +) -> Result<(), ExtractError> { + match algo { + Algorithm::Sha256 => sha2_256::extract(prk, salt, ikm), + Algorithm::Sha384 => sha2_384::extract(prk, salt, ikm), + Algorithm::Sha512 => sha2_512::extract(prk, salt, ikm), + } +} + +/// HKDF expand. The argument names match the specification. +/// The result is written to `okm`. +/// The `algo` argument is used for dynamic algorithm selection. +/// +/// Returns nothing on success. +/// Returns [`ExpandError::ArgumentTooLong`] if one of `prk` or `info` is longer than +/// [`u32::MAX`] bytes. +/// Returns [`ExpandError::PrkTooShort`] if `okm` is shorter than hash length. +/// Returns [`ExpandError::OutputTooLong`] if `okm` is longer than 255 times the respective hash +/// length. +pub fn expand(algo: Algorithm, okm: &mut [U8], prk: &[U8], info: &[u8]) -> Result<(), ExpandError> { + match algo { + Algorithm::Sha256 => sha2_256::expand(okm, prk, info), + Algorithm::Sha384 => sha2_384::expand(okm, prk, info), + Algorithm::Sha512 => sha2_512::expand(okm, prk, info), + } +} + +/// Full HKDF, i.e. both extract and expand, using the `salt` and the input key material `ikm`. +/// The argument names match the specification. The result is written to `okm`. +/// The `algo` argument is used for dynamic algorithm selection. +/// +/// Returns nothing on success. +/// Returns [`ExpandError::ArgumentTooLong`] if one of `prk` or `info` is longer than +/// [`u32::MAX`] bytes. +/// Returns [`ExpandError::PrkTooShort`] if `okm` is shorter than hash length. +/// Returns [`ExpandError::OutputTooLong`] if `okm` is longer than 255 times the respective hash +/// length. +pub fn hkdf( + algo: Algorithm, + okm: &mut [U8], + salt: &[U8], + ikm: &[U8], + info: &[u8], +) -> Result<(), ExpandError> { + match algo { + Algorithm::Sha256 => sha2_256::hkdf(okm, salt, ikm, info), + Algorithm::Sha384 => sha2_384::hkdf(okm, salt, ikm, info), + Algorithm::Sha512 => sha2_512::hkdf(okm, salt, ikm, info), + } +} + +/// Type marker for SHA2-256 hash algorithm. +/// +/// This struct is used as a type parameter for [`Hkdf`] to provide +/// compile-time selection of the SHA2-256 algorithm for HKDF operations. +/// SHA2-256 produces 32-byte (256-bit) hash outputs. +pub struct Sha2_256; + +/// Type marker for SHA2-384 hash algorithm. +/// +/// This struct is used as a type parameter for [`Hkdf`] to provide +/// compile-time selection of the SHA2-384 algorithm for HKDF operations. +/// SHA2-384 produces 48-byte (384-bit) hash outputs. +pub struct Sha2_384; + +/// Type marker for SHA2-512 hash algorithm. +/// +/// This struct is used as a type parameter for [`Hkdf`] to provide +/// compile-time selection of the SHA2-512 algorithm for HKDF operations. +/// SHA2-512 produces 64-byte (512-bit) hash outputs. +pub struct Sha2_512; + +/// HKDF implementation with compile-time algorithm selection. +/// +/// This struct provides type-safe HKDF operations for a specific hash algorithm +/// determined at compile time. The algorithm is specified using type markers +/// like [`Sha2_256`], [`Sha2_384`], or [`Sha2_512`]. +/// +/// The implementation follows RFC 5869 and uses verified cryptographic code +/// from the HACL* project. +/// +/// # Type Parameters +/// +/// * `Algo` - The hash algorithm type marker (e.g., [`Sha2_256`]) +/// +/// # Examples +/// +/// ``` +/// use libcrux_hkdf::{Hkdf, Sha2_256}; +/// use libcrux_secrets::{U8, Classify, ClassifyRef}; +/// +/// let ikm = &[0x0b; 22].classify(); +/// let salt = b"salt".classify_ref(); +/// +/// let mut prk = [0u8; 32].classify(); +/// Hkdf::::extract(&mut prk, salt, ikm).unwrap(); +/// ``` +pub struct Hkdf(PhantomData); + +impl Algorithm { + /// Returns the digest length of the underlying hash function. + pub const fn hash_len(self) -> usize { + match self { + Algorithm::Sha256 => 32, + Algorithm::Sha384 => 48, + Algorithm::Sha512 => 64, + } + } +} + +/// Generates HKDF implementation modules for specific hash algorithms. +/// +/// This macro creates a complete HKDF implementation module for a specific SHA2 algorithm, +/// including both typed API methods on the [`Hkdf`] struct and standalone module functions. +/// It generates implementations for extract, expand, and full HKDF operations with both +/// fixed-size array references and variable-size slice variants. +/// +/// Parameters: +/// +/// * `$struct_name` - The path to the hash algorithm type marker (e.g., `crate::Sha2_256`) +/// * `$name` - The module name to generate (e.g., `sha2_256`) +/// * `$string_name` - A string literal describing the algorithm (e.g., `"SHA2-256"`) +/// * `$mode` - The corresponding [`Algorithm`] enum variant (e.g., `Algorithm::Sha256`) +/// * `$extract` - The name of the HACL extract function (e.g., `extract_sha2_256`) +/// * `$expand` - The name of the HACL expand function (e.g., `expand_sha2_256`) +/// * `$hash_len` - The hash output length in bytes as a literal (e.g., `32`) +/// +/// +/// This generates the `sha2_256` module and implements all HKDF methods for `Hkdf`. +macro_rules! impl_hkdf { + ($struct_name:path, $name:ident, $string_name:literal, $mode:path, $extract:ident, $expand:ident,$hash_len:literal) => { + #[doc = concat!("HKDF implementation for ", $string_name, ".")] + /// + /// This module provides HKDF (HMAC-based Key Derivation Function) operations + /// specifically for the underlying hash algorithm. It includes both standalone + /// functions and methods on the typed [`Hkdf`] struct. + /// + /// The `_arrayref` variants work with compile-time known PRK sizes for better type safety, + /// while the regular variants accept slices and perform runtime validation. + pub mod $name { + use libcrux_secrets::U8; + + use super::*; + + impl Hkdf<$struct_name> { + /// HKDF extract using the `salt` and the input key material `ikm`. + /// The result is written to `prk`. + /// + /// Returns nothing on success. + /// Returns [`ExtractError::ArgumentTooLong`] if one of `ikm` or `salt` is longer than + /// [`u32::MAX`] bytes. + #[inline(always)] + pub fn extract_arrayref( + prk: &mut [U8; $hash_len], + salt: &[U8], + ikm: &[U8], + ) -> Result<(), ArrayReferenceExtractError> { + extract_arrayref(prk, salt, ikm) + } + + /// HKDF extract using the `salt` and the input key material `ikm`. + /// The result is written to `prk`. + /// + /// Returns nothing on success. + /// Returns [`ExtractError::ArgumentTooLong`] if one of `ikm` or `salt` is longer than + /// [`u32::MAX`] bytes. + /// Returns [`ExtractError::PrkTooShort`] if `prk` is shorter than hash length. + #[inline(always)] + pub fn extract( + prk: &mut [U8], + salt: &[U8], + ikm: &[U8], + ) -> Result<(), ExtractError> { + extract(prk, salt, ikm) + } + + /// HKDF expand using the pre-key material `prk` and `info`. + /// The output is written to `okm`. + /// + /// Returns nothing on success. + /// Returns [`ExpandError::OutputTooLong`] if `okm` is too long (longer than + /// `255 * hash_length`) + /// Returns [`ExpandError::ArgumentTooLong`] if one of `prk` or `info` is longer than + /// [`u32::MAX`] bytes. + #[inline(always)] + pub fn expand_arrayref( + okm: &mut [U8], + prk: &[U8; $hash_len], + info: &[u8], + ) -> Result<(), ArrayReferenceExpandError> { + if okm.len() > 255 * $hash_len { + // Output size is too large. HACL doesn't catch this. + return Err(ArrayReferenceExpandError::OutputTooLong); + } + + expand_arrayref(okm, prk, info) + } + + /// HKDF expand using the pre-key material `prk` and `info`. + /// The output is written to `okm`. + /// + /// Returns nothing on success. + /// Returns [`ExpandError::OutputTooLong`] if `okm` is too long (longer than + /// `255 * hash_length`) + /// Returns [`ExpandError::ArgumentTooLong`] if one of `prk` or `info` is longer than + /// [`u32::MAX`] bytes. + /// Returns [`ExpandError::PrkTooShort`] if `prk` is shorter than hash length. + #[inline(always)] + pub fn expand(okm: &mut [U8], prk: &[U8], info: &[u8]) -> Result<(), ExpandError> { + expand(okm, prk, info) + } + + /// Full HKDF using the `salt`, input key material `ikm`, `info`. + /// The result is written to `okm`. + /// The output length is defined through the length of `okm`. + /// Calls `extract` and `expand` with the given input. + /// + /// Returns nothing on success. + /// Returns [`ExpandError::OutputTooLong`] if `okm` is too long (longer than + /// `255 * hash_length`) + /// Returns [`ExpandError::ArgumentTooLong`] if one of `prk` or `info` is longer than + /// [`u32::MAX`] bytes. + #[inline(always)] + pub fn hkdf( + okm: &mut [U8], + salt: &[U8], + ikm: &[U8], + info: &[u8], + ) -> Result<(), ExpandError> { + hkdf(okm, salt, ikm, info) + } + } + + /// HKDF extract using the `salt` and the input key material `ikm`. + /// The result is written to `prk`. + /// + /// Returns nothing on success. + /// Returns [`ExtractError::ArgumentTooLong`] if one of `ikm` or `salt` is longer than + /// [`u32::MAX`] bytes. + #[inline(always)] + pub fn extract_arrayref( + prk: &mut [U8; $hash_len], + salt: &[U8], + ikm: &[U8], + ) -> Result<(), ArrayReferenceExtractError> { + Ok(crate::hacl::$extract( + prk.declassify_ref_mut(), + salt.declassify_ref(), + checked_u32(salt.len())?, + ikm.declassify_ref(), + checked_u32(ikm.len())?, + )) + } + + /// HKDF extract using the `salt` and the input key material `ikm`. + /// The result is written to `prk`. + /// + /// Returns nothing on success. + /// Returns [`ExtractError::ArgumentTooLong`] if one of `ikm` or `salt` is longer than + /// [`u32::MAX`] bytes. + /// Returns [`ExtractError::PrkTooShort`] if `prk` is shorter than hash length. + #[inline(always)] + pub fn extract(prk: &mut [U8], salt: &[U8], ikm: &[U8]) -> Result<(), ExtractError> { + let (prk, _) = prk + .split_at_mut_checked($hash_len) + .ok_or(ExtractError::PrkTooShort)?; + let prk: &mut [U8; $hash_len] = + prk.try_into().map_err(|_| ExtractError::Unknown)?; + + extract_arrayref(prk, salt, ikm).map_err(ExtractError::from) + } + + /// HKDF expand using the pre-key material `prk` and `info`. + /// The output is written to `okm`. + /// + /// Returns nothing on success. + /// Returns [`ExpandError::OutputTooLong`] if `okm` is too long (longer than + /// `255 * hash_length`) + /// Returns [`ExpandError::ArgumentTooLong`] if one of `prk` or `info` is longer than + /// [`u32::MAX`] bytes. + #[inline(always)] + pub fn expand_arrayref( + mut okm: &mut [U8], + prk: &[U8; $hash_len], + info: &[u8], + ) -> Result<(), ArrayReferenceExpandError> { + let okm_len = okm.len(); + if okm_len > 255 * $hash_len { + // Output size is too large. HACL doesn't catch this. + return Err(ArrayReferenceExpandError::OutputTooLong); + } + + Ok(crate::hacl::$expand( + okm.declassify_ref_mut(), + prk.declassify_ref(), + checked_u32(prk.len())?, + info, + checked_u32(info.len())?, + checked_u32(okm_len)?, + )) + } + + /// HKDF expand using the pre-key material `prk` and `info`. + /// The output is written to `okm`. + /// + /// Returns nothing on success. + /// Returns [`ExpandError::OutputTooLong`] if `okm` is too long (longer than + /// `255 * hash_length`) + /// Returns [`ExpandError::ArgumentTooLong`] if one of `prk` or `info` is longer than + /// [`u32::MAX`] bytes. + /// Returns [`ExpandError::PrkTooShort`] if `prk` is shorter than hash length. + #[inline(always)] + pub fn expand(okm: &mut [U8], prk: &[U8], info: &[u8]) -> Result<(), ExpandError> { + let (prk, _) = prk + .split_at_checked($hash_len) + .ok_or(ExpandError::PrkTooShort)?; + let prk: &[U8; $hash_len] = prk.try_into().map_err(|_| ExpandError::Unknown)?; + + expand_arrayref(okm, prk, info).map_err(ExpandError::from) + } + + /// Full HKDF using the `salt`, input key material `ikm`, `info`. + /// The result is written to `okm`. + /// The output length is defined through the length of `okm`. + /// Calls `extract` and `expand` with the given input. + /// + /// Returns nothing on success. + /// Returns [`ExpandError::OutputTooLong`] if `okm` is too long (longer than + /// `255 * hash_length`) + /// Returns [`ExpandError::ArgumentTooLong`] if one of `prk` or `info` is longer than + /// [`u32::MAX`] bytes. + #[inline(always)] + pub fn hkdf( + okm: &mut [U8], + salt: &[U8], + ikm: &[U8], + info: &[u8], + ) -> Result<(), ExpandError> { + let mut prk = [0u8; $hash_len].classify(); + extract(&mut prk, salt, ikm)?; + expand(okm, &prk, info) + } + } + }; +} + +impl_hkdf!( + crate::Sha2_256, + sha2_256, + "SHA2-256", + Algorithm::Sha256, + extract_sha2_256, + expand_sha2_256, + 32 +); + +impl_hkdf!( + crate::Sha2_384, + sha2_384, + "SHA2-384", + Algorithm::Sha384, + extract_sha2_384, + expand_sha2_384, + 48 +); + +impl_hkdf!( + crate::Sha2_512, + sha2_512, + "SHA2-512", + Algorithm::Sha512, + extract_sha2_512, + expand_sha2_512, + 64 +); + +fn checked_u32(num: usize) -> Result { + num.try_into().map_err(|_| ArgumentsTooLongError) +} + +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum ArrayReferenceExtractError { + ArgumentTooLong, + Unknown, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum ExtractError { + PrkTooShort, + ArgumentTooLong, + Unknown, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum ArrayReferenceExpandError { + OutputTooLong, + ArgumentTooLong, + Unknown, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum ExpandError { + OutputTooLong, + PrkTooShort, + ArgumentTooLong, + Unknown, +} + +#[derive(Copy, Clone, Debug, PartialEq)] +struct ArgumentsTooLongError; + +impl From for ExtractError { + fn from(err: ArrayReferenceExtractError) -> Self { + match err { + ArrayReferenceExtractError::ArgumentTooLong => ExtractError::ArgumentTooLong, + ArrayReferenceExtractError::Unknown => ExtractError::Unknown, + } + } +} + +impl From for ExpandError { + fn from(err: ArrayReferenceExpandError) -> Self { + match err { + ArrayReferenceExpandError::OutputTooLong => ExpandError::OutputTooLong, + ArrayReferenceExpandError::ArgumentTooLong => ExpandError::ArgumentTooLong, + ArrayReferenceExpandError::Unknown => ExpandError::Unknown, + } + } +} + +impl From for ExpandError { + fn from(err: ExtractError) -> Self { + match err { + ExtractError::PrkTooShort => ExpandError::PrkTooShort, + ExtractError::ArgumentTooLong => ExpandError::ArgumentTooLong, + ExtractError::Unknown => ExpandError::Unknown, + } + } +} + +impl From for ArrayReferenceExtractError { + fn from(_: ArgumentsTooLongError) -> Self { + ArrayReferenceExtractError::ArgumentTooLong + } +} +impl From for ArrayReferenceExpandError { + fn from(_: ArgumentsTooLongError) -> Self { + ArrayReferenceExpandError::ArgumentTooLong + } +} +impl From for ExtractError { + fn from(_: ArgumentsTooLongError) -> Self { + ExtractError::ArgumentTooLong + } +} +impl From for ExpandError { + fn from(_: ArgumentsTooLongError) -> Self { + ExpandError::ArgumentTooLong + } +} diff --git a/libcrux-hkdf/src/hkdf.rs b/libcrux-hkdf/src/hkdf.rs deleted file mode 100644 index e77d598d2..000000000 --- a/libcrux-hkdf/src/hkdf.rs +++ /dev/null @@ -1,177 +0,0 @@ -//! HKDF -//! -//! This crate implements HKDF on SHA 1 and SHA 2 (except for SHA 224). -#![no_std] - -extern crate alloc; - -use alloc::vec::Vec; - -pub mod hacl; - -mod impl_hacl; - -pub use impl_hacl::{HkdfSha2_256, HkdfSha2_384, HkdfSha2_512}; - -pub trait HkdfMode { - /// The hash algorithm used in this HKDF mode. - const MODE: Algorithm; - - /// HKDF extract using the `salt` and the input key material `ikm`. - /// The result is written to `prk`. - /// - /// Returns nothing on success. - /// Returns [`Error::ArgumentsTooLarge`] if one of `ikm` or `salt` is longer than [`u32::MAX`] - /// bytes. - fn extract(prk: &mut [u8; HASH_LEN], salt: &[u8], ikm: &[u8]) -> Result<(), Error>; - - /// HKDF expand using the pre-key material `prk` and `info`. The output length - /// is defined through the type of the `okm` parameter, that the output is written to. - /// - /// Returns nothing on success. - /// Returns [`Error::OkmTooLarge`] if the requested `OKM_LEN` is large. - /// Returns [`Error::ArgumentsTooLarge`] if `prk` or `info` is longer than [`u32::MAX`] bytes. - fn expand( - okm: &mut [u8; OKM_LEN], - prk: &[u8], - info: &[u8], - ) -> Result<(), Error>; - - /// HKDF expand using the pre-key material `prk` and `info`. The output length - /// is defined by the parameter `okm_len`. - /// - /// Returns the key material in a [`Vec`] of length `okm_len` on success. - /// Returns [`Error::OkmTooLarge`] if the requested `okm_len` is too large. - /// Returns [`Error::ArgumentsTooLarge`] if `prk` or `info` is longer than [`u32::MAX`] bytes. - fn expand_vec(prk: &[u8], info: &[u8], okm_len: usize) -> Result, Error>; - - /// HKDF using the `salt`, input key material `ikm`, `info`. - /// The result is written to `okm`. - /// The output length is defined through the length of `okm`. - /// Calls `extract` and `expand` with the given inputs. - /// - /// Returns nothing on success. - /// Returns [`Error::OkmTooLarge`] if the requested `OKM_LEN` is too large. - /// Returns [`Error::ArgumentsTooLarge`] if one of `ikm`, `salt` or `info` is longer than - /// [`u32::MAX`] bytes. - #[inline(always)] - fn hkdf( - okm: &mut [u8; OKM_LEN], - salt: &[u8], - ikm: &[u8], - info: &[u8], - ) -> Result<(), Error> { - let mut prk = [0u8; HASH_LEN]; - Self::extract(&mut prk, salt, ikm)?; - Self::expand(okm, &prk, info) - } - - /// HKDF using the `salt`, input key material `ikm`, `info`. - /// The output length is defined by the parameter `okm_len`. - /// Calls `extract` and `expand_vec` with the given input. - /// - /// Returns the key material in a [`Vec`] of length `okm_len` on success. - /// Returns [`Error::OkmTooLarge`] if the requested `okm_len` is too large. - /// Returns [`Error::ArgumentsTooLarge`] if `salt`, `ikm` or `info` is longer than [`u32::MAX`] bytes. - #[inline(always)] - fn hkdf_vec(salt: &[u8], ikm: &[u8], info: &[u8], okm_len: usize) -> Result, Error> { - let mut prk = [0u8; HASH_LEN]; - Self::extract(&mut prk, salt, ikm)?; - Self::expand_vec(&prk, info, okm_len) - } -} - -/// The HKDF algorithm defining the used hash function. -#[derive(Copy, Clone, Debug, PartialEq)] -pub enum Algorithm { - Sha256, - Sha384, - Sha512, -} - -impl Algorithm { - /// Returns the length of the underlying hash function. - pub const fn hash_len(self) -> usize { - match self { - Algorithm::Sha256 => 32, - Algorithm::Sha384 => 48, - Algorithm::Sha512 => 64, - } - } -} - -/// HKDF Errors -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum Error { - /// The requested output key material in expand was too large for the used - /// hash function. - OkmTooLarge, - /// At least one function argument has been too large to process. - ArgumentsTooLarge, -} - -/// HKDF extract using hash function `mode`, `salt`, and the input key material `ikm`. -/// Returns the pre-key material in a vector of tag length. -#[inline(always)] -pub fn extract( - alg: Algorithm, - salt: impl AsRef<[u8]>, - ikm: impl AsRef<[u8]>, -) -> Result, Error> { - let salt = salt.as_ref(); - let ikm = ikm.as_ref(); - match alg { - Algorithm::Sha256 => allocbuf(|prk| HkdfSha2_256::extract(prk, salt, ikm)), - Algorithm::Sha384 => allocbuf(|prk| HkdfSha2_384::extract(prk, salt, ikm)), - Algorithm::Sha512 => allocbuf(|prk| HkdfSha2_512::extract(prk, salt, ikm)), - } -} - -/// HKDF expand using hash function `mode`, pre-key material `prk`, `info`, and output length `okm_len`. -/// Returns the key material in a vector of length `okm_len` or [`Error::OkmLengthTooLarge`] -/// if the requested output length is too large. -#[inline(always)] -pub fn expand( - alg: Algorithm, - prk: impl AsRef<[u8]>, - info: impl AsRef<[u8]>, - okm_len: usize, -) -> Result, Error> { - let prk = prk.as_ref(); - let info = info.as_ref(); - match alg { - Algorithm::Sha256 => HkdfSha2_256::expand_vec(prk, info, okm_len), - Algorithm::Sha384 => HkdfSha2_384::expand_vec(prk, info, okm_len), - Algorithm::Sha512 => HkdfSha2_512::expand_vec(prk, info, okm_len), - } -} - -/// HKDF using hash function `mode`, `salt`, input key material `ikm`, `info`, and output length `okm_len`. -/// Calls `extract` and `expand` with the given input. -/// Returns the key material in a vector of length `okm_len` or [`Error::OkmLengthTooLarge`] -/// if the requested output length is too large. -#[inline(always)] -pub fn hkdf( - mode: Algorithm, - salt: impl AsRef<[u8]>, - ikm: impl AsRef<[u8]>, - info: impl AsRef<[u8]>, - okm_len: usize, -) -> Result, Error> { - let salt = salt.as_ref(); - let ikm = ikm.as_ref(); - let info = info.as_ref(); - - match mode { - Algorithm::Sha256 => HkdfSha2_256::hkdf_vec(salt, ikm, info, okm_len), - Algorithm::Sha384 => HkdfSha2_384::hkdf_vec(salt, ikm, info, okm_len), - Algorithm::Sha512 => HkdfSha2_512::hkdf_vec(salt, ikm, info, okm_len), - } -} - -#[inline(always)] -fn allocbuf Result>(f: F) -> Result, E> { - let mut buf = [0u8; N]; - - f(&mut buf).map(|_| buf.into()) -} diff --git a/libcrux-hkdf/src/impl_hacl.rs b/libcrux-hkdf/src/impl_hacl.rs deleted file mode 100644 index 8c034d926..000000000 --- a/libcrux-hkdf/src/impl_hacl.rs +++ /dev/null @@ -1,191 +0,0 @@ -#![allow(dead_code)] - -use crate::{Algorithm, Error, HkdfMode}; - -macro_rules! impl_hkdf { - ($struct_name:ident,$name:ident, $string_name:literal, $mode:path, $extract:ident, $expand:ident,$hash_len:literal) => { - #[doc = "Implementation of HKDF backed by"] - #[doc = $string_name] - pub struct $struct_name; - - pub mod $name { - use super::{checked_u32, $struct_name, Algorithm, Error, HkdfMode}; - use libcrux_hacl_rs::prelude::*; - - impl HkdfMode<$hash_len> for $struct_name { - const MODE: Algorithm = $mode; - - #[inline(always)] - fn extract( - prk: &mut [u8; $hash_len], - salt: &[u8], - ikm: &[u8], - ) -> Result<(), Error> { - extract(prk, salt, ikm) - } - - #[inline(always)] - fn expand( - okm: &mut [u8; OKM_LEN], - prk: &[u8], - info: &[u8], - ) -> Result<(), Error> { - expand(okm, prk, info) - } - - #[inline(always)] - fn expand_vec(prk: &[u8], info: &[u8], okm_len: usize) -> Result, Error> { - expand_vec(prk, info, okm_len) - } - } - - /// HKDF extract using the `salt` and the input key material `ikm`. - /// The result is written to `prk`. - /// - /// Returns nothing on success. - /// Returns [`Error::ArgumentsTooLarge`] if one of `ikm` or `salt` is longer than - /// [`u32::MAX`] bytes. - #[inline(always)] - pub fn extract( - prk: &mut [u8; $hash_len], - salt: &[u8], - ikm: &[u8], - ) -> Result<(), Error> { - Ok(crate::hacl::$extract( - prk, - salt, - checked_u32(salt.len())?, - ikm, - checked_u32(ikm.len())?, - )) - } - - /// HKDF expand using the pre-key material `prk` and `info`. - /// The output is written to `okm`. - /// - /// Returns nothing on success. - /// Returns [`Error::OkmTooLarge`] if the requested `OKM_LEN` is large. - /// Returns [`Error::ArgumentsTooLarge`] if one of `prk` or `info` is longer than - /// [`u32::MAX`] bytes. - #[inline(always)] - pub fn expand( - okm: &mut [u8; OKM_LEN], - prk: &[u8], - info: &[u8], - ) -> Result<(), Error> { - if OKM_LEN > 255 * $hash_len { - // Output size is too large. HACL doesn't catch this. - return Err(Error::OkmTooLarge); - } - - Ok(crate::hacl::$expand( - okm, - prk, - checked_u32(prk.len())?, - info, - checked_u32(info.len())?, - checked_u32(OKM_LEN)?, - )) - } - - /// HKDF expand using the pre-key material `prk` and `info`. The output length - /// is defined by the parameter `okm_len`. - /// - /// Returns the key material in a [`Vec`] of length `okm_len` on success. - /// Returns [`Error::OkmTooLarge`] if the requested `okm_len` is too large. - /// Returns [`Error::ArgumentsTooLarge`] if `prk` or `info` is longer than [`u32::MAX`] bytes. - #[inline(always)] - pub fn expand_vec(prk: &[u8], info: &[u8], okm_len: usize) -> Result, Error> { - if okm_len > 255 * $hash_len { - // Output size is too large. HACL doesn't catch this. - return Err(Error::OkmTooLarge); - } - - let mut okm = vec![0u8; okm_len]; - crate::hacl::$expand( - &mut okm, - prk, - checked_u32(prk.len())?, - info, - checked_u32(info.len())?, - checked_u32(okm_len)?, - ); - Ok(okm) - } - - /// HKDF using the `salt`, input key material `ikm`, `info`. - /// The result is written to `okm`. - /// The output length is defined through the length of `okm`. - /// Calls `extract` and `expand` with the given input. - /// - /// Returns nothing on success. - /// Returns [`Error::OkmTooLarge`] if the requested `OKM_LEN` is too large. - /// Returns [`Error::ArgumentsTooLarge`] if one of `ikm`, `salt` or `info` is longer - /// than [`u32::MAX`] bytes. - #[inline(always)] - pub fn hkdf( - okm: &mut [u8; OKM_LEN], - salt: &[u8], - ikm: &[u8], - info: &[u8], - ) -> Result<(), Error> { - let mut prk = [0u8; $hash_len]; - extract(&mut prk, salt, ikm)?; - expand(okm, &prk, info) - } - - /// HKDF using the `salt`, input key material `ikm`, `info`. - /// The output length is defined by the parameter `okm_len`. - /// Calls `extract` and `expand_vec` with the given input. - /// - /// Returns the key material in a [`Vec`] of length `okm_len` on success. - /// Returns [`Error::OkmTooLarge`] if the requested `okm_len` is too large. - /// Returns [`Error::ArgumentsTooLarge`] if `salt`, `ikm` or `info` is longer than [`u32::MAX`] bytes. - #[inline(always)] - pub fn hkdf_vec( - salt: &[u8], - ikm: &[u8], - info: &[u8], - okm_len: usize, - ) -> Result, Error> { - let mut prk = [0u8; $hash_len]; - extract(&mut prk, salt, ikm)?; - expand_vec(&prk, info, okm_len) - } - } - }; -} - -impl_hkdf!( - HkdfSha2_256, - sha2_256, - "SHA2-256", - Algorithm::Sha256, - extract_sha2_256, - expand_sha2_256, - 32 -); - -impl_hkdf!( - HkdfSha2_384, - sha2_384, - "SHA2-384", - Algorithm::Sha384, - extract_sha2_384, - expand_sha2_384, - 48 -); - -impl_hkdf!( - HkdfSha2_512, - sha2_512, - "SHA2-512", - Algorithm::Sha512, - extract_sha2_512, - expand_sha2_512, - 64 -); - -fn checked_u32(num: usize) -> Result { - num.try_into().map_err(|_| Error::ArgumentsTooLarge) -} diff --git a/libcrux-psq/Cargo.toml b/libcrux-psq/Cargo.toml index 26801b013..51bd5a5ba 100644 --- a/libcrux-psq/Cargo.toml +++ b/libcrux-psq/Cargo.toml @@ -16,9 +16,11 @@ bench = false # so libtest doesn't eat the arguments to criterion [dependencies] libcrux-traits = { version = "0.0.3", path = "../traits" } -libcrux-kem = { version = "=0.0.3", path = "../libcrux-kem", features = ["codec"]} +libcrux-kem = { version = "=0.0.3", path = "../libcrux-kem", features = [ + "codec", +] } libcrux-chacha20poly1305 = { version = "0.0.3", path = "../chacha20poly1305" } -libcrux-hkdf = { version = "=0.0.3", path = "../libcrux-hkdf" } +libcrux-hkdf = { version = "=0.0.3", path = "../crates/algorithms/hkdf" } libcrux-hmac = { version = "=0.0.3", path = "../libcrux-hmac" } libcrux-sha2 = { version = "=0.0.3", path = "../sha2" } classic-mceliece-rust = { version = "3.1.0", features = [ diff --git a/libcrux-psq/src/lib.rs b/libcrux-psq/src/lib.rs index 7fbfdfe5b..463f86f4e 100644 --- a/libcrux-psq/src/lib.rs +++ b/libcrux-psq/src/lib.rs @@ -59,8 +59,8 @@ impl From for Error { } } -impl From for Error { - fn from(_e: libcrux_hkdf::Error) -> Self { +impl From for Error { + fn from(_e: libcrux_hkdf::ExpandError) -> Self { #[cfg(debug_assertions)] print_error("HKDF error", &_e); Self::CryptoError diff --git a/libcrux-psq/src/protocol/api.rs b/libcrux-psq/src/protocol/api.rs index 168a364af..38ea29dcb 100644 --- a/libcrux-psq/src/protocol/api.rs +++ b/libcrux-psq/src/protocol/api.rs @@ -1,6 +1,5 @@ use std::io::Cursor; -use libcrux_hkdf::Algorithm; use rand::CryptoRng; use tls_codec::{ @@ -107,18 +106,17 @@ fn derive_pk_binder( info.tls_serialize(&mut &mut info_buf[..]) .map_err(serialize_error)?; - let prk = libcrux_hkdf::extract( - Algorithm::Sha256, - [], - SerializeBytes::tls_serialize(&key.key).map_err(serialize_error)?, + let mut pk_binder = [0; PK_BINDER_LEN]; + libcrux_hkdf::sha2_256::hkdf( + &mut pk_binder, + &[], + &SerializeBytes::tls_serialize(&key.key).map_err(serialize_error)?, + &info_buf, ) .map_err(|_| Error::CryptoError)?; Ok( - libcrux_hkdf::expand(Algorithm::Sha256, prk, info_buf, PK_BINDER_LEN) - .map_err(|_| Error::CryptoError)? - .try_into() - .map_err(|_| Error::CryptoError)?, // We don't expect this to fail, unless HDKF gave us the wrong output length + pk_binder.try_into().map_err(|_| Error::CryptoError)?, // We don't expect this to fail, unless HDKF gave us the wrong output length ) } diff --git a/libcrux-psq/src/protocol/keys.rs b/libcrux-psq/src/protocol/keys.rs index 7bab65d24..7443e2c18 100644 --- a/libcrux-psq/src/protocol/keys.rs +++ b/libcrux-psq/src/protocol/keys.rs @@ -1,5 +1,4 @@ use libcrux_chacha20poly1305::{decrypt_detached, encrypt_detached, KEY_LEN, NONCE_LEN}; -use libcrux_hkdf::Algorithm; use tls_codec::{ Deserialize, Serialize, SerializeBytes, TlsDeserialize, TlsSerialize, TlsSerializeBytes, TlsSize, @@ -25,23 +24,17 @@ impl std::fmt::Debug for AEADKey { impl AEADKey { fn new(ikm: &impl SerializeBytes, info: &impl SerializeBytes) -> Result { - let prk = libcrux_hkdf::extract( - Algorithm::Sha256, - [], - ikm.tls_serialize().map_err(serialize_error)?, + let mut key = [0; KEY_LEN]; + libcrux_hkdf::sha2_256::hkdf( + &mut key, + &[], + &ikm.tls_serialize().map_err(serialize_error)?, + &info.tls_serialize().map_err(serialize_error)?, ) .map_err(|_| Error::CryptoError)?; Ok(AEADKey( - libcrux_hkdf::expand( - Algorithm::Sha256, - prk, - info.tls_serialize().map_err(serialize_error)?, - KEY_LEN, - ) - .map_err(|_| Error::CryptoError)? - .try_into() - .map_err(|_| Error::CryptoError)?, // We don't expect this to fail, unless HDKF gave us the wrong output length, + key.try_into().map_err(|_| Error::CryptoError)?, // We don't expect this to fail, unless HDKF gave us the wrong output length, [0u8; NONCE_LEN], )) } @@ -131,18 +124,18 @@ const SESSION_KEY_SALT: &[u8] = b"session key salt"; // id_skCS = KDF(skCS, "shared key id") fn session_key_id(key: &AEADKey) -> Result<[u8; SESSION_ID_LENGTH], Error> { - let prk = libcrux_hkdf::extract( - Algorithm::Sha256, + let mut session_id = [0; SESSION_ID_LENGTH]; + + libcrux_hkdf::sha2_256::hkdf( + &mut session_id, SESSION_KEY_SALT, - SerializeBytes::tls_serialize(&key).map_err(serialize_error)?, + &SerializeBytes::tls_serialize(&key).map_err(serialize_error)?, + SESSION_KEY_INFO, ) .map_err(|_| Error::CryptoError)?; Ok( - libcrux_hkdf::expand(Algorithm::Sha256, prk, SESSION_KEY_INFO, SESSION_ID_LENGTH) - .map_err(|_| Error::CryptoError)? - .try_into() - .map_err(|_| Error::CryptoError)?, // We don't expect this to fail, unless HDKF gave us the wrong output length + session_id.try_into().map_err(|_| Error::CryptoError)?, // We don't expect this to fail, unless HDKF gave us the wrong output length ) } diff --git a/libcrux-psq/src/psk_registration.rs b/libcrux-psq/src/psk_registration.rs index 8bf217cdc..64771ce1b 100644 --- a/libcrux-psq/src/psk_registration.rs +++ b/libcrux-psq/src/psk_registration.rs @@ -275,14 +275,9 @@ impl Responder { } fn derive_psk(prk: &[u8; 32]) -> Result { - let psk: [u8; PSK_LENGTH] = libcrux_hkdf::expand( - libcrux_hkdf::Algorithm::Sha256, - prk, - PSK_REGISTRATION_CONTEXT, - PSK_LENGTH, - )? - .try_into() - .map_err(|_| Error::CryptoError)?; + let mut psk = [0u8; PSK_LENGTH]; + libcrux_hkdf::sha2_256::expand(&mut psk, prk, PSK_REGISTRATION_CONTEXT)?; + let psk = psk.try_into().map_err(|_| Error::CryptoError)?; Ok(psk) } @@ -305,8 +300,14 @@ fn derive_cipherstate( } fn derive_key_iv(psk: &[u8; 32], info: &[u8]) -> Result<([u8; NONCE_LEN], [u8; KEY_LEN]), Error> { - let key_iv_bytes = - libcrux_hkdf::expand(libcrux_hkdf::Algorithm::Sha256, psk, info, AEAD_KEY_NONCE)?; + let mut key_iv_bytes = [0; AEAD_KEY_NONCE]; + libcrux_hkdf::expand( + libcrux_hkdf::Algorithm::Sha256, + &mut key_iv_bytes, + psk, + info, + )?; + let (key_bytes, iv_bytes) = key_iv_bytes.split_at(AEAD_KEY_LENGTH); let key = <[u8; AEAD_KEY_LENGTH]>::try_from(key_bytes)?; let iv = <[u8; NONCE_LEN]>::try_from(iv_bytes)?; diff --git a/libcrux-psq/src/traits.rs b/libcrux-psq/src/traits.rs index ff411d8c5..6418f74f4 100644 --- a/libcrux-psq/src/traits.rs +++ b/libcrux-psq/src/traits.rs @@ -1,5 +1,5 @@ //! This module provides common traits for PSQ implementations. -use libcrux_hkdf::{expand as hkdf_expand, Algorithm as HKDF_Algorithm}; +use libcrux_hkdf::sha2_256::expand as hkdf_expand; use libcrux_hmac::{hmac, Algorithm as HMAC_Algorithm}; use libcrux_traits::kem::KEM; use rand::CryptoRng; @@ -159,15 +159,11 @@ fn compare(lhs: &[u8], rhs: &[u8]) -> u8 { // See: https://github.com/cryspen/libcrux/issues/767 // https://github.com/cryspen/libcrux/issues/820 fn compute_psk(k0: &[u8]) -> Result { - let psk: PSQComponent = hkdf_expand( - HKDF_Algorithm::Sha256, - k0, - PSK_CONTEXT, - PSQ_COMPONENT_LENGTH, - ) - .map_err(|_| Error::PSQGenerationError)? - .try_into() - .expect("should receive the correct number of bytes from HKDF"); + let mut psk = [0; PSQ_COMPONENT_LENGTH]; + hkdf_expand(&mut psk, k0, PSK_CONTEXT).map_err(|_| Error::PSQGenerationError)?; + let psk: PSQComponent = psk + .try_into() + .expect("should receive the correct number of bytes from HKDF"); Ok(psk) } @@ -178,8 +174,8 @@ fn compute_k0(pqpk: &[u8], ikm: &[u8], enc: &[u8], sctx: &[u8]) -> Result Result Result { - let km = hkdf_expand(HKDF_Algorithm::Sha256, k0, CONFIRMATION_CONTEXT, KM_LENGTH) - .map_err(|_| Error::PSQGenerationError)?; + let mut km = [0; KM_LENGTH]; + hkdf_expand(&mut km, k0, CONFIRMATION_CONTEXT).map_err(|_| Error::PSQGenerationError)?; let mac: Mac = hmac(HMAC_Algorithm::Sha256, &km, MAC_INPUT, Some(MAC_LENGTH)) .try_into() diff --git a/src/hacl.rs b/src/hacl.rs index 779b5472d..83ea10596 100644 --- a/src/hacl.rs +++ b/src/hacl.rs @@ -28,7 +28,8 @@ pub enum Error { P256ECDH(libcrux_ecdh::p256::Error), P256ECDSA(p256::ecdsa::Error), Ed25519(ed25519::Error), - Hkdf(libcrux_hkdf::Error), + HkdfExtract(libcrux_hkdf::ExtractError), + HkdfExpand(libcrux_hkdf::ExpandError), } impl From for Error { @@ -49,9 +50,15 @@ impl From for Error { } } -impl From for Error { - fn from(val: libcrux_hkdf::Error) -> Self { - Error::Hkdf(val) +impl From for Error { + fn from(val: libcrux_hkdf::ExtractError) -> Self { + Error::HkdfExtract(val) + } +} + +impl From for Error { + fn from(val: libcrux_hkdf::ExpandError) -> Self { + Error::HkdfExpand(val) } } diff --git a/src/hkdf.rs b/src/hkdf.rs index c23d89cd8..d3d73c1b9 100644 --- a/src/hkdf.rs +++ b/src/hkdf.rs @@ -6,4 +6,5 @@ pub use libcrux_hkdf::expand; pub use libcrux_hkdf::extract; pub use libcrux_hkdf::hkdf; pub use libcrux_hkdf::Algorithm; -pub use libcrux_hkdf::Error; +pub use libcrux_hkdf::ExpandError; +pub use libcrux_hkdf::ExtractError; diff --git a/src/hpke/kdf.rs b/src/hpke/kdf.rs index a7d3eb598..650f3c9cc 100644 --- a/src/hpke/kdf.rs +++ b/src/hpke/kdf.rs @@ -99,10 +99,17 @@ pub fn LabeledExtract( labeled_ikm.extend_from_slice(&label); labeled_ikm.extend_from_slice(ikm); - crate::hkdf::extract(hkdf_algorithm(alg), salt, &labeled_ikm).map_err(|err| match err { - libcrux_hkdf::Error::OkmTooLarge => HpkeError::CryptoError, - libcrux_hkdf::Error::ArgumentsTooLarge => HpkeError::InvalidParameters, - }) + let algo = hkdf_algorithm(alg); + + let mut prk = vec![0u8; algo.hash_len()]; + crate::hkdf::extract(algo, &mut prk, salt, &labeled_ikm).map_err(|err| match err { + libcrux_hkdf::ExtractError::ArgumentTooLong => HpkeError::InvalidParameters, + libcrux_hkdf::ExtractError::PrkTooShort | libcrux_hkdf::ExtractError::Unknown => { + HpkeError::CryptoError + } + })?; + + Ok(prk) } /// KDF: Labeled Expand @@ -134,9 +141,10 @@ pub fn LabeledExpand( labeled_info.extend_from_slice(&label); labeled_info.extend_from_slice(info); - match crate::hkdf::expand(hkdf_algorithm(alg), prk, &labeled_info, L) { - Ok(r) => Ok(r), - Err(_) => Err(HpkeError::CryptoError), - } + let algo = hkdf_algorithm(alg); + let mut okm = vec![0; L]; + crate::hkdf::expand(algo, &mut okm, prk, &labeled_info) + .map_err(|_| HpkeError::CryptoError)?; + Ok(okm) } } diff --git a/tests/hkdf.rs b/tests/hkdf.rs index 9d57cfd8b..2e66afc08 100644 --- a/tests/hkdf.rs +++ b/tests/hkdf.rs @@ -19,9 +19,10 @@ fn run_wycheproof() { let comment = &test.comment; println!("Test {i}: {comment}"); - let result = libcrux_hkdf::hkdf(alg, &test.salt, &test.ikm, &test.info, test.size); + let mut okm = vec![0; test.size]; + let result = libcrux_hkdf::hkdf(alg, &mut okm, &test.salt, &test.ikm, &test.info); match (result, test.result) { - (Ok(okm), TestResult::Valid) => { + (Ok(_), TestResult::Valid) => { assert_eq!(okm.as_slice(), test.okm.as_ref()) } (Err(_), TestResult::Invalid) => {}