1use 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#[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 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#[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#[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#[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}