1use alloc::boxed::Box;
2use alloc::vec::Vec;
3
4use zeroize::Zeroize;
5
6use super::{ActiveKeyExchange, hmac};
7use crate::error::Error;
8use crate::version::TLS13;
9
10pub struct HkdfExpanderUsingHmac(Box<dyn hmac::Key>);
12
13impl HkdfExpanderUsingHmac {
14 fn expand_unchecked(&self, info: &[&[u8]], output: &mut [u8]) {
15 let mut term = hmac::Tag::new(b"");
16
17 for (n, chunk) in output
18 .chunks_mut(self.0.tag_len())
19 .enumerate()
20 {
21 term = self
22 .0
23 .sign_concat(term.as_ref(), info, &[(n + 1) as u8]);
24 chunk.copy_from_slice(&term.as_ref()[..chunk.len()]);
25 }
26 }
27}
28
29impl HkdfExpander for HkdfExpanderUsingHmac {
30 fn expand_slice(&self, info: &[&[u8]], output: &mut [u8]) -> Result<(), OutputLengthError> {
31 if output.len() > 255 * self.0.tag_len() {
32 return Err(OutputLengthError);
33 }
34
35 self.expand_unchecked(info, output);
36 Ok(())
37 }
38
39 fn expand_block(&self, info: &[&[u8]]) -> OkmBlock {
40 let mut tag = [0u8; hmac::Tag::MAX_LEN];
41 let reduced_tag = &mut tag[..self.0.tag_len()];
42 self.expand_unchecked(info, reduced_tag);
43 OkmBlock::new(reduced_tag)
44 }
45
46 fn hash_len(&self) -> usize {
47 self.0.tag_len()
48 }
49}
50
51#[allow(clippy::exhaustive_structs)]
53pub struct HkdfUsingHmac<'a>(pub &'a dyn hmac::Hmac);
54
55impl Hkdf for HkdfUsingHmac<'_> {
56 fn extract_from_zero_ikm(&self, salt: Option<&[u8]>) -> Box<dyn HkdfExpander> {
57 let zeroes = [0u8; hmac::Tag::MAX_LEN];
58 Box::new(HkdfExpanderUsingHmac(self.0.with_key(
59 &self.extract_prk_from_secret(salt, &zeroes[..self.0.hash_output_len()]),
60 )))
61 }
62
63 fn extract_from_secret(&self, salt: Option<&[u8]>, secret: &[u8]) -> Box<dyn HkdfExpander> {
64 Box::new(HkdfExpanderUsingHmac(
65 self.0
66 .with_key(&self.extract_prk_from_secret(salt, secret)),
67 ))
68 }
69
70 fn expander_for_okm(&self, okm: &OkmBlock) -> Box<dyn HkdfExpander> {
71 Box::new(HkdfExpanderUsingHmac(self.0.with_key(okm.as_ref())))
72 }
73
74 fn hmac_sign(&self, key: &OkmBlock, message: &[u8]) -> hmac::Tag {
75 self.0
76 .with_key(key.as_ref())
77 .sign(&[message])
78 }
79}
80
81impl HkdfPrkExtract for HkdfUsingHmac<'_> {
82 fn extract_prk_from_secret(&self, salt: Option<&[u8]>, secret: &[u8]) -> Vec<u8> {
83 let zeroes = [0u8; hmac::Tag::MAX_LEN];
84 let salt = match salt {
85 Some(salt) => salt,
86 None => &zeroes[..self.0.hash_output_len()],
87 };
88 self.0
89 .with_key(salt)
90 .sign(&[secret])
91 .as_ref()
92 .to_vec()
93 }
94}
95
96pub trait HkdfExpander: Send + Sync {
98 fn expand_slice(&self, info: &[&[u8]], output: &mut [u8]) -> Result<(), OutputLengthError>;
110
111 fn expand_block(&self, info: &[&[u8]]) -> OkmBlock;
121
122 fn hash_len(&self) -> usize;
126}
127
128pub trait Hkdf: Send + Sync {
136 fn extract_from_zero_ikm(&self, salt: Option<&[u8]>) -> Box<dyn HkdfExpander>;
142
143 fn extract_from_secret(&self, salt: Option<&[u8]>, secret: &[u8]) -> Box<dyn HkdfExpander>;
147
148 fn extract_from_kx_shared_secret(
156 &self,
157 salt: Option<&[u8]>,
158 kx: Box<dyn ActiveKeyExchange>,
159 peer_pub_key: &[u8],
160 ) -> Result<Box<dyn HkdfExpander>, Error> {
161 Ok(self.extract_from_secret(
162 salt,
163 kx.complete_for_tls_version(peer_pub_key, &TLS13)?
164 .secret_bytes(),
165 ))
166 }
167
168 fn expander_for_okm(&self, okm: &OkmBlock) -> Box<dyn HkdfExpander>;
170
171 fn hmac_sign(&self, key: &OkmBlock, message: &[u8]) -> hmac::Tag;
179
180 fn fips(&self) -> bool {
182 false
183 }
184}
185
186pub(crate) trait HkdfPrkExtract: Hkdf {
196 fn extract_prk_from_secret(&self, salt: Option<&[u8]>, secret: &[u8]) -> Vec<u8>;
203}
204
205pub fn expand<T, const N: usize>(expander: &dyn HkdfExpander, info: &[&[u8]]) -> T
215where
216 T: From<[u8; N]>,
217{
218 let mut output = [0u8; N];
219 expander
220 .expand_slice(info, &mut output)
221 .expect("expand type parameter T is too large");
222 T::from(output)
223}
224
225#[derive(Clone)]
227pub struct OkmBlock {
228 buf: [u8; Self::MAX_LEN],
229 used: usize,
230}
231
232impl OkmBlock {
233 pub fn new(bytes: &[u8]) -> Self {
237 let mut tag = Self {
238 buf: [0u8; Self::MAX_LEN],
239 used: bytes.len(),
240 };
241 tag.buf[..bytes.len()].copy_from_slice(bytes);
242 tag
243 }
244
245 pub const MAX_LEN: usize = 64;
247}
248
249impl Drop for OkmBlock {
250 fn drop(&mut self) {
251 self.buf.zeroize();
252 }
253}
254
255impl AsRef<[u8]> for OkmBlock {
256 fn as_ref(&self) -> &[u8] {
257 &self.buf[..self.used]
258 }
259}
260
261#[allow(clippy::exhaustive_structs)]
264#[derive(Debug)]
265pub struct OutputLengthError;
266
267#[cfg(all(test, feature = "ring"))]
268mod tests {
269 use std::prelude::v1::*;
270
271 use super::{Hkdf, HkdfUsingHmac, expand};
272 use crate::crypto::ring::hmac;
275
276 struct ByteArray<const N: usize>([u8; N]);
277
278 impl<const N: usize> From<[u8; N]> for ByteArray<N> {
279 fn from(array: [u8; N]) -> Self {
280 Self(array)
281 }
282 }
283
284 #[test]
287 fn test_case_1() {
288 let hkdf = HkdfUsingHmac(&hmac::HMAC_SHA256);
289 let ikm = &[0x0b; 22];
290 let salt = &[
291 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
292 ];
293 let info: &[&[u8]] = &[
294 &[0xf0, 0xf1, 0xf2],
295 &[0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9],
296 ];
297
298 let output: ByteArray<42> = expand(
299 hkdf.extract_from_secret(Some(salt), ikm)
300 .as_ref(),
301 info,
302 );
303
304 assert_eq!(
305 &output.0,
306 &[
307 0x3c, 0xb2, 0x5f, 0x25, 0xfa, 0xac, 0xd5, 0x7a, 0x90, 0x43, 0x4f, 0x64, 0xd0, 0x36,
308 0x2f, 0x2a, 0x2d, 0x2d, 0x0a, 0x90, 0xcf, 0x1a, 0x5a, 0x4c, 0x5d, 0xb0, 0x2d, 0x56,
309 0xec, 0xc4, 0xc5, 0xbf, 0x34, 0x00, 0x72, 0x08, 0xd5, 0xb8, 0x87, 0x18, 0x58, 0x65
310 ]
311 );
312 }
313
314 #[test]
315 fn test_case_2() {
316 let hkdf = HkdfUsingHmac(&hmac::HMAC_SHA256);
317 let ikm: Vec<u8> = (0x00u8..=0x4f).collect();
318 let salt: Vec<u8> = (0x60u8..=0xaf).collect();
319 let info: Vec<u8> = (0xb0u8..=0xff).collect();
320
321 let output: ByteArray<82> = expand(
322 hkdf.extract_from_secret(Some(&salt), &ikm)
323 .as_ref(),
324 &[&info],
325 );
326
327 assert_eq!(
328 &output.0,
329 &[
330 0xb1, 0x1e, 0x39, 0x8d, 0xc8, 0x03, 0x27, 0xa1, 0xc8, 0xe7, 0xf7, 0x8c, 0x59, 0x6a,
331 0x49, 0x34, 0x4f, 0x01, 0x2e, 0xda, 0x2d, 0x4e, 0xfa, 0xd8, 0xa0, 0x50, 0xcc, 0x4c,
332 0x19, 0xaf, 0xa9, 0x7c, 0x59, 0x04, 0x5a, 0x99, 0xca, 0xc7, 0x82, 0x72, 0x71, 0xcb,
333 0x41, 0xc6, 0x5e, 0x59, 0x0e, 0x09, 0xda, 0x32, 0x75, 0x60, 0x0c, 0x2f, 0x09, 0xb8,
334 0x36, 0x77, 0x93, 0xa9, 0xac, 0xa3, 0xdb, 0x71, 0xcc, 0x30, 0xc5, 0x81, 0x79, 0xec,
335 0x3e, 0x87, 0xc1, 0x4c, 0x01, 0xd5, 0xc1, 0xf3, 0x43, 0x4f, 0x1d, 0x87
336 ]
337 );
338 }
339
340 #[test]
341 fn test_case_3() {
342 let hkdf = HkdfUsingHmac(&hmac::HMAC_SHA256);
343 let ikm = &[0x0b; 22];
344 let salt = &[];
345 let info = &[];
346
347 let output: ByteArray<42> = expand(
348 hkdf.extract_from_secret(Some(salt), ikm)
349 .as_ref(),
350 info,
351 );
352
353 assert_eq!(
354 &output.0,
355 &[
356 0x8d, 0xa4, 0xe7, 0x75, 0xa5, 0x63, 0xc1, 0x8f, 0x71, 0x5f, 0x80, 0x2a, 0x06, 0x3c,
357 0x5a, 0x31, 0xb8, 0xa1, 0x1f, 0x5c, 0x5e, 0xe1, 0x87, 0x9e, 0xc3, 0x45, 0x4e, 0x5f,
358 0x3c, 0x73, 0x8d, 0x2d, 0x9d, 0x20, 0x13, 0x95, 0xfa, 0xa4, 0xb6, 0x1a, 0x96, 0xc8
359 ]
360 );
361 }
362
363 #[test]
364 fn test_salt_not_provided() {
365 let hkdf = HkdfUsingHmac(&hmac::HMAC_SHA384);
372 let ikm = &[0x0b; 40];
373 let info = &[&b"hel"[..], &b"lo"[..]];
374
375 let output: ByteArray<96> = expand(
376 hkdf.extract_from_secret(None, ikm)
377 .as_ref(),
378 info,
379 );
380
381 assert_eq!(
382 &output.0,
383 &[
384 0xd5, 0x45, 0xdd, 0x3a, 0xff, 0x5b, 0x19, 0x46, 0xd4, 0x86, 0xfd, 0xb8, 0xd8, 0x88,
385 0x2e, 0xe0, 0x1c, 0xc1, 0xa5, 0x48, 0xb6, 0x05, 0x75, 0xe4, 0xd7, 0x5d, 0x0f, 0x5f,
386 0x23, 0x40, 0xee, 0x6c, 0x9e, 0x7c, 0x65, 0xd0, 0xee, 0x79, 0xdb, 0xb2, 0x07, 0x1d,
387 0x66, 0xa5, 0x50, 0xc4, 0x8a, 0xa3, 0x93, 0x86, 0x8b, 0x7c, 0x69, 0x41, 0x6b, 0x3e,
388 0x61, 0x44, 0x98, 0xb8, 0xc2, 0xfc, 0x82, 0x82, 0xae, 0xcd, 0x46, 0xcf, 0xb1, 0x47,
389 0xdc, 0xd0, 0x69, 0x0d, 0x19, 0xad, 0xe6, 0x6c, 0x70, 0xfe, 0x87, 0x92, 0x04, 0xb6,
390 0x82, 0x2d, 0x97, 0x7e, 0x46, 0x80, 0x4c, 0xe5, 0x76, 0x72, 0xb4, 0xb8
391 ]
392 );
393 }
394
395 #[test]
396 fn test_output_length_bounds() {
397 let hkdf = HkdfUsingHmac(&hmac::HMAC_SHA256);
398 let ikm = &[];
399 let info = &[];
400
401 let mut output = [0u8; 32 * 255 + 1];
402 assert!(
403 hkdf.extract_from_secret(None, ikm)
404 .expand_slice(info, &mut output)
405 .is_err()
406 );
407 }
408}