wolf_crypto/kdf/
hmac.rs

1//! The `HMAC` Key Derivation Function (`HKDF`).
2//!
3//! This module provides an implementation of HKDF as specified in [`RFC 5869`][1]. `HKDF` is a key
4//! derivation function based on HMAC, designed to derive one or more secret keys from a master
5//! secret key.
6//!
7//! [1]: https://www.rfc-editor.org/rfc/rfc5869
8use wolf_crypto_sys::{wc_HKDF};
9use crate::aead::Aad as Additional;
10use crate::{can_cast_u32, const_can_cast_u32, Unspecified};
11use crate::kdf::{Salt, salt::Empty};
12
13use crate::mac::hmac::algo::{GenericKey, Hash};
14
15/// Performs the `HKDF` operation without input validation.
16///
17/// # Safety
18///
19/// This does not perform any input validation.
20/// The caller must ensure that all input sizes are valid and can be cast to u32.
21#[inline]
22unsafe fn hkdf_unchecked<H: Hash>(
23    key: impl GenericKey<Size = H::KeyLen>,
24    salt: impl Salt<Empty>,
25    additional: impl Additional,
26    into: &mut [u8]
27) {
28    debug_assert!(salt.is_valid_size());
29    debug_assert!(additional.is_valid_size());
30    debug_assert!(can_cast_u32(into.len()));
31
32    // Infallible via types.
33    let _res = wc_HKDF(
34        H::type_id(),
35        key.ptr(),
36        key.size(),
37        salt.ptr(),
38        salt.size(),
39        additional.ptr(),
40        additional.size(),
41        into.as_mut_ptr(),
42        into.len() as u32
43    );
44
45    debug_assert_eq!(_res, 0);
46}
47
48/// Checks if the salt and additional data have valid sizes.
49#[inline]
50#[must_use]
51fn hkdf_predicate<S: Salt<Empty>, A: Additional>(s: &S, a: &A) -> bool {
52    s.is_valid_size() && a.is_valid_size()
53}
54
55/// Performs HKDF and returns the result as a fixed-size array.
56///
57/// # Arguments
58///
59/// * `key` - The input keying material.
60/// * `salt` - The salt value (a non-secret random value).
61/// * `additional` - Additional input (optional context and application specific information).
62///
63/// # Returns
64///
65/// The derived key material with length `KL`.
66///
67/// # Errors
68///
69/// - The length of the `salt` was greater than [`u32::MAX`].
70/// - The length of the `additional` data was greater than [`u32::MAX`].
71/// - The length of the desired key material (`KL`) was greater than [`u32::MAX`].
72///
73/// # Examples
74///
75/// ```
76/// use wolf_crypto::kdf::{hkdf, Sha256};
77///
78/// let key = [42u8; 32];
79/// let salt = b"salt";
80/// let info = b"context information";
81///
82/// let derived_key = hkdf::<Sha256, 32>(key, salt, info).unwrap();
83/// assert_eq!(derived_key.len(), 32);
84/// ```
85#[inline]
86pub fn hkdf<H: Hash, const KL: usize>(
87    key: impl GenericKey<Size = H::KeyLen>,
88    salt: impl Salt<Empty>,
89    additional: impl Additional,
90) -> Result<[u8; KL], Unspecified> {
91    if hkdf_predicate(&salt, &additional) && const_can_cast_u32::<KL>() {
92        let mut out = [0u8; KL];
93        unsafe { hkdf_unchecked::<H>(key, salt, additional, out.as_mut_slice()) };
94        Ok(out)
95    } else {
96        Err(Unspecified)
97    }
98}
99
100/// Performs HKDF and writes the result into the provided output buffer.
101///
102/// # Arguments
103///
104/// * `key` - The input keying material.
105/// * `salt` - The salt value (a non-secret random value).
106/// * `additional` - Additional input (optional context and application specific information).
107/// * `output` - The buffer to write the derived key material into.
108///
109/// # Errors
110///
111/// - The length of the `salt` was greater than [`u32::MAX`].
112/// - The length of the `additional` data was greater than [`u32::MAX`].
113/// - The length of the `output` was greater than [`u32::MAX`].
114///
115/// # Examples
116///
117/// ```
118/// use wolf_crypto::kdf::{hkdf_into, Sha256};
119///
120/// let key = [42u8; 32];
121/// let salt = b"salt";
122/// let info = b"context information";
123/// let mut output = [0u8; 64];
124///
125/// hkdf_into::<Sha256>(key, salt, info, &mut output).unwrap();
126/// ```
127#[inline]
128pub fn hkdf_into<H: Hash>(
129    key: impl GenericKey<Size = H::KeyLen>,
130    salt: impl Salt<Empty>,
131    additional: impl Additional,
132    output: &mut [u8]
133) -> Result<(), Unspecified> {
134    if hkdf_predicate(&salt, &additional) && can_cast_u32(output.len()) {
135        unsafe { hkdf_unchecked::<H>(key, salt, additional, output) };
136        Ok(())
137    } else {
138        Err(Unspecified)
139    }
140}
141
142#[cfg(test)]
143mod property_tests {
144    use super::*;
145    use crate::aes::test_utils::BoundList;
146    use proptest::prelude::*;
147    use hkdf::Hkdf;
148
149    macro_rules! against_rc {
150        (
151            name: $name:ident,
152            cases: $cases:literal,
153            algo: $rc_crate:ident :: $algo:ident,
154            ds: $ds:literal
155        ) => {proptest! {
156        #![proptest_config(ProptestConfig::with_cases(5_000))]
157
158        #[test]
159        fn $name(
160            key in any::<[u8; $ds]>(),
161            salt in any::<Option<BoundList<128>>>()
162        ) {
163            let salt = salt.as_ref().map(BoundList::as_slice);
164
165            let mut rc_out = [0u8; $ds];
166            Hkdf::<$rc_crate::$algo>::new(salt, key.as_slice()).expand(b"", rc_out.as_mut_slice()).unwrap();
167
168            let out = hkdf::<$crate::kdf::$algo, { $ds }>(key, salt, ()).unwrap();
169
170            prop_assert_eq!(out, rc_out);
171        }
172        }};
173    }
174
175    against_rc! {
176        name: rust_crypto_equivalence_sha224,
177        cases: 5000,
178        algo: sha2::Sha224,
179        ds: 28
180    }
181
182    against_rc! {
183        name: rust_crypto_equivalence_sha256,
184        cases: 5000,
185        algo: sha2::Sha256,
186        ds: 32
187    }
188
189    against_rc! {
190        name: rust_crypto_equivalence_sha384,
191        cases: 5000,
192        algo: sha2::Sha384,
193        ds: 48
194    }
195
196    against_rc! {
197        name: rust_crypto_equivalence_sha512,
198        cases: 2500,
199        algo: sha2::Sha512,
200        ds: 64
201    }
202
203    against_rc! {
204        name: rust_crypto_equivalence_sha3_224,
205        cases: 5000,
206        algo: sha3::Sha3_224,
207        ds: 28
208    }
209
210    against_rc! {
211        name: rust_crypto_equivalence_sha3_256,
212        cases: 5000,
213        algo: sha3::Sha3_256,
214        ds: 32
215    }
216
217    against_rc! {
218        name: rust_crypto_equivalence_sha3_384,
219        cases: 5000,
220        algo: sha3::Sha3_384,
221        ds: 48
222    }
223
224    against_rc! {
225        name: rust_crypto_equivalence_sha3_512,
226        cases: 2500,
227        algo: sha3::Sha3_512,
228        ds: 64
229    }
230    
231    mod sha1 {
232        pub use sha1::Sha1 as Sha;
233    }
234    
235    non_fips! {
236        against_rc! {
237            name: rust_crypto_equivalence_sha1,
238            cases: 10000,
239            algo: sha1::Sha,
240            ds: 20
241        }
242        
243        against_rc! {
244            name: rust_crypto_equivalence_md5,
245            cases: 10000,
246            algo: md5::Md5,
247            ds: 16
248        }
249    }
250}