crypto_async_rs/
hkdf.rs

1//! # HKDF (HMAC-based Key Derivation Function)
2//!
3//! This module provides HKDF implementation for key derivation as specified in RFC 5869.
4//! HKDF is a simple key derivation function that can be used to derive one or more
5//! cryptographically strong secret keys from some source of initial keying material.
6//!
7//! ## Features
8//!
9//! - RFC 5869 compliant HKDF implementation
10//! - Two-step process: Extract and Expand
11//! - Support for arbitrary output lengths
12//! - Uses HMAC-SHA256 as the underlying hash function
13//!
14//! ## Example
15//!
16//! ```rust
17//! use crypto_async_rs::hkdf;
18//!
19//! let salt = b"random-salt";
20//! let ikm = b"input-key-material";
21//! let info = b"application-info";
22//!
23//! // Extract phase - derive a pseudorandom key
24//! let prk = hkdf::extract_sha256(salt, ikm);
25//!
26//! // Expand phase - derive multiple keys
27//! let encryption_key = hkdf::expand_sha256(&prk, b"encryption", 32);
28//! let mac_key = hkdf::expand_sha256(&prk, b"mac", 32);
29//! ```
30
31use crate::hmac;
32
33/// Extract a fixed-length pseudorandom key from input keying material
34///
35/// This is the first phase of HKDF. It takes some source of initial keying material
36/// and derives from it a fixed-length pseudorandom key.
37///
38/// # Arguments
39/// * `salt` - Optional salt value (can be empty)
40/// * `ikm` - Input keying material
41///
42/// # Returns
43/// A 32-byte pseudorandom key
44///
45/// # Example
46/// ```rust
47/// use crypto_async_rs::hkdf;
48///
49/// let salt = b"random-salt";
50/// let ikm = b"input-key-material";
51/// let prk = hkdf::extract_sha256(salt, ikm);
52/// ```
53pub fn extract_sha256(salt: &[u8], ikm: &[u8]) -> [u8; 32] {
54    hmac::hmac_sha256(salt, ikm)
55}
56
57/// Generic extract function for use with different HMAC implementations
58pub fn extract<T: AsRef<[u8]>>(salt: &[u8], ikm: &[u8], hmac: fn(&[u8], &[u8]) -> T) -> T {
59    hmac(salt, ikm)
60}
61
62/// Expand a pseudorandom key into additional pseudorandom keys
63///
64/// This is the second phase of HKDF. It expands the pseudorandom key into
65/// one or more additional pseudorandom keys of arbitrary length.
66///
67/// # Arguments
68/// * `prk` - Pseudorandom key (usually from extract phase)
69/// * `info` - Optional context and application specific information
70/// * `l` - Desired output length in bytes
71///
72/// # Returns
73/// A boxed slice containing the derived key material
74///
75/// # Example
76/// ```rust
77/// use crypto_async_rs::hkdf;
78///
79/// let prk = [0u8; 32]; // Pseudorandom key from extract phase
80/// let info = b"encryption-key";
81/// let key = hkdf::expand_sha256(&prk, info, 32);
82/// ```
83pub fn expand_sha256(prk: &[u8], info: &[u8], l: usize) -> Box<[u8]> {
84    //    let n = ((l / 32) as f32).ceil() as usize;
85    let mut result: Vec<u8> = Vec::with_capacity(l);
86    let text_max_len = 32 + info.len() + 1;
87
88    let mut t: Vec<u8> = Vec::with_capacity(text_max_len);
89    let mut i = 0usize;
90    while result.len() < l {
91        t.extend_from_slice(info);
92        i += 1;
93        t.push(i as u8);
94        let hmac_hash = hmac::hmac_sha256(prk, &t);
95        t.truncate(0);
96        t.extend_from_slice(&hmac_hash);
97        result.extend_from_slice(&hmac_hash);
98    }
99
100    result.truncate(l);
101    result.into_boxed_slice()
102}
103
104pub fn expand<T: AsRef<[u8]>>(
105    prk: &[u8],
106    info: &[u8],
107    l: usize,
108    hmac: fn(&[u8], &[u8]) -> T,
109) -> Box<[u8]> {
110    //    let n = ((l / 32) as f32).ceil() as usize;
111    let mut result: Vec<u8> = Vec::with_capacity(l);
112    let text_max_len = 64 + info.len() + 1;
113
114    let mut t: Vec<u8> = Vec::with_capacity(text_max_len);
115    let mut i = 0usize;
116    while result.len() < l {
117        t.extend_from_slice(info);
118        i += 1;
119        t.push(i as u8);
120        let hmac_hash = hmac(prk, &t);
121        t.truncate(0);
122        t.extend_from_slice(&hmac_hash.as_ref());
123        result.extend_from_slice(&hmac_hash.as_ref());
124    }
125
126    result.truncate(l);
127    result.into_boxed_slice()
128}
129
130#[cfg(test)]
131mod tests {
132    use super::*;
133    use crate::hmac::hmac_sha256;
134
135    #[test]
136    fn test_extract_expand_sha256_1() {
137        //(80 octets)
138        let ikm: [u8; 80] = [
139            0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
140            0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b,
141            0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29,
142            0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
143            0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45,
144            0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
145        ];
146        //(80 octets)
147        let salt: [u8; 80] = [
148            0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d,
149            0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b,
150            0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
151            0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
152            0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5,
153            0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
154        ];
155
156        let info: [u8; 80] = [
157            0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd,
158            0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb,
159            0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9,
160            0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
161            0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5,
162            0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
163        ];
164
165        let l = 82usize;
166
167        let prk: [u8; 32] = [
168            0x06, 0xa6, 0xb8, 0x8c, 0x58, 0x53, 0x36, 0x1a, 0x06, 0x10, 0x4c, 0x9c, 0xeb, 0x35,
169            0xb4, 0x5c, 0xef, 0x76, 0x00, 0x14, 0x90, 0x46, 0x71, 0x01, 0x4a, 0x19, 0x3f, 0x40,
170            0xc1, 0x5f, 0xc2, 0x44,
171        ];
172
173        let okm: [u8; 82] = [
174            0xb1, 0x1e, 0x39, 0x8d, 0xc8, 0x03, 0x27, 0xa1, 0xc8, 0xe7, 0xf7, 0x8c, 0x59, 0x6a,
175            0x49, 0x34, 0x4f, 0x01, 0x2e, 0xda, 0x2d, 0x4e, 0xfa, 0xd8, 0xa0, 0x50, 0xcc, 0x4c,
176            0x19, 0xaf, 0xa9, 0x7c, 0x59, 0x04, 0x5a, 0x99, 0xca, 0xc7, 0x82, 0x72, 0x71, 0xcb,
177            0x41, 0xc6, 0x5e, 0x59, 0x0e, 0x09, 0xda, 0x32, 0x75, 0x60, 0x0c, 0x2f, 0x09, 0xb8,
178            0x36, 0x77, 0x93, 0xa9, 0xac, 0xa3, 0xdb, 0x71, 0xcc, 0x30, 0xc5, 0x81, 0x79, 0xec,
179            0x3e, 0x87, 0xc1, 0x4c, 0x01, 0xd5, 0xc1, 0xf3, 0x43, 0x4f, 0x1d, 0x87,
180        ];
181
182        let result = extract(&salt, &ikm, hmac_sha256);
183        assert_eq!(result, prk);
184
185        let result: &[u8] = &expand(&prk, &info, l, hmac_sha256);
186        assert_eq!(result.len(), l);
187        assert_eq!(result, &okm[..]);
188    }
189
190    #[test]
191    fn test_extract_expand_sha256_2() {
192        let ikm: [u8; 22] = [
193            0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
194            0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
195        ];
196        //(80 octets)
197        let salt: [u8; 13] = [
198            0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
199        ];
200
201        let info: [u8; 10] = [0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9];
202
203        let l = 42usize;
204
205        let prk: [u8; 32] = [
206            0x07, 0x77, 0x09, 0x36, 0x2c, 0x2e, 0x32, 0xdf, 0x0d, 0xdc, 0x3f, 0x0d, 0xc4, 0x7b,
207            0xba, 0x63, 0x90, 0xb6, 0xc7, 0x3b, 0xb5, 0x0f, 0x9c, 0x31, 0x22, 0xec, 0x84, 0x4a,
208            0xd7, 0xc2, 0xb3, 0xe5,
209        ];
210
211        let okm: [u8; 42] = [
212            0x3c, 0xb2, 0x5f, 0x25, 0xfa, 0xac, 0xd5, 0x7a, 0x90, 0x43, 0x4f, 0x64, 0xd0, 0x36,
213            0x2f, 0x2a, 0x2d, 0x2d, 0x0a, 0x90, 0xcf, 0x1a, 0x5a, 0x4c, 0x5d, 0xb0, 0x2d, 0x56,
214            0xec, 0xc4, 0xc5, 0xbf, 0x34, 0x00, 0x72, 0x08, 0xd5, 0xb8, 0x87, 0x18, 0x58, 0x65,
215        ];
216
217        let result = extract(&salt, &ikm, hmac_sha256);
218        assert_eq!(result, prk);
219
220        let result: &[u8] = &expand(&prk, &info, l, hmac_sha256);
221        assert_eq!(result.len(), l);
222        assert_eq!(result, &okm[..]);
223    }
224
225    #[test]
226    fn test_extract_expand_sha256_3() {
227        let ikm: [u8; 22] = [
228            0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
229            0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
230        ];
231        let l = 42usize;
232
233        let prk: [u8; 32] = [
234            0x19, 0xef, 0x24, 0xa3, 0x2c, 0x71, 0x7b, 0x16, 0x7f, 0x33, 0xa9, 0x1d, 0x6f, 0x64,
235            0x8b, 0xdf, 0x96, 0x59, 0x67, 0x76, 0xaf, 0xdb, 0x63, 0x77, 0xac, 0x43, 0x4c, 0x1c,
236            0x29, 0x3c, 0xcb, 0x04,
237        ];
238
239        let okm: [u8; 42] = [
240            0x8d, 0xa4, 0xe7, 0x75, 0xa5, 0x63, 0xc1, 0x8f, 0x71, 0x5f, 0x80, 0x2a, 0x06, 0x3c,
241            0x5a, 0x31, 0xb8, 0xa1, 0x1f, 0x5c, 0x5e, 0xe1, 0x87, 0x9e, 0xc3, 0x45, 0x4e, 0x5f,
242            0x3c, 0x73, 0x8d, 0x2d, 0x9d, 0x20, 0x13, 0x95, 0xfa, 0xa4, 0xb6, 0x1a, 0x96, 0xc8,
243        ];
244
245        let result = extract(&[], &ikm, hmac_sha256);
246        assert_eq!(result, prk);
247
248        let result: &[u8] = &expand(&prk, &[], l, hmac_sha256);
249        assert_eq!(result.len(), l);
250        assert_eq!(result, &okm[..]);
251    }
252}