1use aes::Aes128;
4use aes::cipher::{BlockEncrypt, KeyInit, generic_array::GenericArray};
5
6use crate::error::Error;
7use crate::header::InitialHeader;
8
9const QUIC_V1: u32 = 0x0000_0001;
10const QUIC_V2: u32 = 0x6b33_43cf;
11
12const INITIAL_SALT_V1: [u8; 20] = [
13 0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3, 0x4d, 0x17, 0x9a, 0xe6, 0xa4, 0xc8, 0x0c, 0xad,
14 0xcc, 0xbb, 0x7f, 0x0a,
15];
16
17const INITIAL_SALT_V2: [u8; 20] = [
18 0x0d, 0xed, 0xe3, 0xde, 0xf7, 0x00, 0xa6, 0xdb, 0x81, 0x93, 0x81, 0xbe, 0x6e, 0x26, 0x9d, 0xcb,
19 0xf9, 0xbd, 0x2e, 0xd9,
20];
21
22struct VersionParams {
23 salt: &'static [u8; 20],
24 key_label: &'static [u8],
25 iv_label: &'static [u8],
26 hp_label: &'static [u8],
27}
28
29fn version_params(version: u32) -> Result<VersionParams, Error> {
30 match version {
31 QUIC_V1 => Ok(VersionParams {
32 salt: &INITIAL_SALT_V1,
33 key_label: b"quic key",
34 iv_label: b"quic iv",
35 hp_label: b"quic hp",
36 }),
37 QUIC_V2 => Ok(VersionParams {
38 salt: &INITIAL_SALT_V2,
39 key_label: b"quicv2 key",
40 iv_label: b"quicv2 iv",
41 hp_label: b"quicv2 hp",
42 }),
43 _ => Err(Error::UnsupportedVersion(version)),
44 }
45}
46
47fn build_hkdf_label(label: &[u8], context: &[u8], len: usize) -> Vec<u8> {
48 let full_label_len = 6 + label.len();
49 let total = 2 + 1 + full_label_len + 1 + context.len();
50 let mut out = Vec::with_capacity(total);
51 out.extend_from_slice(&(len as u16).to_be_bytes());
52 out.push(full_label_len as u8);
53 out.extend_from_slice(b"tls13 ");
54 out.extend_from_slice(label);
55 out.push(context.len() as u8);
56 out.extend_from_slice(context);
57 out
58}
59
60fn remove_header_protection(
61 first_byte: u8,
62 payload: &[u8],
63 hp_key: &[u8],
64) -> Result<(u64, usize, u8), Error> {
65 if payload.len() < 20 {
66 return Err(Error::BufferTooShort {
67 need: 20,
68 have: payload.len(),
69 });
70 }
71
72 let cipher =
73 Aes128::new_from_slice(hp_key).map_err(|e| Error::DecryptionFailed(format!("HP key: {e}")))?;
74 let mut mask = [0u8; 16];
75 mask.copy_from_slice(&payload[4..20]);
76 cipher.encrypt_block(GenericArray::from_mut_slice(&mut mask));
77
78 let unprotected_first = first_byte ^ (mask[0] & 0x0f);
79 let pn_len = usize::from((unprotected_first & 0x03) + 1);
80
81 if pn_len > payload.len() {
82 return Err(Error::BufferTooShort {
83 need: pn_len,
84 have: payload.len(),
85 });
86 }
87
88 let mut pn = 0u64;
89 for i in 0..pn_len {
90 pn = (pn << 8) | u64::from(payload[i] ^ mask[1 + i]);
91 }
92
93 Ok((pn, pn_len, unprotected_first))
94}
95
96#[cfg(feature = "ring")]
97mod backend {
98 use crate::error::Error;
99 use ring::{aead, hkdf};
100
101 struct HkdfLen(usize);
102
103 impl hkdf::KeyType for HkdfLen {
104 fn len(&self) -> usize {
105 self.0
106 }
107 }
108
109 pub(super) fn derive_client_initial_secret(salt: &[u8], dcid: &[u8]) -> Result<Vec<u8>, Error> {
110 let salt = hkdf::Salt::new(hkdf::HKDF_SHA256, salt);
111 let initial_secret = salt.extract(dcid);
112 let label = super::build_hkdf_label(b"client in", &[], 32);
113 expand_prk(&initial_secret, &label, 32)
114 }
115
116 pub(super) fn hkdf_expand_label(
117 secret: &[u8],
118 label: &[u8],
119 len: usize,
120 ) -> Result<Vec<u8>, Error> {
121 let prk = hkdf::Prk::new_less_safe(hkdf::HKDF_SHA256, secret);
122 let info = super::build_hkdf_label(label, &[], len);
123 expand_prk(&prk, &info, len)
124 }
125
126 fn expand_prk(prk: &hkdf::Prk, info: &[u8], len: usize) -> Result<Vec<u8>, Error> {
127 let mut out = vec![0u8; len];
128 prk
129 .expand(&[info], HkdfLen(len))
130 .and_then(|okm| okm.fill(&mut out))
131 .map_err(|_| Error::DecryptionFailed("HKDF expand failed".into()))?;
132 Ok(out)
133 }
134
135 pub(super) fn aead_open(
136 key: &[u8],
137 nonce_bytes: &[u8; 12],
138 aad: &[u8],
139 ciphertext: &[u8],
140 ) -> Result<Vec<u8>, Error> {
141 let unbound = aead::UnboundKey::new(&aead::AES_128_GCM, key)
142 .map_err(|_| Error::DecryptionFailed("invalid AES-GCM key".into()))?;
143 let opening_key = aead::LessSafeKey::new(unbound);
144 let nonce = aead::Nonce::try_assume_unique_for_key(nonce_bytes)
145 .map_err(|_| Error::DecryptionFailed("invalid nonce".into()))?;
146 let mut buf = ciphertext.to_vec();
147 let plaintext_len = opening_key
148 .open_in_place(nonce, aead::Aad::from(aad), &mut buf)
149 .map_err(|_| Error::DecryptionFailed("AEAD decryption failed".into()))?
150 .len();
151 buf.truncate(plaintext_len);
152 Ok(buf)
153 }
154}
155
156#[cfg(feature = "aws-lc-rs")]
157mod backend {
158 use crate::error::Error;
159 use aws_lc_rs::{aead, hkdf};
160
161 struct HkdfLen(usize);
162
163 impl hkdf::KeyType for HkdfLen {
164 fn len(&self) -> usize {
165 self.0
166 }
167 }
168
169 pub(super) fn derive_client_initial_secret(salt: &[u8], dcid: &[u8]) -> Result<Vec<u8>, Error> {
170 let salt = hkdf::Salt::new(hkdf::HKDF_SHA256, salt);
171 let initial_secret = salt.extract(dcid);
172 let label = super::build_hkdf_label(b"client in", &[], 32);
173 expand_prk(&initial_secret, &label, 32)
174 }
175
176 pub(super) fn hkdf_expand_label(
177 secret: &[u8],
178 label: &[u8],
179 len: usize,
180 ) -> Result<Vec<u8>, Error> {
181 let prk = hkdf::Prk::new_less_safe(hkdf::HKDF_SHA256, secret);
182 let info = super::build_hkdf_label(label, &[], len);
183 expand_prk(&prk, &info, len)
184 }
185
186 fn expand_prk(prk: &hkdf::Prk, info: &[u8], len: usize) -> Result<Vec<u8>, Error> {
187 let mut out = vec![0u8; len];
188 prk
189 .expand(&[info], HkdfLen(len))
190 .and_then(|okm| okm.fill(&mut out))
191 .map_err(|_| Error::DecryptionFailed("HKDF expand failed".into()))?;
192 Ok(out)
193 }
194
195 pub(super) fn aead_open(
196 key: &[u8],
197 nonce_bytes: &[u8; 12],
198 aad: &[u8],
199 ciphertext: &[u8],
200 ) -> Result<Vec<u8>, Error> {
201 let unbound = aead::UnboundKey::new(&aead::AES_128_GCM, key)
202 .map_err(|_| Error::DecryptionFailed("invalid AES-GCM key".into()))?;
203 let opening_key = aead::LessSafeKey::new(unbound);
204 let nonce = aead::Nonce::try_assume_unique_for_key(nonce_bytes)
205 .map_err(|_| Error::DecryptionFailed("invalid nonce".into()))?;
206 let mut buf = ciphertext.to_vec();
207 let plaintext_len = opening_key
208 .open_in_place(nonce, aead::Aad::from(aad), &mut buf)
209 .map_err(|_| Error::DecryptionFailed("AEAD decryption failed".into()))?
210 .len();
211 buf.truncate(plaintext_len);
212 Ok(buf)
213 }
214}
215
216pub fn decrypt_initial(packet: &[u8], header: &InitialHeader<'_>) -> Result<Vec<u8>, Error> {
231 let _ = packet;
232 let params = version_params(header.version)?;
233
234 let client_secret = backend::derive_client_initial_secret(params.salt, header.dcid)?;
235 let key = backend::hkdf_expand_label(&client_secret, params.key_label, 16)?;
236 let iv = backend::hkdf_expand_label(&client_secret, params.iv_label, 12)?;
237 let hp = backend::hkdf_expand_label(&client_secret, params.hp_label, 16)?;
238
239 let (pn, pn_len, unprotected_first) =
240 remove_header_protection(header.first_byte, header.payload, &hp)?;
241
242 let mut aad = Vec::with_capacity(header.header_bytes.len() + pn_len);
243 aad.push(unprotected_first);
244 aad.extend_from_slice(&header.header_bytes[1..]);
245 for i in 0..pn_len {
246 aad.push((pn >> (8 * (pn_len - 1 - i))) as u8);
247 }
248
249 let mut nonce = <[u8; 12]>::try_from(iv.as_slice())
250 .map_err(|_| Error::DecryptionFailed("unexpected IV length".into()))?;
251 let pn_offset = 12 - pn_len;
252 for i in 0..pn_len {
253 nonce[pn_offset + i] ^= (pn >> (8 * (pn_len - 1 - i))) as u8;
254 }
255
256 let encrypted_payload = &header.payload[pn_len..];
257
258 #[cfg(feature = "tracing")]
259 tracing::debug!(
260 version = header.version,
261 dcid_len = header.dcid.len(),
262 payload_len = encrypted_payload.len(),
263 "decrypting QUIC Initial packet"
264 );
265
266 backend::aead_open(&key, &nonce, &aad, encrypted_payload)
267}