Mercurial > hg > nginx-quic
comparison src/event/ngx_event_quic_protection.c @ 7687:69345a26ba69 quic
Split transport and crypto parts into separate files.
New files:
src/event/ngx_event_quic_protection.h
src/event/ngx_event_quic_protection.c
The protection.h header provides interface to the crypto part of the QUIC:
2 functions to initialize corresponding secrets:
ngx_quic_set_initial_secret()
ngx_quic_set_encryption_secret()
and 2 functions to deal with packet processing:
ngx_quic_encrypt()
ngx_quic_decrypt()
Also, structures representing secrets are defined there.
All functions require SSL connection and a pool, only crypto operations
inside, no access to nginx connections or events.
Currently pool->log is used for the logging (instead of original c->log).
author | Vladimir Homutov <vl@nginx.com> |
---|---|
date | Mon, 16 Mar 2020 19:00:47 +0300 |
parents | |
children | ae35ccba7aa6 |
comparison
equal
deleted
inserted
replaced
7686:7ada2feeac18 | 7687:69345a26ba69 |
---|---|
1 | |
2 /* | |
3 * Copyright (C) Nginx, Inc. | |
4 */ | |
5 | |
6 | |
7 #include <ngx_config.h> | |
8 #include <ngx_core.h> | |
9 #include <ngx_event.h> | |
10 | |
11 | |
12 #define NGX_QUIC_IV_LEN 12 | |
13 | |
14 #define NGX_AES_128_GCM_SHA256 0x1301 | |
15 #define NGX_AES_256_GCM_SHA384 0x1302 | |
16 #define NGX_CHACHA20_POLY1305_SHA256 0x1303 | |
17 | |
18 | |
19 #ifdef OPENSSL_IS_BORINGSSL | |
20 #define ngx_quic_cipher_t EVP_AEAD | |
21 #else | |
22 #define ngx_quic_cipher_t EVP_CIPHER | |
23 #endif | |
24 | |
25 typedef struct { | |
26 const ngx_quic_cipher_t *c; | |
27 const EVP_CIPHER *hp; | |
28 const EVP_MD *d; | |
29 } ngx_quic_ciphers_t; | |
30 | |
31 | |
32 static ngx_int_t ngx_hkdf_expand(u_char *out_key, size_t out_len, | |
33 const EVP_MD *digest, const u_char *prk, size_t prk_len, | |
34 const u_char *info, size_t info_len); | |
35 static ngx_int_t ngx_hkdf_extract(u_char *out_key, size_t *out_len, | |
36 const EVP_MD *digest, const u_char *secret, size_t secret_len, | |
37 const u_char *salt, size_t salt_len); | |
38 | |
39 static uint64_t ngx_quic_parse_pn(u_char **pos, ngx_int_t len, u_char *mask); | |
40 static ngx_int_t ngx_quic_ciphers(ngx_ssl_conn_t *ssl_conn, | |
41 ngx_quic_ciphers_t *ciphers, enum ssl_encryption_level_t level); | |
42 | |
43 static ngx_int_t ngx_quic_tls_open(ngx_pool_t *pool, const ngx_quic_cipher_t *cipher, | |
44 ngx_quic_secret_t *s, ngx_str_t *out, u_char *nonce, ngx_str_t *in, | |
45 ngx_str_t *ad); | |
46 static ngx_int_t ngx_quic_tls_seal(ngx_pool_t *pool, | |
47 const ngx_quic_cipher_t *cipher, ngx_quic_secret_t *s, ngx_str_t *out, | |
48 u_char *nonce, ngx_str_t *in, ngx_str_t *ad); | |
49 static ngx_int_t ngx_quic_tls_hp(ngx_log_t *log, const EVP_CIPHER *cipher, | |
50 ngx_quic_secret_t *s, u_char *out, u_char *in); | |
51 static ngx_int_t ngx_quic_hkdf_expand(ngx_pool_t *pool, const EVP_MD *digest, | |
52 ngx_str_t *out, ngx_str_t *label, const uint8_t *prk, size_t prk_len); | |
53 | |
54 static ngx_int_t ngx_quic_create_long_packet(ngx_pool_t *pool, | |
55 ngx_ssl_conn_t *ssl_conn, ngx_quic_header_t *pkt, ngx_str_t *in, | |
56 ngx_str_t *res); | |
57 | |
58 static ngx_int_t ngx_quic_create_short_packet(ngx_pool_t *pool, | |
59 ngx_ssl_conn_t *ssl_conn, ngx_quic_header_t *pkt, ngx_str_t *in, | |
60 ngx_str_t *res); | |
61 | |
62 | |
63 static ngx_int_t | |
64 ngx_quic_ciphers(ngx_ssl_conn_t *ssl_conn, ngx_quic_ciphers_t *ciphers, | |
65 enum ssl_encryption_level_t level) | |
66 { | |
67 ngx_int_t id, len; | |
68 | |
69 if (level == ssl_encryption_initial) { | |
70 id = NGX_AES_128_GCM_SHA256; | |
71 | |
72 } else { | |
73 id = SSL_CIPHER_get_id(SSL_get_current_cipher(ssl_conn)) & 0xffff; | |
74 } | |
75 | |
76 switch (id) { | |
77 | |
78 case NGX_AES_128_GCM_SHA256: | |
79 #ifdef OPENSSL_IS_BORINGSSL | |
80 ciphers->c = EVP_aead_aes_128_gcm(); | |
81 #else | |
82 ciphers->c = EVP_aes_128_gcm(); | |
83 #endif | |
84 ciphers->hp = EVP_aes_128_ctr(); | |
85 ciphers->d = EVP_sha256(); | |
86 len = 16; | |
87 break; | |
88 | |
89 case NGX_AES_256_GCM_SHA384: | |
90 #ifdef OPENSSL_IS_BORINGSSL | |
91 ciphers->c = EVP_aead_aes_256_gcm(); | |
92 #else | |
93 ciphers->c = EVP_aes_256_gcm(); | |
94 #endif | |
95 ciphers->hp = EVP_aes_256_ctr(); | |
96 ciphers->d = EVP_sha384(); | |
97 len = 32; | |
98 break; | |
99 | |
100 case NGX_CHACHA20_POLY1305_SHA256: | |
101 #ifdef OPENSSL_IS_BORINGSSL | |
102 ciphers->c = EVP_aead_chacha20_poly1305(); | |
103 #else | |
104 ciphers->c = EVP_chacha20_poly1305(); | |
105 #endif | |
106 #ifdef OPENSSL_IS_BORINGSSL | |
107 ciphers->hp = (const EVP_CIPHER *) EVP_aead_chacha20_poly1305(); | |
108 #else | |
109 ciphers->hp = EVP_chacha20(); | |
110 #endif | |
111 ciphers->d = EVP_sha256(); | |
112 len = 32; | |
113 break; | |
114 | |
115 default: | |
116 return NGX_ERROR; | |
117 } | |
118 | |
119 return len; | |
120 } | |
121 | |
122 | |
123 ngx_int_t | |
124 ngx_quic_set_initial_secret(ngx_pool_t *pool, ngx_quic_secrets_t *qsec, | |
125 ngx_str_t *secret) | |
126 { | |
127 size_t is_len; | |
128 uint8_t is[SHA256_DIGEST_LENGTH]; | |
129 ngx_uint_t i; | |
130 const EVP_MD *digest; | |
131 const EVP_CIPHER *cipher; | |
132 | |
133 static const uint8_t salt[20] = | |
134 "\xc3\xee\xf7\x12\xc7\x2e\xbb\x5a\x11\xa7" | |
135 "\xd2\x43\x2b\xb4\x63\x65\xbe\xf9\xf5\x02"; | |
136 | |
137 /* AEAD_AES_128_GCM prior to handshake, quic-tls-23#section-5.3 */ | |
138 | |
139 cipher = EVP_aes_128_gcm(); | |
140 digest = EVP_sha256(); | |
141 | |
142 if (ngx_hkdf_extract(is, &is_len, digest, secret->data, secret->len, | |
143 salt, sizeof(salt)) | |
144 != NGX_OK) | |
145 { | |
146 return NGX_ERROR; | |
147 } | |
148 | |
149 ngx_str_t iss = { | |
150 .data = is, | |
151 .len = is_len | |
152 }; | |
153 | |
154 ngx_quic_hexdump0(pool->log, "salt", salt, sizeof(salt)); | |
155 ngx_quic_hexdump0(pool->log, "initial secret", is, is_len); | |
156 | |
157 /* draft-ietf-quic-tls-23#section-5.2 */ | |
158 qsec->client.in.secret.len = SHA256_DIGEST_LENGTH; | |
159 qsec->server.in.secret.len = SHA256_DIGEST_LENGTH; | |
160 | |
161 qsec->client.in.key.len = EVP_CIPHER_key_length(cipher); | |
162 qsec->server.in.key.len = EVP_CIPHER_key_length(cipher); | |
163 | |
164 qsec->client.in.hp.len = EVP_CIPHER_key_length(cipher); | |
165 qsec->server.in.hp.len = EVP_CIPHER_key_length(cipher); | |
166 | |
167 qsec->client.in.iv.len = EVP_CIPHER_iv_length(cipher); | |
168 qsec->server.in.iv.len = EVP_CIPHER_iv_length(cipher); | |
169 | |
170 struct { | |
171 ngx_str_t label; | |
172 ngx_str_t *key; | |
173 ngx_str_t *prk; | |
174 } seq[] = { | |
175 | |
176 /* draft-ietf-quic-tls-23#section-5.2 */ | |
177 { ngx_string("tls13 client in"), &qsec->client.in.secret, &iss }, | |
178 { | |
179 ngx_string("tls13 quic key"), | |
180 &qsec->client.in.key, | |
181 &qsec->client.in.secret, | |
182 }, | |
183 { | |
184 ngx_string("tls13 quic iv"), | |
185 &qsec->client.in.iv, | |
186 &qsec->client.in.secret, | |
187 }, | |
188 { | |
189 /* AEAD_AES_128_GCM prior to handshake, quic-tls-23#section-5.4.1 */ | |
190 ngx_string("tls13 quic hp"), | |
191 &qsec->client.in.hp, | |
192 &qsec->client.in.secret, | |
193 }, | |
194 { ngx_string("tls13 server in"), &qsec->server.in.secret, &iss }, | |
195 { | |
196 /* AEAD_AES_128_GCM prior to handshake, quic-tls-23#section-5.3 */ | |
197 ngx_string("tls13 quic key"), | |
198 &qsec->server.in.key, | |
199 &qsec->server.in.secret, | |
200 }, | |
201 { | |
202 ngx_string("tls13 quic iv"), | |
203 &qsec->server.in.iv, | |
204 &qsec->server.in.secret, | |
205 }, | |
206 { | |
207 /* AEAD_AES_128_GCM prior to handshake, quic-tls-23#section-5.4.1 */ | |
208 ngx_string("tls13 quic hp"), | |
209 &qsec->server.in.hp, | |
210 &qsec->server.in.secret, | |
211 }, | |
212 | |
213 }; | |
214 | |
215 for (i = 0; i < (sizeof(seq) / sizeof(seq[0])); i++) { | |
216 | |
217 if (ngx_quic_hkdf_expand(pool, digest, seq[i].key, &seq[i].label, | |
218 seq[i].prk->data, seq[i].prk->len) | |
219 != NGX_OK) | |
220 { | |
221 return NGX_ERROR; | |
222 } | |
223 } | |
224 | |
225 return NGX_OK; | |
226 } | |
227 | |
228 | |
229 static ngx_int_t | |
230 ngx_quic_hkdf_expand(ngx_pool_t *pool, const EVP_MD *digest, ngx_str_t *out, | |
231 ngx_str_t *label, const uint8_t *prk, size_t prk_len) | |
232 { | |
233 size_t info_len; | |
234 uint8_t *p; | |
235 uint8_t info[20]; | |
236 | |
237 out->data = ngx_pnalloc(pool, out->len); | |
238 if (out->data == NULL) { | |
239 return NGX_ERROR; | |
240 } | |
241 | |
242 info_len = 2 + 1 + label->len + 1; | |
243 | |
244 info[0] = 0; | |
245 info[1] = out->len; | |
246 info[2] = label->len; | |
247 p = ngx_cpymem(&info[3], label->data, label->len); | |
248 *p = '\0'; | |
249 | |
250 if (ngx_hkdf_expand(out->data, out->len, digest, | |
251 prk, prk_len, info, info_len) | |
252 != NGX_OK) | |
253 { | |
254 ngx_ssl_error(NGX_LOG_INFO, pool->log, 0, | |
255 "ngx_hkdf_expand(%V) failed", label); | |
256 return NGX_ERROR; | |
257 } | |
258 | |
259 ngx_quic_hexdump(pool->log, "%V info", info, info_len, label); | |
260 ngx_quic_hexdump(pool->log, "%V key", out->data, out->len, label); | |
261 | |
262 return NGX_OK; | |
263 } | |
264 | |
265 | |
266 static ngx_int_t | |
267 ngx_hkdf_expand(u_char *out_key, size_t out_len, const EVP_MD *digest, | |
268 const uint8_t *prk, size_t prk_len, const u_char *info, size_t info_len) | |
269 { | |
270 #ifdef OPENSSL_IS_BORINGSSL | |
271 if (HKDF_expand(out_key, out_len, digest, prk, prk_len, info, info_len) | |
272 == 0) | |
273 { | |
274 return NGX_ERROR; | |
275 } | |
276 #else | |
277 | |
278 EVP_PKEY_CTX *pctx; | |
279 | |
280 pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL); | |
281 | |
282 if (EVP_PKEY_derive_init(pctx) <= 0) { | |
283 return NGX_ERROR; | |
284 } | |
285 | |
286 if (EVP_PKEY_CTX_hkdf_mode(pctx, EVP_PKEY_HKDEF_MODE_EXPAND_ONLY) <= 0) { | |
287 return NGX_ERROR; | |
288 } | |
289 | |
290 if (EVP_PKEY_CTX_set_hkdf_md(pctx, digest) <= 0) { | |
291 return NGX_ERROR; | |
292 } | |
293 | |
294 if (EVP_PKEY_CTX_set1_hkdf_key(pctx, prk, prk_len) <= 0) { | |
295 return NGX_ERROR; | |
296 } | |
297 | |
298 if (EVP_PKEY_CTX_add1_hkdf_info(pctx, info, info_len) <= 0) { | |
299 return NGX_ERROR; | |
300 } | |
301 | |
302 if (EVP_PKEY_derive(pctx, out_key, &out_len) <= 0) { | |
303 return NGX_ERROR; | |
304 } | |
305 | |
306 #endif | |
307 | |
308 return NGX_OK; | |
309 } | |
310 | |
311 | |
312 static ngx_int_t | |
313 ngx_hkdf_extract(u_char *out_key, size_t *out_len, const EVP_MD *digest, | |
314 const u_char *secret, size_t secret_len, const u_char *salt, | |
315 size_t salt_len) | |
316 { | |
317 #ifdef OPENSSL_IS_BORINGSSL | |
318 if (HKDF_extract(out_key, out_len, digest, secret, secret_len, salt, | |
319 salt_len) | |
320 == 0) | |
321 { | |
322 return NGX_ERROR; | |
323 } | |
324 #else | |
325 | |
326 EVP_PKEY_CTX *pctx; | |
327 | |
328 pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL); | |
329 | |
330 if (EVP_PKEY_derive_init(pctx) <= 0) { | |
331 return NGX_ERROR; | |
332 } | |
333 | |
334 if (EVP_PKEY_CTX_hkdf_mode(pctx, EVP_PKEY_HKDEF_MODE_EXTRACT_ONLY) <= 0) { | |
335 return NGX_ERROR; | |
336 } | |
337 | |
338 if (EVP_PKEY_CTX_set_hkdf_md(pctx, digest) <= 0) { | |
339 return NGX_ERROR; | |
340 } | |
341 | |
342 if (EVP_PKEY_CTX_set1_hkdf_key(pctx, secret, secret_len) <= 0) { | |
343 return NGX_ERROR; | |
344 } | |
345 | |
346 if (EVP_PKEY_CTX_set1_hkdf_salt(pctx, salt, salt_len) <= 0) { | |
347 return NGX_ERROR; | |
348 } | |
349 | |
350 if (EVP_PKEY_derive(pctx, out_key, out_len) <= 0) { | |
351 return NGX_ERROR; | |
352 } | |
353 | |
354 #endif | |
355 | |
356 return NGX_OK; | |
357 } | |
358 | |
359 | |
360 static ngx_int_t | |
361 ngx_quic_tls_open(ngx_pool_t *pool, const ngx_quic_cipher_t *cipher, | |
362 ngx_quic_secret_t *s, ngx_str_t *out, u_char *nonce, ngx_str_t *in, | |
363 ngx_str_t *ad) | |
364 { | |
365 ngx_log_t *log; | |
366 | |
367 log = pool->log; // TODO: pass log ? | |
368 | |
369 out->len = in->len - EVP_GCM_TLS_TAG_LEN; | |
370 out->data = ngx_pnalloc(pool, out->len); | |
371 if (out->data == NULL) { | |
372 return NGX_ERROR; | |
373 } | |
374 | |
375 #ifdef OPENSSL_IS_BORINGSSL | |
376 EVP_AEAD_CTX *ctx; | |
377 | |
378 ctx = EVP_AEAD_CTX_new(cipher, s->key.data, s->key.len, | |
379 EVP_AEAD_DEFAULT_TAG_LENGTH); | |
380 if (ctx == NULL) { | |
381 ngx_ssl_error(NGX_LOG_INFO, log, 0, "EVP_AEAD_CTX_new() failed"); | |
382 return NGX_ERROR; | |
383 } | |
384 | |
385 if (EVP_AEAD_CTX_open(ctx, out->data, &out->len, out->len, nonce, s->iv.len, | |
386 in->data, in->len, ad->data, ad->len) | |
387 != 1) | |
388 { | |
389 EVP_AEAD_CTX_free(ctx); | |
390 ngx_ssl_error(NGX_LOG_INFO, log, 0, "EVP_AEAD_CTX_open() failed"); | |
391 return NGX_ERROR; | |
392 } | |
393 | |
394 EVP_AEAD_CTX_free(ctx); | |
395 #else | |
396 int len; | |
397 u_char *tag; | |
398 EVP_CIPHER_CTX *ctx; | |
399 | |
400 ctx = EVP_CIPHER_CTX_new(); | |
401 if (ctx == NULL) { | |
402 ngx_ssl_error(NGX_LOG_INFO, log, 0, "EVP_CIPHER_CTX_new() failed"); | |
403 return NGX_ERROR; | |
404 } | |
405 | |
406 if (EVP_DecryptInit_ex(ctx, cipher, NULL, NULL, NULL) != 1) { | |
407 EVP_CIPHER_CTX_free(ctx); | |
408 ngx_ssl_error(NGX_LOG_INFO, log, 0, "EVP_DecryptInit_ex() failed"); | |
409 return NGX_ERROR; | |
410 } | |
411 | |
412 if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, s->iv.len, NULL) | |
413 == 0) | |
414 { | |
415 EVP_CIPHER_CTX_free(ctx); | |
416 ngx_ssl_error(NGX_LOG_INFO, log, 0, | |
417 "EVP_CIPHER_CTX_ctrl(EVP_CTRL_GCM_SET_IVLEN) failed"); | |
418 return NGX_ERROR; | |
419 } | |
420 | |
421 if (EVP_DecryptInit_ex(ctx, NULL, NULL, s->key.data, nonce) != 1) { | |
422 EVP_CIPHER_CTX_free(ctx); | |
423 ngx_ssl_error(NGX_LOG_INFO, log, 0, "EVP_DecryptInit_ex() failed"); | |
424 return NGX_ERROR; | |
425 } | |
426 | |
427 if (EVP_DecryptUpdate(ctx, NULL, &len, ad->data, ad->len) != 1) { | |
428 EVP_CIPHER_CTX_free(ctx); | |
429 ngx_ssl_error(NGX_LOG_INFO, log, 0, "EVP_DecryptUpdate() failed"); | |
430 return NGX_ERROR; | |
431 } | |
432 | |
433 if (EVP_DecryptUpdate(ctx, out->data, &len, in->data, | |
434 in->len - EVP_GCM_TLS_TAG_LEN) | |
435 != 1) | |
436 { | |
437 EVP_CIPHER_CTX_free(ctx); | |
438 ngx_ssl_error(NGX_LOG_INFO, log, 0, "EVP_DecryptUpdate() failed"); | |
439 return NGX_ERROR; | |
440 } | |
441 | |
442 out->len = len; | |
443 tag = in->data + in->len - EVP_GCM_TLS_TAG_LEN; | |
444 | |
445 if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, EVP_GCM_TLS_TAG_LEN, tag) | |
446 == 0) | |
447 { | |
448 EVP_CIPHER_CTX_free(ctx); | |
449 ngx_ssl_error(NGX_LOG_INFO, log, 0, | |
450 "EVP_CIPHER_CTX_ctrl(EVP_CTRL_GCM_SET_TAG) failed"); | |
451 return NGX_ERROR; | |
452 } | |
453 | |
454 if (EVP_DecryptFinal_ex(ctx, out->data + len, &len) <= 0) { | |
455 EVP_CIPHER_CTX_free(ctx); | |
456 ngx_ssl_error(NGX_LOG_INFO, log, 0, "EVP_DecryptFinal_ex failed"); | |
457 return NGX_ERROR; | |
458 } | |
459 | |
460 out->len += len; | |
461 | |
462 EVP_CIPHER_CTX_free(ctx); | |
463 #endif | |
464 | |
465 return NGX_OK; | |
466 } | |
467 | |
468 | |
469 static ngx_int_t | |
470 ngx_quic_tls_seal(ngx_pool_t *pool, const ngx_quic_cipher_t *cipher, | |
471 ngx_quic_secret_t *s, ngx_str_t *out, u_char *nonce, ngx_str_t *in, | |
472 ngx_str_t *ad) | |
473 { | |
474 ngx_log_t *log; | |
475 | |
476 log = pool->log; // TODO: pass log ? | |
477 | |
478 out->len = in->len + EVP_GCM_TLS_TAG_LEN; | |
479 out->data = ngx_pnalloc(pool, out->len); | |
480 if (out->data == NULL) { | |
481 return NGX_ERROR; | |
482 } | |
483 | |
484 #ifdef OPENSSL_IS_BORINGSSL | |
485 EVP_AEAD_CTX *ctx; | |
486 | |
487 ctx = EVP_AEAD_CTX_new(cipher, s->key.data, s->key.len, | |
488 EVP_AEAD_DEFAULT_TAG_LENGTH); | |
489 if (ctx == NULL) { | |
490 ngx_ssl_error(NGX_LOG_INFO, log, 0, "EVP_AEAD_CTX_new() failed"); | |
491 return NGX_ERROR; | |
492 } | |
493 | |
494 if (EVP_AEAD_CTX_seal(ctx, out->data, &out->len, out->len, nonce, s->iv.len, | |
495 in->data, in->len, ad->data, ad->len) | |
496 != 1) | |
497 { | |
498 EVP_AEAD_CTX_free(ctx); | |
499 ngx_ssl_error(NGX_LOG_INFO, log, 0, "EVP_AEAD_CTX_seal() failed"); | |
500 return NGX_ERROR; | |
501 } | |
502 | |
503 EVP_AEAD_CTX_free(ctx); | |
504 #else | |
505 int len; | |
506 EVP_CIPHER_CTX *ctx; | |
507 | |
508 ctx = EVP_CIPHER_CTX_new(); | |
509 if (ctx == NULL) { | |
510 ngx_ssl_error(NGX_LOG_INFO, log, 0, "EVP_CIPHER_CTX_new() failed"); | |
511 return NGX_ERROR; | |
512 } | |
513 | |
514 if (EVP_EncryptInit_ex(ctx, cipher, NULL, NULL, NULL) != 1) { | |
515 EVP_CIPHER_CTX_free(ctx); | |
516 ngx_ssl_error(NGX_LOG_INFO, log, 0, "EVP_EncryptInit_ex() failed"); | |
517 return NGX_ERROR; | |
518 } | |
519 | |
520 if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, s->iv.len, NULL) | |
521 == 0) | |
522 { | |
523 EVP_CIPHER_CTX_free(ctx); | |
524 ngx_ssl_error(NGX_LOG_INFO, log, 0, | |
525 "EVP_CIPHER_CTX_ctrl(EVP_CTRL_GCM_SET_IVLEN) failed"); | |
526 return NGX_ERROR; | |
527 } | |
528 | |
529 if (EVP_EncryptInit_ex(ctx, NULL, NULL, s->key.data, nonce) != 1) { | |
530 EVP_CIPHER_CTX_free(ctx); | |
531 ngx_ssl_error(NGX_LOG_INFO, log, 0, "EVP_EncryptInit_ex() failed"); | |
532 return NGX_ERROR; | |
533 } | |
534 | |
535 if (EVP_EncryptUpdate(ctx, NULL, &len, ad->data, ad->len) != 1) { | |
536 EVP_CIPHER_CTX_free(ctx); | |
537 ngx_ssl_error(NGX_LOG_INFO, log, 0, "EVP_EncryptUpdate() failed"); | |
538 return NGX_ERROR; | |
539 } | |
540 | |
541 if (EVP_EncryptUpdate(ctx, out->data, &len, in->data, in->len) != 1) { | |
542 EVP_CIPHER_CTX_free(ctx); | |
543 ngx_ssl_error(NGX_LOG_INFO, log, 0, "EVP_EncryptUpdate() failed"); | |
544 return NGX_ERROR; | |
545 } | |
546 | |
547 out->len = len; | |
548 | |
549 if (EVP_EncryptFinal_ex(ctx, out->data + out->len, &len) <= 0) { | |
550 EVP_CIPHER_CTX_free(ctx); | |
551 ngx_ssl_error(NGX_LOG_INFO, log, 0, "EVP_EncryptFinal_ex failed"); | |
552 return NGX_ERROR; | |
553 } | |
554 | |
555 out->len += len; | |
556 | |
557 if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, EVP_GCM_TLS_TAG_LEN, | |
558 out->data + in->len) | |
559 == 0) | |
560 { | |
561 EVP_CIPHER_CTX_free(ctx); | |
562 ngx_ssl_error(NGX_LOG_INFO, log, 0, | |
563 "EVP_CIPHER_CTX_ctrl(EVP_CTRL_GCM_GET_TAG) failed"); | |
564 return NGX_ERROR; | |
565 } | |
566 | |
567 EVP_CIPHER_CTX_free(ctx); | |
568 | |
569 out->len += EVP_GCM_TLS_TAG_LEN; | |
570 #endif | |
571 return NGX_OK; | |
572 } | |
573 | |
574 | |
575 static ngx_int_t | |
576 ngx_quic_tls_hp(ngx_log_t *log, const EVP_CIPHER *cipher, | |
577 ngx_quic_secret_t *s, u_char *out, u_char *in) | |
578 { | |
579 int outlen; | |
580 EVP_CIPHER_CTX *ctx; | |
581 u_char zero[5] = {0}; | |
582 | |
583 #ifdef OPENSSL_IS_BORINGSSL | |
584 uint32_t counter; | |
585 | |
586 ngx_memcpy(&counter, in, sizeof(uint32_t)); | |
587 | |
588 if (cipher == (const EVP_CIPHER *) EVP_aead_chacha20_poly1305()) { | |
589 CRYPTO_chacha_20(out, zero, 5, s->hp.data, &in[4], counter); | |
590 return NGX_OK; | |
591 } | |
592 #endif | |
593 | |
594 ctx = EVP_CIPHER_CTX_new(); | |
595 if (ctx == NULL) { | |
596 return NGX_ERROR; | |
597 } | |
598 | |
599 if (EVP_EncryptInit_ex(ctx, cipher, NULL, s->hp.data, in) != 1) { | |
600 ngx_ssl_error(NGX_LOG_INFO, log, 0, "EVP_EncryptInit_ex() failed"); | |
601 goto failed; | |
602 } | |
603 | |
604 if (!EVP_EncryptUpdate(ctx, out, &outlen, zero, 5)) { | |
605 ngx_ssl_error(NGX_LOG_INFO, log, 0, "EVP_EncryptUpdate() failed"); | |
606 goto failed; | |
607 } | |
608 | |
609 if (!EVP_EncryptFinal_ex(ctx, out + 5, &outlen)) { | |
610 ngx_ssl_error(NGX_LOG_INFO, log, 0, "EVP_EncryptFinal_Ex() failed"); | |
611 goto failed; | |
612 } | |
613 | |
614 EVP_CIPHER_CTX_free(ctx); | |
615 | |
616 return NGX_OK; | |
617 | |
618 failed: | |
619 | |
620 EVP_CIPHER_CTX_free(ctx); | |
621 | |
622 return NGX_ERROR; | |
623 } | |
624 | |
625 | |
626 int | |
627 ngx_quic_set_encryption_secret(ngx_pool_t *pool, ngx_ssl_conn_t *ssl_conn, | |
628 enum ssl_encryption_level_t level, const uint8_t *secret, | |
629 size_t secret_len, ngx_quic_peer_secrets_t *qsec) | |
630 { | |
631 ngx_int_t key_len; | |
632 ngx_uint_t i; | |
633 ngx_quic_secret_t *peer_secret; | |
634 ngx_quic_ciphers_t ciphers; | |
635 | |
636 key_len = ngx_quic_ciphers(ssl_conn, &ciphers, level); | |
637 | |
638 if (key_len == NGX_ERROR) { | |
639 ngx_ssl_error(NGX_LOG_INFO, pool->log, 0, "unexpected cipher"); | |
640 return 0; | |
641 } | |
642 | |
643 switch (level) { | |
644 | |
645 case ssl_encryption_handshake: | |
646 peer_secret= &qsec->hs; | |
647 break; | |
648 | |
649 case ssl_encryption_application: | |
650 peer_secret = &qsec->ad; | |
651 break; | |
652 | |
653 default: | |
654 return 0; | |
655 } | |
656 | |
657 peer_secret->key.len = key_len; | |
658 peer_secret->iv.len = NGX_QUIC_IV_LEN; | |
659 peer_secret->hp.len = key_len; | |
660 | |
661 struct { | |
662 ngx_str_t label; | |
663 ngx_str_t *key; | |
664 const uint8_t *secret; | |
665 } seq[] = { | |
666 { ngx_string("tls13 quic key"), &peer_secret->key, secret }, | |
667 { ngx_string("tls13 quic iv"), &peer_secret->iv, secret }, | |
668 { ngx_string("tls13 quic hp"), &peer_secret->hp, secret }, | |
669 }; | |
670 | |
671 for (i = 0; i < (sizeof(seq) / sizeof(seq[0])); i++) { | |
672 | |
673 if (ngx_quic_hkdf_expand(pool, ciphers.d, seq[i].key, &seq[i].label, | |
674 seq[i].secret, secret_len) | |
675 != NGX_OK) | |
676 { | |
677 return 0; | |
678 } | |
679 } | |
680 | |
681 return 1; | |
682 } | |
683 | |
684 | |
685 static ngx_int_t | |
686 ngx_quic_create_long_packet(ngx_pool_t *pool, ngx_ssl_conn_t *ssl_conn, | |
687 ngx_quic_header_t *pkt, ngx_str_t *payload, ngx_str_t *res) | |
688 { | |
689 u_char *p, *pnp, *nonce, *sample, *packet; | |
690 uint64_t pn; | |
691 ngx_log_t *log; | |
692 ngx_str_t ad, out; | |
693 ngx_quic_ciphers_t ciphers; | |
694 u_char mask[16]; | |
695 | |
696 log = pool->log; | |
697 | |
698 out.len = payload->len + EVP_GCM_TLS_TAG_LEN; | |
699 | |
700 ad.data = ngx_alloc(346 /*max header*/, log); | |
701 if (ad.data == 0) { | |
702 return NGX_ERROR; | |
703 } | |
704 | |
705 p = ad.data; | |
706 | |
707 *p++ = pkt->flags; | |
708 | |
709 p = ngx_quic_write_uint32(p, quic_version); | |
710 | |
711 *p++ = pkt->scid.len; | |
712 p = ngx_cpymem(p, pkt->scid.data, pkt->scid.len); | |
713 | |
714 *p++ = pkt->dcid.len; | |
715 p = ngx_cpymem(p, pkt->dcid.data, pkt->dcid.len); | |
716 | |
717 if (pkt->level == ssl_encryption_initial) { | |
718 ngx_quic_build_int(&p, pkt->token.len); | |
719 } | |
720 | |
721 ngx_quic_build_int(&p, out.len + 1); // length (inc. pnl) | |
722 pnp = p; | |
723 | |
724 pn = *pkt->number; | |
725 | |
726 *p++ = pn; | |
727 | |
728 ad.len = p - ad.data; | |
729 | |
730 ngx_quic_hexdump0(log, "ad", ad.data, ad.len); | |
731 | |
732 if (ngx_quic_ciphers(ssl_conn, &ciphers, pkt->level) == NGX_ERROR) { | |
733 return NGX_ERROR; | |
734 } | |
735 | |
736 nonce = ngx_pstrdup(pool, &pkt->secret->iv); | |
737 if (pkt->level == ssl_encryption_handshake) { | |
738 nonce[11] ^= pn; | |
739 } | |
740 | |
741 ngx_quic_hexdump0(log, "server_iv", pkt->secret->iv.data, 12); | |
742 ngx_quic_hexdump0(log, "nonce", nonce, 12); | |
743 | |
744 if (ngx_quic_tls_seal(pool, ciphers.c, pkt->secret, &out, | |
745 nonce, payload, &ad) | |
746 != NGX_OK) | |
747 { | |
748 return NGX_ERROR; | |
749 } | |
750 | |
751 sample = &out.data[3]; // pnl=0 | |
752 if (ngx_quic_tls_hp(log, ciphers.hp, pkt->secret, mask, sample) != NGX_OK) { | |
753 return NGX_ERROR; | |
754 } | |
755 | |
756 ngx_quic_hexdump0(log, "sample", sample, 16); | |
757 ngx_quic_hexdump0(log, "mask", mask, 16); | |
758 ngx_quic_hexdump0(log, "hp_key", pkt->secret->hp.data, 16); | |
759 | |
760 // header protection, pnl = 0 | |
761 ad.data[0] ^= mask[0] & 0x0f; | |
762 *pnp ^= mask[1]; | |
763 | |
764 packet = ngx_alloc(ad.len + out.len, log); | |
765 if (packet == 0) { | |
766 return NGX_ERROR; | |
767 } | |
768 | |
769 p = ngx_cpymem(packet, ad.data, ad.len); | |
770 p = ngx_cpymem(p, out.data, out.len); | |
771 | |
772 res->data = packet; | |
773 res->len = p - packet; | |
774 | |
775 return NGX_OK; | |
776 } | |
777 | |
778 | |
779 static ngx_int_t | |
780 ngx_quic_create_short_packet(ngx_pool_t *pool, ngx_ssl_conn_t *ssl_conn, | |
781 ngx_quic_header_t *pkt, ngx_str_t *payload, ngx_str_t *res) | |
782 { | |
783 u_char *p, *pnp, *nonce, *sample, *packet; | |
784 ngx_log_t *log; | |
785 ngx_str_t ad, out; | |
786 ngx_quic_ciphers_t ciphers; | |
787 u_char mask[16]; | |
788 | |
789 log = pool->log; | |
790 | |
791 out.len = payload->len + EVP_GCM_TLS_TAG_LEN; | |
792 | |
793 ad.data = ngx_alloc(25 /*max header*/, log); | |
794 if (ad.data == 0) { | |
795 return NGX_ERROR; | |
796 } | |
797 | |
798 p = ad.data; | |
799 | |
800 *p++ = 0x40; | |
801 | |
802 p = ngx_cpymem(p, pkt->scid.data, pkt->scid.len); | |
803 | |
804 pnp = p; | |
805 | |
806 *p++ = (*pkt->number); | |
807 | |
808 ad.len = p - ad.data; | |
809 | |
810 ngx_quic_hexdump0(log, "ad", ad.data, ad.len); | |
811 | |
812 if (ngx_quic_ciphers(ssl_conn, &ciphers, pkt->level) == NGX_ERROR) { | |
813 return NGX_ERROR; | |
814 } | |
815 | |
816 nonce = ngx_pstrdup(pool, &pkt->secret->iv); | |
817 if (pkt->level == ssl_encryption_handshake | |
818 || pkt->level == ssl_encryption_application) | |
819 { | |
820 nonce[11] ^= *pkt->number; | |
821 } | |
822 | |
823 ngx_quic_hexdump0(log, "server_iv", pkt->secret->iv.data, 12); | |
824 ngx_quic_hexdump0(log, "nonce", nonce, 12); | |
825 | |
826 if (ngx_quic_tls_seal(pool, ciphers.c, pkt->secret, &out, | |
827 nonce, payload, &ad) | |
828 != NGX_OK) | |
829 { | |
830 return NGX_ERROR; | |
831 } | |
832 | |
833 ngx_quic_hexdump0(log, "out", out.data, out.len); | |
834 | |
835 sample = &out.data[3]; // pnl=0 | |
836 if (ngx_quic_tls_hp(log, ciphers.hp, pkt->secret, mask, sample) != NGX_OK) { | |
837 return NGX_ERROR; | |
838 } | |
839 | |
840 ngx_quic_hexdump0(log, "sample", sample, 16); | |
841 ngx_quic_hexdump0(log, "mask", mask, 16); | |
842 ngx_quic_hexdump0(log, "hp_key", pkt->secret->hp.data, 16); | |
843 | |
844 // header protection, pnl = 0 | |
845 ad.data[0] ^= mask[0] & 0x1f; | |
846 *pnp ^= mask[1]; | |
847 | |
848 packet = ngx_alloc(ad.len + out.len, log); | |
849 if (packet == 0) { | |
850 return NGX_ERROR; | |
851 } | |
852 | |
853 p = ngx_cpymem(packet, ad.data, ad.len); | |
854 p = ngx_cpymem(p, out.data, out.len); | |
855 | |
856 ngx_quic_hexdump0(log, "packet", packet, p - packet); | |
857 | |
858 res->data = packet; | |
859 res->len = p - packet; | |
860 | |
861 return NGX_OK; | |
862 } | |
863 | |
864 static uint64_t | |
865 ngx_quic_parse_pn(u_char **pos, ngx_int_t len, u_char *mask) | |
866 { | |
867 u_char *p; | |
868 uint64_t value; | |
869 | |
870 p = *pos; | |
871 value = *p++ ^ *mask++; | |
872 | |
873 while (--len) { | |
874 value = (value << 8) + (*p++ ^ *mask++); | |
875 } | |
876 | |
877 *pos = p; | |
878 return value; | |
879 } | |
880 | |
881 | |
882 ngx_int_t | |
883 ngx_quic_encrypt(ngx_pool_t *pool, ngx_ssl_conn_t *ssl_conn, | |
884 ngx_quic_header_t *pkt, ngx_str_t *payload, ngx_str_t *res) | |
885 { | |
886 if (pkt->level == ssl_encryption_application) { | |
887 return ngx_quic_create_short_packet(pool, ssl_conn, pkt, payload, res); | |
888 } | |
889 | |
890 return ngx_quic_create_long_packet(pool, ssl_conn, pkt, payload, res); | |
891 } | |
892 | |
893 | |
894 ngx_int_t | |
895 ngx_quic_decrypt(ngx_pool_t *pool, ngx_ssl_conn_t *ssl_conn, | |
896 ngx_quic_header_t *pkt) | |
897 { | |
898 u_char clearflags, *p, *sample; | |
899 uint8_t *nonce; | |
900 uint64_t pn; | |
901 ngx_log_t *log; | |
902 ngx_int_t pnl, rc; | |
903 ngx_str_t in, ad; | |
904 ngx_quic_ciphers_t ciphers; | |
905 uint8_t mask[16]; | |
906 | |
907 log = pool->log; | |
908 | |
909 if (ngx_quic_ciphers(ssl_conn, &ciphers, pkt->level) == NGX_ERROR) { | |
910 return NGX_ERROR; | |
911 } | |
912 | |
913 p = pkt->raw->pos; | |
914 | |
915 /* draft-ietf-quic-tls-23#section-5.4.2: | |
916 * the Packet Number field is assumed to be 4 bytes long | |
917 * draft-ietf-quic-tls-23#section-5.4.[34]: | |
918 * AES-Based and ChaCha20-Based header protections sample 16 bytes | |
919 */ | |
920 | |
921 sample = p + 4; | |
922 | |
923 ngx_quic_hexdump0(log, "sample", sample, 16); | |
924 | |
925 /* header protection */ | |
926 | |
927 if (ngx_quic_tls_hp(log, ciphers.hp, pkt->secret, mask, sample) != NGX_OK) { | |
928 return NGX_ERROR; | |
929 } | |
930 | |
931 if (pkt->flags & NGX_QUIC_PKT_LONG) { | |
932 clearflags = pkt->flags ^ (mask[0] & 0x0f); | |
933 | |
934 } else { | |
935 clearflags = pkt->flags ^ (mask[0] & 0x1f); | |
936 } | |
937 | |
938 pnl = (clearflags & 0x03) + 1; | |
939 pn = ngx_quic_parse_pn(&p, pnl, &mask[1]); | |
940 | |
941 pkt->pn = pn; | |
942 | |
943 ngx_quic_hexdump0(log, "mask", mask, 5); | |
944 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, log, 0, | |
945 "quic clear flags: %xi", clearflags); | |
946 ngx_log_debug2(NGX_LOG_DEBUG_EVENT, log, 0, | |
947 "quic packet number: %uL, len: %xi", pn, pnl); | |
948 | |
949 /* packet protection */ | |
950 | |
951 in.data = p; | |
952 | |
953 if (pkt->flags & NGX_QUIC_PKT_LONG) { | |
954 in.len = pkt->len - pnl; | |
955 | |
956 } else { | |
957 in.len = pkt->data + pkt->len - p; | |
958 } | |
959 | |
960 ad.len = p - pkt->data; | |
961 ad.data = ngx_pnalloc(pool, ad.len); | |
962 if (ad.data == NULL) { | |
963 return NGX_ERROR; | |
964 } | |
965 | |
966 ngx_memcpy(ad.data, pkt->data, ad.len); | |
967 ad.data[0] = clearflags; | |
968 | |
969 do { | |
970 ad.data[ad.len - pnl] = pn >> (8 * (pnl - 1)) % 256; | |
971 } while (--pnl); | |
972 | |
973 nonce = ngx_pstrdup(pool, &pkt->secret->iv); | |
974 nonce[11] ^= pn; | |
975 | |
976 ngx_quic_hexdump0(log, "nonce", nonce, 12); | |
977 ngx_quic_hexdump0(log, "ad", ad.data, ad.len); | |
978 | |
979 rc = ngx_quic_tls_open(pool, ciphers.c, pkt->secret, &pkt->payload, | |
980 nonce, &in, &ad); | |
981 | |
982 ngx_quic_hexdump0(log, "packet payload", | |
983 pkt->payload.data, pkt->payload.len); | |
984 | |
985 return rc; | |
986 } | |
987 |