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 12210 : void const * conn_id, ulong conn_id_sz ) {
21 12210 : fd_hmac_sha256( conn_id, conn_id_sz, salt, salt_sz, output );
22 12210 : }
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 316686 : ulong label_sz ) {
30 316686 : fd_tls_hkdf_expand_label( out, out_sz, secret, label, label_sz, NULL, 0UL );
31 316686 : }
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 12210 : int is_server ) {
39 : /* Initial Packets
40 : from rfc:
41 : initial_salt = 0x38762cf7f55934b34d179ae6a4c80cadccbb7f0a */
42 12210 : uchar const * initial_salt = FD_QUIC_CRYPTO_V1_INITIAL_SALT;
43 12210 : ulong initial_salt_sz = sizeof(FD_QUIC_CRYPTO_V1_INITIAL_SALT);
44 12210 : fd_quic_hkdf_extract( secrets->initial_secret,
45 12210 : initial_salt, initial_salt_sz,
46 12210 : conn_id, conn_id_sz );
47 :
48 12210 : uchar * read_secret = secrets->secret[0][0];
49 12210 : uchar * write_secret = secrets->secret[0][1];
50 12210 : uchar * client_secret = is_server ? read_secret : write_secret;
51 12210 : uchar * server_secret = is_server ? write_secret : read_secret;
52 :
53 12210 : fd_quic_hkdf_expand_label(
54 12210 : client_secret, FD_QUIC_SECRET_SZ,
55 12210 : secrets->initial_secret,
56 12210 : FD_QUIC_CRYPTO_LABEL_CLIENT_IN,
57 12210 : FD_QUIC_CRYPTO_LABEL_CLIENT_IN_LEN );
58 :
59 12210 : fd_quic_hkdf_expand_label(
60 12210 : server_secret, FD_QUIC_SECRET_SZ,
61 12210 : secrets->initial_secret,
62 12210 : FD_QUIC_CRYPTO_LABEL_SERVER_IN,
63 12210 : FD_QUIC_CRYPTO_LABEL_SERVER_IN_LEN );
64 12210 : }
65 :
66 : void
67 : fd_quic_key_update_derive( fd_quic_crypto_secrets_t * secrets,
68 12234 : 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 12234 : uint enc_level = fd_quic_enc_level_appdata_id;
74 :
75 36702 : for( ulong j=0UL; j<2UL; j++ ) {
76 24468 : fd_quic_hkdf_expand_label(
77 24468 : secrets->new_secret[j], FD_QUIC_SECRET_SZ,
78 24468 : secrets->secret[enc_level][j],
79 24468 : FD_QUIC_CRYPTO_LABEL_KEY_UPDATE, FD_QUIC_CRYPTO_LABEL_KEY_UPDATE_LEN );
80 24468 : }
81 :
82 36702 : for( ulong j=0UL; j<2UL; j++ ) {
83 24468 : fd_quic_hkdf_expand_label(
84 24468 : new_keys[j].pkt_key, FD_AES_128_KEY_SZ,
85 24468 : secrets->new_secret[j],
86 24468 : FD_QUIC_CRYPTO_LABEL_QUIC_KEY,
87 24468 : FD_QUIC_CRYPTO_LABEL_QUIC_KEY_LEN );
88 :
89 24468 : fd_quic_hkdf_expand_label(
90 24468 : new_keys[j].iv, FD_AES_GCM_IV_SZ,
91 24468 : secrets->new_secret[j],
92 24468 : FD_QUIC_CRYPTO_LABEL_QUIC_IV,
93 24468 : FD_QUIC_CRYPTO_LABEL_QUIC_IV_LEN );
94 24468 : }
95 12234 : }
96 :
97 :
98 : void
99 : fd_quic_gen_keys(
100 : fd_quic_crypto_keys_t * keys,
101 72954 : 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 72954 : fd_quic_hkdf_expand_label(
108 72954 : keys->pkt_key, FD_AES_128_KEY_SZ,
109 72954 : secret,
110 72954 : FD_QUIC_CRYPTO_LABEL_QUIC_KEY,
111 72954 : 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 72954 : fd_quic_hkdf_expand_label(
118 72954 : keys->hp_key, FD_AES_128_KEY_SZ,
119 72954 : secret,
120 72954 : FD_QUIC_CRYPTO_LABEL_QUIC_HP,
121 72954 : FD_QUIC_CRYPTO_LABEL_QUIC_HP_LEN );
122 :
123 : /* quic iv */
124 72954 : fd_quic_hkdf_expand_label(
125 72954 : keys->iv, FD_AES_GCM_IV_SZ,
126 72954 : secret,
127 72954 : FD_QUIC_CRYPTO_LABEL_QUIC_IV,
128 72954 : FD_QUIC_CRYPTO_LABEL_QUIC_IV_LEN );
129 72954 : }
130 :
131 : /* encrypt a packet
132 :
133 : uses the keys in keys to encrypt the packet "pkt" with header "hdr"
134 : (of length pkt_sz, and hdr_sz respectively) into out.
135 :
136 : out should have enough space to contain the full output with extra space
137 : for a full block which depends on the cipher
138 :
139 : *out_sz is used to determine the amount of buffer space left at *out
140 : if enough space is not available, the function fails and returns
141 : FD_QUIC_FAILED
142 : *out_sz is also set to the number of bytes written into *out at the end
143 :
144 : args
145 : out the destination for the encrypted output
146 : out_sz a pointer to the size of the buffer (on input) and the size of
147 : the written bytes (on output)
148 : hdr the input header bytes
149 : hdr_sz the size of the header in bytes
150 : pkt the input packet bytes
151 : pkt_sz the size of the packet in bytes (frames after packet number not including MAC tag)
152 : keys a pointer to the keys to use
153 : pkt_number needed to create the nonce used in encryption
154 : likely points to the packet number within "hdr"
155 : pkt_number_sz the size of the packet number in bytes
156 : */
157 :
158 : int
159 : fd_quic_crypto_encrypt(
160 : uchar * const out,
161 : ulong * const out_sz,
162 : uchar const * const hdr,
163 : ulong const hdr_sz,
164 : uchar const * const pkt,
165 : ulong const pkt_sz,
166 : fd_quic_crypto_keys_t const * const pkt_keys,
167 : fd_quic_crypto_keys_t const * const hp_keys,
168 11688742 : ulong const pkt_number ) {
169 :
170 :
171 : /* ensure we have enough space in the output buffer
172 : most space used by cipher:
173 : header bytes (just XORed)
174 : input bytes (encrypted)
175 : tag bytes */
176 :
177 : /* bound on the bytes needed for cipher output */
178 11688742 : ulong cipher_out_bound = hdr_sz + pkt_sz + FD_QUIC_CRYPTO_TAG_SZ;
179 :
180 11688742 : if( FD_UNLIKELY( *out_sz < cipher_out_bound ) ) {
181 0 : FD_DEBUG( FD_LOG_WARNING(( "fd_quic_crypto_encrypt: output buffer not big enough" )) );
182 0 : return FD_QUIC_FAILED;
183 0 : }
184 :
185 11688742 : if( FD_UNLIKELY( ( hdr_sz < 4 ) | ( hdr_sz > INT_MAX ) ) ) {
186 0 : FD_DEBUG( FD_LOG_WARNING(( "fd_quic_crypto_encrypt: packet header size out of bounds" )) );
187 0 : return FD_QUIC_FAILED;
188 0 : }
189 :
190 : /* bounds check */
191 11688742 : if( FD_UNLIKELY( pkt_sz > INT_MAX ) ) return FD_QUIC_FAILED;
192 :
193 : /* copy the header into the output */
194 11688742 : fd_memcpy( out, hdr, hdr_sz );
195 :
196 : /* first byte needed in a couple of places */
197 11688742 : uchar first = out[0];
198 11688742 : ulong pkt_number_sz = fd_quic_h0_pkt_num_len( first ) + 1u;
199 11688742 : uchar const * pkt_number_ptr = out + hdr_sz - pkt_number_sz;
200 :
201 11688742 : uchar nonce[FD_QUIC_NONCE_SZ] = {0};
202 11688742 : fd_quic_get_nonce( nonce, pkt_keys->iv, pkt_number );
203 :
204 : // Initial packets cipher uses AEAD_AES_128_GCM with keys derived from the Destination Connection ID field of the
205 : // first Initial packet sent by the client; see rfc9001 Section 5.2.
206 11688742 : fd_aes_gcm_t pkt_cipher[1];
207 11688742 : fd_aes_128_gcm_init( pkt_cipher, pkt_keys->pkt_key, nonce );
208 :
209 : /* cipher_text is start of encrypted packet bytes, which starts after the header */
210 11688742 : uchar * cipher_text = out + hdr_sz;
211 11688742 : uchar * tag = cipher_text + pkt_sz;
212 11688742 : uchar * pkt_end = tag + FD_QUIC_CRYPTO_TAG_SZ;
213 :
214 11688742 : fd_aes_gcm_encrypt( pkt_cipher, cipher_text, pkt, pkt_sz, hdr, hdr_sz, tag );
215 :
216 11688742 : *out_sz = (ulong)( pkt_end - out );
217 :
218 : /* Header protection */
219 :
220 : /* sample start is defined as 4 bytes after the start of the packet number
221 : so shorter packet numbers means sample starts later in the cipher text */
222 11688742 : uchar const * sample = pkt_number_ptr + 4;
223 :
224 11688742 : fd_aes_key_t ecb[1];
225 11688742 : fd_aes_set_encrypt_key( hp_keys->hp_key, 128, ecb );
226 11688742 : uchar hp_cipher[16];
227 11688742 : fd_aes_encrypt( sample, hp_cipher, ecb );
228 :
229 : /* hp_cipher is mask */
230 11688742 : uchar const * mask = hp_cipher;
231 :
232 11688742 : uchar long_hdr = first & 0x80u; /* long header? */
233 11688742 : out[0] ^= (uchar)( mask[0] & ( long_hdr ? 0x0fu : 0x1fu ) );
234 :
235 11688742 : ulong pkt_number_off = hdr_sz - pkt_number_sz;
236 :
237 58443698 : for( ulong j = 0; j < pkt_number_sz; ++j ) {
238 46754956 : out[pkt_number_off + j] ^= mask[1+j];
239 46754956 : }
240 :
241 11688742 : return FD_QUIC_SUCCESS;
242 11688742 : }
243 :
244 : int
245 : fd_quic_crypto_decrypt(
246 : uchar * buf,
247 : ulong buf_sz,
248 : ulong pkt_number_off,
249 : ulong pkt_number,
250 11682484 : fd_quic_crypto_keys_t const * keys ) {
251 :
252 11682484 : if( FD_UNLIKELY( ( pkt_number_off >= buf_sz ) |
253 11682484 : ( buf_sz < FD_QUIC_SHORTEST_PKT ) ) ) {
254 3 : FD_DEBUG( FD_LOG_WARNING( ( "fd_quic_crypto_decrypt: cipher text buffer too small" ) ) );
255 3 : return FD_QUIC_FAILED;
256 3 : }
257 :
258 : /* Derive header size */
259 11682481 : uint first = buf[0];
260 11682481 : ulong pkt_number_sz = fd_quic_h0_pkt_num_len( first ) + 1u;
261 11682481 : uchar * hdr = buf;
262 11682481 : ulong hdr_sz = pkt_number_off + pkt_number_sz;
263 :
264 : /* calculate nonce for decryption
265 : nonce is quic-iv XORed with *reconstructed* packet-number
266 : packet number is 1-4 bytes, so only XOR last pkt_number_sz bytes */
267 11682481 : uchar nonce[FD_QUIC_NONCE_SZ] = {0};
268 11682481 : fd_quic_get_nonce( nonce, keys->iv, pkt_number );
269 :
270 11682481 : if( FD_UNLIKELY( ( buf_sz < hdr_sz ) |
271 11682481 : ( buf_sz < hdr_sz+FD_QUIC_CRYPTO_TAG_SZ ) ) )
272 0 : return FD_QUIC_FAILED;
273 :
274 : /* Derive offsets
275 :
276 : +----------+ <-- buf
277 : | Header |
278 : +----------+ <-- out
279 : | Payload |
280 : +----------+ <-- gcm_tag
281 : | GCM Tag |
282 : +----------+ <-- buf_end */
283 :
284 11682481 : uchar * const out = buf + hdr_sz;
285 11682481 : uchar * const buf_end = buf + buf_sz;
286 11682481 : uchar * const gcm_tag = buf_end - FD_QUIC_CRYPTO_TAG_SZ;
287 11682481 : ulong const gcm_sz = (ulong)( gcm_tag - out );
288 :
289 11682481 : fd_aes_gcm_t pkt_cipher[1];
290 11682481 : fd_aes_128_gcm_init( pkt_cipher, keys->pkt_key, nonce );
291 :
292 11682481 : int decrypt_ok = fd_aes_gcm_decrypt( pkt_cipher,
293 11682481 : out /* ciphertext */, out /* plaintext */,
294 11682481 : gcm_sz, /* size of plaintext */
295 11682481 : hdr, hdr_sz, /* associated data */
296 11682481 : gcm_tag /* auth tag */ );
297 11682481 : if( FD_UNLIKELY( !decrypt_ok ) ) {
298 4020081 : FD_DEBUG( FD_LOG_WARNING(( "fd_aes_gcm_decrypt failed" )) );
299 4020081 : return FD_QUIC_FAILED;
300 4020081 : }
301 :
302 7662400 : return FD_QUIC_SUCCESS;
303 11682481 : }
304 :
305 :
306 : int
307 : fd_quic_crypto_decrypt_hdr(
308 : uchar * buf,
309 : ulong buf_sz,
310 : ulong pkt_number_off,
311 11682487 : fd_quic_crypto_keys_t const * keys ) {
312 :
313 : /* bounds checks */
314 11682487 : if( FD_UNLIKELY( ( buf_sz < FD_QUIC_CRYPTO_TAG_SZ ) |
315 11682487 : ( pkt_number_off >= buf_sz ) ) ) {
316 6 : FD_DEBUG( FD_LOG_WARNING(( "decrypt hdr: bounds checks failed" )) );
317 6 : return FD_QUIC_FAILED;
318 6 : }
319 :
320 11682481 : uint first = buf[0]; /* first byte */
321 11682481 : uint long_hdr = first & 0x80u; /* long header? (this bit is not encrypted) */
322 11682481 : ulong sample_off = pkt_number_off + 4;
323 :
324 11682481 : if( FD_UNLIKELY( sample_off + FD_QUIC_HP_SAMPLE_SZ > buf_sz ) ) {
325 6 : FD_DEBUG( FD_LOG_WARNING(( "decrypt hdr: not enough bytes for a sample" )) );
326 6 : return FD_QUIC_FAILED;
327 6 : }
328 :
329 11682475 : uchar * sample = buf + sample_off;
330 :
331 : /* TODO this is hardcoded to AES-128 */
332 11682475 : uchar hp_cipher[16];
333 11682475 : fd_aes_key_t ecb[1];
334 11682475 : fd_aes_set_encrypt_key( keys->hp_key, 128, ecb );
335 11682475 : fd_aes_encrypt( sample, hp_cipher, ecb );
336 :
337 : /* hp_cipher is mask */
338 11682475 : uchar const * mask = hp_cipher;
339 :
340 : /* undo first byte mask */
341 11682475 : first ^= (uint)mask[0] & ( long_hdr ? 0x0fu : 0x1fu );
342 11682475 : buf[0] = (uchar)first;
343 :
344 : /* now we can calculate the actual packet number size */
345 11682475 : ulong pkt_number_sz = fd_quic_h0_pkt_num_len( first ) + 1u;
346 11682475 : if( pkt_number_off+pkt_number_sz > buf_sz ) {
347 0 : FD_DEBUG( FD_LOG_WARNING(( "decrypt hdr: not enough bytes for packet number" )) );
348 0 : return FD_QUIC_FAILED;
349 0 : }
350 :
351 : /* undo packet number encryption */
352 51980267 : for( ulong j = 0u; j < pkt_number_sz; ++j ) {
353 40297792 : buf[ pkt_number_off + j ] ^= mask[ 1u+j ];
354 40297792 : }
355 :
356 11682475 : return FD_QUIC_SUCCESS;
357 11682475 : }
|