1+ use rustls:: crypto:: tls13:: { self , Hkdf as RustlsHkdf } ;
2+ use alloc:: boxed:: Box ;
3+ use alloc:: vec;
4+ use core:: mem;
5+ use alloc:: vec:: Vec ;
6+ use wolfcrypt_rs:: * ;
7+
8+ use crate :: error:: check_if_zero;
9+ use crate :: hmac:: hmac:: WCShaHmac ;
10+
11+ pub struct WCHkdfUsingHmac ( pub WCShaHmac ) ;
12+
13+ impl RustlsHkdf for WCHkdfUsingHmac {
14+ fn extract_from_zero_ikm (
15+ & self ,
16+ salt : Option < & [ u8 ] > ,
17+ ) -> Box < dyn rustls:: crypto:: tls13:: HkdfExpander > {
18+ let hash_len = self . 0 . hash_len ( ) ;
19+ let ikm = vec ! [ 0u8 ; hash_len] ;
20+ self . extract_from_secret ( salt, & ikm)
21+ }
22+
23+ fn extract_from_secret (
24+ & self ,
25+ salt : Option < & [ u8 ] > ,
26+ ikm : & [ u8 ] ,
27+ ) -> Box < dyn rustls:: crypto:: tls13:: HkdfExpander > {
28+ let hash_len = self . 0 . hash_len ( ) ;
29+ let mut extracted_key = vec ! [ 0u8 ; hash_len] ;
30+ let zero_salt = vec ! [ 0u8 ; hash_len] ;
31+ let salt_bytes = salt. unwrap_or ( & zero_salt) ;
32+
33+ let ret = unsafe {
34+ wc_HKDF_Extract (
35+ self . 0 . hash_type ( ) . try_into ( ) . unwrap ( ) ,
36+ salt_bytes. as_ptr ( ) ,
37+ salt_bytes. len ( ) as u32 ,
38+ ikm. as_ptr ( ) ,
39+ ikm. len ( ) as u32 ,
40+ extracted_key. as_mut_ptr ( ) ,
41+ )
42+ } ;
43+ check_if_zero ( ret) . unwrap ( ) ;
44+
45+ Box :: new ( WolfHkdfExpander :: new ( extracted_key, self . 0 . hash_type ( ) . try_into ( ) . unwrap ( ) , self . 0 . hash_len ( ) ) )
46+ }
47+
48+ fn expander_for_okm (
49+ & self ,
50+ okm : & rustls:: crypto:: tls13:: OkmBlock ,
51+ ) -> Box < dyn rustls:: crypto:: tls13:: HkdfExpander > {
52+ Box :: new ( WolfHkdfExpander {
53+ extracted_key : okm. as_ref ( ) . to_vec ( ) ,
54+ hash_type : self . 0 . hash_type ( ) . try_into ( ) . unwrap ( ) ,
55+ hash_len : self . 0 . hash_len ( ) ,
56+ } )
57+ }
58+
59+ fn hmac_sign (
60+ & self ,
61+ key : & rustls:: crypto:: tls13:: OkmBlock ,
62+ message : & [ u8 ] ,
63+ ) -> rustls:: crypto:: hmac:: Tag {
64+ let mut hmac = vec ! [ 0u8 ; self . 0 . hash_len( ) ] ;
65+ let mut hmac_ctx = unsafe { mem:: zeroed ( ) } ;
66+
67+ let mut ret = unsafe {
68+ wc_HmacSetKey (
69+ & mut hmac_ctx,
70+ self . 0 . hash_type ( ) . try_into ( ) . unwrap ( ) ,
71+ key. as_ref ( ) . as_ptr ( ) ,
72+ key. as_ref ( ) . len ( ) as u32 ,
73+ )
74+ } ;
75+ check_if_zero ( ret) . unwrap ( ) ;
76+
77+ ret = unsafe {
78+ wc_HmacUpdate (
79+ & mut hmac_ctx,
80+ message. as_ptr ( ) ,
81+ message. len ( ) as u32 ,
82+ )
83+ } ;
84+ check_if_zero ( ret) . unwrap ( ) ;
85+
86+ ret = unsafe {
87+ wc_HmacFinal (
88+ & mut hmac_ctx,
89+ hmac. as_mut_ptr ( ) ,
90+ )
91+ } ;
92+ check_if_zero ( ret) . unwrap ( ) ;
93+
94+ unsafe {
95+ wc_HmacFree (
96+ & mut hmac_ctx,
97+ )
98+ } ;
99+ check_if_zero ( ret) . unwrap ( ) ;
100+
101+ rustls:: crypto:: hmac:: Tag :: new ( & hmac)
102+ }
103+ }
104+
105+ /// Expander implementation that holds the extracted key material from HKDF extract phase
106+ struct WolfHkdfExpander {
107+ extracted_key : Vec < u8 > , // The pseudorandom key (PRK) output from HKDF-Extract
108+ hash_type : i32 , // The wolfSSL hash algorithm identifier
109+ hash_len : usize , // Length of the hash function output
110+ }
111+
112+ impl WolfHkdfExpander {
113+ fn new ( extracted_key : Vec < u8 > , hash_type : i32 , hash_len : usize ) -> Self {
114+ Self {
115+ extracted_key,
116+ hash_type,
117+ hash_len,
118+ }
119+ }
120+ }
121+
122+ impl tls13:: HkdfExpander for WolfHkdfExpander {
123+ fn expand_slice (
124+ & self ,
125+ info : & [ & [ u8 ] ] ,
126+ output : & mut [ u8 ] ,
127+ ) -> Result < ( ) , tls13:: OutputLengthError > {
128+ let info_concat = info. concat ( ) ;
129+
130+ if output. len ( ) > 255 * self . hash_len {
131+ return Err ( tls13:: OutputLengthError ) ;
132+ }
133+
134+ unsafe {
135+ wc_HKDF_Expand (
136+ self . hash_type ,
137+ self . extracted_key . as_ptr ( ) ,
138+ self . extracted_key . len ( ) as u32 ,
139+ info_concat. as_ptr ( ) ,
140+ info_concat. len ( ) as u32 ,
141+ output. as_mut_ptr ( ) ,
142+ output. len ( ) as u32 ,
143+ ) ;
144+ }
145+
146+ Ok ( ( ) )
147+ }
148+
149+ fn expand_block ( & self , info : & [ & [ u8 ] ] ) -> tls13:: OkmBlock {
150+ let mut output = vec ! [ 0u8 ; self . hash_len] ;
151+ self . expand_slice ( info, & mut output)
152+ . expect ( "expand_block failed" ) ;
153+ tls13:: OkmBlock :: new ( & output)
154+ }
155+
156+ fn hash_len ( & self ) -> usize {
157+ self . hash_len
158+ }
159+ }
160+
161+ #[ cfg( test) ]
162+ mod tests {
163+ use super :: * ;
164+ use hex_literal:: hex;
165+
166+ /// Tests the HKDF implementation against RFC 5869 test vector A.1
167+ /// This is the primary compliance test using SHA-256
168+ #[ test]
169+ fn test_hkdf_sha256 ( ) {
170+ // Test vectors from RFC 5869 Appendix A.1
171+ let ikm = hex ! ( "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b" ) ;
172+ let salt = hex ! ( "000102030405060708090a0b0c" ) ;
173+ let info = hex ! ( "f0f1f2f3f4f5f6f7f8f9" ) ;
174+ let expected_okm = hex ! (
175+ "3cb25f25faacd57a90434f64d0362f2a"
176+ "2d2d0a90cf1a5a4c5db02d56ecc4c5bf"
177+ "34007208d5b887185865"
178+ ) ;
179+
180+ let hkdf = WCHkdfUsingHmac ( WCShaHmac :: new ( wc_HashType_WC_HASH_TYPE_SHA256) ) ;
181+ let expander = hkdf. extract_from_secret ( Some ( & salt) , & ikm) ;
182+
183+ let mut okm = vec ! [ 0u8 ; 42 ] ; // Length from test vector
184+ expander. expand_slice ( & [ & info] , & mut okm) . unwrap ( ) ;
185+
186+ assert_eq ! ( & okm[ ..] , & expected_okm[ ..] ) ;
187+ }
188+
189+ /// Tests HKDF with SHA-384 to ensure it works with different hash functions
190+ /// Note: This test doesn't verify against RFC test vectors
191+ #[ test]
192+ fn test_hkdf_sha384 ( ) {
193+ // Test with SHA384
194+ let ikm = hex ! ( "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b" ) ;
195+ let salt = hex ! ( "000102030405060708090a0b0c" ) ;
196+ let info = hex ! ( "f0f1f2f3f4f5f6f7f8f9" ) ;
197+
198+ let hkdf = WCHkdfUsingHmac ( WCShaHmac :: new ( wc_HashType_WC_HASH_TYPE_SHA384) ) ;
199+ let expander = hkdf. extract_from_secret ( Some ( & salt) , & ikm) ;
200+
201+ let mut okm = vec ! [ 0u8 ; 48 ] ; // SHA384 output length
202+ expander. expand_slice ( & [ & info] , & mut okm) . unwrap ( ) ;
203+
204+ // Just verify we can generate output - actual value would need a verified test vector
205+ assert ! ( !okm. iter( ) . all( |& x| x == 0 ) ) ;
206+ }
207+
208+ /// Verifies that the HKDF implementation correctly enforces the output length limit
209+ /// The limit is 255 times the hash length as specified in RFC 5869
210+ #[ test]
211+ fn test_hkdf_output_length_limit ( ) {
212+ let hkdf = WCHkdfUsingHmac ( WCShaHmac :: new ( wc_HashType_WC_HASH_TYPE_SHA256) ) ;
213+ let expander = hkdf. extract_from_zero_ikm ( None ) ;
214+
215+ // Maximum allowed length (255 * hash_len)
216+ let max_len = 255 * 32 ;
217+ let mut okm = vec ! [ 0u8 ; max_len] ;
218+ assert ! ( expander. expand_slice( & [ & [ ] ] , & mut okm) . is_ok( ) ) ;
219+
220+ // Exceeding maximum length should fail
221+ let mut okm = vec ! [ 0u8 ; max_len + 1 ] ;
222+ assert ! ( expander. expand_slice( & [ & [ ] ] , & mut okm) . is_err( ) ) ;
223+ }
224+
225+ /// Tests the special case of zero input key material
226+ /// This is important for TLS 1.3 which sometimes requires derivation from zero IKM
227+ #[ test]
228+ fn test_hkdf_zero_ikm ( ) {
229+ let hkdf = WCHkdfUsingHmac ( WCShaHmac :: new ( wc_HashType_WC_HASH_TYPE_SHA256) ) ;
230+ let salt = hex ! ( "000102030405060708090a0b0c" ) ;
231+ let info = hex ! ( "f0f1f2f3f4f5f6f7f8f9" ) ;
232+
233+ let expander = hkdf. extract_from_zero_ikm ( Some ( & salt) ) ;
234+
235+ let mut okm1 = vec ! [ 0u8 ; 32 ] ;
236+ expander. expand_slice ( & [ & info] , & mut okm1) . unwrap ( ) ;
237+
238+ // Verify that zero IKM produces consistent output
239+ let expander2 = hkdf. extract_from_zero_ikm ( Some ( & salt) ) ;
240+ let mut okm2 = vec ! [ 0u8 ; 32 ] ;
241+ expander2. expand_slice ( & [ & info] , & mut okm2) . unwrap ( ) ;
242+
243+ assert_eq ! ( okm1, okm2) ;
244+ }
245+
246+ /// Tests that the implementation correctly handles multiple info components
247+ /// Verifies that passing multiple info slices produces the same result as their concatenation
248+ #[ test]
249+ fn test_hkdf_multiple_info_components ( ) {
250+ let hkdf = WCHkdfUsingHmac ( WCShaHmac :: new ( wc_HashType_WC_HASH_TYPE_SHA256) ) ;
251+ let salt = hex ! ( "000102030405060708090a0b0c" ) ;
252+ let info1 = hex ! ( "f0f1f2f3" ) ;
253+ let info2 = hex ! ( "f4f5f6f7" ) ;
254+ let info3 = hex ! ( "f8f9" ) ;
255+
256+ let expander = hkdf. extract_from_zero_ikm ( Some ( & salt) ) ;
257+
258+ // Test with multiple info components
259+ let mut okm1 = vec ! [ 0u8 ; 32 ] ;
260+ expander. expand_slice ( & [ & info1, & info2, & info3] , & mut okm1) . unwrap ( ) ;
261+
262+ // Test with concatenated info
263+ let mut info_concat = Vec :: new ( ) ;
264+ info_concat. extend_from_slice ( & info1) ;
265+ info_concat. extend_from_slice ( & info2) ;
266+ info_concat. extend_from_slice ( & info3) ;
267+
268+ let mut okm2 = vec ! [ 0u8 ; 32 ] ;
269+ expander. expand_slice ( & [ & info_concat] , & mut okm2) . unwrap ( ) ;
270+
271+ // Results should be identical
272+ assert_eq ! ( okm1, okm2) ;
273+ }
274+ }
0 commit comments