From 19a6871253d02cd4e7b90765396f654c8107ef4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC=20=D0=9F=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=20=5BArtyom=20Pavlov=5D?= Date: Mon, 13 Oct 2025 06:56:36 +0300 Subject: [PATCH] hkdf: simplify generic signature --- hkdf/src/hmac_impl.rs | 16 +++--- hkdf/src/lib.rs | 119 +++++++++++++-------------------------- hkdf/tests/wycheproof.rs | 20 +++---- 3 files changed, 54 insertions(+), 101 deletions(-) diff --git a/hkdf/src/hmac_impl.rs b/hkdf/src/hmac_impl.rs index a5bcd6f..e3b7b9a 100644 --- a/hkdf/src/hmac_impl.rs +++ b/hkdf/src/hmac_impl.rs @@ -7,7 +7,7 @@ use hmac::{EagerHash, Hmac, SimpleHmac}; /// Trait representing a HMAC implementation. /// /// Most users should use [`Hmac`] or [`SimpleHmac`]. -pub trait HmacImpl: Clone { +pub trait HmacImpl: Clone + OutputSizeUser { /// Create new HMAC state with the given key. fn new_from_slice(key: &[u8]) -> Self; @@ -15,10 +15,10 @@ pub trait HmacImpl: Clone { fn update(&mut self, data: &[u8]); /// Finalize the HMAC state and get generated tag. - fn finalize(self) -> Output; + fn finalize(self) -> Output; } -impl HmacImpl for Hmac { +impl HmacImpl for Hmac { #[inline(always)] fn new_from_slice(key: &[u8]) -> Self { KeyInit::new_from_slice(key).expect("HMAC can take a key of any size") @@ -30,13 +30,12 @@ impl HmacImpl for Hmac { } #[inline(always)] - fn finalize(self) -> Output { - Output::::try_from(&self.finalize_fixed()[..]) - .expect("Output and Output> are always equal to each other") + fn finalize(self) -> Output { + self.finalize_fixed() } } -impl HmacImpl for SimpleHmac +impl HmacImpl for SimpleHmac where H: Digest + BlockSizeUser + Clone, { @@ -52,7 +51,6 @@ where #[inline(always)] fn finalize(self) -> Output { - Output::::try_from(&self.finalize_fixed()[..]) - .expect("Output and Output> are always equal to each other") + self.finalize_fixed() } } diff --git a/hkdf/src/lib.rs b/hkdf/src/lib.rs index 11c233b..d330f4a 100644 --- a/hkdf/src/lib.rs +++ b/hkdf/src/lib.rs @@ -8,63 +8,44 @@ #![forbid(unsafe_code)] #![warn(missing_docs)] -pub use hmac; - -use core::fmt; -use core::marker::PhantomData; -use hmac::digest::{ - Output, OutputSizeUser, array::typenum::Unsigned, crypto_common::AlgorithmName, +use hmac::{ + Hmac, SimpleHmac, + digest::{Output, OutputSizeUser, array::typenum::Unsigned}, }; -use hmac::{Hmac, SimpleHmac}; mod errors; mod hmac_impl; pub use errors::{InvalidLength, InvalidPrkLength}; +pub use hmac; pub use hmac_impl::HmacImpl; -/// [`HkdfExtract`] variant which uses [`SimpleHmac`] for underlying HMAC -/// implementation. -pub type SimpleHkdfExtract = HkdfExtract>; -/// [`Hkdf`] variant which uses [`SimpleHmac`] for underlying HMAC -/// implementation. -pub type SimpleHkdf = Hkdf>; - -/// Structure representing the streaming context of an HKDF-Extract operation -/// ```rust -/// # use hkdf::{Hkdf, HkdfExtract}; -/// # use sha2::Sha256; -/// let mut extract_ctx = HkdfExtract::::new(Some(b"mysalt")); -/// extract_ctx.input_ikm(b"hello"); -/// extract_ctx.input_ikm(b" world"); -/// let (streamed_res, _) = extract_ctx.finalize(); +/// [`GenericHkdfExtract`] variant which uses [`Hmac`] for the underlying HMAC implementation. +pub type HkdfExtract = GenericHkdfExtract>; +/// [`GenericHkdf`] variant which uses [`Hmac`] for the underlying HMAC implementation. +pub type Hkdf = GenericHkdf>; + +/// [`GenericHkdfExtract`] variant which uses [`SimpleHmac`] for the underlying HMAC implementation. +pub type SimpleHkdfExtract = GenericHkdfExtract>; +/// [`GenericHkdf`] variant which uses [`SimpleHmac`] for the underlying HMAC implementation. +pub type SimpleHkdf = GenericHkdf>; + +/// Structure representing the streaming context of an HKDF-Extract operation. /// -/// let (oneshot_res, _) = Hkdf::::extract(Some(b"mysalt"), b"hello world"); -/// assert_eq!(streamed_res, oneshot_res); -/// ``` -#[derive(Clone)] -pub struct HkdfExtract> -where - H: OutputSizeUser, - I: HmacImpl, -{ - hmac: I, - _pd: PhantomData, +/// This type is generic over HMAC implementation. Most users should use +/// [`HkdfExtract`] or [`SimpleHkdfExtract`] type aliases. +#[derive(Clone, Debug)] +pub struct GenericHkdfExtract { + hmac: H, } -impl HkdfExtract -where - H: OutputSizeUser, - I: HmacImpl, -{ +impl GenericHkdfExtract { /// Initiates the HKDF-Extract context with the given optional salt pub fn new(salt: Option<&[u8]>) -> Self { let default_salt = Output::::default(); let salt = salt.unwrap_or(&default_salt); - Self { - hmac: I::new_from_slice(salt), - _pd: PhantomData, - } + let hmac = H::new_from_slice(salt); + Self { hmac } } /// Feeds in additional input key material to the HKDF-Extract context @@ -74,35 +55,25 @@ where /// Completes the HKDF-Extract operation, returning both the generated pseudorandom key and /// `Hkdf` struct for expanding. - pub fn finalize(self) -> (Output, Hkdf) { + pub fn finalize(self) -> (Output, GenericHkdf) { let prk = self.hmac.finalize(); - let hkdf = Hkdf::from_prk(&prk).expect("PRK size is correct"); + let hkdf = GenericHkdf::::from_prk(&prk).expect("PRK size is correct"); (prk, hkdf) } } -impl fmt::Debug for HkdfExtract -where - H: OutputSizeUser, - I: HmacImpl + AlgorithmName, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("HkdfExtract<")?; - ::write_alg_name(f)?; - f.write_str("> { ... }") - } -} - /// Structure representing the HKDF, capable of HKDF-Expand and HKDF-Extract operations. /// Recommendations for the correct usage of the parameters can be found in the /// [crate root](index.html#usage). -#[derive(Clone)] -pub struct Hkdf = Hmac> { - hmac: I, - _pd: PhantomData, +/// +/// This type is generic over HMAC implementation. Most users should use +/// [`Hkdf`] or [`SimpleHkdf`] type aliases. +#[derive(Clone, Debug)] +pub struct GenericHkdf { + hmac: H, } -impl> Hkdf { +impl GenericHkdf { /// Convenience method for [`extract`][Hkdf::extract] when the generated /// pseudorandom key can be ignored and only HKDF-Expand operation is needed. This is the most /// common constructor. @@ -114,20 +85,19 @@ impl> Hkdf { /// Create `Hkdf` from an already cryptographically strong pseudorandom key /// as per section 3.3 from RFC5869. pub fn from_prk(prk: &[u8]) -> Result { - // section 2.3 specifies that prk must be "at least HashLen octets" - if prk.len() < ::OutputSize::to_usize() { + // section 2.3 specifies that `prk` must be "at least HashLen octets" + let hash_len = ::OutputSize::to_usize(); + if prk.len() < hash_len { return Err(InvalidPrkLength); } - Ok(Self { - hmac: I::new_from_slice(prk), - _pd: PhantomData, - }) + let hmac = H::new_from_slice(prk); + Ok(Self { hmac }) } /// The RFC5869 HKDF-Extract operation returning both the generated /// pseudorandom key and `Hkdf` struct for expanding. pub fn extract(salt: Option<&[u8]>, ikm: &[u8]) -> (Output, Self) { - let mut extract_ctx = HkdfExtract::new(salt); + let mut extract_ctx = GenericHkdfExtract::::new(salt); extract_ctx.input_ikm(ikm); extract_ctx.finalize() } @@ -180,16 +150,3 @@ impl> Hkdf { self.expand_multi_info(&[info], okm) } } - -impl fmt::Debug for Hkdf -where - H: OutputSizeUser, - I: HmacImpl, - I: AlgorithmName, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("Hkdf<")?; - ::write_alg_name(f)?; - f.write_str("> { ... }") - } -} diff --git a/hkdf/tests/wycheproof.rs b/hkdf/tests/wycheproof.rs index 30cddca..45ede3b 100644 --- a/hkdf/tests/wycheproof.rs +++ b/hkdf/tests/wycheproof.rs @@ -1,14 +1,12 @@ use blobby::Blob4Iterator; -use hkdf::{Hkdf, HmacImpl}; +use hkdf::{GenericHkdf, HmacImpl}; use hmac::{Hmac, SimpleHmac}; -use sha1::Sha1; -use sha2::{Sha256, Sha384, Sha512, digest::OutputSizeUser}; -fn test>(data: &[u8]) { +fn test(data: &[u8]) { for (i, row) in Blob4Iterator::new(data).unwrap().enumerate() { let [ikm, salt, info, okm] = row.unwrap(); - let prk = Hkdf::::new(Some(salt), ikm); + let prk = GenericHkdf::::new(Some(salt), ikm); let mut got_okm = vec![0; okm.len()]; let mut err = None; @@ -37,13 +35,13 @@ macro_rules! new_test { #[test] fn $name() { let data = include_bytes!(concat!("data/", $test_name, ".blb")); - test::<$hash, Hmac<$hash>>(data); - test::<$hash, SimpleHmac<$hash>>(data); + test::>(data); + test::>(data); } }; } -new_test!(wycheproof_sha1, "wycheproof-sha1", Sha1); -new_test!(wycheproof_sha256, "wycheproof-sha256", Sha256); -new_test!(wycheproof_sha384, "wycheproof-sha384", Sha384); -new_test!(wycheproof_sha512, "wycheproof-sha512", Sha512); +new_test!(wycheproof_sha1, "wycheproof-sha1", sha1::Sha1); +new_test!(wycheproof_sha256, "wycheproof-sha256", sha2::Sha256); +new_test!(wycheproof_sha384, "wycheproof-sha384", sha2::Sha384); +new_test!(wycheproof_sha512, "wycheproof-sha512", sha2::Sha512);