Skip to content

Commit 127c5ac

Browse files
authored
hkdf: simplify generic signatures (#155)
This PR renames `Hkdf` and `HkdfExtract` to `GenericHkdf` and `GenericHkdfExtract` respectively and simplifies their generic signature. Now the types are generic over single type `H: HmacImpl`. It also removes the generic parameter from the `HmacImpl` trait. Finally, it introduces `HkdfExtract` and `Hkdf` aliases to simplify migration for users.
1 parent 2d1977a commit 127c5ac

File tree

3 files changed

+54
-101
lines changed

3 files changed

+54
-101
lines changed

hkdf/src/hmac_impl.rs

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,18 @@ use hmac::{EagerHash, Hmac, SimpleHmac};
77
/// Trait representing a HMAC implementation.
88
///
99
/// Most users should use [`Hmac`] or [`SimpleHmac`].
10-
pub trait HmacImpl<H: OutputSizeUser>: Clone {
10+
pub trait HmacImpl: Clone + OutputSizeUser {
1111
/// Create new HMAC state with the given key.
1212
fn new_from_slice(key: &[u8]) -> Self;
1313

1414
/// Update HMAC state.
1515
fn update(&mut self, data: &[u8]);
1616

1717
/// Finalize the HMAC state and get generated tag.
18-
fn finalize(self) -> Output<H>;
18+
fn finalize(self) -> Output<Self>;
1919
}
2020

21-
impl<H: EagerHash> HmacImpl<H> for Hmac<H> {
21+
impl<H: EagerHash> HmacImpl for Hmac<H> {
2222
#[inline(always)]
2323
fn new_from_slice(key: &[u8]) -> Self {
2424
KeyInit::new_from_slice(key).expect("HMAC can take a key of any size")
@@ -30,13 +30,12 @@ impl<H: EagerHash> HmacImpl<H> for Hmac<H> {
3030
}
3131

3232
#[inline(always)]
33-
fn finalize(self) -> Output<H> {
34-
Output::<H>::try_from(&self.finalize_fixed()[..])
35-
.expect("Output<H> and Output<Hmac<H>> are always equal to each other")
33+
fn finalize(self) -> Output<Self> {
34+
self.finalize_fixed()
3635
}
3736
}
3837

39-
impl<H> HmacImpl<H> for SimpleHmac<H>
38+
impl<H> HmacImpl for SimpleHmac<H>
4039
where
4140
H: Digest + BlockSizeUser + Clone,
4241
{
@@ -52,7 +51,6 @@ where
5251

5352
#[inline(always)]
5453
fn finalize(self) -> Output<H> {
55-
Output::<H>::try_from(&self.finalize_fixed()[..])
56-
.expect("Output<H> and Output<SimpleHmac<H>> are always equal to each other")
54+
self.finalize_fixed()
5755
}
5856
}

hkdf/src/lib.rs

Lines changed: 38 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -8,63 +8,44 @@
88
#![forbid(unsafe_code)]
99
#![warn(missing_docs)]
1010

11-
pub use hmac;
12-
13-
use core::fmt;
14-
use core::marker::PhantomData;
15-
use hmac::digest::{
16-
Output, OutputSizeUser, array::typenum::Unsigned, crypto_common::AlgorithmName,
11+
use hmac::{
12+
Hmac, SimpleHmac,
13+
digest::{Output, OutputSizeUser, array::typenum::Unsigned},
1714
};
18-
use hmac::{Hmac, SimpleHmac};
1915

2016
mod errors;
2117
mod hmac_impl;
2218

2319
pub use errors::{InvalidLength, InvalidPrkLength};
20+
pub use hmac;
2421
pub use hmac_impl::HmacImpl;
2522

26-
/// [`HkdfExtract`] variant which uses [`SimpleHmac`] for underlying HMAC
27-
/// implementation.
28-
pub type SimpleHkdfExtract<H> = HkdfExtract<H, SimpleHmac<H>>;
29-
/// [`Hkdf`] variant which uses [`SimpleHmac`] for underlying HMAC
30-
/// implementation.
31-
pub type SimpleHkdf<H> = Hkdf<H, SimpleHmac<H>>;
32-
33-
/// Structure representing the streaming context of an HKDF-Extract operation
34-
/// ```rust
35-
/// # use hkdf::{Hkdf, HkdfExtract};
36-
/// # use sha2::Sha256;
37-
/// let mut extract_ctx = HkdfExtract::<Sha256>::new(Some(b"mysalt"));
38-
/// extract_ctx.input_ikm(b"hello");
39-
/// extract_ctx.input_ikm(b" world");
40-
/// let (streamed_res, _) = extract_ctx.finalize();
23+
/// [`GenericHkdfExtract`] variant which uses [`Hmac`] for the underlying HMAC implementation.
24+
pub type HkdfExtract<H> = GenericHkdfExtract<Hmac<H>>;
25+
/// [`GenericHkdf`] variant which uses [`Hmac`] for the underlying HMAC implementation.
26+
pub type Hkdf<H> = GenericHkdf<Hmac<H>>;
27+
28+
/// [`GenericHkdfExtract`] variant which uses [`SimpleHmac`] for the underlying HMAC implementation.
29+
pub type SimpleHkdfExtract<H> = GenericHkdfExtract<SimpleHmac<H>>;
30+
/// [`GenericHkdf`] variant which uses [`SimpleHmac`] for the underlying HMAC implementation.
31+
pub type SimpleHkdf<H> = GenericHkdf<SimpleHmac<H>>;
32+
33+
/// Structure representing the streaming context of an HKDF-Extract operation.
4134
///
42-
/// let (oneshot_res, _) = Hkdf::<Sha256>::extract(Some(b"mysalt"), b"hello world");
43-
/// assert_eq!(streamed_res, oneshot_res);
44-
/// ```
45-
#[derive(Clone)]
46-
pub struct HkdfExtract<H, I = Hmac<H>>
47-
where
48-
H: OutputSizeUser,
49-
I: HmacImpl<H>,
50-
{
51-
hmac: I,
52-
_pd: PhantomData<H>,
35+
/// This type is generic over HMAC implementation. Most users should use
36+
/// [`HkdfExtract`] or [`SimpleHkdfExtract`] type aliases.
37+
#[derive(Clone, Debug)]
38+
pub struct GenericHkdfExtract<H: HmacImpl> {
39+
hmac: H,
5340
}
5441

55-
impl<H, I> HkdfExtract<H, I>
56-
where
57-
H: OutputSizeUser,
58-
I: HmacImpl<H>,
59-
{
42+
impl<H: HmacImpl> GenericHkdfExtract<H> {
6043
/// Initiates the HKDF-Extract context with the given optional salt
6144
pub fn new(salt: Option<&[u8]>) -> Self {
6245
let default_salt = Output::<H>::default();
6346
let salt = salt.unwrap_or(&default_salt);
64-
Self {
65-
hmac: I::new_from_slice(salt),
66-
_pd: PhantomData,
67-
}
47+
let hmac = H::new_from_slice(salt);
48+
Self { hmac }
6849
}
6950

7051
/// Feeds in additional input key material to the HKDF-Extract context
@@ -74,35 +55,25 @@ where
7455

7556
/// Completes the HKDF-Extract operation, returning both the generated pseudorandom key and
7657
/// `Hkdf` struct for expanding.
77-
pub fn finalize(self) -> (Output<H>, Hkdf<H, I>) {
58+
pub fn finalize(self) -> (Output<H>, GenericHkdf<H>) {
7859
let prk = self.hmac.finalize();
79-
let hkdf = Hkdf::from_prk(&prk).expect("PRK size is correct");
60+
let hkdf = GenericHkdf::<H>::from_prk(&prk).expect("PRK size is correct");
8061
(prk, hkdf)
8162
}
8263
}
8364

84-
impl<H, I> fmt::Debug for HkdfExtract<H, I>
85-
where
86-
H: OutputSizeUser,
87-
I: HmacImpl<H> + AlgorithmName,
88-
{
89-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
90-
f.write_str("HkdfExtract<")?;
91-
<I as AlgorithmName>::write_alg_name(f)?;
92-
f.write_str("> { ... }")
93-
}
94-
}
95-
9665
/// Structure representing the HKDF, capable of HKDF-Expand and HKDF-Extract operations.
9766
/// Recommendations for the correct usage of the parameters can be found in the
9867
/// [crate root](index.html#usage).
99-
#[derive(Clone)]
100-
pub struct Hkdf<H: OutputSizeUser, I: HmacImpl<H> = Hmac<H>> {
101-
hmac: I,
102-
_pd: PhantomData<H>,
68+
///
69+
/// This type is generic over HMAC implementation. Most users should use
70+
/// [`Hkdf`] or [`SimpleHkdf`] type aliases.
71+
#[derive(Clone, Debug)]
72+
pub struct GenericHkdf<H: HmacImpl> {
73+
hmac: H,
10374
}
10475

105-
impl<H: OutputSizeUser, I: HmacImpl<H>> Hkdf<H, I> {
76+
impl<H: HmacImpl> GenericHkdf<H> {
10677
/// Convenience method for [`extract`][Hkdf::extract] when the generated
10778
/// pseudorandom key can be ignored and only HKDF-Expand operation is needed. This is the most
10879
/// common constructor.
@@ -114,20 +85,19 @@ impl<H: OutputSizeUser, I: HmacImpl<H>> Hkdf<H, I> {
11485
/// Create `Hkdf` from an already cryptographically strong pseudorandom key
11586
/// as per section 3.3 from RFC5869.
11687
pub fn from_prk(prk: &[u8]) -> Result<Self, InvalidPrkLength> {
117-
// section 2.3 specifies that prk must be "at least HashLen octets"
118-
if prk.len() < <H as OutputSizeUser>::OutputSize::to_usize() {
88+
// section 2.3 specifies that `prk` must be "at least HashLen octets"
89+
let hash_len = <H as OutputSizeUser>::OutputSize::to_usize();
90+
if prk.len() < hash_len {
11991
return Err(InvalidPrkLength);
12092
}
121-
Ok(Self {
122-
hmac: I::new_from_slice(prk),
123-
_pd: PhantomData,
124-
})
93+
let hmac = H::new_from_slice(prk);
94+
Ok(Self { hmac })
12595
}
12696

12797
/// The RFC5869 HKDF-Extract operation returning both the generated
12898
/// pseudorandom key and `Hkdf` struct for expanding.
12999
pub fn extract(salt: Option<&[u8]>, ikm: &[u8]) -> (Output<H>, Self) {
130-
let mut extract_ctx = HkdfExtract::new(salt);
100+
let mut extract_ctx = GenericHkdfExtract::<H>::new(salt);
131101
extract_ctx.input_ikm(ikm);
132102
extract_ctx.finalize()
133103
}
@@ -180,16 +150,3 @@ impl<H: OutputSizeUser, I: HmacImpl<H>> Hkdf<H, I> {
180150
self.expand_multi_info(&[info], okm)
181151
}
182152
}
183-
184-
impl<H, I> fmt::Debug for Hkdf<H, I>
185-
where
186-
H: OutputSizeUser,
187-
I: HmacImpl<H>,
188-
I: AlgorithmName,
189-
{
190-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
191-
f.write_str("Hkdf<")?;
192-
<I as AlgorithmName>::write_alg_name(f)?;
193-
f.write_str("> { ... }")
194-
}
195-
}

hkdf/tests/wycheproof.rs

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
use blobby::Blob4Iterator;
2-
use hkdf::{Hkdf, HmacImpl};
2+
use hkdf::{GenericHkdf, HmacImpl};
33
use hmac::{Hmac, SimpleHmac};
4-
use sha1::Sha1;
5-
use sha2::{Sha256, Sha384, Sha512, digest::OutputSizeUser};
64

7-
fn test<H: OutputSizeUser, I: HmacImpl<H>>(data: &[u8]) {
5+
fn test<H: HmacImpl>(data: &[u8]) {
86
for (i, row) in Blob4Iterator::new(data).unwrap().enumerate() {
97
let [ikm, salt, info, okm] = row.unwrap();
108

11-
let prk = Hkdf::<H, I>::new(Some(salt), ikm);
9+
let prk = GenericHkdf::<H>::new(Some(salt), ikm);
1210
let mut got_okm = vec![0; okm.len()];
1311

1412
let mut err = None;
@@ -37,13 +35,13 @@ macro_rules! new_test {
3735
#[test]
3836
fn $name() {
3937
let data = include_bytes!(concat!("data/", $test_name, ".blb"));
40-
test::<$hash, Hmac<$hash>>(data);
41-
test::<$hash, SimpleHmac<$hash>>(data);
38+
test::<Hmac<$hash>>(data);
39+
test::<SimpleHmac<$hash>>(data);
4240
}
4341
};
4442
}
4543

46-
new_test!(wycheproof_sha1, "wycheproof-sha1", Sha1);
47-
new_test!(wycheproof_sha256, "wycheproof-sha256", Sha256);
48-
new_test!(wycheproof_sha384, "wycheproof-sha384", Sha384);
49-
new_test!(wycheproof_sha512, "wycheproof-sha512", Sha512);
44+
new_test!(wycheproof_sha1, "wycheproof-sha1", sha1::Sha1);
45+
new_test!(wycheproof_sha256, "wycheproof-sha256", sha2::Sha256);
46+
new_test!(wycheproof_sha384, "wycheproof-sha384", sha2::Sha384);
47+
new_test!(wycheproof_sha512, "wycheproof-sha512", sha2::Sha512);

0 commit comments

Comments
 (0)