Line data Source code
1 : #include "fd_quic_crypto_suites.h"
2 : #include "../fd_quic.h"
3 :
4 : #include <assert.h>
5 : #include <limits.h>
6 :
7 : #include "../../../ballet/aes/fd_aes_base.h"
8 : #include "../../../ballet/aes/fd_aes_gcm.h"
9 : #include "../../../ballet/hmac/fd_hmac.h"
10 : #include "../templ/fd_quic_parse_util.h"
11 :
12 : /* FD_QUIC_CRYPTO_V1_INITIAL_SALT is the salt to the initial secret
13 : HKDF in QUIC v1. */
14 :
15 : static uchar const FD_QUIC_CRYPTO_V1_INITIAL_SALT[ 20UL ] = {
16 : 0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3,
17 : 0x4d, 0x17, 0x9a, 0xe6, 0xa4, 0xc8, 0x0c, 0xad,
18 : 0xcc, 0xbb, 0x7f, 0x0a };
19 :
20 : static inline void
21 : fd_quic_hkdf_extract( void * output,
22 : void const * salt, ulong salt_sz,
23 12396 : void const * conn_id, ulong conn_id_sz ) {
24 12396 : fd_hmac_sha256( conn_id, conn_id_sz, salt, salt_sz, output );
25 12396 : }
26 :
27 : static inline void
28 : fd_quic_hkdf_expand_label( uchar * out,
29 : ulong out_sz,
30 : uchar const secret[ 32 ],
31 : char const * label,
32 243744 : ulong label_sz ) {
33 243744 : fd_tls_hkdf_expand_label( out, out_sz, secret, label, label_sz, NULL, 0UL );
34 243744 : }
35 :
36 : void
37 : fd_quic_gen_initial_secret(
38 : fd_quic_crypto_secrets_t * secrets,
39 : uchar const * conn_id,
40 12396 : ulong conn_id_sz ) {
41 : /* Initial Packets
42 : from rfc:
43 : initial_salt = 0x38762cf7f55934b34d179ae6a4c80cadccbb7f0a */
44 12396 : uchar const * initial_salt = FD_QUIC_CRYPTO_V1_INITIAL_SALT;
45 12396 : ulong initial_salt_sz = sizeof(FD_QUIC_CRYPTO_V1_INITIAL_SALT);
46 12396 : fd_quic_hkdf_extract( secrets->initial_secret,
47 12396 : initial_salt, initial_salt_sz,
48 12396 : conn_id, conn_id_sz );
49 12396 : }
50 :
51 :
52 : void
53 : fd_quic_gen_secrets(
54 : fd_quic_crypto_secrets_t * secrets,
55 12396 : uint enc_level ) {
56 12396 : uchar * client_secret = secrets->secret[enc_level][0];
57 12396 : uchar * server_secret = secrets->secret[enc_level][1];
58 :
59 12396 : fd_quic_hkdf_expand_label(
60 12396 : client_secret, FD_QUIC_SECRET_SZ,
61 12396 : secrets->initial_secret,
62 12396 : FD_QUIC_CRYPTO_LABEL_CLIENT_IN,
63 12396 : FD_QUIC_CRYPTO_LABEL_CLIENT_IN_LEN );
64 :
65 12396 : fd_quic_hkdf_expand_label(
66 12396 : server_secret, FD_QUIC_SECRET_SZ,
67 12396 : secrets->initial_secret,
68 12396 : FD_QUIC_CRYPTO_LABEL_SERVER_IN,
69 12396 : FD_QUIC_CRYPTO_LABEL_SERVER_IN_LEN );
70 12396 : }
71 :
72 :
73 : /* generate new secrets
74 :
75 : Used during key update to generate new secrets from the
76 : existing secrets
77 :
78 : see rfc9001 section 6, rfc8446 section 7.2 */
79 : void
80 48 : fd_quic_gen_new_secrets( fd_quic_crypto_secrets_t * secrets ) {
81 : /* Defined as:
82 : application_traffic_secret_N+1 =
83 : HKDF-Expand-Label(application_traffic_secret_N,
84 : "traffic upd", "", Hash.length) */
85 48 : uint enc_level = fd_quic_enc_level_appdata_id;
86 48 : uchar * client_secret = secrets->new_secret[0];
87 48 : uchar * server_secret = secrets->new_secret[1];
88 :
89 48 : uchar * old_client_secret = secrets->secret[enc_level][0];
90 48 : uchar * old_server_secret = secrets->secret[enc_level][1];
91 :
92 48 : fd_quic_hkdf_expand_label(
93 48 : client_secret, FD_QUIC_SECRET_SZ,
94 48 : old_client_secret,
95 48 : FD_QUIC_CRYPTO_LABEL_KEY_UPDATE, FD_QUIC_CRYPTO_LABEL_KEY_UPDATE_LEN );
96 :
97 48 : fd_quic_hkdf_expand_label(
98 48 : server_secret, FD_QUIC_SECRET_SZ,
99 48 : old_server_secret,
100 48 : FD_QUIC_CRYPTO_LABEL_KEY_UPDATE, FD_QUIC_CRYPTO_LABEL_KEY_UPDATE_LEN );
101 48 : }
102 :
103 :
104 : void
105 : fd_quic_gen_keys(
106 : fd_quic_crypto_keys_t * keys,
107 72888 : uchar const secret[ 32 ] ) {
108 :
109 : /* quic key */
110 :
111 : /* output length passed with "quic hp" and "quic key" must be the key size from
112 : the current cipher */
113 72888 : fd_quic_hkdf_expand_label(
114 72888 : keys->pkt_key, FD_AES_128_KEY_SZ,
115 72888 : secret,
116 72888 : FD_QUIC_CRYPTO_LABEL_QUIC_KEY,
117 72888 : FD_QUIC_CRYPTO_LABEL_QUIC_KEY_LEN );
118 :
119 : /* quic hp */
120 :
121 : /* output length passed with "quic hp" and "quic key" must be the key size from
122 : the current cipher */
123 72888 : fd_quic_hkdf_expand_label(
124 72888 : keys->hp_key, FD_AES_128_KEY_SZ,
125 72888 : secret,
126 72888 : FD_QUIC_CRYPTO_LABEL_QUIC_HP,
127 72888 : FD_QUIC_CRYPTO_LABEL_QUIC_HP_LEN );
128 :
129 : /* quic iv */
130 72888 : fd_quic_hkdf_expand_label(
131 72888 : keys->iv, FD_AES_GCM_IV_SZ,
132 72888 : secret,
133 72888 : FD_QUIC_CRYPTO_LABEL_QUIC_IV,
134 72888 : FD_QUIC_CRYPTO_LABEL_QUIC_IV_LEN );
135 72888 : }
136 :
137 :
138 : /* generates packet key and iv key
139 : used by key update
140 :
141 : TODO this overlaps with fd_quic_gen_keys, split into gen_hp_keys and gen_pkt_keys */
142 : void
143 : fd_quic_gen_new_keys(
144 : fd_quic_crypto_keys_t * keys,
145 96 : uchar const secret[ 32 ] ) {
146 : /* quic key */
147 96 : fd_quic_hkdf_expand_label(
148 96 : keys->pkt_key, FD_AES_128_KEY_SZ,
149 96 : secret,
150 96 : FD_QUIC_CRYPTO_LABEL_QUIC_KEY,
151 96 : FD_QUIC_CRYPTO_LABEL_QUIC_KEY_LEN );
152 :
153 : /* quic iv */
154 96 : fd_quic_hkdf_expand_label(
155 96 : keys->iv, FD_AES_GCM_IV_SZ,
156 96 : secret,
157 96 : FD_QUIC_CRYPTO_LABEL_QUIC_IV,
158 96 : FD_QUIC_CRYPTO_LABEL_QUIC_IV_LEN );
159 96 : }
160 :
161 :
162 : /* encrypt a packet
163 :
164 : uses the keys in keys to encrypt the packet "pkt" with header "hdr"
165 : (of length pkt_sz, and hdr_sz respectively) into out.
166 :
167 : out should have enough space to contain the full output with extra space
168 : for a full block which depends on the cipher
169 :
170 : *out_sz is used to determine the amount of buffer space left at *out
171 : if enough space is not available, the function fails and returns
172 : FD_QUIC_FAILED
173 : *out_sz is also set to the number of bytes written into *out at the end
174 :
175 : args
176 : out the destination for the encrypted output
177 : out_sz a pointer to the size of the buffer (on input) and the size of
178 : the written bytes (on output)
179 : hdr the input header bytes
180 : hdr_sz the size of the header in bytes
181 : pkt the input packet bytes
182 : pkt_sz the size of the packet in bytes (frames after packet number not including MAC tag)
183 : keys a pointer to the keys to use
184 : pkt_number needed to create the nonce used in encryption
185 : likely points to the packet number within "hdr"
186 : pkt_number_sz the size of the packet number in bytes
187 : */
188 :
189 : int
190 : fd_quic_crypto_encrypt(
191 : uchar * const out,
192 : ulong * const out_sz,
193 : uchar const * const hdr,
194 : ulong const hdr_sz,
195 : uchar const * const pkt,
196 : ulong const pkt_sz,
197 : fd_quic_crypto_keys_t const * const pkt_keys,
198 : fd_quic_crypto_keys_t const * const hp_keys,
199 25412576 : ulong const pkt_number ) {
200 :
201 :
202 : /* ensure we have enough space in the output buffer
203 : most space used by cipher:
204 : header bytes (just XORed)
205 : input bytes (encrypted)
206 : tag bytes */
207 :
208 : /* bound on the bytes needed for cipher output */
209 25412576 : ulong cipher_out_bound = hdr_sz + pkt_sz + FD_QUIC_CRYPTO_TAG_SZ;
210 :
211 25412576 : if( FD_UNLIKELY( *out_sz < cipher_out_bound ) ) {
212 0 : FD_DEBUG( FD_LOG_WARNING(( "fd_quic_crypto_encrypt: output buffer not big enough" )) );
213 0 : return FD_QUIC_FAILED;
214 0 : }
215 :
216 25412576 : if( FD_UNLIKELY( ( hdr_sz < 4 ) | ( hdr_sz > INT_MAX ) ) ) {
217 0 : FD_DEBUG( FD_LOG_WARNING(( "fd_quic_crypto_encrypt: packet header size out of bounds" )) );
218 0 : return FD_QUIC_FAILED;
219 0 : }
220 :
221 : /* bounds check */
222 25412576 : if( FD_UNLIKELY( pkt_sz > INT_MAX ) ) return FD_QUIC_FAILED;
223 :
224 : /* copy the header into the output */
225 25412576 : fd_memcpy( out, hdr, hdr_sz );
226 :
227 : /* first byte needed in a couple of places */
228 25412576 : uchar first = out[0];
229 25412576 : ulong pkt_number_sz = fd_quic_h0_pkt_num_len( first ) + 1u;
230 25412576 : uchar const * pkt_number_ptr = out + hdr_sz - pkt_number_sz;
231 :
232 : // nonce is quic-iv XORed with packet-number
233 : // packet number is 1-4 bytes, so only XOR last pkt_number_sz bytes
234 25412576 : uchar nonce[FD_QUIC_NONCE_SZ] = {0};
235 25412576 : uint nonce_tmp = FD_QUIC_NONCE_SZ - 4;
236 25412576 : uchar const * quic_iv = pkt_keys->iv;
237 25412576 : memcpy( nonce, quic_iv, nonce_tmp );
238 127062880 : for( uint k = 0; k < 4; ++k ) {
239 101650304 : uint j = nonce_tmp + k;
240 101650304 : nonce[j] = (uchar)( quic_iv[j] ^ ( (uchar)( (pkt_number>>( (3u - k) * 8u ))&0xFF ) ) );
241 101650304 : }
242 :
243 : // Initial packets cipher uses AEAD_AES_128_GCM with keys derived from the Destination Connection ID field of the
244 : // first Initial packet sent by the client; see rfc9001 Section 5.2.
245 :
246 25412576 : fd_aes_gcm_t pkt_cipher[1];
247 25412576 : fd_aes_128_gcm_init( pkt_cipher, pkt_keys->pkt_key, nonce );
248 :
249 : /* cipher_text is start of encrypted packet bytes, which starts after the header */
250 25412576 : uchar * cipher_text = out + hdr_sz;
251 25412576 : uchar * tag = cipher_text + pkt_sz;
252 25412576 : uchar * pkt_end = tag + FD_QUIC_CRYPTO_TAG_SZ;
253 :
254 25412576 : fd_aes_gcm_encrypt( pkt_cipher, cipher_text, pkt, pkt_sz, hdr, hdr_sz, tag );
255 :
256 25412576 : *out_sz = (ulong)( pkt_end - out );
257 :
258 : /* Header protection */
259 :
260 : /* sample start is defined as 4 bytes after the start of the packet number
261 : so shorter packet numbers means sample starts later in the cipher text */
262 25412576 : uchar const * sample = pkt_number_ptr + 4;
263 :
264 25412576 : fd_aes_key_t ecb[1];
265 25412576 : fd_aes_set_encrypt_key( hp_keys->hp_key, 128, ecb );
266 25412576 : uchar hp_cipher[16];
267 25412576 : fd_aes_encrypt( sample, hp_cipher, ecb );
268 :
269 : /* hp_cipher is mask */
270 25412576 : uchar const * mask = hp_cipher;
271 :
272 25412576 : uchar long_hdr = first & 0x80u; /* long header? */
273 25412576 : out[0] ^= (uchar)( mask[0] & ( long_hdr ? 0x0fu : 0x1fu ) );
274 :
275 25412576 : ulong pkt_number_off = hdr_sz - pkt_number_sz;
276 :
277 127062880 : for( ulong j = 0; j < pkt_number_sz; ++j ) {
278 101650304 : out[pkt_number_off + j] ^= mask[1+j];
279 101650304 : }
280 :
281 25412576 : return FD_QUIC_SUCCESS;
282 25412576 : }
283 :
284 : int
285 : fd_quic_crypto_decrypt(
286 : uchar * buf,
287 : ulong buf_sz,
288 : ulong pkt_number_off,
289 : ulong pkt_number,
290 24201634 : fd_quic_crypto_keys_t const * keys ) {
291 :
292 24201634 : if( FD_UNLIKELY( ( pkt_number_off >= buf_sz ) |
293 24201634 : ( buf_sz < FD_QUIC_SHORTEST_PKT ) ) ) {
294 165 : FD_DEBUG( FD_LOG_WARNING( ( "fd_quic_crypto_decrypt: cipher text buffer too small" ) ) );
295 165 : return FD_QUIC_FAILED;
296 165 : }
297 :
298 : /* Derive header size */
299 24201469 : uint first = buf[0];
300 24201469 : ulong pkt_number_sz = fd_quic_h0_pkt_num_len( first ) + 1u;
301 24201469 : uchar * hdr = buf;
302 24201469 : ulong hdr_sz = pkt_number_off + pkt_number_sz;
303 :
304 : /* calculate nonce for decryption
305 : nonce is quic-iv XORed with *reconstructed* packet-number
306 : packet number is 1-4 bytes, so only XOR last pkt_number_sz bytes */
307 24201469 : uchar nonce[FD_QUIC_NONCE_SZ] = {0};
308 24201469 : uint nonce_tmp = FD_QUIC_NONCE_SZ - 4;
309 24201469 : uchar const * quic_iv = keys->iv;
310 24201469 : fd_memcpy( nonce, quic_iv, nonce_tmp );
311 121007345 : for( uint k = 0; k < 4; ++k ) {
312 96805876 : uint j = nonce_tmp + k;
313 96805876 : nonce[j] = (uchar)( quic_iv[j] ^ ( (uchar)( (pkt_number>>( (3u - k) * 8u ))&0xFF ) ) );
314 96805876 : }
315 :
316 24201469 : if( FD_UNLIKELY( ( buf_sz < hdr_sz ) |
317 24201469 : ( buf_sz < hdr_sz+FD_QUIC_CRYPTO_TAG_SZ ) ) )
318 15 : return FD_QUIC_FAILED;
319 :
320 : /* Derive offsets
321 :
322 : +----------+ <-- buf
323 : | Header |
324 : +----------+ <-- out
325 : | Payload |
326 : +----------+ <-- gcm_tag
327 : | GCM Tag |
328 : +----------+ <-- buf_end */
329 :
330 24201454 : uchar * const out = buf + hdr_sz;
331 24201454 : uchar * const buf_end = buf + buf_sz;
332 24201454 : uchar * const gcm_tag = buf_end - FD_QUIC_CRYPTO_TAG_SZ;
333 24201454 : ulong const gcm_sz = (ulong)( gcm_tag - out );
334 :
335 24201454 : fd_aes_gcm_t pkt_cipher[1];
336 24201454 : fd_aes_128_gcm_init( pkt_cipher, keys->pkt_key, nonce );
337 :
338 24201454 : int decrypt_ok =
339 24201454 : fd_aes_gcm_decrypt( pkt_cipher,
340 24201454 : out /* ciphertext */, out /* plaintext */,
341 24201454 : gcm_sz, /* size of plaintext */
342 24201454 : hdr, hdr_sz, /* associated data */
343 24201454 : gcm_tag /* auth tag */ );
344 24201454 : if( FD_UNLIKELY( !decrypt_ok ) ) {
345 6000294 : FD_DEBUG( FD_LOG_WARNING(( "fd_aes_gcm_decrypt failed" )) );
346 6000294 : return FD_QUIC_FAILED;
347 6000294 : }
348 :
349 18201160 : return FD_QUIC_SUCCESS;
350 24201454 : }
351 :
352 :
353 : int
354 : fd_quic_crypto_decrypt_hdr(
355 : uchar * buf,
356 : ulong buf_sz,
357 : ulong pkt_number_off,
358 24201658 : fd_quic_crypto_keys_t const * keys ) {
359 :
360 : /* bounds checks */
361 24201658 : if( FD_UNLIKELY( ( buf_sz < FD_QUIC_CRYPTO_TAG_SZ ) |
362 24201658 : ( pkt_number_off >= buf_sz ) ) ) {
363 6 : FD_DEBUG( FD_LOG_WARNING(( "decrypt hdr: bounds checks failed" )) );
364 6 : return FD_QUIC_FAILED;
365 6 : }
366 :
367 24201652 : uint first = buf[0]; /* first byte */
368 24201652 : uint long_hdr = first & 0x80u; /* long header? (this bit is not encrypted) */
369 24201652 : ulong sample_off = pkt_number_off + 4;
370 :
371 24201652 : if( FD_UNLIKELY( sample_off + FD_QUIC_HP_SAMPLE_SZ > buf_sz ) ) {
372 27 : FD_DEBUG( FD_LOG_WARNING(( "decrypt hdr: not enough bytes for a sample" )) );
373 27 : return FD_QUIC_FAILED;
374 27 : }
375 :
376 24201625 : uchar * sample = buf + sample_off;
377 :
378 : /* TODO this is hardcoded to AES-128 */
379 24201625 : uchar hp_cipher[16];
380 24201625 : fd_aes_key_t ecb[1];
381 24201625 : fd_aes_set_encrypt_key( keys->hp_key, 128, ecb );
382 24201625 : fd_aes_encrypt( sample, hp_cipher, ecb );
383 :
384 : /* hp_cipher is mask */
385 24201625 : uchar const * mask = hp_cipher;
386 :
387 : /* undo first byte mask */
388 24201625 : first ^= (uint)mask[0] & ( long_hdr ? 0x0fu : 0x1fu );
389 24201625 : buf[0] = (uchar)first;
390 :
391 : /* now we can calculate the actual packet number size */
392 24201625 : ulong pkt_number_sz = fd_quic_h0_pkt_num_len( first ) + 1u;
393 :
394 : /* undo packet number encryption */
395 111405800 : for( ulong j = 0u; j < pkt_number_sz; ++j ) {
396 87204175 : buf[ pkt_number_off + j ] ^= mask[ 1u+j ];
397 87204175 : }
398 :
399 24201625 : return FD_QUIC_SUCCESS;
400 24201652 : }
|