/rust/registry/src/index.crates.io-1949cf8c6b5b557f/aws-lc-rs-1.14.1/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 static HKDF_SHA1_FOR_LEGACY_USE_ONLY: Algorithm = |
63 | | Algorithm(hmac::HMAC_SHA1_FOR_LEGACY_USE_ONLY); |
64 | | |
65 | | /// HKDF using HMAC-SHA-256. |
66 | | pub static HKDF_SHA256: Algorithm = Algorithm(hmac::HMAC_SHA256); |
67 | | |
68 | | /// HKDF using HMAC-SHA-384. |
69 | | pub static HKDF_SHA384: Algorithm = Algorithm(hmac::HMAC_SHA384); |
70 | | |
71 | | /// HKDF using HMAC-SHA-512. |
72 | | pub static HKDF_SHA512: Algorithm = Algorithm(hmac::HMAC_SHA512); |
73 | | |
74 | | /// General Salt length's for HKDF don't normally exceed 256 bits. |
75 | | /// We set the limit to something tolerable, so that the Salt structure can be stack allocatable. |
76 | | const MAX_HKDF_SALT_LEN: usize = 80; |
77 | | |
78 | | /// General Info length's for HKDF don't normally exceed 256 bits. |
79 | | /// We set the default capacity to a value larger than should be needed |
80 | | /// so that the value passed to |`HKDF_expand`| is only allocated once. |
81 | | const HKDF_INFO_DEFAULT_CAPACITY_LEN: usize = 300; |
82 | | |
83 | | /// The maximum output size of a PRK computed by |`HKDF_extract`| is the maximum digest |
84 | | /// size that can be outputted by *AWS-LC*. |
85 | | const MAX_HKDF_PRK_LEN: usize = digest::MAX_OUTPUT_LEN; |
86 | | |
87 | | impl KeyType for Algorithm { |
88 | 0 | fn len(&self) -> usize { |
89 | 0 | self.0.digest_algorithm().output_len |
90 | 0 | } |
91 | | } |
92 | | |
93 | | /// A salt for HKDF operations. |
94 | | pub struct Salt { |
95 | | algorithm: Algorithm, |
96 | | bytes: [u8; MAX_HKDF_SALT_LEN], |
97 | | len: usize, |
98 | | } |
99 | | |
100 | | #[allow(clippy::missing_fields_in_debug)] |
101 | | impl fmt::Debug for Salt { |
102 | 0 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
103 | 0 | f.debug_struct("hkdf::Salt") |
104 | 0 | .field("algorithm", &self.algorithm.0) |
105 | 0 | .finish() |
106 | 0 | } |
107 | | } |
108 | | |
109 | | impl Drop for Salt { |
110 | 0 | fn drop(&mut self) { |
111 | 0 | self.bytes.zeroize(); |
112 | 0 | } |
113 | | } |
114 | | |
115 | | impl Salt { |
116 | | /// Constructs a new `Salt` with the given value based on the given digest |
117 | | /// algorithm. |
118 | | /// |
119 | | /// Constructing a `Salt` is relatively expensive so it is good to reuse a |
120 | | /// `Salt` object instead of re-constructing `Salt`s with the same value. |
121 | | /// |
122 | | // # FIPS |
123 | | // The following conditions must be met: |
124 | | // * Algorithm is one of the following: |
125 | | // * `HKDF_SHA1_FOR_LEGACY_USE_ONLY` |
126 | | // * `HKDF_SHA256` |
127 | | // * `HKDF_SHA384` |
128 | | // * `HKDF_SHA512` |
129 | | // * `value.len() > 0` is true |
130 | | // |
131 | | /// # Panics |
132 | | /// `new` panics if the salt length exceeds the limit |
133 | | #[must_use] |
134 | 0 | pub fn new(algorithm: Algorithm, value: &[u8]) -> Self { |
135 | 0 | Salt::try_new(algorithm, value).expect("Salt length limit exceeded.") |
136 | 0 | } |
137 | | |
138 | 0 | fn try_new(algorithm: Algorithm, value: &[u8]) -> Result<Salt, Unspecified> { |
139 | 0 | let salt_len = value.len(); |
140 | 0 | if salt_len > MAX_HKDF_SALT_LEN { |
141 | 0 | return Err(Unspecified); |
142 | 0 | } |
143 | 0 | let mut salt_bytes = [0u8; MAX_HKDF_SALT_LEN]; |
144 | 0 | salt_bytes[0..salt_len].copy_from_slice(value); |
145 | 0 | Ok(Self { |
146 | 0 | algorithm, |
147 | 0 | bytes: salt_bytes, |
148 | 0 | len: salt_len, |
149 | 0 | }) |
150 | 0 | } |
151 | | |
152 | | /// The [HKDF-Extract] operation. |
153 | | /// |
154 | | /// [HKDF-Extract]: https://tools.ietf.org/html/rfc5869#section-2.2 |
155 | | /// |
156 | | /// # Panics |
157 | | /// Panics if the extract operation is unable to be performed |
158 | | #[inline] |
159 | | #[must_use] |
160 | 0 | pub fn extract(&self, secret: &[u8]) -> Prk { |
161 | 0 | Prk { |
162 | 0 | algorithm: self.algorithm, |
163 | 0 | mode: PrkMode::ExtractExpand { |
164 | 0 | secret: Arc::from(ZeroizeBoxSlice::from(secret)), |
165 | 0 | salt: self.bytes, |
166 | 0 | salt_len: self.len, |
167 | 0 | }, |
168 | 0 | } |
169 | 0 | } |
170 | | |
171 | | /// The algorithm used to derive this salt. |
172 | | #[inline] |
173 | | #[must_use] |
174 | 0 | pub fn algorithm(&self) -> Algorithm { |
175 | 0 | Algorithm(self.algorithm.hmac_algorithm()) |
176 | 0 | } |
177 | | } |
178 | | |
179 | | #[allow(clippy::assertions_on_constants)] |
180 | | const _: () = assert!(MAX_HKDF_PRK_LEN <= MAX_HKDF_SALT_LEN); |
181 | | |
182 | | impl From<Okm<'_, Algorithm>> for Salt { |
183 | 0 | fn from(okm: Okm<'_, Algorithm>) -> Self { |
184 | 0 | let algorithm = okm.prk.algorithm; |
185 | 0 | let mut salt_bytes = [0u8; MAX_HKDF_SALT_LEN]; |
186 | 0 | let salt_len = okm.len().len(); |
187 | 0 | okm.fill(&mut salt_bytes[..salt_len]).unwrap(); |
188 | 0 | Self { |
189 | 0 | algorithm, |
190 | 0 | bytes: salt_bytes, |
191 | 0 | len: salt_len, |
192 | 0 | } |
193 | 0 | } |
194 | | } |
195 | | |
196 | | /// The length of the OKM (Output Keying Material) for a `Prk::expand()` call. |
197 | | #[allow(clippy::len_without_is_empty)] |
198 | | pub trait KeyType { |
199 | | /// The length that `Prk::expand()` should expand its input to. |
200 | | fn len(&self) -> usize; |
201 | | } |
202 | | |
203 | | #[derive(Clone)] |
204 | | enum PrkMode { |
205 | | Expand { |
206 | | key_bytes: [u8; MAX_HKDF_PRK_LEN], |
207 | | key_len: usize, |
208 | | }, |
209 | | ExtractExpand { |
210 | | secret: Arc<ZeroizeBoxSlice<u8>>, |
211 | | salt: [u8; MAX_HKDF_SALT_LEN], |
212 | | salt_len: usize, |
213 | | }, |
214 | | } |
215 | | |
216 | | impl PrkMode { |
217 | 0 | fn fill(&self, algorithm: Algorithm, out: &mut [u8], info: &[u8]) -> Result<(), Unspecified> { |
218 | 0 | let digest = *digest::match_digest_type(&algorithm.0.digest_algorithm().id); |
219 | | |
220 | 0 | match &self { |
221 | 0 | PrkMode::Expand { key_bytes, key_len } => unsafe { |
222 | 0 | if 1 != indicator_check!(HKDF_expand( |
223 | 0 | out.as_mut_ptr(), |
224 | 0 | out.len(), |
225 | 0 | digest, |
226 | 0 | key_bytes.as_ptr(), |
227 | 0 | *key_len, |
228 | 0 | info.as_ptr(), |
229 | 0 | info.len(), |
230 | 0 | )) { |
231 | 0 | return Err(Unspecified); |
232 | 0 | } |
233 | | }, |
234 | | PrkMode::ExtractExpand { |
235 | 0 | secret, |
236 | 0 | salt, |
237 | 0 | salt_len, |
238 | | } => { |
239 | 0 | if 1 != indicator_check!(unsafe { |
240 | 0 | HKDF( |
241 | 0 | out.as_mut_ptr(), |
242 | 0 | out.len(), |
243 | 0 | digest, |
244 | 0 | secret.as_ptr(), |
245 | 0 | secret.len(), |
246 | 0 | salt.as_ptr(), |
247 | 0 | *salt_len, |
248 | 0 | info.as_ptr(), |
249 | 0 | info.len(), |
250 | 0 | ) |
251 | 0 | }) { |
252 | 0 | return Err(Unspecified); |
253 | 0 | } |
254 | | } |
255 | | } |
256 | | |
257 | 0 | Ok(()) |
258 | 0 | } |
259 | | } |
260 | | |
261 | | impl fmt::Debug for PrkMode { |
262 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
263 | 0 | match self { |
264 | 0 | Self::Expand { .. } => f.debug_struct("Expand").finish_non_exhaustive(), |
265 | 0 | Self::ExtractExpand { .. } => f.debug_struct("ExtractExpand").finish_non_exhaustive(), |
266 | | } |
267 | 0 | } |
268 | | } |
269 | | |
270 | | struct ZeroizeBoxSlice<T: Zeroize>(Box<[T]>); |
271 | | |
272 | | impl<T: Zeroize> core::ops::Deref for ZeroizeBoxSlice<T> { |
273 | | type Target = [T]; |
274 | | |
275 | 0 | fn deref(&self) -> &Self::Target { |
276 | 0 | &self.0 |
277 | 0 | } |
278 | | } |
279 | | |
280 | | impl<T: Clone + Zeroize> From<&[T]> for ZeroizeBoxSlice<T> { |
281 | 0 | fn from(value: &[T]) -> Self { |
282 | 0 | Self(Vec::from(value).into_boxed_slice()) |
283 | 0 | } |
284 | | } |
285 | | |
286 | | impl<T: Zeroize> Drop for ZeroizeBoxSlice<T> { |
287 | 0 | fn drop(&mut self) { |
288 | 0 | self.0.zeroize(); |
289 | 0 | } |
290 | | } |
291 | | |
292 | | /// A HKDF PRK (pseudorandom key). |
293 | | #[derive(Clone)] |
294 | | pub struct Prk { |
295 | | algorithm: Algorithm, |
296 | | mode: PrkMode, |
297 | | } |
298 | | |
299 | | #[allow(clippy::missing_fields_in_debug)] |
300 | | impl fmt::Debug for Prk { |
301 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
302 | 0 | f.debug_struct("hkdf::Prk") |
303 | 0 | .field("algorithm", &self.algorithm.0) |
304 | 0 | .field("mode", &self.mode) |
305 | 0 | .finish() |
306 | 0 | } |
307 | | } |
308 | | |
309 | | impl Prk { |
310 | | /// Construct a new `Prk` directly with the given value. |
311 | | /// |
312 | | /// Usually one can avoid using this. It is useful when the application |
313 | | /// intentionally wants to leak the PRK secret, e.g. to implement |
314 | | /// `SSLKEYLOGFILE` functionality. |
315 | | /// |
316 | | // # FIPS |
317 | | // This function must not be used. |
318 | | // |
319 | | // See [`Salt::extract`]. |
320 | | // |
321 | | /// # Panics |
322 | | /// Panics if the given Prk length exceeds the limit |
323 | | #[must_use] |
324 | 0 | pub fn new_less_safe(algorithm: Algorithm, value: &[u8]) -> Self { |
325 | 0 | Prk::try_new_less_safe(algorithm, value).expect("Prk length limit exceeded.") |
326 | 0 | } |
327 | | |
328 | 0 | fn try_new_less_safe(algorithm: Algorithm, value: &[u8]) -> Result<Prk, Unspecified> { |
329 | 0 | let key_len = value.len(); |
330 | 0 | if key_len > MAX_HKDF_PRK_LEN { |
331 | 0 | return Err(Unspecified); |
332 | 0 | } |
333 | 0 | let mut key_bytes = [0u8; MAX_HKDF_PRK_LEN]; |
334 | 0 | key_bytes[0..key_len].copy_from_slice(value); |
335 | 0 | Ok(Self { |
336 | 0 | algorithm, |
337 | 0 | mode: PrkMode::Expand { key_bytes, key_len }, |
338 | 0 | }) |
339 | 0 | } |
340 | | |
341 | | /// The [HKDF-Expand] operation. |
342 | | /// |
343 | | /// [HKDF-Expand]: https://tools.ietf.org/html/rfc5869#section-2.3 |
344 | | /// |
345 | | /// # Errors |
346 | | /// Returns `error::Unspecified` if: |
347 | | /// * `len` is more than 255 times the digest algorithm's output length. |
348 | | // # FIPS |
349 | | // The following conditions must be met: |
350 | | // * `Prk` must be constructed using `Salt::extract` prior to calling |
351 | | // this method. |
352 | | // * After concatination of the `info` slices the resulting `[u8].len() > 0` is true. |
353 | | #[inline] |
354 | 0 | pub fn expand<'a, L: KeyType>( |
355 | 0 | &'a self, |
356 | 0 | info: &'a [&'a [u8]], |
357 | 0 | len: L, |
358 | 0 | ) -> Result<Okm<'a, L>, Unspecified> { |
359 | 0 | let len_cached = len.len(); |
360 | 0 | if len_cached > 255 * self.algorithm.0.digest_algorithm().output_len { |
361 | 0 | return Err(Unspecified); |
362 | 0 | } |
363 | 0 | let mut info_bytes: Vec<u8> = Vec::with_capacity(HKDF_INFO_DEFAULT_CAPACITY_LEN); |
364 | 0 | let mut info_len = 0; |
365 | 0 | for &byte_ary in info { |
366 | 0 | info_bytes.extend_from_slice(byte_ary); |
367 | 0 | info_len += byte_ary.len(); |
368 | 0 | } |
369 | 0 | let info_bytes = info_bytes.into_boxed_slice(); |
370 | 0 | Ok(Okm { |
371 | 0 | prk: self, |
372 | 0 | info_bytes, |
373 | 0 | info_len, |
374 | 0 | len, |
375 | 0 | }) |
376 | 0 | } |
377 | | } |
378 | | |
379 | | impl From<Okm<'_, Algorithm>> for Prk { |
380 | 0 | fn from(okm: Okm<Algorithm>) -> Self { |
381 | 0 | let algorithm = okm.len; |
382 | 0 | let key_len = okm.len.len(); |
383 | 0 | let mut key_bytes = [0u8; MAX_HKDF_PRK_LEN]; |
384 | 0 | okm.fill(&mut key_bytes[0..key_len]).unwrap(); |
385 | | |
386 | 0 | Self { |
387 | 0 | algorithm, |
388 | 0 | mode: PrkMode::Expand { key_bytes, key_len }, |
389 | 0 | } |
390 | 0 | } |
391 | | } |
392 | | |
393 | | /// An HKDF OKM (Output Keying Material) |
394 | | /// |
395 | | /// Intentionally not `Clone` or `Copy` as an OKM is generally only safe to |
396 | | /// use once. |
397 | | pub struct Okm<'a, L: KeyType> { |
398 | | prk: &'a Prk, |
399 | | info_bytes: Box<[u8]>, |
400 | | info_len: usize, |
401 | | len: L, |
402 | | } |
403 | | |
404 | | impl<L: KeyType> fmt::Debug for Okm<'_, L> { |
405 | 0 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
406 | 0 | f.debug_struct("hkdf::Okm").field("prk", &self.prk).finish() |
407 | 0 | } |
408 | | } |
409 | | |
410 | | impl<L: KeyType> Drop for Okm<'_, L> { |
411 | 0 | fn drop(&mut self) { |
412 | 0 | self.info_bytes.zeroize(); |
413 | 0 | } Unexecuted instantiation: <aws_lc_rs::hkdf::Okm<aws_lc_rs::hkdf::Algorithm> as core::ops::drop::Drop>::drop Unexecuted instantiation: <aws_lc_rs::hkdf::Okm<aws_lc_rs::hmac::Algorithm> as core::ops::drop::Drop>::drop Unexecuted instantiation: <aws_lc_rs::hkdf::Okm<&aws_lc_rs::aead::Algorithm> as core::ops::drop::Drop>::drop Unexecuted instantiation: <aws_lc_rs::hkdf::Okm<&aws_lc_rs::cipher::Algorithm> as core::ops::drop::Drop>::drop Unexecuted instantiation: <aws_lc_rs::hkdf::Okm<&aws_lc_rs::aead::quic::Algorithm> as core::ops::drop::Drop>::drop |
414 | | } |
415 | | |
416 | | impl<L: KeyType> Okm<'_, L> { |
417 | | /// The `OkmLength` given to `Prk::expand()`. |
418 | | #[inline] |
419 | 0 | pub fn len(&self) -> &L { |
420 | 0 | &self.len |
421 | 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 |
422 | | |
423 | | /// Fills `out` with the output of the HKDF-Expand operation for the given |
424 | | /// inputs. |
425 | | /// |
426 | | // # FIPS |
427 | | // The following conditions must be met: |
428 | | // * Algorithm is one of the following: |
429 | | // * `HKDF_SHA1_FOR_LEGACY_USE_ONLY` |
430 | | // * `HKDF_SHA256` |
431 | | // * `HKDF_SHA384` |
432 | | // * `HKDF_SHA512` |
433 | | // * The [`Okm`] was constructed from a [`Prk`] created with [`Salt::extract`] and: |
434 | | // * The `value.len()` passed to [`Salt::new`] was non-zero. |
435 | | // * The `info_len` from [`Prk::expand`] was non-zero. |
436 | | // |
437 | | /// # Errors |
438 | | /// `error::Unspecified` if the requested output length differs from the length specified by |
439 | | /// `L: KeyType`. |
440 | | #[inline] |
441 | 0 | pub fn fill(self, out: &mut [u8]) -> Result<(), Unspecified> { |
442 | 0 | if out.len() != self.len.len() { |
443 | 0 | return Err(Unspecified); |
444 | 0 | } |
445 | | |
446 | 0 | self.prk |
447 | 0 | .mode |
448 | 0 | .fill(self.prk.algorithm, out, &self.info_bytes[..self.info_len])?; |
449 | | |
450 | 0 | Ok(()) |
451 | 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 |
452 | | } |
453 | | |
454 | | #[cfg(test)] |
455 | | mod tests { |
456 | | use crate::hkdf::{Salt, HKDF_SHA256, HKDF_SHA384}; |
457 | | |
458 | | #[cfg(feature = "fips")] |
459 | | mod fips; |
460 | | |
461 | | #[test] |
462 | | fn hkdf_coverage() { |
463 | | // Something would have gone horribly wrong for this to not pass, but we test this so our |
464 | | // coverage reports will look better. |
465 | | assert_ne!(HKDF_SHA256, HKDF_SHA384); |
466 | | assert_eq!("Algorithm(Algorithm(SHA256))", format!("{HKDF_SHA256:?}")); |
467 | | } |
468 | | |
469 | | #[test] |
470 | | fn test_debug() { |
471 | | const SALT: &[u8; 32] = &[ |
472 | | 29, 113, 120, 243, 11, 202, 39, 222, 206, 81, 163, 184, 122, 153, 52, 192, 98, 195, |
473 | | 240, 32, 34, 19, 160, 128, 178, 111, 97, 232, 113, 101, 221, 143, |
474 | | ]; |
475 | | const SECRET1: &[u8; 32] = &[ |
476 | | 157, 191, 36, 107, 110, 131, 193, 6, 175, 226, 193, 3, 168, 133, 165, 181, 65, 120, |
477 | | 194, 152, 31, 92, 37, 191, 73, 222, 41, 112, 207, 236, 196, 174, |
478 | | ]; |
479 | | |
480 | | const INFO1: &[&[u8]] = &[ |
481 | | &[ |
482 | | 2, 130, 61, 83, 192, 248, 63, 60, 211, 73, 169, 66, 101, 160, 196, 212, 250, 113, |
483 | | ], |
484 | | &[ |
485 | | 80, 46, 248, 123, 78, 204, 171, 178, 67, 204, 96, 27, 131, 24, |
486 | | ], |
487 | | ]; |
488 | | |
489 | | let alg = HKDF_SHA256; |
490 | | let salt = Salt::new(alg, SALT); |
491 | | let prk = salt.extract(SECRET1); |
492 | | let okm = prk.expand(INFO1, alg).unwrap(); |
493 | | |
494 | | assert_eq!( |
495 | | "hkdf::Salt { algorithm: Algorithm(SHA256) }", |
496 | | format!("{salt:?}") |
497 | | ); |
498 | | assert_eq!( |
499 | | "hkdf::Prk { algorithm: Algorithm(SHA256), mode: ExtractExpand { .. } }", |
500 | | format!("{prk:?}") |
501 | | ); |
502 | | assert_eq!( |
503 | | "hkdf::Okm { prk: hkdf::Prk { algorithm: Algorithm(SHA256), mode: ExtractExpand { .. } } }", |
504 | | format!("{okm:?}") |
505 | | ); |
506 | | } |
507 | | } |