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) -> Result<Vec<u8>, Error> {
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 let len_u16 = u16::try_from(len)
52 .map_err(|_| Error::DecryptionFailed("HKDF output length overflow".into()))?;
53 let label_u8 = u8::try_from(full_label_len)
54 .map_err(|_| Error::DecryptionFailed("HKDF label length overflow".into()))?;
55 let ctx_u8 = u8::try_from(context.len())
56 .map_err(|_| Error::DecryptionFailed("HKDF context length overflow".into()))?;
57 out.extend_from_slice(&len_u16.to_be_bytes());
58 out.push(label_u8);
59 out.extend_from_slice(b"tls13 ");
60 out.extend_from_slice(label);
61 out.push(ctx_u8);
62 out.extend_from_slice(context);
63 Ok(out)
64}
65
66fn remove_header_protection(
67 first_byte: u8,
68 payload: &[u8],
69 hp_key: &[u8],
70) -> Result<(u64, usize, u8), Error> {
71 if payload.len() < 20 {
72 return Err(Error::BufferTooShort {
73 need: 20,
74 have: payload.len(),
75 });
76 }
77
78 let cipher =
79 Aes128::new_from_slice(hp_key).map_err(|e| Error::DecryptionFailed(format!("HP key: {e}")))?;
80 let mut mask = [0u8; 16];
81 mask.copy_from_slice(&payload[4..20]);
82 cipher.encrypt_block(GenericArray::from_mut_slice(&mut mask));
83
84 let unprotected_first = first_byte ^ (mask[0] & 0x0f);
85 let pn_len = usize::from((unprotected_first & 0x03) + 1);
86
87 if pn_len > payload.len() {
88 return Err(Error::BufferTooShort {
89 need: pn_len,
90 have: payload.len(),
91 });
92 }
93
94 let mut pn = 0u64;
95 for i in 0..pn_len {
96 pn = (pn << 8) | u64::from(payload[i] ^ mask[1 + i]);
97 }
98
99 Ok((pn, pn_len, unprotected_first))
100}
101
102#[cfg(feature = "ring")]
103mod backend {
104 use crate::error::Error;
105 use ring::{aead, hkdf};
106
107 struct HkdfLen(usize);
108
109 impl hkdf::KeyType for HkdfLen {
110 fn len(&self) -> usize {
111 self.0
112 }
113 }
114
115 pub(super) fn derive_client_initial_secret(salt: &[u8], dcid: &[u8]) -> Result<Vec<u8>, Error> {
116 let salt = hkdf::Salt::new(hkdf::HKDF_SHA256, salt);
117 let initial_secret = salt.extract(dcid);
118 let label = super::build_hkdf_label(b"client in", &[], 32)?;
119 expand_prk(&initial_secret, &label, 32)
120 }
121
122 pub(super) fn hkdf_expand_label(
123 secret: &[u8],
124 label: &[u8],
125 len: usize,
126 ) -> Result<Vec<u8>, Error> {
127 let prk = hkdf::Prk::new_less_safe(hkdf::HKDF_SHA256, secret);
128 let info = super::build_hkdf_label(label, &[], len)?;
129 expand_prk(&prk, &info, len)
130 }
131
132 fn expand_prk(prk: &hkdf::Prk, info: &[u8], len: usize) -> Result<Vec<u8>, Error> {
133 let mut out = vec![0u8; len];
134 prk
135 .expand(&[info], HkdfLen(len))
136 .and_then(|okm| okm.fill(&mut out))
137 .map_err(|_| Error::DecryptionFailed("HKDF expand failed".into()))?;
138 Ok(out)
139 }
140
141 pub(super) fn aead_open(
142 key: &[u8],
143 nonce_bytes: &[u8; 12],
144 aad: &[u8],
145 ciphertext: &[u8],
146 ) -> Result<Vec<u8>, Error> {
147 let unbound = aead::UnboundKey::new(&aead::AES_128_GCM, key)
148 .map_err(|_| Error::DecryptionFailed("invalid AES-GCM key".into()))?;
149 let opening_key = aead::LessSafeKey::new(unbound);
150 let nonce = aead::Nonce::try_assume_unique_for_key(nonce_bytes)
151 .map_err(|_| Error::DecryptionFailed("invalid nonce".into()))?;
152 let mut buf = ciphertext.to_vec();
153 let plaintext_len = opening_key
154 .open_in_place(nonce, aead::Aad::from(aad), &mut buf)
155 .map_err(|_| Error::DecryptionFailed("AEAD decryption failed".into()))?
156 .len();
157 buf.truncate(plaintext_len);
158 Ok(buf)
159 }
160}
161
162#[cfg(feature = "aws-lc-rs")]
163mod backend {
164 use crate::error::Error;
165 use aws_lc_rs::{aead, hkdf};
166
167 struct HkdfLen(usize);
168
169 impl hkdf::KeyType for HkdfLen {
170 fn len(&self) -> usize {
171 self.0
172 }
173 }
174
175 pub(super) fn derive_client_initial_secret(salt: &[u8], dcid: &[u8]) -> Result<Vec<u8>, Error> {
176 let salt = hkdf::Salt::new(hkdf::HKDF_SHA256, salt);
177 let initial_secret = salt.extract(dcid);
178 let label = super::build_hkdf_label(b"client in", &[], 32)?;
179 expand_prk(&initial_secret, &label, 32)
180 }
181
182 pub(super) fn hkdf_expand_label(
183 secret: &[u8],
184 label: &[u8],
185 len: usize,
186 ) -> Result<Vec<u8>, Error> {
187 let prk = hkdf::Prk::new_less_safe(hkdf::HKDF_SHA256, secret);
188 let info = super::build_hkdf_label(label, &[], len)?;
189 expand_prk(&prk, &info, len)
190 }
191
192 fn expand_prk(prk: &hkdf::Prk, info: &[u8], len: usize) -> Result<Vec<u8>, Error> {
193 let mut out = vec![0u8; len];
194 prk
195 .expand(&[info], HkdfLen(len))
196 .and_then(|okm| okm.fill(&mut out))
197 .map_err(|_| Error::DecryptionFailed("HKDF expand failed".into()))?;
198 Ok(out)
199 }
200
201 pub(super) fn aead_open(
202 key: &[u8],
203 nonce_bytes: &[u8; 12],
204 aad: &[u8],
205 ciphertext: &[u8],
206 ) -> Result<Vec<u8>, Error> {
207 let unbound = aead::UnboundKey::new(&aead::AES_128_GCM, key)
208 .map_err(|_| Error::DecryptionFailed("invalid AES-GCM key".into()))?;
209 let opening_key = aead::LessSafeKey::new(unbound);
210 let nonce = aead::Nonce::try_assume_unique_for_key(nonce_bytes)
211 .map_err(|_| Error::DecryptionFailed("invalid nonce".into()))?;
212 let mut buf = ciphertext.to_vec();
213 let plaintext_len = opening_key
214 .open_in_place(nonce, aead::Aad::from(aad), &mut buf)
215 .map_err(|_| Error::DecryptionFailed("AEAD decryption failed".into()))?
216 .len();
217 buf.truncate(plaintext_len);
218 Ok(buf)
219 }
220}
221
222pub fn decrypt_initial(header: &InitialHeader<'_>) -> Result<Vec<u8>, Error> {
237 let params = version_params(header.version)?;
238
239 let client_secret = backend::derive_client_initial_secret(params.salt, header.dcid)?;
240 let key = backend::hkdf_expand_label(&client_secret, params.key_label, 16)?;
241 let iv = backend::hkdf_expand_label(&client_secret, params.iv_label, 12)?;
242 let hp = backend::hkdf_expand_label(&client_secret, params.hp_label, 16)?;
243
244 let (pn, pn_len, unprotected_first) =
245 remove_header_protection(header.first_byte, header.payload, &hp)?;
246
247 let mut aad = Vec::with_capacity(header.header_bytes.len() + pn_len);
248 aad.push(unprotected_first);
249 aad.extend_from_slice(&header.header_bytes[1..]);
250 for i in 0..pn_len {
251 aad.push((pn >> (8 * (pn_len - 1 - i))) as u8);
252 }
253
254 let mut nonce = <[u8; 12]>::try_from(iv.as_slice())
255 .map_err(|_| Error::DecryptionFailed("unexpected IV length".into()))?;
256 let pn_offset = 12 - pn_len;
257 for i in 0..pn_len {
258 nonce[pn_offset + i] ^= (pn >> (8 * (pn_len - 1 - i))) as u8;
259 }
260
261 let encrypted_payload = &header.payload[pn_len..];
262
263 #[cfg(feature = "tracing")]
264 tracing::debug!(
265 version = header.version,
266 dcid_len = header.dcid.len(),
267 payload_len = encrypted_payload.len(),
268 "decrypting QUIC Initial packet"
269 );
270
271 backend::aead_open(&key, &nonce, &aad, encrypted_payload)
272}