rustls/crypto/ring/
quic.rs

1#![allow(clippy::duplicate_mod)]
2
3use alloc::boxed::Box;
4
5use super::ring_like::aead;
6use crate::crypto::cipher::{AeadKey, Iv, Nonce};
7use crate::error::Error;
8use crate::quic;
9
10pub(crate) struct HeaderProtectionKey(aead::quic::HeaderProtectionKey);
11
12impl HeaderProtectionKey {
13    pub(crate) fn new(key: AeadKey, alg: &'static aead::quic::Algorithm) -> Self {
14        Self(aead::quic::HeaderProtectionKey::new(alg, key.as_ref()).unwrap())
15    }
16
17    fn xor_in_place(
18        &self,
19        sample: &[u8],
20        first: &mut u8,
21        packet_number: &mut [u8],
22        masked: bool,
23    ) -> Result<(), Error> {
24        // This implements "Header Protection Application" almost verbatim.
25        // <https://datatracker.ietf.org/doc/html/rfc9001#section-5.4.1>
26
27        let mask = self
28            .0
29            .new_mask(sample)
30            .map_err(|_| Error::General("sample of invalid length".into()))?;
31
32        // The `unwrap()` will not panic because `new_mask` returns a
33        // non-empty result.
34        let (first_mask, pn_mask) = mask.split_first().unwrap();
35
36        // It is OK for the `mask` to be longer than `packet_number`,
37        // but a valid `packet_number` will never be longer than `mask`.
38        if packet_number.len() > pn_mask.len() {
39            return Err(Error::General("packet number too long".into()));
40        }
41
42        // Infallible from this point on. Before this point, `first` and
43        // `packet_number` are unchanged.
44
45        const LONG_HEADER_FORM: u8 = 0x80;
46        let bits = match *first & LONG_HEADER_FORM == LONG_HEADER_FORM {
47            true => 0x0f,  // Long header: 4 bits masked
48            false => 0x1f, // Short header: 5 bits masked
49        };
50
51        let first_plain = match masked {
52            // When unmasking, use the packet length bits after unmasking
53            true => *first ^ (first_mask & bits),
54            // When masking, use the packet length bits before masking
55            false => *first,
56        };
57        let pn_len = (first_plain & 0x03) as usize + 1;
58
59        *first ^= first_mask & bits;
60        for (dst, m) in packet_number
61            .iter_mut()
62            .zip(pn_mask)
63            .take(pn_len)
64        {
65            *dst ^= m;
66        }
67
68        Ok(())
69    }
70}
71
72impl quic::HeaderProtectionKey for HeaderProtectionKey {
73    fn encrypt_in_place(
74        &self,
75        sample: &[u8],
76        first: &mut u8,
77        packet_number: &mut [u8],
78    ) -> Result<(), Error> {
79        self.xor_in_place(sample, first, packet_number, false)
80    }
81
82    fn decrypt_in_place(
83        &self,
84        sample: &[u8],
85        first: &mut u8,
86        packet_number: &mut [u8],
87    ) -> Result<(), Error> {
88        self.xor_in_place(sample, first, packet_number, true)
89    }
90
91    #[inline]
92    fn sample_len(&self) -> usize {
93        self.0.algorithm().sample_len()
94    }
95}
96
97pub(crate) struct PacketKey {
98    /// Encrypts or decrypts a packet's payload
99    key: aead::LessSafeKey,
100    /// Computes unique nonces for each packet
101    iv: Iv,
102    /// Confidentiality limit (see [`quic::PacketKey::confidentiality_limit`])
103    confidentiality_limit: u64,
104    /// Integrity limit (see [`quic::PacketKey::integrity_limit`])
105    integrity_limit: u64,
106}
107
108impl PacketKey {
109    pub(crate) fn new(
110        key: AeadKey,
111        iv: Iv,
112        confidentiality_limit: u64,
113        integrity_limit: u64,
114        aead_algorithm: &'static aead::Algorithm,
115    ) -> Self {
116        Self {
117            key: aead::LessSafeKey::new(
118                aead::UnboundKey::new(aead_algorithm, key.as_ref()).unwrap(),
119            ),
120            iv,
121            confidentiality_limit,
122            integrity_limit,
123        }
124    }
125}
126
127impl quic::PacketKey for PacketKey {
128    fn encrypt_in_place(
129        &self,
130        packet_number: u64,
131        header: &[u8],
132        payload: &mut [u8],
133    ) -> Result<quic::Tag, Error> {
134        let aad = aead::Aad::from(header);
135        let nonce = aead::Nonce::assume_unique_for_key(Nonce::new(&self.iv, packet_number).0);
136        let tag = self
137            .key
138            .seal_in_place_separate_tag(nonce, aad, payload)
139            .map_err(|_| Error::EncryptError)?;
140        Ok(quic::Tag::from(tag.as_ref()))
141    }
142
143    /// Decrypt a QUIC packet
144    ///
145    /// Takes the packet `header`, which is used as the additional authenticated data, and the
146    /// `payload`, which includes the authentication tag.
147    ///
148    /// If the return value is `Ok`, the decrypted payload can be found in `payload`, up to the
149    /// length found in the return value.
150    fn decrypt_in_place<'a>(
151        &self,
152        packet_number: u64,
153        header: &[u8],
154        payload: &'a mut [u8],
155    ) -> Result<&'a [u8], Error> {
156        let payload_len = payload.len();
157        let aad = aead::Aad::from(header);
158        let nonce = aead::Nonce::assume_unique_for_key(Nonce::new(&self.iv, packet_number).0);
159        self.key
160            .open_in_place(nonce, aad, payload)
161            .map_err(|_| Error::DecryptError)?;
162
163        let plain_len = payload_len - self.key.algorithm().tag_len();
164        Ok(&payload[..plain_len])
165    }
166
167    /// Tag length for the underlying AEAD algorithm
168    #[inline]
169    fn tag_len(&self) -> usize {
170        self.key.algorithm().tag_len()
171    }
172
173    /// Confidentiality limit (see [`quic::PacketKey::confidentiality_limit`])
174    fn confidentiality_limit(&self) -> u64 {
175        self.confidentiality_limit
176    }
177
178    /// Integrity limit (see [`quic::PacketKey::integrity_limit`])
179    fn integrity_limit(&self) -> u64 {
180        self.integrity_limit
181    }
182}
183
184pub(crate) struct KeyBuilder {
185    pub(crate) packet_alg: &'static aead::Algorithm,
186    pub(crate) header_alg: &'static aead::quic::Algorithm,
187    pub(crate) confidentiality_limit: u64,
188    pub(crate) integrity_limit: u64,
189}
190
191impl quic::Algorithm for KeyBuilder {
192    fn packet_key(&self, key: AeadKey, iv: Iv) -> Box<dyn quic::PacketKey> {
193        Box::new(PacketKey::new(
194            key,
195            iv,
196            self.confidentiality_limit,
197            self.integrity_limit,
198            self.packet_alg,
199        ))
200    }
201
202    fn header_protection_key(&self, key: AeadKey) -> Box<dyn quic::HeaderProtectionKey> {
203        Box::new(HeaderProtectionKey::new(key, self.header_alg))
204    }
205
206    fn aead_key_len(&self) -> usize {
207        self.packet_alg.key_len()
208    }
209
210    fn fips(&self) -> bool {
211        super::fips()
212    }
213}
214
215test_for_each_provider! {
216    use std::dbg;
217    use crate::common_state::Side;
218    use crate::crypto::tls13::OkmBlock;
219    use crate::quic::*;
220    use provider::tls13::{
221        TLS13_AES_128_GCM_SHA256_INTERNAL, TLS13_CHACHA20_POLY1305_SHA256_INTERNAL,
222    };
223
224    fn test_short_packet(version: Version, expected: &[u8]) {
225        const PN: u64 = 654360564;
226        const SECRET: &[u8] = &[
227            0x9a, 0xc3, 0x12, 0xa7, 0xf8, 0x77, 0x46, 0x8e, 0xbe, 0x69, 0x42, 0x27, 0x48, 0xad,
228            0x00, 0xa1, 0x54, 0x43, 0xf1, 0x82, 0x03, 0xa0, 0x7d, 0x60, 0x60, 0xf6, 0x88, 0xf3,
229            0x0f, 0x21, 0x63, 0x2b,
230        ];
231
232        let secret = OkmBlock::new(SECRET);
233        let builder = KeyBuilder::new(
234            &secret,
235            version,
236            TLS13_CHACHA20_POLY1305_SHA256_INTERNAL
237                .quic
238                .unwrap(),
239            TLS13_CHACHA20_POLY1305_SHA256_INTERNAL.hkdf_provider,
240        );
241        let packet = builder.packet_key();
242        let hpk = builder.header_protection_key();
243
244        const PLAIN: &[u8] = &[0x42, 0x00, 0xbf, 0xf4, 0x01];
245
246        let mut buf = PLAIN.to_vec();
247        let (header, payload) = buf.split_at_mut(4);
248        let tag = packet
249            .encrypt_in_place(PN, header, payload)
250            .unwrap();
251        buf.extend(tag.as_ref());
252
253        let pn_offset = 1;
254        let (header, sample) = buf.split_at_mut(pn_offset + 4);
255        let (first, rest) = header.split_at_mut(1);
256        let sample = &sample[..hpk.sample_len()];
257        hpk.encrypt_in_place(sample, &mut first[0], dbg!(rest))
258            .unwrap();
259
260        assert_eq!(&buf, expected);
261
262        let (header, sample) = buf.split_at_mut(pn_offset + 4);
263        let (first, rest) = header.split_at_mut(1);
264        let sample = &sample[..hpk.sample_len()];
265        hpk.decrypt_in_place(sample, &mut first[0], rest)
266            .unwrap();
267
268        let (header, payload_tag) = buf.split_at_mut(4);
269        let plain = packet
270            .decrypt_in_place(PN, header, payload_tag)
271            .unwrap();
272
273        assert_eq!(plain, &PLAIN[4..]);
274    }
275
276    #[test]
277    fn short_packet_header_protection() {
278        // https://www.rfc-editor.org/rfc/rfc9001.html#name-chacha20-poly1305-short-hea
279        test_short_packet(
280            Version::V1,
281            &[
282                0x4c, 0xfe, 0x41, 0x89, 0x65, 0x5e, 0x5c, 0xd5, 0x5c, 0x41, 0xf6, 0x90, 0x80, 0x57,
283                0x5d, 0x79, 0x99, 0xc2, 0x5a, 0x5b, 0xfb,
284            ],
285        );
286    }
287
288    #[test]
289    fn key_update_test_vector() {
290        fn equal_okm(x: &OkmBlock, y: &OkmBlock) -> bool {
291            x.as_ref() == y.as_ref()
292        }
293
294        let mut secrets = Secrets::new(
295            // Constant dummy values for reproducibility
296            OkmBlock::new(
297                &[
298                    0xb8, 0x76, 0x77, 0x08, 0xf8, 0x77, 0x23, 0x58, 0xa6, 0xea, 0x9f, 0xc4, 0x3e,
299                    0x4a, 0xdd, 0x2c, 0x96, 0x1b, 0x3f, 0x52, 0x87, 0xa6, 0xd1, 0x46, 0x7e, 0xe0,
300                    0xae, 0xab, 0x33, 0x72, 0x4d, 0xbf,
301                ][..],
302            ),
303            OkmBlock::new(
304                &[
305                    0x42, 0xdc, 0x97, 0x21, 0x40, 0xe0, 0xf2, 0xe3, 0x98, 0x45, 0xb7, 0x67, 0x61,
306                    0x34, 0x39, 0xdc, 0x67, 0x58, 0xca, 0x43, 0x25, 0x9b, 0x87, 0x85, 0x06, 0x82,
307                    0x4e, 0xb1, 0xe4, 0x38, 0xd8, 0x55,
308                ][..],
309            ),
310            TLS13_AES_128_GCM_SHA256_INTERNAL,
311            TLS13_AES_128_GCM_SHA256_INTERNAL
312                .quic
313                .unwrap(),
314            Side::Client,
315            Version::V1,
316        );
317        secrets.update();
318
319        assert!(equal_okm(
320            &secrets.client,
321            &OkmBlock::new(
322                &[
323                    0x42, 0xca, 0xc8, 0xc9, 0x1c, 0xd5, 0xeb, 0x40, 0x68, 0x2e, 0x43, 0x2e, 0xdf,
324                    0x2d, 0x2b, 0xe9, 0xf4, 0x1a, 0x52, 0xca, 0x6b, 0x22, 0xd8, 0xe6, 0xcd, 0xb1,
325                    0xe8, 0xac, 0xa9, 0x6, 0x1f, 0xce
326                ][..]
327            )
328        ));
329        assert!(equal_okm(
330            &secrets.server,
331            &OkmBlock::new(
332                &[
333                    0xeb, 0x7f, 0x5e, 0x2a, 0x12, 0x3f, 0x40, 0x7d, 0xb4, 0x99, 0xe3, 0x61, 0xca,
334                    0xe5, 0x90, 0xd4, 0xd9, 0x92, 0xe1, 0x4b, 0x7a, 0xce, 0x3, 0xc2, 0x44, 0xe0,
335                    0x42, 0x21, 0x15, 0xb6, 0xd3, 0x8a
336                ][..]
337            )
338        ));
339    }
340
341    #[test]
342    fn short_packet_header_protection_v2() {
343        // https://www.ietf.org/archive/id/draft-ietf-quic-v2-10.html#name-chacha20-poly1305-short-head
344        test_short_packet(
345            Version::V2,
346            &[
347                0x55, 0x58, 0xb1, 0xc6, 0x0a, 0xe7, 0xb6, 0xb9, 0x32, 0xbc, 0x27, 0xd7, 0x86, 0xf4,
348                0xbc, 0x2b, 0xb2, 0x0f, 0x21, 0x62, 0xba,
349            ],
350        );
351    }
352
353    #[test]
354    fn initial_test_vector_v2() {
355        // https://www.ietf.org/archive/id/draft-ietf-quic-v2-10.html#name-sample-packet-protection-2
356        let icid = [0x83, 0x94, 0xc8, 0xf0, 0x3e, 0x51, 0x57, 0x08];
357        let server = Keys::initial(
358            Version::V2,
359            TLS13_AES_128_GCM_SHA256_INTERNAL,
360            TLS13_AES_128_GCM_SHA256_INTERNAL
361                .quic
362                .unwrap(),
363            &icid,
364            Side::Server,
365        );
366        let mut server_payload = [
367            0x02, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x40, 0x5a, 0x02, 0x00, 0x00, 0x56, 0x03,
368            0x03, 0xee, 0xfc, 0xe7, 0xf7, 0xb3, 0x7b, 0xa1, 0xd1, 0x63, 0x2e, 0x96, 0x67, 0x78,
369            0x25, 0xdd, 0xf7, 0x39, 0x88, 0xcf, 0xc7, 0x98, 0x25, 0xdf, 0x56, 0x6d, 0xc5, 0x43,
370            0x0b, 0x9a, 0x04, 0x5a, 0x12, 0x00, 0x13, 0x01, 0x00, 0x00, 0x2e, 0x00, 0x33, 0x00,
371            0x24, 0x00, 0x1d, 0x00, 0x20, 0x9d, 0x3c, 0x94, 0x0d, 0x89, 0x69, 0x0b, 0x84, 0xd0,
372            0x8a, 0x60, 0x99, 0x3c, 0x14, 0x4e, 0xca, 0x68, 0x4d, 0x10, 0x81, 0x28, 0x7c, 0x83,
373            0x4d, 0x53, 0x11, 0xbc, 0xf3, 0x2b, 0xb9, 0xda, 0x1a, 0x00, 0x2b, 0x00, 0x02, 0x03,
374            0x04,
375        ];
376        let mut server_header = [
377            0xd1, 0x6b, 0x33, 0x43, 0xcf, 0x00, 0x08, 0xf0, 0x67, 0xa5, 0x50, 0x2a, 0x42, 0x62,
378            0xb5, 0x00, 0x40, 0x75, 0x00, 0x01,
379        ];
380        let tag = server
381            .local
382            .packet
383            .encrypt_in_place(1, &server_header, &mut server_payload)
384            .unwrap();
385        let (first, rest) = server_header.split_at_mut(1);
386        let rest_len = rest.len();
387        server
388            .local
389            .header
390            .encrypt_in_place(
391                &server_payload[2..18],
392                &mut first[0],
393                &mut rest[rest_len - 2..],
394            )
395            .unwrap();
396        let mut server_packet = server_header.to_vec();
397        server_packet.extend(server_payload);
398        server_packet.extend(tag.as_ref());
399        let expected_server_packet = [
400            0xdc, 0x6b, 0x33, 0x43, 0xcf, 0x00, 0x08, 0xf0, 0x67, 0xa5, 0x50, 0x2a, 0x42, 0x62,
401            0xb5, 0x00, 0x40, 0x75, 0xd9, 0x2f, 0xaa, 0xf1, 0x6f, 0x05, 0xd8, 0xa4, 0x39, 0x8c,
402            0x47, 0x08, 0x96, 0x98, 0xba, 0xee, 0xa2, 0x6b, 0x91, 0xeb, 0x76, 0x1d, 0x9b, 0x89,
403            0x23, 0x7b, 0xbf, 0x87, 0x26, 0x30, 0x17, 0x91, 0x53, 0x58, 0x23, 0x00, 0x35, 0xf7,
404            0xfd, 0x39, 0x45, 0xd8, 0x89, 0x65, 0xcf, 0x17, 0xf9, 0xaf, 0x6e, 0x16, 0x88, 0x6c,
405            0x61, 0xbf, 0xc7, 0x03, 0x10, 0x6f, 0xba, 0xf3, 0xcb, 0x4c, 0xfa, 0x52, 0x38, 0x2d,
406            0xd1, 0x6a, 0x39, 0x3e, 0x42, 0x75, 0x75, 0x07, 0x69, 0x80, 0x75, 0xb2, 0xc9, 0x84,
407            0xc7, 0x07, 0xf0, 0xa0, 0x81, 0x2d, 0x8c, 0xd5, 0xa6, 0x88, 0x1e, 0xaf, 0x21, 0xce,
408            0xda, 0x98, 0xf4, 0xbd, 0x23, 0xf6, 0xfe, 0x1a, 0x3e, 0x2c, 0x43, 0xed, 0xd9, 0xce,
409            0x7c, 0xa8, 0x4b, 0xed, 0x85, 0x21, 0xe2, 0xe1, 0x40,
410        ];
411        assert_eq!(server_packet[..], expected_server_packet[..]);
412    }
413}