1use crate::{error::*, hmac};
23
24pub fn extract_and_expand(
25 salt: &hmac::SigningKey,
26 secret: &[u8],
27 info: &[u8],
28 out: &mut [u8],
29) -> Result<()> {
30 let prk = extract(salt, secret)?;
31 expand(&prk, info, out)?;
32 Ok(())
33}
34
35pub fn extract(salt: &hmac::SigningKey, secret: &[u8]) -> Result<hmac::SigningKey> {
36 let prk = hmac::sign(salt, secret)?;
37 Ok(hmac::SigningKey::new(salt.digest_algorithm(), prk.as_ref()))
38}
39
40pub fn expand(prk: &hmac::SigningKey, info: &[u8], out: &mut [u8]) -> Result<()> {
41 let mut derived =
42 nss::pk11::sym_key::hkdf_expand(prk.digest_alg, &prk.key_value, info, out.len())?;
43 out.swap_with_slice(&mut derived[0..out.len()]);
44 Ok(())
45}
46
47#[cfg(test)]
48mod tests {
49 use super::*;
50 use crate::digest;
51 use nss::ensure_initialized;
52
53 #[test]
54 fn hkdf_produces_correct_result() {
55 ensure_initialized();
56 let secret = hex::decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b").unwrap();
57 let salt = hex::decode("000102030405060708090a0b0c").unwrap();
58 let info = hex::decode("f0f1f2f3f4f5f6f7f8f9").unwrap();
59 let expected_out = hex::decode(
60 "3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b887185865",
61 )
62 .unwrap();
63 let salt = hmac::SigningKey::new(&digest::SHA256, &salt);
64 let mut out = vec![0u8; expected_out.len()];
65 extract_and_expand(&salt, &secret, &info, &mut out).unwrap();
66 assert_eq!(out, expected_out);
67 }
68
69 #[test]
70 fn hkdf_rejects_gigantic_salt() {
71 ensure_initialized();
72 if (u32::MAX as usize) < usize::MAX {
73 let salt_bytes = vec![0; (u32::MAX as usize) + 1];
74 let salt = hmac::SigningKey {
75 digest_alg: &digest::SHA256,
76 key_value: salt_bytes,
77 };
78 let mut out = vec![0u8; 8];
79 assert!(extract_and_expand(&salt, b"secret", b"info", &mut out).is_err());
80 }
81 }
82
83 #[test]
84 fn hkdf_rejects_gigantic_secret() {
85 ensure_initialized();
86 if (u32::MAX as usize) < usize::MAX {
87 let salt = hmac::SigningKey::new(&digest::SHA256, b"salt");
88 let secret = vec![0; (u32::MAX as usize) + 1];
89 let mut out = vec![0u8; 8];
90 assert!(extract_and_expand(&salt, secret.as_slice(), b"info", &mut out).is_err());
91 }
92 }
93
94 #[test]
98 fn hkdf_rejects_gigantic_output_buffers() {
99 ensure_initialized();
100 let salt = hmac::SigningKey::new(&digest::SHA256, b"salt");
101 let mut out = vec![0u8; 8160 + 1]; assert!(extract_and_expand(&salt, b"secret", b"info", &mut out).is_err());
103 }
104
105 #[test]
106 fn hkdf_rejects_zero_length_output_buffer() {
107 ensure_initialized();
108 let salt = hmac::SigningKey::new(&digest::SHA256, b"salt");
109 let mut out = vec![0u8; 0];
110 assert!(extract_and_expand(&salt, b"secret", b"info", &mut out).is_err());
111 }
112
113 #[test]
114 fn hkdf_can_produce_small_output() {
115 ensure_initialized();
116 let salt = hmac::SigningKey::new(&digest::SHA256, b"salt");
117 let mut out = vec![0u8; 1];
118 assert!(extract_and_expand(&salt, b"secret", b"info", &mut out).is_ok());
119 }
120
121 #[test]
122 fn hkdf_accepts_zero_length_info() {
123 ensure_initialized();
124 let salt = hmac::SigningKey::new(&digest::SHA256, b"salt");
125 let mut out = vec![0u8; 32];
126 assert!(extract_and_expand(&salt, b"secret", b"", &mut out).is_ok());
127 }
128
129 #[test]
130 fn hkdf_expand_rejects_short_prk() {
131 ensure_initialized();
132 let prk = hmac::SigningKey::new(&digest::SHA256, b"too short"); let mut out = vec![0u8; 8];
134 assert!(expand(&prk, b"info", &mut out).is_ok());
135 }
136}