Mercurial > hg > nginx-quic
comparison src/event/ngx_event_quic.c @ 7650:ec1f84996990 quic
Split frame and packet generation into separate steps.
While there, a number of QUIC constants from spec defined and magic numbers
were replaced.
author | Vladimir Homutov <vl@nginx.com> |
---|---|
date | Tue, 03 Mar 2020 13:30:30 +0300 |
parents | 6091506af0f7 |
children | 6a76d9657772 |
comparison
equal
deleted
inserted
replaced
7649:6091506af0f7 | 7650:ec1f84996990 |
---|---|
72 | 72 |
73 #define ngx_quic_hexdump0(log, fmt, data, len) \ | 73 #define ngx_quic_hexdump0(log, fmt, data, len) \ |
74 ngx_quic_hexdump(log, fmt "%s", data, len, "") \ | 74 ngx_quic_hexdump(log, fmt "%s", data, len, "") \ |
75 | 75 |
76 | 76 |
77 /* 17.2. Long Header Packets */ | |
78 | |
79 #define NGX_QUIC_PKT_LONG 0x80 | |
80 | |
81 #define NGX_QUIC_PKT_INITIAL 0xc0 | |
82 #define NGX_QUIC_PKT_HANDSHAKE 0xe0 | |
83 | |
84 /* 12.4. Frames and Frame Types */ | |
85 #define NGX_QUIC_FT_PADDING 0x00 | |
86 #define NGX_QUIC_FT_PING 0x01 | |
87 #define NGX_QUIC_FT_ACK 0x02 | |
88 #define NGX_QUIC_FT_ACK_ECN 0x03 | |
89 #define NGX_QUIC_FT_RESET_STREAM 0x04 | |
90 #define NGX_QUIC_FT_STOP_SENDING 0x05 | |
91 #define NGX_QUIC_FT_CRYPTO 0x06 | |
92 #define NGX_QUIC_FT_NEW_TOKEN 0x07 | |
93 #define NGX_QUIC_FT_STREAM 0x08 // - 0x0f | |
94 #define NGX_QUIC_FT_MAX_DATA 0x10 | |
95 #define NGX_QUIC_FT_MAX_STREAM_DATA 0x11 | |
96 #define NGX_QUIC_FT_MAX_STREAMS 0x12 | |
97 #define NGX_QUIC_FT_MAX_STREAMS2 0x13 // XXX | |
98 #define NGX_QUIC_FT_DATA_BLOCKED 0x14 | |
99 #define NGX_QUIC_FT_STREAM_DATA_BLOCKED 0x15 | |
100 #define NGX_QUIC_FT_STREAMS_BLOCKED 0x16 | |
101 #define NGX_QUIC_FT_STREAMS_BLOCKED2 0x17 // XXX | |
102 #define NGX_QUIC_FT_NEW_CONNECTION_ID 0x18 | |
103 #define NGX_QUIC_FT_RETIRE_CONNECTION_ID 0x19 | |
104 #define NGX_QUIC_FT_PATH_CHALLENGE 0x1a | |
105 #define NGX_QUIC_FT_PATH_RESPONSE 0x1b | |
106 #define NGX_QUIC_FT_CONNECTION_CLOSE 0x1c | |
107 #define NGX_QUIC_FT_CONNECTION_CLOSE2 0x1d // XXX | |
108 #define NGX_QUIC_FT_HANDSHAKE_DONE 0x1e | |
109 | |
77 | 110 |
78 /* TODO: real states, these are stubs */ | 111 /* TODO: real states, these are stubs */ |
79 typedef enum { | 112 typedef enum { |
80 NGX_QUIC_ST_INITIAL, | 113 NGX_QUIC_ST_INITIAL, |
81 NGX_QUIC_ST_HANDSHAKE, | 114 NGX_QUIC_ST_HANDSHAKE, |
99 ngx_str_t out; // stub for some kind of output queue | 132 ngx_str_t out; // stub for some kind of output queue |
100 | 133 |
101 ngx_str_t scid; | 134 ngx_str_t scid; |
102 ngx_str_t dcid; | 135 ngx_str_t dcid; |
103 ngx_str_t token; | 136 ngx_str_t token; |
137 | |
138 /* current packet numbers for each namespace */ | |
139 ngx_uint_t initial_pn; | |
140 ngx_uint_t handshake_pn; | |
141 ngx_uint_t appdata_pn; | |
104 | 142 |
105 ngx_quic_secret_t client_in; | 143 ngx_quic_secret_t client_in; |
106 ngx_quic_secret_t client_hs; | 144 ngx_quic_secret_t client_hs; |
107 ngx_quic_secret_t client_ad; | 145 ngx_quic_secret_t client_ad; |
108 ngx_quic_secret_t server_in; | 146 ngx_quic_secret_t server_in; |
109 ngx_quic_secret_t server_hs; | 147 ngx_quic_secret_t server_hs; |
110 ngx_quic_secret_t server_ad; | 148 ngx_quic_secret_t server_ad; |
111 }; | 149 }; |
112 | 150 |
113 | 151 |
152 typedef enum ssl_encryption_level_t ngx_quic_level_t; | |
153 | |
154 typedef struct { | |
155 ngx_quic_secret_t *secret; | |
156 ngx_uint_t type; | |
157 ngx_uint_t *number; | |
158 ngx_uint_t flags; | |
159 ngx_uint_t version; | |
160 ngx_str_t *token; | |
161 ngx_quic_level_t level; | |
162 } ngx_quic_header_t; | |
163 | |
164 | |
114 static ngx_int_t ngx_quic_new_connection(ngx_connection_t *c, ngx_ssl_t *ssl, | 165 static ngx_int_t ngx_quic_new_connection(ngx_connection_t *c, ngx_ssl_t *ssl, |
115 ngx_buf_t *b); | 166 ngx_buf_t *b); |
116 static ngx_int_t ngx_quic_handshake_input(ngx_connection_t *c, ngx_buf_t *b); | 167 static ngx_int_t ngx_quic_handshake_input(ngx_connection_t *c, ngx_buf_t *b); |
117 | 168 |
118 static int ngx_quic_set_encryption_secrets(ngx_ssl_conn_t *ssl_conn, | 169 static int ngx_quic_set_encryption_secrets(ngx_ssl_conn_t *ssl_conn, |
119 enum ssl_encryption_level_t level, const uint8_t *read_secret, | 170 enum ssl_encryption_level_t level, const uint8_t *read_secret, |
120 const uint8_t *write_secret, size_t secret_len); | 171 const uint8_t *write_secret, size_t secret_len); |
121 static int ngx_quic_add_handshake_data(ngx_ssl_conn_t *ssl_conn, | 172 static int ngx_quic_add_handshake_data(ngx_ssl_conn_t *ssl_conn, |
122 enum ssl_encryption_level_t level, const uint8_t *data, size_t len); | 173 enum ssl_encryption_level_t level, const uint8_t *data, size_t len); |
174 static ngx_int_t ngx_quic_create_long_packet(ngx_connection_t *c, | |
175 ngx_ssl_conn_t *ssl_conn, ngx_quic_header_t *pkt, ngx_str_t *in, | |
176 ngx_str_t *res); | |
123 static int ngx_quic_flush_flight(ngx_ssl_conn_t *ssl_conn); | 177 static int ngx_quic_flush_flight(ngx_ssl_conn_t *ssl_conn); |
124 static int ngx_quic_send_alert(ngx_ssl_conn_t *ssl_conn, | 178 static int ngx_quic_send_alert(ngx_ssl_conn_t *ssl_conn, |
125 enum ssl_encryption_level_t level, uint8_t alert); | 179 enum ssl_encryption_level_t level, uint8_t alert); |
126 | 180 |
127 static uint64_t ngx_quic_parse_pn(u_char **pos, ngx_int_t len, u_char *mask); | 181 static uint64_t ngx_quic_parse_pn(u_char **pos, ngx_int_t len, u_char *mask); |
286 | 340 |
287 return 1; | 341 return 1; |
288 } | 342 } |
289 | 343 |
290 | 344 |
345 static ngx_int_t | |
346 ngx_quic_create_long_packet(ngx_connection_t *c, ngx_ssl_conn_t *ssl_conn, | |
347 ngx_quic_header_t *pkt, ngx_str_t *payload, ngx_str_t *res) | |
348 { | |
349 u_char *p, *pnp, *name, *nonce, *sample, *packet; | |
350 ngx_str_t ad, out; | |
351 const EVP_CIPHER *cipher; | |
352 ngx_quic_connection_t *qc; | |
353 | |
354 u_char mask[16]; | |
355 | |
356 qc = c->quic; | |
357 | |
358 out.len = payload->len + EVP_GCM_TLS_TAG_LEN; | |
359 | |
360 ad.data = ngx_alloc(346 /*max header*/, c->log); | |
361 if (ad.data == 0) { | |
362 return NGX_ERROR; | |
363 } | |
364 | |
365 p = ad.data; | |
366 | |
367 *p++ = pkt->flags; | |
368 | |
369 p = ngx_quic_write_uint32(p, quic_version); | |
370 | |
371 *p++ = qc->scid.len; | |
372 p = ngx_cpymem(p, qc->scid.data, qc->scid.len); | |
373 | |
374 *p++ = qc->dcid.len; | |
375 p = ngx_cpymem(p, qc->dcid.data, qc->dcid.len); | |
376 | |
377 if (pkt->token) { // if pkt->flags & initial ? | |
378 ngx_quic_build_int(&p, pkt->token->len); | |
379 } | |
380 | |
381 ngx_quic_build_int(&p, out.len + 1); // length (inc. pnl) | |
382 pnp = p; | |
383 | |
384 *p++ = (*pkt->number)++; | |
385 | |
386 ad.len = p - ad.data; | |
387 | |
388 ngx_quic_hexdump0(c->log, "ad", ad.data, ad.len); | |
389 | |
390 name = (u_char *) SSL_get_cipher(ssl_conn); | |
391 | |
392 if (ngx_strcasecmp(name, (u_char *) "TLS_AES_128_GCM_SHA256") == 0 | |
393 || ngx_strcasecmp(name, (u_char *) "(NONE)") == 0) | |
394 { | |
395 cipher = EVP_aes_128_gcm(); | |
396 | |
397 } else if (ngx_strcasecmp(name, (u_char *) "TLS_AES_256_GCM_SHA384") == 0) { | |
398 cipher = EVP_aes_256_gcm(); | |
399 | |
400 } else { | |
401 return NGX_ERROR; | |
402 } | |
403 | |
404 nonce = ngx_pstrdup(c->pool, &pkt->secret->iv); | |
405 if (pkt->level == ssl_encryption_handshake) { | |
406 nonce[11] ^= (*pkt->number - 1); | |
407 } | |
408 | |
409 ngx_quic_hexdump0(c->log, "server_iv", pkt->secret->iv.data, 12); | |
410 ngx_quic_hexdump0(c->log, "nonce", nonce, 12); | |
411 | |
412 if (ngx_quic_tls_seal(c, cipher, pkt->secret, &out, nonce, payload, &ad) != NGX_OK) { | |
413 return NGX_ERROR; | |
414 } | |
415 | |
416 sample = &out.data[3]; // pnl=0 | |
417 if (ngx_quic_tls_hp(c, EVP_aes_128_ecb(), pkt->secret, mask, sample) != NGX_OK) { | |
418 return NGX_ERROR; | |
419 } | |
420 | |
421 ngx_quic_hexdump0(c->log, "sample", sample, 16); | |
422 ngx_quic_hexdump0(c->log, "mask", mask, 16); | |
423 ngx_quic_hexdump0(c->log, "hp_key", pkt->secret->hp.data, 16); | |
424 | |
425 // header protection, pnl = 0 | |
426 ad.data[0] ^= mask[0] & 0x0f; | |
427 *pnp ^= mask[1]; | |
428 | |
429 packet = ngx_alloc(ad.len + out.len, c->log); | |
430 if (packet == 0) { | |
431 return NGX_ERROR; | |
432 } | |
433 | |
434 p = ngx_cpymem(packet, ad.data, ad.len); | |
435 p = ngx_cpymem(p, out.data, out.len); | |
436 | |
437 res->data = packet; | |
438 res->len = p - packet; | |
439 | |
440 return NGX_OK; | |
441 } | |
442 | |
443 | |
444 static void | |
445 ngx_quic_create_ack(u_char **p) | |
446 { | |
447 ngx_quic_build_int(p, NGX_QUIC_FT_ACK); | |
448 ngx_quic_build_int(p, 0); | |
449 ngx_quic_build_int(p, 0); | |
450 ngx_quic_build_int(p, 0); | |
451 ngx_quic_build_int(p, 0); | |
452 } | |
453 | |
454 | |
455 static void | |
456 ngx_quic_create_crypto(u_char **p, u_char *data, size_t len) | |
457 { | |
458 ngx_quic_build_int(p, NGX_QUIC_FT_CRYPTO); | |
459 ngx_quic_build_int(p, 0); | |
460 ngx_quic_build_int(p, len); | |
461 *p = ngx_cpymem(*p, data, len); | |
462 } | |
463 | |
464 | |
465 | |
291 static int | 466 static int |
292 ngx_quic_add_handshake_data(ngx_ssl_conn_t *ssl_conn, | 467 ngx_quic_add_handshake_data(ngx_ssl_conn_t *ssl_conn, |
293 enum ssl_encryption_level_t level, const uint8_t *data, size_t len) | 468 enum ssl_encryption_level_t level, const uint8_t *data, size_t len) |
294 { | 469 { |
295 u_char *p, *pnp, *name, *nonce, *sample; | 470 u_char *p; |
296 ngx_str_t in, out, ad; | 471 ngx_str_t payload, res; |
297 static int pn; | |
298 const EVP_CIPHER *cipher; | |
299 ngx_connection_t *c; | 472 ngx_connection_t *c; |
300 ngx_quic_secret_t *secret; | 473 ngx_quic_header_t pkt; |
301 ngx_quic_connection_t *qc; | 474 ngx_quic_connection_t *qc; |
302 u_char mask[16]; | 475 |
476 ngx_str_t initial_token = ngx_null_string; | |
303 | 477 |
304 c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn); | 478 c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn); |
305 qc = c->quic; | 479 qc = c->quic; |
306 | 480 |
307 //ngx_ssl_handshake_log(c); // TODO: enable again | 481 //ngx_ssl_handshake_log(c); // TODO: enable again |
308 | 482 |
309 switch (level) { | 483 ngx_memzero(&pkt, sizeof(ngx_quic_header_t)); |
310 | 484 |
311 case ssl_encryption_initial: | 485 pkt.level = level; |
312 secret = &qc->server_in; | 486 |
313 break; | 487 payload.data = ngx_alloc(4 + len + 5 /*minimal ACK*/, c->log); |
314 | 488 if (payload.data == 0) { |
315 case ssl_encryption_handshake: | |
316 secret = &qc->server_hs; | |
317 break; | |
318 | |
319 default: | |
320 return 0; | 489 return 0; |
321 } | 490 } |
322 | 491 p = payload.data; |
323 ngx_quic_hexdump(c->log, "level:%d read", data, len, level); | 492 |
324 | 493 ngx_quic_create_crypto(&p, (u_char *) data, len); |
325 in.data = ngx_alloc(4 + len + 5 /*minimal ACK*/, c->log); | 494 |
326 if (in.data == 0) { | 495 if (level == ssl_encryption_initial) { |
496 ngx_quic_create_ack(&p); | |
497 | |
498 pkt.number = &qc->initial_pn; | |
499 pkt.flags = NGX_QUIC_PKT_INITIAL; | |
500 pkt.secret = &qc->server_in; | |
501 | |
502 pkt.token = &initial_token; | |
503 | |
504 } else if (level == ssl_encryption_handshake) { | |
505 pkt.number = &qc->handshake_pn; | |
506 pkt.flags = NGX_QUIC_PKT_HANDSHAKE; | |
507 pkt.secret = &qc->server_hs; | |
508 | |
509 pkt.token = NULL; | |
510 | |
511 } else { | |
512 pkt.number = &qc->appdata_pn; | |
513 } | |
514 | |
515 payload.len = p - payload.data; | |
516 | |
517 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, | |
518 "ngx_quic_add_handshake_data: clear_len:%uz", | |
519 payload.len); | |
520 | |
521 if (ngx_quic_create_long_packet(c, ssl_conn, &pkt, &payload, &res) | |
522 != NGX_OK) | |
523 { | |
327 return 0; | 524 return 0; |
328 } | 525 } |
329 | 526 |
330 p = in.data; | |
331 ngx_quic_build_int(&p, 6); // crypto frame | |
332 ngx_quic_build_int(&p, 0); | |
333 ngx_quic_build_int(&p, len); | |
334 p = ngx_cpymem(p, data, len); | |
335 | |
336 if (level == ssl_encryption_initial) { | |
337 ngx_quic_build_int(&p, 2); // ack frame | |
338 ngx_quic_build_int(&p, 0); | |
339 ngx_quic_build_int(&p, 0); | |
340 ngx_quic_build_int(&p, 0); | |
341 ngx_quic_build_int(&p, 0); | |
342 } | |
343 | |
344 in.len = p - in.data; | |
345 out.len = in.len + EVP_GCM_TLS_TAG_LEN; | |
346 | |
347 ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, | |
348 "ngx_quic_add_handshake_data: clear_len:%uz, ciphertext_len:%uz", | |
349 in.len, out.len); | |
350 | |
351 ad.data = ngx_alloc(346 /*max header*/, c->log); | |
352 if (ad.data == 0) { | |
353 return 0; | |
354 } | |
355 | |
356 p = ad.data; | |
357 if (level == ssl_encryption_initial) { | |
358 *p++ = 0xc0; // initial, packet number len | |
359 } else if (level == ssl_encryption_handshake) { | |
360 *p++ = 0xe0; // handshake, packet number len | |
361 } | |
362 p = ngx_quic_write_uint32(p, quic_version); | |
363 *p++ = qc->scid.len; | |
364 p = ngx_cpymem(p, qc->scid.data, qc->scid.len); | |
365 *p++ = qc->dcid.len; | |
366 p = ngx_cpymem(p, qc->dcid.data, qc->dcid.len); | |
367 if (level == ssl_encryption_initial) { | |
368 ngx_quic_build_int(&p, 0); // token length | |
369 } | |
370 ngx_quic_build_int(&p, out.len + 1); // length (inc. pnl) | |
371 pnp = p; | |
372 | |
373 if (level == ssl_encryption_initial) { | |
374 *p++ = 0; // packet number 0 | |
375 | |
376 } else if (level == ssl_encryption_handshake) { | |
377 *p++ = pn++; | |
378 } | |
379 | |
380 ad.len = p - ad.data; | |
381 | |
382 ngx_quic_hexdump0(c->log, "ad", data, len); | |
383 | |
384 name = (u_char *) SSL_get_cipher(ssl_conn); | |
385 | |
386 if (ngx_strcasecmp(name, (u_char *) "TLS_AES_128_GCM_SHA256") == 0 | |
387 || ngx_strcasecmp(name, (u_char *) "(NONE)") == 0) | |
388 { | |
389 cipher = EVP_aes_128_gcm(); | |
390 | |
391 } else if (ngx_strcasecmp(name, (u_char *) "TLS_AES_256_GCM_SHA384") == 0) { | |
392 cipher = EVP_aes_256_gcm(); | |
393 | |
394 } else { | |
395 return 0; | |
396 } | |
397 | |
398 nonce = ngx_pstrdup(c->pool, &secret->iv); | |
399 if (level == ssl_encryption_handshake) { | |
400 nonce[11] ^= (pn - 1); | |
401 } | |
402 | |
403 ngx_quic_hexdump0(c->log, "server_iv", secret->iv.data, 12); | |
404 ngx_quic_hexdump(c->log, "sample: n=%d nonce", nonce, 12, pn - 1); | |
405 | |
406 if (ngx_quic_tls_seal(c, cipher, secret, &out, nonce, &in, &ad) != NGX_OK) | |
407 { | |
408 return 0; | |
409 } | |
410 | |
411 sample = &out.data[3]; // pnl=0 | |
412 if (ngx_quic_tls_hp(c, EVP_aes_128_ecb(), secret, mask, sample) != NGX_OK) { | |
413 return 0; | |
414 } | |
415 | |
416 ngx_quic_hexdump0(c->log, "sample", sample, 16); | |
417 ngx_quic_hexdump0(c->log, "mask", mask, 16); | |
418 ngx_quic_hexdump0(c->log, "hp_key", secret->hp.data, 16); | |
419 | |
420 // header protection, pnl = 0 | |
421 ad.data[0] ^= mask[0] & 0x0f; | |
422 *pnp ^= mask[1]; | |
423 | |
424 u_char *packet = ngx_alloc(ad.len + out.len, c->log); | |
425 if (packet == 0) { | |
426 return 0; | |
427 } | |
428 | |
429 p = ngx_cpymem(packet, ad.data, ad.len); | |
430 p = ngx_cpymem(p, out.data, out.len); | |
431 | |
432 ngx_quic_hexdump0(c->log, "packet", packet, p - packet); | |
433 | |
434 // TODO: save state of data to send into qc (push into queue) | 527 // TODO: save state of data to send into qc (push into queue) |
435 | 528 qc->out = res; |
436 qc->out.data = packet; | |
437 qc->out.len = p - packet; | |
438 | 529 |
439 if (ngx_quic_output(c) != NGX_OK) { | 530 if (ngx_quic_output(c) != NGX_OK) { |
440 return 0; | 531 return 0; |
441 } | 532 } |
442 | 533 |
477 ngx_quic_new_connection(ngx_connection_t *c, ngx_ssl_t *ssl, ngx_buf_t *b) | 568 ngx_quic_new_connection(ngx_connection_t *c, ngx_ssl_t *ssl, ngx_buf_t *b) |
478 { | 569 { |
479 int n, sslerr; | 570 int n, sslerr; |
480 ngx_quic_connection_t *qc; | 571 ngx_quic_connection_t *qc; |
481 | 572 |
482 if ((b->pos[0] & 0xf0) != 0xc0) { | 573 if ((b->pos[0] & 0xf0) != NGX_QUIC_PKT_INITIAL) { |
483 ngx_log_error(NGX_LOG_INFO, c->log, 0, "invalid initial packet"); | 574 ngx_log_error(NGX_LOG_INFO, c->log, 0, "invalid initial packet"); |
484 return NGX_ERROR; | 575 return NGX_ERROR; |
485 } | 576 } |
486 | 577 |
487 if (ngx_buf_size(b) < 1200) { | 578 if (ngx_buf_size(b) < 1200) { |
805 p = bb->pos; | 896 p = bb->pos; |
806 b = bb->start; | 897 b = bb->start; |
807 | 898 |
808 ngx_quic_hexdump0(c->log, "input", buf, n); | 899 ngx_quic_hexdump0(c->log, "input", buf, n); |
809 | 900 |
810 if ((p[0] & 0xf0) != 0xe0) { | 901 if ((p[0] & 0xf0) != NGX_QUIC_PKT_HANDSHAKE) { |
811 ngx_log_error(NGX_LOG_INFO, c->log, 0, "invalid packet type"); | 902 ngx_log_error(NGX_LOG_INFO, c->log, 0, "invalid packet type"); |
812 return NGX_ERROR; | 903 return NGX_ERROR; |
813 } | 904 } |
814 | 905 |
815 ngx_int_t flags = *p++; | 906 ngx_int_t flags = *p++; |