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
215#[cfg(test)]
216#[macro_rules_attribute::apply(test_for_each_provider)]
217mod tests {
218    use std::dbg;
219
220    use super::provider::tls13::{
221        TLS13_AES_128_GCM_SHA256_INTERNAL, TLS13_CHACHA20_POLY1305_SHA256_INTERNAL,
222    };
223    use crate::common_state::Side;
224    use crate::crypto::tls13::OkmBlock;
225    use crate::quic::*;
226
227    fn test_short_packet(version: Version, expected: &[u8]) {
228        const PN: u64 = 654360564;
229        const SECRET: &[u8] = &[
230            0x9a, 0xc3, 0x12, 0xa7, 0xf8, 0x77, 0x46, 0x8e, 0xbe, 0x69, 0x42, 0x27, 0x48, 0xad,
231            0x00, 0xa1, 0x54, 0x43, 0xf1, 0x82, 0x03, 0xa0, 0x7d, 0x60, 0x60, 0xf6, 0x88, 0xf3,
232            0x0f, 0x21, 0x63, 0x2b,
233        ];
234
235        let secret = OkmBlock::new(SECRET);
236        let builder = KeyBuilder::new(
237            &secret,
238            version,
239            TLS13_CHACHA20_POLY1305_SHA256_INTERNAL
240                .quic
241                .unwrap(),
242            TLS13_CHACHA20_POLY1305_SHA256_INTERNAL.hkdf_provider,
243        );
244        let packet = builder.packet_key();
245        let hpk = builder.header_protection_key();
246
247        const PLAIN: &[u8] = &[0x42, 0x00, 0xbf, 0xf4, 0x01];
248
249        let mut buf = PLAIN.to_vec();
250        let (header, payload) = buf.split_at_mut(4);
251        let tag = packet
252            .encrypt_in_place(PN, header, payload)
253            .unwrap();
254        buf.extend(tag.as_ref());
255
256        let pn_offset = 1;
257        let (header, sample) = buf.split_at_mut(pn_offset + 4);
258        let (first, rest) = header.split_at_mut(1);
259        let sample = &sample[..hpk.sample_len()];
260        hpk.encrypt_in_place(sample, &mut first[0], dbg!(rest))
261            .unwrap();
262
263        assert_eq!(&buf, expected);
264
265        let (header, sample) = buf.split_at_mut(pn_offset + 4);
266        let (first, rest) = header.split_at_mut(1);
267        let sample = &sample[..hpk.sample_len()];
268        hpk.decrypt_in_place(sample, &mut first[0], rest)
269            .unwrap();
270
271        let (header, payload_tag) = buf.split_at_mut(4);
272        let plain = packet
273            .decrypt_in_place(PN, header, payload_tag)
274            .unwrap();
275
276        assert_eq!(plain, &PLAIN[4..]);
277    }
278
279    #[test]
280    fn short_packet_header_protection() {
281        // https://www.rfc-editor.org/rfc/rfc9001.html#name-chacha20-poly1305-short-hea
282        test_short_packet(
283            Version::V1,
284            &[
285                0x4c, 0xfe, 0x41, 0x89, 0x65, 0x5e, 0x5c, 0xd5, 0x5c, 0x41, 0xf6, 0x90, 0x80, 0x57,
286                0x5d, 0x79, 0x99, 0xc2, 0x5a, 0x5b, 0xfb,
287            ],
288        );
289    }
290
291    #[test]
292    fn key_update_test_vector() {
293        fn equal_okm(x: &OkmBlock, y: &OkmBlock) -> bool {
294            x.as_ref() == y.as_ref()
295        }
296
297        let mut secrets = Secrets::new(
298            // Constant dummy values for reproducibility
299            OkmBlock::new(
300                &[
301                    0xb8, 0x76, 0x77, 0x08, 0xf8, 0x77, 0x23, 0x58, 0xa6, 0xea, 0x9f, 0xc4, 0x3e,
302                    0x4a, 0xdd, 0x2c, 0x96, 0x1b, 0x3f, 0x52, 0x87, 0xa6, 0xd1, 0x46, 0x7e, 0xe0,
303                    0xae, 0xab, 0x33, 0x72, 0x4d, 0xbf,
304                ][..],
305            ),
306            OkmBlock::new(
307                &[
308                    0x42, 0xdc, 0x97, 0x21, 0x40, 0xe0, 0xf2, 0xe3, 0x98, 0x45, 0xb7, 0x67, 0x61,
309                    0x34, 0x39, 0xdc, 0x67, 0x58, 0xca, 0x43, 0x25, 0x9b, 0x87, 0x85, 0x06, 0x82,
310                    0x4e, 0xb1, 0xe4, 0x38, 0xd8, 0x55,
311                ][..],
312            ),
313            TLS13_AES_128_GCM_SHA256_INTERNAL,
314            TLS13_AES_128_GCM_SHA256_INTERNAL
315                .quic
316                .unwrap(),
317            Side::Client,
318            Version::V1,
319        );
320        secrets.update();
321
322        assert!(equal_okm(
323            &secrets.client,
324            &OkmBlock::new(
325                &[
326                    0x42, 0xca, 0xc8, 0xc9, 0x1c, 0xd5, 0xeb, 0x40, 0x68, 0x2e, 0x43, 0x2e, 0xdf,
327                    0x2d, 0x2b, 0xe9, 0xf4, 0x1a, 0x52, 0xca, 0x6b, 0x22, 0xd8, 0xe6, 0xcd, 0xb1,
328                    0xe8, 0xac, 0xa9, 0x6, 0x1f, 0xce
329                ][..]
330            )
331        ));
332        assert!(equal_okm(
333            &secrets.server,
334            &OkmBlock::new(
335                &[
336                    0xeb, 0x7f, 0x5e, 0x2a, 0x12, 0x3f, 0x40, 0x7d, 0xb4, 0x99, 0xe3, 0x61, 0xca,
337                    0xe5, 0x90, 0xd4, 0xd9, 0x92, 0xe1, 0x4b, 0x7a, 0xce, 0x3, 0xc2, 0x44, 0xe0,
338                    0x42, 0x21, 0x15, 0xb6, 0xd3, 0x8a
339                ][..]
340            )
341        ));
342    }
343
344    #[test]
345    fn short_packet_header_protection_v2() {
346        // https://www.ietf.org/archive/id/draft-ietf-quic-v2-10.html#name-chacha20-poly1305-short-head
347        test_short_packet(
348            Version::V2,
349            &[
350                0x55, 0x58, 0xb1, 0xc6, 0x0a, 0xe7, 0xb6, 0xb9, 0x32, 0xbc, 0x27, 0xd7, 0x86, 0xf4,
351                0xbc, 0x2b, 0xb2, 0x0f, 0x21, 0x62, 0xba,
352            ],
353        );
354    }
355
356    #[test]
357    fn initial_test_vector_v2() {
358        // https://www.ietf.org/archive/id/draft-ietf-quic-v2-10.html#name-sample-packet-protection-2
359        let icid = [0x83, 0x94, 0xc8, 0xf0, 0x3e, 0x51, 0x57, 0x08];
360        let server = Keys::initial(
361            Version::V2,
362            TLS13_AES_128_GCM_SHA256_INTERNAL,
363            TLS13_AES_128_GCM_SHA256_INTERNAL
364                .quic
365                .unwrap(),
366            &icid,
367            Side::Server,
368        );
369        let mut server_payload = [
370            0x02, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x40, 0x5a, 0x02, 0x00, 0x00, 0x56, 0x03,
371            0x03, 0xee, 0xfc, 0xe7, 0xf7, 0xb3, 0x7b, 0xa1, 0xd1, 0x63, 0x2e, 0x96, 0x67, 0x78,
372            0x25, 0xdd, 0xf7, 0x39, 0x88, 0xcf, 0xc7, 0x98, 0x25, 0xdf, 0x56, 0x6d, 0xc5, 0x43,
373            0x0b, 0x9a, 0x04, 0x5a, 0x12, 0x00, 0x13, 0x01, 0x00, 0x00, 0x2e, 0x00, 0x33, 0x00,
374            0x24, 0x00, 0x1d, 0x00, 0x20, 0x9d, 0x3c, 0x94, 0x0d, 0x89, 0x69, 0x0b, 0x84, 0xd0,
375            0x8a, 0x60, 0x99, 0x3c, 0x14, 0x4e, 0xca, 0x68, 0x4d, 0x10, 0x81, 0x28, 0x7c, 0x83,
376            0x4d, 0x53, 0x11, 0xbc, 0xf3, 0x2b, 0xb9, 0xda, 0x1a, 0x00, 0x2b, 0x00, 0x02, 0x03,
377            0x04,
378        ];
379        let mut server_header = [
380            0xd1, 0x6b, 0x33, 0x43, 0xcf, 0x00, 0x08, 0xf0, 0x67, 0xa5, 0x50, 0x2a, 0x42, 0x62,
381            0xb5, 0x00, 0x40, 0x75, 0x00, 0x01,
382        ];
383        let tag = server
384            .local
385            .packet
386            .encrypt_in_place(1, &server_header, &mut server_payload)
387            .unwrap();
388        let (first, rest) = server_header.split_at_mut(1);
389        let rest_len = rest.len();
390        server
391            .local
392            .header
393            .encrypt_in_place(
394                &server_payload[2..18],
395                &mut first[0],
396                &mut rest[rest_len - 2..],
397            )
398            .unwrap();
399        let mut server_packet = server_header.to_vec();
400        server_packet.extend(server_payload);
401        server_packet.extend(tag.as_ref());
402        let expected_server_packet = [
403            0xdc, 0x6b, 0x33, 0x43, 0xcf, 0x00, 0x08, 0xf0, 0x67, 0xa5, 0x50, 0x2a, 0x42, 0x62,
404            0xb5, 0x00, 0x40, 0x75, 0xd9, 0x2f, 0xaa, 0xf1, 0x6f, 0x05, 0xd8, 0xa4, 0x39, 0x8c,
405            0x47, 0x08, 0x96, 0x98, 0xba, 0xee, 0xa2, 0x6b, 0x91, 0xeb, 0x76, 0x1d, 0x9b, 0x89,
406            0x23, 0x7b, 0xbf, 0x87, 0x26, 0x30, 0x17, 0x91, 0x53, 0x58, 0x23, 0x00, 0x35, 0xf7,
407            0xfd, 0x39, 0x45, 0xd8, 0x89, 0x65, 0xcf, 0x17, 0xf9, 0xaf, 0x6e, 0x16, 0x88, 0x6c,
408            0x61, 0xbf, 0xc7, 0x03, 0x10, 0x6f, 0xba, 0xf3, 0xcb, 0x4c, 0xfa, 0x52, 0x38, 0x2d,
409            0xd1, 0x6a, 0x39, 0x3e, 0x42, 0x75, 0x75, 0x07, 0x69, 0x80, 0x75, 0xb2, 0xc9, 0x84,
410            0xc7, 0x07, 0xf0, 0xa0, 0x81, 0x2d, 0x8c, 0xd5, 0xa6, 0x88, 0x1e, 0xaf, 0x21, 0xce,
411            0xda, 0x98, 0xf4, 0xbd, 0x23, 0xf6, 0xfe, 0x1a, 0x3e, 0x2c, 0x43, 0xed, 0xd9, 0xce,
412            0x7c, 0xa8, 0x4b, 0xed, 0x85, 0x21, 0xe2, 0xe1, 0x40,
413        ];
414        assert_eq!(server_packet[..], expected_server_packet[..]);
415    }
416}