/rust/registry/src/index.crates.io-1949cf8c6b5b557f/aws-lc-rs-1.15.4/src/hkdf.rs
Line | Count | Source |
1 | | // Copyright 2015 Brian Smith. |
2 | | // SPDX-License-Identifier: ISC |
3 | | // Modifications copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. |
4 | | // SPDX-License-Identifier: Apache-2.0 OR ISC |
5 | | |
6 | | //! HMAC-based Extract-and-Expand Key Derivation Function. |
7 | | //! |
8 | | //! HKDF is specified in [RFC 5869]. |
9 | | //! |
10 | | //! [RFC 5869]: https://tools.ietf.org/html/rfc5869 |
11 | | //! |
12 | | //! # Example |
13 | | //! ``` |
14 | | //! use aws_lc_rs::{aead, hkdf, hmac, rand}; |
15 | | //! |
16 | | //! // Generate a (non-secret) salt value |
17 | | //! let mut salt_bytes = [0u8; 32]; |
18 | | //! rand::fill(&mut salt_bytes).unwrap(); |
19 | | //! |
20 | | //! // Extract pseudo-random key from secret keying materials |
21 | | //! let salt = hkdf::Salt::new(hkdf::HKDF_SHA256, &salt_bytes); |
22 | | //! let pseudo_random_key = salt.extract(b"secret input keying material"); |
23 | | //! |
24 | | //! // Derive HMAC key |
25 | | //! let hmac_key_material = pseudo_random_key |
26 | | //! .expand( |
27 | | //! &[b"hmac contextual info"], |
28 | | //! hkdf::HKDF_SHA256.hmac_algorithm(), |
29 | | //! ) |
30 | | //! .unwrap(); |
31 | | //! let hmac_key = hmac::Key::from(hmac_key_material); |
32 | | //! |
33 | | //! // Derive UnboundKey for AES-128-GCM |
34 | | //! let aes_keying_material = pseudo_random_key |
35 | | //! .expand(&[b"aes contextual info"], &aead::AES_128_GCM) |
36 | | //! .unwrap(); |
37 | | //! let aead_unbound_key = aead::UnboundKey::from(aes_keying_material); |
38 | | //! ``` |
39 | | |
40 | | use crate::aws_lc::{HKDF_expand, HKDF}; |
41 | | use crate::error::Unspecified; |
42 | | use crate::fips::indicator_check; |
43 | | use crate::{digest, hmac}; |
44 | | use alloc::sync::Arc; |
45 | | use core::fmt; |
46 | | use zeroize::Zeroize; |
47 | | |
48 | | /// An HKDF algorithm. |
49 | | #[derive(Clone, Copy, Debug, Eq, PartialEq)] |
50 | | pub struct Algorithm(hmac::Algorithm); |
51 | | |
52 | | impl Algorithm { |
53 | | /// The underlying HMAC algorithm. |
54 | | #[inline] |
55 | | #[must_use] |
56 | 0 | pub fn hmac_algorithm(&self) -> hmac::Algorithm { |
57 | 0 | self.0 |
58 | 0 | } |
59 | | } |
60 | | |
61 | | /// HKDF using HMAC-SHA-1. Obsolete. |
62 | | pub const HKDF_SHA1_FOR_LEGACY_USE_ONLY: Algorithm = Algorithm(hmac::HMAC_SHA1_FOR_LEGACY_USE_ONLY); |
63 | | |
64 | | /// HKDF using HMAC-SHA-256. |
65 | | pub const HKDF_SHA256: Algorithm = Algorithm(hmac::HMAC_SHA256); |
66 | | |
67 | | /// HKDF using HMAC-SHA-384. |
68 | | pub const HKDF_SHA384: Algorithm = Algorithm(hmac::HMAC_SHA384); |
69 | | |
70 | | /// HKDF using HMAC-SHA-512. |
71 | | pub const HKDF_SHA512: Algorithm = Algorithm(hmac::HMAC_SHA512); |
72 | | |
73 | | /// General Info length's for HKDF don't normally exceed 256 bits. |
74 | | /// We set the default capacity to a value larger than should be needed |
75 | | /// so that the value passed to |`HKDF_expand`| is only allocated once. |
76 | | const HKDF_INFO_DEFAULT_CAPACITY_LEN: usize = 80; |
77 | | |
78 | | /// The maximum output size of a PRK computed by |`HKDF_extract`| is the maximum digest |
79 | | /// size that can be outputted by *AWS-LC*. |
80 | | const MAX_HKDF_PRK_LEN: usize = digest::MAX_OUTPUT_LEN; |
81 | | |
82 | | impl KeyType for Algorithm { |
83 | 0 | fn len(&self) -> usize { |
84 | 0 | self.0.digest_algorithm().output_len |
85 | 0 | } |
86 | | } |
87 | | |
88 | | /// A salt for HKDF operations. |
89 | | pub struct Salt { |
90 | | algorithm: Algorithm, |
91 | | bytes: Arc<[u8]>, |
92 | | } |
93 | | |
94 | | #[allow(clippy::missing_fields_in_debug)] |
95 | | impl fmt::Debug for Salt { |
96 | 0 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
97 | 0 | f.debug_struct("hkdf::Salt") |
98 | 0 | .field("algorithm", &self.algorithm.0) |
99 | 0 | .finish() |
100 | 0 | } |
101 | | } |
102 | | |
103 | | impl Salt { |
104 | | /// Constructs a new `Salt` with the given value based on the given digest |
105 | | /// algorithm. |
106 | | /// |
107 | | /// Constructing a `Salt` is relatively expensive so it is good to reuse a |
108 | | /// `Salt` object instead of re-constructing `Salt`s with the same value. |
109 | | /// |
110 | | // # FIPS |
111 | | // The following conditions must be met: |
112 | | // * Algorithm is one of the following: |
113 | | // * `HKDF_SHA1_FOR_LEGACY_USE_ONLY` |
114 | | // * `HKDF_SHA256` |
115 | | // * `HKDF_SHA384` |
116 | | // * `HKDF_SHA512` |
117 | | // * `value.len() > 0` is true |
118 | | // |
119 | | /// # Panics |
120 | | /// `new` panics if salt creation fails |
121 | | #[must_use] |
122 | 0 | pub fn new(algorithm: Algorithm, value: &[u8]) -> Self { |
123 | 0 | Self { |
124 | 0 | algorithm, |
125 | 0 | bytes: Arc::from(value), |
126 | 0 | } |
127 | 0 | } |
128 | | |
129 | | /// The [HKDF-Extract] operation. |
130 | | /// |
131 | | /// [HKDF-Extract]: https://tools.ietf.org/html/rfc5869#section-2.2 |
132 | | /// |
133 | | /// # Panics |
134 | | /// Panics if the extract operation is unable to be performed |
135 | | #[inline] |
136 | | #[must_use] |
137 | 0 | pub fn extract(&self, secret: &[u8]) -> Prk { |
138 | 0 | Prk { |
139 | 0 | algorithm: self.algorithm, |
140 | 0 | mode: PrkMode::ExtractExpand { |
141 | 0 | secret: Arc::new(ZeroizeBoxSlice::from(secret)), |
142 | 0 | salt: Arc::clone(&self.bytes), |
143 | 0 | }, |
144 | 0 | } |
145 | 0 | } |
146 | | |
147 | | /// The algorithm used to derive this salt. |
148 | | #[inline] |
149 | | #[must_use] |
150 | 0 | pub fn algorithm(&self) -> Algorithm { |
151 | 0 | Algorithm(self.algorithm.hmac_algorithm()) |
152 | 0 | } |
153 | | } |
154 | | |
155 | | impl From<Okm<'_, Algorithm>> for Salt { |
156 | 0 | fn from(okm: Okm<'_, Algorithm>) -> Self { |
157 | 0 | let algorithm = okm.prk.algorithm; |
158 | 0 | let salt_len = okm.len().len(); |
159 | 0 | let mut salt_bytes = vec![0u8; salt_len]; |
160 | 0 | okm.fill(&mut salt_bytes).unwrap(); |
161 | 0 | Self { |
162 | 0 | algorithm, |
163 | 0 | bytes: Arc::from(salt_bytes.as_slice()), |
164 | 0 | } |
165 | 0 | } |
166 | | } |
167 | | |
168 | | /// The length of the OKM (Output Keying Material) for a `Prk::expand()` call. |
169 | | #[allow(clippy::len_without_is_empty)] |
170 | | pub trait KeyType { |
171 | | /// The length that `Prk::expand()` should expand its input to. |
172 | | fn len(&self) -> usize; |
173 | | } |
174 | | |
175 | | #[derive(Clone)] |
176 | | enum PrkMode { |
177 | | Expand { |
178 | | key_bytes: [u8; MAX_HKDF_PRK_LEN], |
179 | | key_len: usize, |
180 | | }, |
181 | | ExtractExpand { |
182 | | secret: Arc<ZeroizeBoxSlice<u8>>, |
183 | | salt: Arc<[u8]>, |
184 | | }, |
185 | | } |
186 | | |
187 | | impl PrkMode { |
188 | 0 | fn fill(&self, algorithm: Algorithm, out: &mut [u8], info: &[u8]) -> Result<(), Unspecified> { |
189 | 0 | let digest = digest::match_digest_type(&algorithm.0.digest_algorithm().id).as_const_ptr(); |
190 | | |
191 | 0 | match &self { |
192 | 0 | PrkMode::Expand { key_bytes, key_len } => unsafe { |
193 | 0 | if 1 != indicator_check!(HKDF_expand( |
194 | 0 | out.as_mut_ptr(), |
195 | 0 | out.len(), |
196 | 0 | digest, |
197 | 0 | key_bytes.as_ptr(), |
198 | 0 | *key_len, |
199 | 0 | info.as_ptr(), |
200 | 0 | info.len(), |
201 | 0 | )) { |
202 | 0 | return Err(Unspecified); |
203 | 0 | } |
204 | | }, |
205 | 0 | PrkMode::ExtractExpand { secret, salt } => { |
206 | 0 | if 1 != indicator_check!(unsafe { |
207 | 0 | HKDF( |
208 | 0 | out.as_mut_ptr(), |
209 | 0 | out.len(), |
210 | 0 | digest, |
211 | 0 | secret.as_ptr(), |
212 | 0 | secret.len(), |
213 | 0 | salt.as_ptr(), |
214 | 0 | salt.len(), |
215 | 0 | info.as_ptr(), |
216 | 0 | info.len(), |
217 | 0 | ) |
218 | 0 | }) { |
219 | 0 | return Err(Unspecified); |
220 | 0 | } |
221 | | } |
222 | | } |
223 | | |
224 | 0 | Ok(()) |
225 | 0 | } |
226 | | } |
227 | | |
228 | | impl fmt::Debug for PrkMode { |
229 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
230 | 0 | match self { |
231 | 0 | Self::Expand { .. } => f.debug_struct("Expand").finish_non_exhaustive(), |
232 | 0 | Self::ExtractExpand { .. } => f.debug_struct("ExtractExpand").finish_non_exhaustive(), |
233 | | } |
234 | 0 | } |
235 | | } |
236 | | |
237 | | struct ZeroizeBoxSlice<T: Zeroize>(Box<[T]>); |
238 | | |
239 | | impl<T: Zeroize> core::ops::Deref for ZeroizeBoxSlice<T> { |
240 | | type Target = [T]; |
241 | | |
242 | 0 | fn deref(&self) -> &Self::Target { |
243 | 0 | &self.0 |
244 | 0 | } |
245 | | } |
246 | | |
247 | | impl<T: Clone + Zeroize> From<&[T]> for ZeroizeBoxSlice<T> { |
248 | 0 | fn from(value: &[T]) -> Self { |
249 | 0 | Self(Vec::from(value).into_boxed_slice()) |
250 | 0 | } |
251 | | } |
252 | | |
253 | | impl<T: Zeroize> Drop for ZeroizeBoxSlice<T> { |
254 | 0 | fn drop(&mut self) { |
255 | 0 | self.0.zeroize(); |
256 | 0 | } |
257 | | } |
258 | | |
259 | | /// A HKDF PRK (pseudorandom key). |
260 | | #[derive(Clone)] |
261 | | pub struct Prk { |
262 | | algorithm: Algorithm, |
263 | | mode: PrkMode, |
264 | | } |
265 | | |
266 | | impl Drop for Prk { |
267 | 0 | fn drop(&mut self) { |
268 | | if let PrkMode::Expand { |
269 | 0 | ref mut key_bytes, .. |
270 | 0 | } = self.mode |
271 | 0 | { |
272 | 0 | key_bytes.zeroize(); |
273 | 0 | } |
274 | 0 | } |
275 | | } |
276 | | |
277 | | #[allow(clippy::missing_fields_in_debug)] |
278 | | impl fmt::Debug for Prk { |
279 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
280 | 0 | f.debug_struct("hkdf::Prk") |
281 | 0 | .field("algorithm", &self.algorithm.0) |
282 | 0 | .field("mode", &self.mode) |
283 | 0 | .finish() |
284 | 0 | } |
285 | | } |
286 | | |
287 | | impl Prk { |
288 | | /// Construct a new `Prk` directly with the given value. |
289 | | /// |
290 | | /// Usually one can avoid using this. It is useful when the application |
291 | | /// intentionally wants to leak the PRK secret, e.g. to implement |
292 | | /// `SSLKEYLOGFILE` functionality. |
293 | | /// |
294 | | // # FIPS |
295 | | // The following conditions must be met: |
296 | | // * Algorithm is one of the following: |
297 | | // * `HKDF_SHA1_FOR_LEGACY_USE_ONLY` |
298 | | // * `HKDF_SHA256` |
299 | | // * `HKDF_SHA384` |
300 | | // * `HKDF_SHA512` |
301 | | // * The `info_len` from [`Prk::expand`] is non-zero. |
302 | | // |
303 | | /// # Panics |
304 | | /// Panics if the given Prk length exceeds the limit |
305 | | #[must_use] |
306 | 0 | pub fn new_less_safe(algorithm: Algorithm, value: &[u8]) -> Self { |
307 | 0 | Prk::try_new_less_safe(algorithm, value).expect("Prk length limit exceeded.") |
308 | 0 | } |
309 | | |
310 | 0 | fn try_new_less_safe(algorithm: Algorithm, value: &[u8]) -> Result<Prk, Unspecified> { |
311 | 0 | let key_len = value.len(); |
312 | 0 | if key_len > MAX_HKDF_PRK_LEN { |
313 | 0 | return Err(Unspecified); |
314 | 0 | } |
315 | 0 | let mut key_bytes = [0u8; MAX_HKDF_PRK_LEN]; |
316 | 0 | key_bytes[0..key_len].copy_from_slice(value); |
317 | 0 | Ok(Self { |
318 | 0 | algorithm, |
319 | 0 | mode: PrkMode::Expand { key_bytes, key_len }, |
320 | 0 | }) |
321 | 0 | } |
322 | | |
323 | | /// The [HKDF-Expand] operation. |
324 | | /// |
325 | | /// [HKDF-Expand]: https://tools.ietf.org/html/rfc5869#section-2.3 |
326 | | /// |
327 | | /// # Errors |
328 | | /// Returns `error::Unspecified` if: |
329 | | /// * `len` is more than 255 times the digest algorithm's output length. |
330 | | // # FIPS |
331 | | // The following conditions must be met: |
332 | | // * `Prk` must be constructed using `Salt::extract` prior to calling |
333 | | // this method. |
334 | | // * After concatination of the `info` slices the resulting `[u8].len() > 0` is true. |
335 | | #[inline] |
336 | 0 | pub fn expand<'a, L: KeyType>( |
337 | 0 | &'a self, |
338 | 0 | info: &'a [&'a [u8]], |
339 | 0 | len: L, |
340 | 0 | ) -> Result<Okm<'a, L>, Unspecified> { |
341 | 0 | let len_cached = len.len(); |
342 | 0 | if len_cached > 255 * self.algorithm.0.digest_algorithm().output_len { |
343 | 0 | return Err(Unspecified); |
344 | 0 | } |
345 | 0 | Ok(Okm { |
346 | 0 | prk: self, |
347 | 0 | info, |
348 | 0 | len, |
349 | 0 | }) |
350 | 0 | } |
351 | | } |
352 | | |
353 | | impl From<Okm<'_, Algorithm>> for Prk { |
354 | 0 | fn from(okm: Okm<Algorithm>) -> Self { |
355 | 0 | let algorithm = okm.len; |
356 | 0 | let key_len = okm.len.len(); |
357 | 0 | let mut key_bytes = [0u8; MAX_HKDF_PRK_LEN]; |
358 | 0 | okm.fill(&mut key_bytes[0..key_len]).unwrap(); |
359 | | |
360 | 0 | Self { |
361 | 0 | algorithm, |
362 | 0 | mode: PrkMode::Expand { key_bytes, key_len }, |
363 | 0 | } |
364 | 0 | } |
365 | | } |
366 | | |
367 | | /// An HKDF OKM (Output Keying Material) |
368 | | /// |
369 | | /// Intentionally not `Clone` or `Copy` as an OKM is generally only safe to |
370 | | /// use once. |
371 | | pub struct Okm<'a, L: KeyType> { |
372 | | prk: &'a Prk, |
373 | | info: &'a [&'a [u8]], |
374 | | len: L, |
375 | | } |
376 | | |
377 | | impl<L: KeyType> fmt::Debug for Okm<'_, L> { |
378 | 0 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
379 | 0 | f.debug_struct("hkdf::Okm").field("prk", &self.prk).finish() |
380 | 0 | } |
381 | | } |
382 | | |
383 | | /// Concatenates info slices into a contiguous buffer for HKDF operations. |
384 | | /// Uses stack allocation for typical cases, heap allocation for large info. |
385 | | /// Info is public context data per RFC 5869, so no zeroization is needed. |
386 | | #[inline] |
387 | 0 | fn concatenate_info<F, R>(info: &[&[u8]], f: F) -> R |
388 | 0 | where |
389 | 0 | F: FnOnce(&[u8]) -> R, |
390 | | { |
391 | 0 | let info_len: usize = info.iter().map(|s| s.len()).sum(); Unexecuted instantiation: aws_lc_rs::hkdf::concatenate_info::<<aws_lc_rs::hkdf::Okm<aws_lc_rs::hkdf::Algorithm>>::fill::{closure#0}, core::result::Result<(), aws_lc_rs::error::Unspecified>>::{closure#0}Unexecuted instantiation: aws_lc_rs::hkdf::concatenate_info::<<aws_lc_rs::hkdf::Okm<aws_lc_rs::hmac::Algorithm>>::fill::{closure#0}, core::result::Result<(), aws_lc_rs::error::Unspecified>>::{closure#0}Unexecuted instantiation: aws_lc_rs::hkdf::concatenate_info::<<aws_lc_rs::hkdf::Okm<&aws_lc_rs::aead::Algorithm>>::fill::{closure#0}, core::result::Result<(), aws_lc_rs::error::Unspecified>>::{closure#0}Unexecuted instantiation: aws_lc_rs::hkdf::concatenate_info::<<aws_lc_rs::hkdf::Okm<&aws_lc_rs::cipher::Algorithm>>::fill::{closure#0}, core::result::Result<(), aws_lc_rs::error::Unspecified>>::{closure#0}Unexecuted instantiation: aws_lc_rs::hkdf::concatenate_info::<<aws_lc_rs::hkdf::Okm<&aws_lc_rs::aead::quic::Algorithm>>::fill::{closure#0}, core::result::Result<(), aws_lc_rs::error::Unspecified>>::{closure#0} |
392 | | |
393 | | // Info is public; no need to zeroize. |
394 | 0 | if info_len <= HKDF_INFO_DEFAULT_CAPACITY_LEN { |
395 | | // Use stack buffer for typical case (avoids heap allocation) |
396 | 0 | let mut stack_buf = [0u8; HKDF_INFO_DEFAULT_CAPACITY_LEN]; |
397 | 0 | let mut pos = 0; |
398 | 0 | for &slice in info { |
399 | 0 | stack_buf[pos..pos + slice.len()].copy_from_slice(slice); |
400 | 0 | pos += slice.len(); |
401 | 0 | } |
402 | | |
403 | 0 | f(&stack_buf[..info_len]) |
404 | | } else { |
405 | | // Heap allocation for rare large info case |
406 | 0 | let mut heap_buf = Vec::with_capacity(info_len); |
407 | 0 | for &slice in info { |
408 | 0 | heap_buf.extend_from_slice(slice); |
409 | 0 | } |
410 | | |
411 | 0 | f(&heap_buf) |
412 | | } |
413 | 0 | } Unexecuted instantiation: aws_lc_rs::hkdf::concatenate_info::<<aws_lc_rs::hkdf::Okm<aws_lc_rs::hkdf::Algorithm>>::fill::{closure#0}, core::result::Result<(), aws_lc_rs::error::Unspecified>>Unexecuted instantiation: aws_lc_rs::hkdf::concatenate_info::<<aws_lc_rs::hkdf::Okm<aws_lc_rs::hmac::Algorithm>>::fill::{closure#0}, core::result::Result<(), aws_lc_rs::error::Unspecified>>Unexecuted instantiation: aws_lc_rs::hkdf::concatenate_info::<<aws_lc_rs::hkdf::Okm<&aws_lc_rs::aead::Algorithm>>::fill::{closure#0}, core::result::Result<(), aws_lc_rs::error::Unspecified>>Unexecuted instantiation: aws_lc_rs::hkdf::concatenate_info::<<aws_lc_rs::hkdf::Okm<&aws_lc_rs::cipher::Algorithm>>::fill::{closure#0}, core::result::Result<(), aws_lc_rs::error::Unspecified>>Unexecuted instantiation: aws_lc_rs::hkdf::concatenate_info::<<aws_lc_rs::hkdf::Okm<&aws_lc_rs::aead::quic::Algorithm>>::fill::{closure#0}, core::result::Result<(), aws_lc_rs::error::Unspecified>> |
414 | | |
415 | | impl<L: KeyType> Okm<'_, L> { |
416 | | /// The `OkmLength` given to `Prk::expand()`. |
417 | | #[inline] |
418 | 0 | pub fn len(&self) -> &L { |
419 | 0 | &self.len |
420 | 0 | } Unexecuted instantiation: <aws_lc_rs::hkdf::Okm<aws_lc_rs::hkdf::Algorithm>>::len Unexecuted instantiation: <aws_lc_rs::hkdf::Okm<aws_lc_rs::hmac::Algorithm>>::len Unexecuted instantiation: <aws_lc_rs::hkdf::Okm<&aws_lc_rs::aead::Algorithm>>::len Unexecuted instantiation: <aws_lc_rs::hkdf::Okm<&aws_lc_rs::cipher::Algorithm>>::len Unexecuted instantiation: <aws_lc_rs::hkdf::Okm<&aws_lc_rs::aead::quic::Algorithm>>::len |
421 | | |
422 | | /// Fills `out` with the output of the HKDF-Expand operation for the given |
423 | | /// inputs. |
424 | | /// |
425 | | // # FIPS |
426 | | // The following conditions must be met: |
427 | | // * Algorithm is one of the following: |
428 | | // * `HKDF_SHA1_FOR_LEGACY_USE_ONLY` |
429 | | // * `HKDF_SHA256` |
430 | | // * `HKDF_SHA384` |
431 | | // * `HKDF_SHA512` |
432 | | // * The [`Okm`] was constructed from a [`Prk`] created with [`Salt::extract`] and: |
433 | | // * The `value.len()` passed to [`Salt::new`] was non-zero. |
434 | | // * The `info_len` from [`Prk::expand`] was non-zero. |
435 | | // |
436 | | /// # Errors |
437 | | /// `error::Unspecified` if the requested output length differs from the length specified by |
438 | | /// `L: KeyType`. |
439 | | #[inline] |
440 | 0 | pub fn fill(self, out: &mut [u8]) -> Result<(), Unspecified> { |
441 | 0 | if out.len() != self.len.len() { |
442 | 0 | return Err(Unspecified); |
443 | 0 | } |
444 | | |
445 | 0 | concatenate_info(self.info, |info_bytes| { |
446 | 0 | self.prk.mode.fill(self.prk.algorithm, out, info_bytes) |
447 | 0 | }) Unexecuted instantiation: <aws_lc_rs::hkdf::Okm<aws_lc_rs::hkdf::Algorithm>>::fill::{closure#0}Unexecuted instantiation: <aws_lc_rs::hkdf::Okm<aws_lc_rs::hmac::Algorithm>>::fill::{closure#0}Unexecuted instantiation: <aws_lc_rs::hkdf::Okm<&aws_lc_rs::aead::Algorithm>>::fill::{closure#0}Unexecuted instantiation: <aws_lc_rs::hkdf::Okm<&aws_lc_rs::cipher::Algorithm>>::fill::{closure#0}Unexecuted instantiation: <aws_lc_rs::hkdf::Okm<&aws_lc_rs::aead::quic::Algorithm>>::fill::{closure#0} |
448 | 0 | } Unexecuted instantiation: <aws_lc_rs::hkdf::Okm<aws_lc_rs::hkdf::Algorithm>>::fill Unexecuted instantiation: <aws_lc_rs::hkdf::Okm<aws_lc_rs::hmac::Algorithm>>::fill Unexecuted instantiation: <aws_lc_rs::hkdf::Okm<&aws_lc_rs::aead::Algorithm>>::fill Unexecuted instantiation: <aws_lc_rs::hkdf::Okm<&aws_lc_rs::cipher::Algorithm>>::fill Unexecuted instantiation: <aws_lc_rs::hkdf::Okm<&aws_lc_rs::aead::quic::Algorithm>>::fill |
449 | | } |
450 | | |
451 | | #[cfg(test)] |
452 | | mod tests { |
453 | | use crate::hkdf::{Salt, HKDF_SHA256, HKDF_SHA384}; |
454 | | |
455 | | #[cfg(feature = "fips")] |
456 | | mod fips; |
457 | | |
458 | | #[test] |
459 | | fn hkdf_coverage() { |
460 | | // Something would have gone horribly wrong for this to not pass, but we test this so our |
461 | | // coverage reports will look better. |
462 | | assert_ne!(HKDF_SHA256, HKDF_SHA384); |
463 | | assert_eq!("Algorithm(Algorithm(SHA256))", format!("{HKDF_SHA256:?}")); |
464 | | } |
465 | | |
466 | | #[test] |
467 | | fn test_debug() { |
468 | | const SALT: &[u8; 32] = &[ |
469 | | 29, 113, 120, 243, 11, 202, 39, 222, 206, 81, 163, 184, 122, 153, 52, 192, 98, 195, |
470 | | 240, 32, 34, 19, 160, 128, 178, 111, 97, 232, 113, 101, 221, 143, |
471 | | ]; |
472 | | const SECRET1: &[u8; 32] = &[ |
473 | | 157, 191, 36, 107, 110, 131, 193, 6, 175, 226, 193, 3, 168, 133, 165, 181, 65, 120, |
474 | | 194, 152, 31, 92, 37, 191, 73, 222, 41, 112, 207, 236, 196, 174, |
475 | | ]; |
476 | | |
477 | | const INFO1: &[&[u8]] = &[ |
478 | | &[ |
479 | | 2, 130, 61, 83, 192, 248, 63, 60, 211, 73, 169, 66, 101, 160, 196, 212, 250, 113, |
480 | | ], |
481 | | &[ |
482 | | 80, 46, 248, 123, 78, 204, 171, 178, 67, 204, 96, 27, 131, 24, |
483 | | ], |
484 | | ]; |
485 | | |
486 | | let alg = HKDF_SHA256; |
487 | | let salt = Salt::new(alg, SALT); |
488 | | let prk = salt.extract(SECRET1); |
489 | | let okm = prk.expand(INFO1, alg).unwrap(); |
490 | | |
491 | | assert_eq!( |
492 | | "hkdf::Salt { algorithm: Algorithm(SHA256) }", |
493 | | format!("{salt:?}") |
494 | | ); |
495 | | assert_eq!( |
496 | | "hkdf::Prk { algorithm: Algorithm(SHA256), mode: ExtractExpand { .. } }", |
497 | | format!("{prk:?}") |
498 | | ); |
499 | | assert_eq!( |
500 | | "hkdf::Okm { prk: hkdf::Prk { algorithm: Algorithm(SHA256), mode: ExtractExpand { .. } } }", |
501 | | format!("{okm:?}") |
502 | | ); |
503 | | } |
504 | | |
505 | | #[test] |
506 | | fn test_long_salt() { |
507 | | // Test with a salt longer than the previous 80-byte limit |
508 | | let long_salt = vec![0x42u8; 100]; |
509 | | |
510 | | // This should work now that we removed the MAX_HKDF_SALT_LEN restriction |
511 | | let salt = Salt::new(HKDF_SHA256, &long_salt); |
512 | | |
513 | | // Test the extract operation still works |
514 | | let secret = b"test secret key material"; |
515 | | let prk = salt.extract(secret); |
516 | | |
517 | | // Test expand operation |
518 | | let info_data = b"test context info"; |
519 | | let info = [info_data.as_slice()]; |
520 | | let okm = prk.expand(&info, HKDF_SHA256).unwrap(); |
521 | | |
522 | | // Fill output buffer |
523 | | let mut output = [0u8; 32]; |
524 | | okm.fill(&mut output).unwrap(); |
525 | | |
526 | | // Test with an even longer salt to demonstrate flexibility |
527 | | let very_long_salt = vec![0x55u8; 500]; |
528 | | let very_long_salt_obj = Salt::new(HKDF_SHA256, &very_long_salt); |
529 | | let prk2 = very_long_salt_obj.extract(secret); |
530 | | let okm2 = prk2.expand(&info, HKDF_SHA256).unwrap(); |
531 | | let mut output2 = [0u8; 32]; |
532 | | okm2.fill(&mut output2).unwrap(); |
533 | | |
534 | | // Verify outputs are different (they should be due to different salts) |
535 | | assert_ne!(output, output2); |
536 | | } |
537 | | } |