ytls_util/
hkdf_label.rs

1//! HKDF Label utility for TLS 1.3
2//! These are very easy to get wrong but here is a quick smol utility to spit them out.
3
4/// HKDF Labels with SHA256 which are used in HKDF-Extract-Expand Key through TLS 1.3 Key Schedule
5pub struct HkdfLabelSha256;
6
7enum ServerOrClient {
8    Server,
9    Client,
10}
11
12impl HkdfLabelSha256 {
13    pub fn tls13_c_e_traffic(ctx: &[u8; 32]) -> [u8; 54] {
14        let mut r: [u8; 54] = [0; 54];
15        const PREFIX: [u8; 17] = *b"tls13 c e traffic";
16        r[1] = 53;
17        r[2..18].copy_from_slice(&PREFIX);
18        r[18] = 32;
19        r[19..54].copy_from_slice(ctx);
20        r
21    }
22
23    pub const fn tls13_res_binder() -> [u8; 20] {
24        //b"tls13 res binder
25        [
26            0, 32, 16, 116, 108, 115, 49, 51, 32, 0x72, 0x65, 0x73, 0x20, 0x62, 0x69, 0x6E, 0x64,
27            0x65, 0x72, 00,
28        ]
29    }
30    /// Used for "derived" Application keys phase (same as early_secret feeding handshake)
31    #[inline]
32    pub fn tls13_derived_secret_sha256() -> [u8; 49] {
33        Self::tls13_early_secret_sha256()
34    }
35    /// Early secret has empty SHA256 ctx given no PSK
36    #[inline]
37    pub fn tls13_early_secret_sha256() -> [u8; 49] {
38        //b"tls13 derived" + e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
39        let r: [u8; 49] = [
40            // Hashlen u16
41            0, 32, // Len of of "tls13 derived" (1 byte)
42            13, // tls13\s (6 bytes)
43            116, 108, 115, 49, 51, 32, // derived (7 bytes)
44            100, 101, 114, 105, 118, 101, 100, // Len of "ctx" (1 byte)
45            32,  // ctx = empty SHA256("") (32 bytes)
46            0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f,
47            0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b,
48            0x78, 0x52, 0xb8, 0x55,
49        ];
50
51        assert_eq!(&r[3..16], b"tls13 derived");
52        r
53    }
54    #[inline]
55    pub fn tls13_hanshake_finished(hash_len: u8) -> [u8; 18] {
56        //b"tls13 finished"
57        let r: [u8; 18] = [
58            0, hash_len, 14, 0x74, 0x6C, 0x73, 0x31, 0x33, 0x20, 0x66, 0x69, 0x6E, 0x69, 0x73,
59            0x68, 0x65, 0x64, 0x00,
60        ];
61        assert_eq!(&r[3..17], b"tls13 finished");
62        r
63    }
64    #[inline]
65    pub fn tls13_secret_key(key_len: u8) -> [u8; 13] {
66        //b"tls13 key"
67        let r: [u8; 13] = [0, key_len, 9, 116, 108, 115, 49, 51, 32, 107, 101, 121, 0];
68        assert_eq!(&r[3..12], b"tls13 key");
69        r
70    }
71    #[inline]
72    pub fn tls13_secret_iv(iv_len: u8) -> [u8; 12] {
73        //b"tls13 iv"
74        let r: [u8; 12] = [0, iv_len, 8, 116, 108, 115, 49, 51, 32, 105, 118, 00];
75        assert_eq!(&r[3..11], b"tls13 iv");
76        r
77    }
78    #[inline]
79    fn _tls13_application_traffic(which: ServerOrClient, ctx: &[u8; 32]) -> [u8; 54] {
80        let prefix: [u8; 18] = match which {
81            ServerOrClient::Client => *b"tls13 c ap traffic",
82            ServerOrClient::Server => *b"tls13 s ap traffic",
83        };
84        let mut r: [u8; 54] = [0; 54];
85        r[1] = 32;
86        r[2] = 18;
87        r[3..21].copy_from_slice(&prefix);
88        r[21] = 32;
89        r[22..54].copy_from_slice(ctx);
90        r
91    }
92    #[inline]
93    fn _tls13_handshake_traffic(which: ServerOrClient, ctx: &[u8; 32]) -> [u8; 54] {
94        let prefix: [u8; 18] = match which {
95            ServerOrClient::Client => *b"tls13 c hs traffic",
96            ServerOrClient::Server => *b"tls13 s hs traffic",
97        };
98        let mut r: [u8; 54] = [0; 54];
99        r[1] = 32;
100        r[2] = 18;
101        r[3..21].copy_from_slice(&prefix);
102        r[21] = 32;
103        r[22..54].copy_from_slice(ctx);
104        r
105    }
106    /// Handshake traffic uses Client+ServerHello Transcript hash for ctx
107    #[inline]
108    pub fn tls13_client_handshake_traffic(ctx: &[u8; 32]) -> [u8; 54] {
109        Self::_tls13_handshake_traffic(ServerOrClient::Client, ctx)
110    }
111    #[inline]
112    pub fn tls13_server_handshake_traffic(ctx: &[u8; 32]) -> [u8; 54] {
113        Self::_tls13_handshake_traffic(ServerOrClient::Server, ctx)
114    }
115    //// Application traffic uses handshakes transcript hash for ctx
116    #[inline]
117    pub fn tls13_client_application_traffic(ctx: &[u8; 32]) -> [u8; 54] {
118        Self::_tls13_application_traffic(ServerOrClient::Client, ctx)
119    }
120    #[inline]
121    pub fn tls13_server_application_traffic(ctx: &[u8; 32]) -> [u8; 54] {
122        Self::_tls13_application_traffic(ServerOrClient::Server, ctx)
123    }
124}
125
126#[cfg(test)]
127mod test_rfc8448 {
128    // https://datatracker.ietf.org/doc/rfc8448/
129
130    use super::*;
131    use hex_literal::hex;
132    use hkdf::{hmac::Hmac, GenericHkdf, Hkdf};
133    use sha2::Sha256;
134
135    const fn client_hello() -> &'static [u8; 196] {
136        &hex!(
137            "01 00 00 c0 03 03 cb 34 ec b1 e7 81 63
138         ba 1c 38 c6 da cb 19 6a 6d ff a2 1a 8d 99 12 ec 18 a2 ef 62 83
139         02 4d ec e7 00 00 06 13 01 13 03 13 02 01 00 00 91 00 00 00 0b
140         00 09 00 00 06 73 65 72 76 65 72 ff 01 00 01 00 00 0a 00 14 00
141         12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 01 03 01 04 00 23
142         00 00 00 33 00 26 00 24 00 1d 00 20 99 38 1d e5 60 e4 bd 43 d2
143         3d 8e 43 5a 7d ba fe b3 c0 6e 51 c1 3c ae 4d 54 13 69 1e 52 9a
144         af 2c 00 2b 00 03 02 03 04 00 0d 00 20 00 1e 04 03 05 03 06 03
145         02 03 08 04 08 05 08 06 04 01 05 01 06 01 02 01 04 02 05 02 06
146         02 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01"
147        )
148    }
149
150    const fn server_hello() -> &'static [u8; 90] {
151        &hex!(
152            "02 00 00 56 03 03 a6 af 06 a4 12 18 60
153         dc 5e 6e 60 24 9c d3 4c 95 93 0c 8a c5 cb 14 34 da c1 55 77 2e
154         d3 e2 69 28 00 13 01 00 00 2e 00 33 00 24 00 1d 00 20 c9 82 88
155         76 11 20 95 fe 66 76 2b db f7 c6 72 e1 56 d6 cc 25 3b 83 3d f1
156         dd 69 b1 b0 4e 75 1f 0f 00 2b 00 02 03 04"
157        )
158    }
159
160    // Early secret without PSK is constructed using zero-hash-len
161    fn early_secret_no_psk() -> ([u8; 32], GenericHkdf<Hmac<Sha256>>) {
162        //*****************************************************
163        //  early_secret = HKDF-Extract(salt: 00, key: 00...)
164        //-----------------------------------------------------
165        let ikm: [u8; 32] = [0; 32];
166        let salt: [u8; 1] = [0; 1];
167        let (early_secret, hk) = Hkdf::<Sha256>::extract(Some(&salt[..]), &ikm);
168        (early_secret.into(), hk)
169    }
170
171    // Check the early secret without PSK is correct
172    #[test]
173    fn early_secret_no_psk_ok() {
174        let (ek, _hk) = early_secret_no_psk();
175        assert_eq!(
176            ek,
177            hex!(
178                "33 ad 0a 1c 60 7e c0 3b 09 e6 cd 98 93 68 0c
179         e2 10 ad f3 00 aa 1f 26 60 e1 b2 2e 10 f1 70 f9 2a"
180            )
181        );
182    }
183
184    // Check the derive secret is correct for SHA256
185    fn derived_sha256() -> ([u8; 32], GenericHkdf<Hmac<Sha256>>) {
186        let (_ek, hk) = early_secret_no_psk();
187        //*****************************************************
188        // empty_hash = SHA256("")
189        // derived_secret = HKDF-Expand-Label(key: early_secret, label: "derived", ctx: empty_hash, len: 32)
190        //-----------------------------------------------------
191        let label_derived = HkdfLabelSha256::tls13_early_secret_sha256();
192        assert_eq!(
193            label_derived,
194            hex!(
195                "00 20 0d 74 6c 73 31 33 20 64 65 72 69 76 65 64
196         20 e3 b0 c4 42 98 fc 1c 14 9a fb f4 c8 99 6f b9 24 27 ae 41 e4
197         64 9b 93 4c a4 95 99 1b 78 52 b8 55"
198            )
199        );
200
201        let mut derived_secret: [u8; 32] = [0; 32];
202        hk.expand(&label_derived, &mut derived_secret).unwrap();
203        (derived_secret, hk)
204    }
205
206    #[test]
207    fn derived_sha256_ok() {
208        let (derived_secret, _hk) = derived_sha256();
209        assert_eq!(
210            derived_secret,
211            hex!(
212                "6f 26 15 a1 08 c7 02 c5 67 8f 54 fc 9d ba
213         b6 97 16 c0 76 18 9c 48 25 0c eb ea c3 57 6c 36 11 ba"
214            )
215        );
216    }
217
218    fn shared_secret() -> &'static [u8; 32] {
219        &hex!(
220            "8b d4 05 4f b5 5b 9d 63 fd fb ac f9 f0 4b 9f 0d
221         35 e6 d6 3f 53 75 63 ef d4 62 72 90 0f 89 49 2d"
222        )
223    }
224
225    fn handshake_secret() -> ([u8; 32], GenericHkdf<Hmac<Sha256>>) {
226        let (derived_secret, _derived_hk) = derived_sha256();
227        let shared_secret = shared_secret();
228        let (handshake_secret, hk) = Hkdf::<Sha256>::extract(Some(&derived_secret), shared_secret);
229        (handshake_secret.into(), hk)
230    }
231
232    #[test]
233    fn handshake_secret_ok() {
234        let (handshake_secret, _hk) = handshake_secret();
235        assert_eq!(
236            handshake_secret,
237            hex!(
238                "1d c8 26 e9 36 06 aa 6f dc 0a ad c1 2f 74 1b
239         01 04 6a a6 b9 9f 69 1e d2 21 a9 f0 ca 04 3f be ac"
240            )
241        );
242    }
243
244    // Test case derived Client+Server Hello handshake hash
245    // Used to derive c/s hs traffic keys
246    fn handshake_traffic_hash_input() -> &'static [u8; 32] {
247        &hex!(
248            "86 0c 06 ed c0 78 58 ee 8e 78 f0 e7 42 8c 58 ed
249         d6 b4 3f 2c a3 e6 e9 5f 02 ed 06 3c f0 e1 ca d8"
250        )
251    }
252
253    #[test]
254    fn label_tls13_server_handshake_traffic_label_ok() {
255        let hello_hash = handshake_traffic_hash_input();
256        let label = HkdfLabelSha256::tls13_server_handshake_traffic(hello_hash);
257
258        assert_eq!(
259            &label,
260            &hex!(
261                "00 20 12 74 6c 73 31 33 20 73 20 68 73 20 74 72
262         61 66 66 69 63 20 86 0c 06 ed c0 78 58 ee 8e 78 f0 e7 42 8c 58
263         ed d6 b4 3f 2c a3 e6 e9 5f 02 ed 06 3c f0 e1 ca d8"
264            )
265        );
266    }
267
268    //*****************************************************
269    // server_secret = HKDF-Expand-Label(key: handshake_secret, label: "s hs traffic", ctx: hello_hash, len: 48)
270    //-----------------------------------------------------
271    fn server_hs_traffic_secret() -> ([u8; 32], GenericHkdf<Hmac<Sha256>>) {
272        let (_handshake_secret, hk) = handshake_secret();
273        let hello_hash = handshake_traffic_hash_input();
274        let label = HkdfLabelSha256::tls13_server_handshake_traffic(hello_hash);
275        let mut server_secret: [u8; 32] = [0; 32];
276        hk.expand(&label, &mut server_secret).unwrap();
277        (server_secret, hk)
278    }
279
280    #[test]
281    fn server_hs_traffic_secret_ok() {
282        let (server_secret, _hk) = server_hs_traffic_secret();
283        assert_eq!(
284            &server_secret,
285            &hex!(
286                "b6 7b 7d 69 0c c1 6c 4e 75 e5 42 13 cb 2d
287         37 b4 e9 c9 12 bc de d9 10 5d 42 be fd 59 d3 91 ad 38"
288            )
289        );
290    }
291
292    #[test]
293    fn server_hs_traffic_secret_key_ok() {
294        let (server_secret, _hk) = server_hs_traffic_secret();
295
296        let hk = Hkdf::<Sha256>::from_prk(&server_secret).expect("PRK should be large enough");
297        let mut server_handshake_key: [u8; 16] = [0; 16];
298        let key_label = HkdfLabelSha256::tls13_secret_key(16);
299        assert_eq!(&key_label, &hex!("00 10 09 74 6c 73 31 33 20 6b 65 79 00"));
300        hk.expand(&key_label, &mut server_handshake_key).unwrap();
301        assert_eq!(
302            &server_handshake_key,
303            &hex!("3f ce 51 60 09 c2 17 27 d0 f2 e4 e8 6e e4 03 bc")
304        );
305    }
306
307    #[test]
308    fn server_hs_traffic_secret_iv_ok() {
309        let (server_secret, _hk) = server_hs_traffic_secret();
310
311        let hk = Hkdf::<Sha256>::from_prk(&server_secret).expect("PRK should be large enough");
312        let mut server_handshake_iv: [u8; 12] = [0; 12];
313        let iv_label = HkdfLabelSha256::tls13_secret_iv(12);
314        assert_eq!(&iv_label, &hex!("00 0c 08 74 6c 73 31 33 20 69 76 00"));
315        hk.expand(&iv_label, &mut server_handshake_iv).unwrap();
316        assert_eq!(
317            &server_handshake_iv,
318            &hex!("5d 31 3e b2 67 12 76 ee 13 00 0b 30")
319        );
320    }
321}