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 : /* 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 19846030 : 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 19846030 : ulong cipher_out_bound = hdr_sz + pkt_sz + FD_QUIC_CRYPTO_TAG_SZ;
179 :
180 19846030 : 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 19846030 : 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 19846030 : if( FD_UNLIKELY( pkt_sz > INT_MAX ) ) return FD_QUIC_FAILED;
192 :
193 : /* copy the header into the output */
194 19846030 : fd_memcpy( out, hdr, hdr_sz );
195 :
196 : /* first byte needed in a couple of places */
197 19846030 : uchar first = out[0];
198 19846030 : ulong pkt_number_sz = fd_quic_h0_pkt_num_len( first ) + 1u;
199 19846030 : uchar const * pkt_number_ptr = out + hdr_sz - pkt_number_sz;
200 :
201 19846030 : uchar nonce[FD_QUIC_NONCE_SZ] = {0};
202 19846030 : 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 19846030 : fd_aes_gcm_t pkt_cipher[1];
207 19846030 : 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 19846030 : uchar * cipher_text = out + hdr_sz;
211 19846030 : uchar * tag = cipher_text + pkt_sz;
212 19846030 : uchar * pkt_end = tag + FD_QUIC_CRYPTO_TAG_SZ;
213 :
214 19846030 : fd_aes_gcm_encrypt( pkt_cipher, cipher_text, pkt, pkt_sz, hdr, hdr_sz, tag );
215 :
216 19846030 : *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 19846030 : uchar const * sample = pkt_number_ptr + 4;
223 :
224 19846030 : fd_aes_key_t ecb[1];
225 19846030 : fd_aes_set_encrypt_key( hp_keys->hp_key, 128, ecb );
226 19846030 : uchar hp_cipher[16];
227 19846030 : fd_aes_encrypt( sample, hp_cipher, ecb );
228 :
229 : /* hp_cipher is mask */
230 19846030 : uchar const * mask = hp_cipher;
231 :
232 19846030 : uchar long_hdr = first & 0x80u; /* long header? */
233 19846030 : out[0] ^= (uchar)( mask[0] & ( long_hdr ? 0x0fu : 0x1fu ) );
234 :
235 19846030 : ulong pkt_number_off = hdr_sz - pkt_number_sz;
236 :
237 99230138 : for( ulong j = 0; j < pkt_number_sz; ++j ) {
238 79384108 : out[pkt_number_off + j] ^= mask[1+j];
239 79384108 : }
240 :
241 19846030 : return FD_QUIC_SUCCESS;
242 19846030 : }
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 19840204 : fd_quic_crypto_keys_t const * keys ) {
251 :
252 19840204 : if( FD_UNLIKELY( ( pkt_number_off >= buf_sz ) |
253 19840204 : ( buf_sz < FD_QUIC_SHORTEST_PKT ) ) ) {
254 105 : FD_DEBUG( FD_LOG_WARNING( ( "fd_quic_crypto_decrypt: cipher text buffer too small" ) ) );
255 105 : return FD_QUIC_FAILED;
256 105 : }
257 :
258 : /* Derive header size */
259 19840099 : uint first = buf[0];
260 19840099 : ulong pkt_number_sz = fd_quic_h0_pkt_num_len( first ) + 1u;
261 19840099 : uchar * hdr = buf;
262 19840099 : 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 19840099 : uchar nonce[FD_QUIC_NONCE_SZ] = {0};
268 19840099 : fd_quic_get_nonce( nonce, keys->iv, pkt_number );
269 :
270 19840099 : if( FD_UNLIKELY( ( buf_sz < hdr_sz ) |
271 19840099 : ( buf_sz < hdr_sz+FD_QUIC_CRYPTO_TAG_SZ ) ) )
272 3 : 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 19840096 : uchar * const out = buf + hdr_sz;
285 19840096 : uchar * const buf_end = buf + buf_sz;
286 19840096 : uchar * const gcm_tag = buf_end - FD_QUIC_CRYPTO_TAG_SZ;
287 19840096 : ulong const gcm_sz = (ulong)( gcm_tag - out );
288 :
289 19840096 : fd_aes_gcm_t pkt_cipher[1];
290 19840096 : fd_aes_128_gcm_init( pkt_cipher, keys->pkt_key, nonce );
291 :
292 19840096 : int decrypt_ok =
293 19840096 : fd_aes_gcm_decrypt( pkt_cipher,
294 19840096 : out /* ciphertext */, out /* plaintext */,
295 19840096 : gcm_sz, /* size of plaintext */
296 19840096 : hdr, hdr_sz, /* associated data */
297 19840096 : gcm_tag /* auth tag */ );
298 19840096 : if( FD_UNLIKELY( !decrypt_ok ) ) {
299 6000135 : FD_DEBUG( FD_LOG_WARNING(( "fd_aes_gcm_decrypt failed" )) );
300 6000135 : return FD_QUIC_FAILED;
301 6000135 : }
302 :
303 13839961 : return FD_QUIC_SUCCESS;
304 19840096 : }
305 :
306 :
307 : int
308 : fd_quic_crypto_decrypt_hdr(
309 : uchar * buf,
310 : ulong buf_sz,
311 : ulong pkt_number_off,
312 19840207 : fd_quic_crypto_keys_t const * keys ) {
313 :
314 : /* bounds checks */
315 19840207 : if( FD_UNLIKELY( ( buf_sz < FD_QUIC_CRYPTO_TAG_SZ ) |
316 19840207 : ( pkt_number_off >= buf_sz ) ) ) {
317 6 : FD_DEBUG( FD_LOG_WARNING(( "decrypt hdr: bounds checks failed" )) );
318 6 : return FD_QUIC_FAILED;
319 6 : }
320 :
321 19840201 : uint first = buf[0]; /* first byte */
322 19840201 : uint long_hdr = first & 0x80u; /* long header? (this bit is not encrypted) */
323 19840201 : ulong sample_off = pkt_number_off + 4;
324 :
325 19840201 : if( FD_UNLIKELY( sample_off + FD_QUIC_HP_SAMPLE_SZ > buf_sz ) ) {
326 6 : FD_DEBUG( FD_LOG_WARNING(( "decrypt hdr: not enough bytes for a sample" )) );
327 6 : return FD_QUIC_FAILED;
328 6 : }
329 :
330 19840195 : uchar * sample = buf + sample_off;
331 :
332 : /* TODO this is hardcoded to AES-128 */
333 19840195 : uchar hp_cipher[16];
334 19840195 : fd_aes_key_t ecb[1];
335 19840195 : fd_aes_set_encrypt_key( keys->hp_key, 128, ecb );
336 19840195 : fd_aes_encrypt( sample, hp_cipher, ecb );
337 :
338 : /* hp_cipher is mask */
339 19840195 : uchar const * mask = hp_cipher;
340 :
341 : /* undo first byte mask */
342 19840195 : first ^= (uint)mask[0] & ( long_hdr ? 0x0fu : 0x1fu );
343 19840195 : buf[0] = (uchar)first;
344 :
345 : /* now we can calculate the actual packet number size */
346 19840195 : ulong pkt_number_sz = fd_quic_h0_pkt_num_len( first ) + 1u;
347 19840195 : if( pkt_number_off+pkt_number_sz > buf_sz ) {
348 0 : FD_DEBUG( FD_LOG_WARNING(( "decrypt hdr: not enough bytes for packet number" )) );
349 0 : return FD_QUIC_FAILED;
350 0 : }
351 :
352 : /* undo packet number encryption */
353 89600543 : for( ulong j = 0u; j < pkt_number_sz; ++j ) {
354 69760348 : buf[ pkt_number_off + j ] ^= mask[ 1u+j ];
355 69760348 : }
356 :
357 19840195 : return FD_QUIC_SUCCESS;
358 19840195 : }
|