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