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