1use alloc::boxed::Box;
2use alloc::vec::Vec;
3
4use zeroize::Zeroize;
5
6use super::{hmac, ActiveKeyExchange};
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
51pub struct HkdfUsingHmac<'a>(pub &'a dyn hmac::Hmac);
53
54impl Hkdf for HkdfUsingHmac<'_> {
55 fn extract_from_zero_ikm(&self, salt: Option<&[u8]>) -> Box<dyn HkdfExpander> {
56 let zeroes = [0u8; hmac::Tag::MAX_LEN];
57 Box::new(HkdfExpanderUsingHmac(self.0.with_key(
58 &self.extract_prk_from_secret(salt, &zeroes[..self.0.hash_output_len()]),
59 )))
60 }
61
62 fn extract_from_secret(&self, salt: Option<&[u8]>, secret: &[u8]) -> Box<dyn HkdfExpander> {
63 Box::new(HkdfExpanderUsingHmac(
64 self.0
65 .with_key(&self.extract_prk_from_secret(salt, secret)),
66 ))
67 }
68
69 fn expander_for_okm(&self, okm: &OkmBlock) -> Box<dyn HkdfExpander> {
70 Box::new(HkdfExpanderUsingHmac(self.0.with_key(okm.as_ref())))
71 }
72
73 fn hmac_sign(&self, key: &OkmBlock, message: &[u8]) -> hmac::Tag {
74 self.0
75 .with_key(key.as_ref())
76 .sign(&[message])
77 }
78}
79
80impl HkdfPrkExtract for HkdfUsingHmac<'_> {
81 fn extract_prk_from_secret(&self, salt: Option<&[u8]>, secret: &[u8]) -> Vec<u8> {
82 let zeroes = [0u8; hmac::Tag::MAX_LEN];
83 let salt = match salt {
84 Some(salt) => salt,
85 None => &zeroes[..self.0.hash_output_len()],
86 };
87 self.0
88 .with_key(salt)
89 .sign(&[secret])
90 .as_ref()
91 .to_vec()
92 }
93}
94
95pub trait HkdfExpander: Send + Sync {
97 fn expand_slice(&self, info: &[&[u8]], output: &mut [u8]) -> Result<(), OutputLengthError>;
109
110 fn expand_block(&self, info: &[&[u8]]) -> OkmBlock;
120
121 fn hash_len(&self) -> usize;
125}
126
127pub trait Hkdf: Send + Sync {
135 fn extract_from_zero_ikm(&self, salt: Option<&[u8]>) -> Box<dyn HkdfExpander>;
141
142 fn extract_from_secret(&self, salt: Option<&[u8]>, secret: &[u8]) -> Box<dyn HkdfExpander>;
146
147 fn extract_from_kx_shared_secret(
155 &self,
156 salt: Option<&[u8]>,
157 kx: Box<dyn ActiveKeyExchange>,
158 peer_pub_key: &[u8],
159 ) -> Result<Box<dyn HkdfExpander>, Error> {
160 Ok(self.extract_from_secret(
161 salt,
162 kx.complete_for_tls_version(peer_pub_key, &TLS13)?
163 .secret_bytes(),
164 ))
165 }
166
167 fn expander_for_okm(&self, okm: &OkmBlock) -> Box<dyn HkdfExpander>;
169
170 fn hmac_sign(&self, key: &OkmBlock, message: &[u8]) -> hmac::Tag;
178
179 #[cfg(unstable_api_not_supported)] 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#[derive(Debug)]
264pub struct OutputLengthError;
265
266#[cfg(all(test, feature = "ring"))]
267mod tests {
268 use std::prelude::v1::*;
269
270 use super::{expand, Hkdf, HkdfUsingHmac};
271 use crate::crypto::ring::hmac;
274
275 struct ByteArray<const N: usize>([u8; N]);
276
277 impl<const N: usize> From<[u8; N]> for ByteArray<N> {
278 fn from(array: [u8; N]) -> Self {
279 Self(array)
280 }
281 }
282
283 #[test]
286 fn test_case_1() {
287 let hkdf = HkdfUsingHmac(&hmac::HMAC_SHA256);
288 let ikm = &[0x0b; 22];
289 let salt = &[
290 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
291 ];
292 let info: &[&[u8]] = &[
293 &[0xf0, 0xf1, 0xf2],
294 &[0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9],
295 ];
296
297 let output: ByteArray<42> = expand(
298 hkdf.extract_from_secret(Some(salt), ikm)
299 .as_ref(),
300 info,
301 );
302
303 assert_eq!(
304 &output.0,
305 &[
306 0x3c, 0xb2, 0x5f, 0x25, 0xfa, 0xac, 0xd5, 0x7a, 0x90, 0x43, 0x4f, 0x64, 0xd0, 0x36,
307 0x2f, 0x2a, 0x2d, 0x2d, 0x0a, 0x90, 0xcf, 0x1a, 0x5a, 0x4c, 0x5d, 0xb0, 0x2d, 0x56,
308 0xec, 0xc4, 0xc5, 0xbf, 0x34, 0x00, 0x72, 0x08, 0xd5, 0xb8, 0x87, 0x18, 0x58, 0x65
309 ]
310 );
311 }
312
313 #[test]
314 fn test_case_2() {
315 let hkdf = HkdfUsingHmac(&hmac::HMAC_SHA256);
316 let ikm: Vec<u8> = (0x00u8..=0x4f).collect();
317 let salt: Vec<u8> = (0x60u8..=0xaf).collect();
318 let info: Vec<u8> = (0xb0u8..=0xff).collect();
319
320 let output: ByteArray<82> = expand(
321 hkdf.extract_from_secret(Some(&salt), &ikm)
322 .as_ref(),
323 &[&info],
324 );
325
326 assert_eq!(
327 &output.0,
328 &[
329 0xb1, 0x1e, 0x39, 0x8d, 0xc8, 0x03, 0x27, 0xa1, 0xc8, 0xe7, 0xf7, 0x8c, 0x59, 0x6a,
330 0x49, 0x34, 0x4f, 0x01, 0x2e, 0xda, 0x2d, 0x4e, 0xfa, 0xd8, 0xa0, 0x50, 0xcc, 0x4c,
331 0x19, 0xaf, 0xa9, 0x7c, 0x59, 0x04, 0x5a, 0x99, 0xca, 0xc7, 0x82, 0x72, 0x71, 0xcb,
332 0x41, 0xc6, 0x5e, 0x59, 0x0e, 0x09, 0xda, 0x32, 0x75, 0x60, 0x0c, 0x2f, 0x09, 0xb8,
333 0x36, 0x77, 0x93, 0xa9, 0xac, 0xa3, 0xdb, 0x71, 0xcc, 0x30, 0xc5, 0x81, 0x79, 0xec,
334 0x3e, 0x87, 0xc1, 0x4c, 0x01, 0xd5, 0xc1, 0xf3, 0x43, 0x4f, 0x1d, 0x87
335 ]
336 );
337 }
338
339 #[test]
340 fn test_case_3() {
341 let hkdf = HkdfUsingHmac(&hmac::HMAC_SHA256);
342 let ikm = &[0x0b; 22];
343 let salt = &[];
344 let info = &[];
345
346 let output: ByteArray<42> = expand(
347 hkdf.extract_from_secret(Some(salt), ikm)
348 .as_ref(),
349 info,
350 );
351
352 assert_eq!(
353 &output.0,
354 &[
355 0x8d, 0xa4, 0xe7, 0x75, 0xa5, 0x63, 0xc1, 0x8f, 0x71, 0x5f, 0x80, 0x2a, 0x06, 0x3c,
356 0x5a, 0x31, 0xb8, 0xa1, 0x1f, 0x5c, 0x5e, 0xe1, 0x87, 0x9e, 0xc3, 0x45, 0x4e, 0x5f,
357 0x3c, 0x73, 0x8d, 0x2d, 0x9d, 0x20, 0x13, 0x95, 0xfa, 0xa4, 0xb6, 0x1a, 0x96, 0xc8
358 ]
359 );
360 }
361
362 #[test]
363 fn test_salt_not_provided() {
364 let hkdf = HkdfUsingHmac(&hmac::HMAC_SHA384);
371 let ikm = &[0x0b; 40];
372 let info = &[&b"hel"[..], &b"lo"[..]];
373
374 let output: ByteArray<96> = expand(
375 hkdf.extract_from_secret(None, ikm)
376 .as_ref(),
377 info,
378 );
379
380 assert_eq!(
381 &output.0,
382 &[
383 0xd5, 0x45, 0xdd, 0x3a, 0xff, 0x5b, 0x19, 0x46, 0xd4, 0x86, 0xfd, 0xb8, 0xd8, 0x88,
384 0x2e, 0xe0, 0x1c, 0xc1, 0xa5, 0x48, 0xb6, 0x05, 0x75, 0xe4, 0xd7, 0x5d, 0x0f, 0x5f,
385 0x23, 0x40, 0xee, 0x6c, 0x9e, 0x7c, 0x65, 0xd0, 0xee, 0x79, 0xdb, 0xb2, 0x07, 0x1d,
386 0x66, 0xa5, 0x50, 0xc4, 0x8a, 0xa3, 0x93, 0x86, 0x8b, 0x7c, 0x69, 0x41, 0x6b, 0x3e,
387 0x61, 0x44, 0x98, 0xb8, 0xc2, 0xfc, 0x82, 0x82, 0xae, 0xcd, 0x46, 0xcf, 0xb1, 0x47,
388 0xdc, 0xd0, 0x69, 0x0d, 0x19, 0xad, 0xe6, 0x6c, 0x70, 0xfe, 0x87, 0x92, 0x04, 0xb6,
389 0x82, 0x2d, 0x97, 0x7e, 0x46, 0x80, 0x4c, 0xe5, 0x76, 0x72, 0xb4, 0xb8
390 ]
391 );
392 }
393
394 #[test]
395 fn test_output_length_bounds() {
396 let hkdf = HkdfUsingHmac(&hmac::HMAC_SHA256);
397 let ikm = &[];
398 let info = &[];
399
400 let mut output = [0u8; 32 * 255 + 1];
401 assert!(hkdf
402 .extract_from_secret(None, ikm)
403 .expand_slice(info, &mut output)
404 .is_err());
405 }
406}