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