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