# HG changeset patch # User Vladimir Homutov # Date 1582896205 -10800 # Node ID b28ea685a56e8f20930c3a51b80fa39c8f990c1b # Parent 3cb4f16426a5ff8cad297fbfcfe93955dec6ef37 Moved all QUIC code into ngx_event_quic.c Introduced ngx_quic_input() and ngx_quic_output() as interface between nginx and protocol. They are the only functions that are exported. While there, added copyrights. diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -90,328 +90,6 @@ static char *ngx_openssl_engine(ngx_conf static void ngx_openssl_exit(ngx_cycle_t *cycle); -#if NGX_OPENSSL_QUIC - -static int -quic_set_encryption_secrets(ngx_ssl_conn_t *ssl_conn, - enum ssl_encryption_level_t level, const uint8_t *read_secret, - const uint8_t *write_secret, size_t secret_len) -{ - u_char *name; - ngx_uint_t i; - const EVP_MD *digest; - const EVP_CIPHER *cipher; - ngx_connection_t *c; - ngx_quic_secret_t *client, *server; - - c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn); - - ngx_ssl_handshake_log(c); - -#if (NGX_DEBUG) - if (c->log->log_level & NGX_LOG_DEBUG_EVENT) { - u_char buf[64]; - size_t m; - - m = ngx_hex_dump(buf, (u_char *) read_secret, secret_len) - buf; - ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0, - "set_encryption_secrets: read %*s, len: %uz, level:%d", - m, buf, secret_len, (int) level); - - m = ngx_hex_dump(buf, (u_char *) write_secret, secret_len) - buf; - ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0, - "set_encryption_secrets: write %*s, len: %uz, level:%d", - m, buf, secret_len, (int) level); - } -#endif - - name = (u_char *) SSL_get_cipher(ssl_conn); - - if (ngx_strcasecmp(name, (u_char *) "TLS_AES_128_GCM_SHA256") == 0 - || ngx_strcasecmp(name, (u_char *) "(NONE)") == 0) - { - cipher = EVP_aes_128_gcm(); - digest = EVP_sha256(); - - } else if (ngx_strcasecmp(name, (u_char *) "TLS_AES_256_GCM_SHA384") == 0) { - cipher = EVP_aes_256_gcm(); - digest = EVP_sha384(); - - } else { - ngx_ssl_error(NGX_LOG_INFO, c->log, 0, "unexpected cipher"); - return 0; - } - - switch (level) { - - case ssl_encryption_handshake: - client = &c->quic->client_hs; - server = &c->quic->server_hs; - - break; - - case ssl_encryption_application: - client = &c->quic->client_ad; - server = &c->quic->server_ad; - - break; - - default: - return 0; - } - - client->key.len = EVP_CIPHER_key_length(cipher); - server->key.len = EVP_CIPHER_key_length(cipher); - - client->iv.len = EVP_CIPHER_iv_length(cipher); - server->iv.len = EVP_CIPHER_iv_length(cipher); - - client->hp.len = EVP_CIPHER_key_length(cipher); - server->hp.len = EVP_CIPHER_key_length(cipher); - - struct { - ngx_str_t label; - ngx_str_t *key; - const uint8_t *secret; - } seq[] = { - { ngx_string("tls13 quic key"), &client->key, read_secret }, - { ngx_string("tls13 quic iv"), &client->iv, read_secret }, - { ngx_string("tls13 quic hp"), &client->hp, read_secret }, - { ngx_string("tls13 quic key"), &server->key, write_secret }, - { ngx_string("tls13 quic iv"), &server->iv, write_secret }, - { ngx_string("tls13 quic hp"), &server->hp, write_secret }, - }; - - for (i = 0; i < (sizeof(seq) / sizeof(seq[0])); i++) { - - if (ngx_quic_hkdf_expand(c, digest, seq[i].key, &seq[i].label, - seq[i].secret, secret_len) - != NGX_OK) - { - return 0; - } - } - - return 1; -} - - -static int -quic_add_handshake_data(ngx_ssl_conn_t *ssl_conn, - enum ssl_encryption_level_t level, const uint8_t *data, size_t len) -{ - u_char *p, *pnp, *name, *nonce, *sample; - ngx_int_t m; - ngx_str_t in, out, ad; - static int pn; - const EVP_CIPHER *cipher; - ngx_connection_t *c; - ngx_quic_secret_t *secret; - ngx_quic_connection_t *qc; - u_char buf[2048], mask[16]; - - c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn); - qc = c->quic; - - ngx_ssl_handshake_log(c); - - switch (level) { - - case ssl_encryption_initial: - secret = &qc->server_in; - break; - - case ssl_encryption_handshake: - secret = &qc->server_hs; - break; - - default: - return 0; - } - - m = ngx_hex_dump(buf, (u_char *) data, ngx_min(len, 1024)) - buf; - ngx_log_debug5(NGX_LOG_DEBUG_EVENT, c->log, 0, - "quic_add_handshake_data: %*s%s, len: %uz, level:%d", - m, buf, len < 2048 ? "" : "...", len, (int) level); - - in.data = ngx_alloc(4 + len + 5 /*minimal ACK*/, c->log); - if (in.data == 0) { - return 0; - } - - p = in.data; - ngx_quic_build_int(&p, 6); // crypto frame - ngx_quic_build_int(&p, 0); - ngx_quic_build_int(&p, len); - p = ngx_cpymem(p, data, len); - - if (level == ssl_encryption_initial) { - ngx_quic_build_int(&p, 2); // ack frame - ngx_quic_build_int(&p, 0); - ngx_quic_build_int(&p, 0); - ngx_quic_build_int(&p, 0); - ngx_quic_build_int(&p, 0); - } - - in.len = p - in.data; - out.len = in.len + EVP_GCM_TLS_TAG_LEN; - - ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, - "quic_add_handshake_data: clear_len:%uz, ciphertext_len:%uz", - in.len, out.len); - - ad.data = ngx_alloc(346 /*max header*/, c->log); - if (ad.data == 0) { - return 0; - } - - p = ad.data; - if (level == ssl_encryption_initial) { - *p++ = 0xc0; // initial, packet number len - } else if (level == ssl_encryption_handshake) { - *p++ = 0xe0; // handshake, packet number len - } - p = ngx_quic_write_uint32(p, quic_version); - *p++ = qc->scid.len; - p = ngx_cpymem(p, qc->scid.data, qc->scid.len); - *p++ = qc->dcid.len; - p = ngx_cpymem(p, qc->dcid.data, qc->dcid.len); - if (level == ssl_encryption_initial) { - ngx_quic_build_int(&p, 0); // token length - } - ngx_quic_build_int(&p, out.len + 1); // length (inc. pnl) - pnp = p; - - if (level == ssl_encryption_initial) { - *p++ = 0; // packet number 0 - - } else if (level == ssl_encryption_handshake) { - *p++ = pn++; - } - - ad.len = p - ad.data; - - m = ngx_hex_dump(buf, ad.data, ad.len) - buf; - ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, - "quic_add_handshake_data ad: %*s, len: %uz", - m, buf, ad.len); - - - name = (u_char *) SSL_get_cipher(ssl_conn); - - if (ngx_strcasecmp(name, (u_char *) "TLS_AES_128_GCM_SHA256") == 0 - || ngx_strcasecmp(name, (u_char *) "(NONE)") == 0) - { - cipher = EVP_aes_128_gcm(); - - } else if (ngx_strcasecmp(name, (u_char *) "TLS_AES_256_GCM_SHA384") == 0) { - cipher = EVP_aes_256_gcm(); - - } else { - return 0; - } - - nonce = ngx_pstrdup(c->pool, &secret->iv); - if (level == ssl_encryption_handshake) { - nonce[11] ^= (pn - 1); - } - - m = ngx_hex_dump(buf, (u_char *) secret->iv.data, 12) - buf; - ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, - "quic_add_handshake_data sample: server_iv %*s", - m, buf); - m = ngx_hex_dump(buf, (u_char *) nonce, 12) - buf; - ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, - "quic_add_handshake_data sample: n=%d nonce %*s", - pn - 1, m, buf); - - if (ngx_quic_tls_seal(c, cipher, secret, &out, nonce, &in, &ad) != NGX_OK) - { - return 0; - } - - sample = &out.data[3]; // pnl=0 - if (ngx_quic_tls_hp(c, EVP_aes_128_ecb(), secret, mask, sample) != NGX_OK) { - return 0; - } - - m = ngx_hex_dump(buf, (u_char *) sample, 16) - buf; - ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, - "quic_add_handshake_data sample: %*s, len: %uz", - m, buf, 16); - - m = ngx_hex_dump(buf, (u_char *) mask, 16) - buf; - ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, - "quic_add_handshake_data mask: %*s, len: %uz", - m, buf, 16); - - m = ngx_hex_dump(buf, (u_char *) secret->hp.data, 16) - buf; - ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, - "quic_add_handshake_data hp_key: %*s, len: %uz", - m, buf, 16); - - // header protection, pnl = 0 - ad.data[0] ^= mask[0] & 0x0f; - *pnp ^= mask[1]; - - u_char *packet = ngx_alloc(ad.len + out.len, c->log); - if (packet == 0) { - return 0; - } - - p = ngx_cpymem(packet, ad.data, ad.len); - p = ngx_cpymem(p, out.data, out.len); - - m = ngx_hex_dump(buf, (u_char *) packet, ngx_min(1024, p - packet)) - buf; - ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0, - "quic_add_handshake_data packet: %*s%s, len: %uz", - m, buf, len < 2048 ? "" : "...", p - packet); - - c->send(c, packet, p - packet); - - return 1; -} - - -static int -quic_flush_flight(ngx_ssl_conn_t *ssl_conn) -{ - ngx_connection_t *c; - - c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn); - - ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "quic_flush_flight()"); - - return 1; -} - - -static int -quic_send_alert(ngx_ssl_conn_t *ssl_conn, enum ssl_encryption_level_t level, - uint8_t alert) -{ - ngx_connection_t *c; - - c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn); - - ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, - "quic_send_alert(), lvl=%d, alert=%d", - (int) level, (int) alert); - - return 1; -} - - -static SSL_QUIC_METHOD quic_method = { - quic_set_encryption_secrets, - quic_add_handshake_data, - quic_flush_flight, - quic_send_alert, -}; - -#endif - - static ngx_command_t ngx_openssl_commands[] = { { ngx_string("ssl_engine"), @@ -1790,7 +1468,7 @@ ngx_ssl_quic(ngx_conf_t *cf, ngx_ssl_t * #if NGX_OPENSSL_QUIC - SSL_CTX_set_quic_method(ssl->ctx, &quic_method); + ngx_quic_init_ssl_methods(ssl->ctx); return NGX_OK; #else diff --git a/src/event/ngx_event_quic.c b/src/event/ngx_event_quic.c --- a/src/event/ngx_event_quic.c +++ b/src/event/ngx_event_quic.c @@ -1,6 +1,1031 @@ + +/* + * Copyright (C) Nginx, Inc. + */ + + #include #include -#include + + +#define quic_version 0xff000018 + + +#if (NGX_HAVE_NONALIGNED) + +#define ngx_quic_parse_uint16(p) ntohs(*(uint16_t *) (p)) +#define ngx_quic_parse_uint32(p) ntohl(*(uint32_t *) (p)) + +#define ngx_quic_write_uint16 ngx_quic_write_uint16_aligned +#define ngx_quic_write_uint32 ngx_quic_write_uint32_aligned + +#else + +#define ngx_quic_parse_uint16(p) ((p)[0] << 8 | (p)[1]) +#define ngx_quic_parse_uint32(p) \ + ((uint32_t) (p)[0] << 24 | (p)[1] << 16 | (p)[2] << 8 | (p)[3]) + +#define ngx_quic_write_uint16(p, s) \ + ((p)[0] = (u_char) ((s) >> 8), \ + (p)[1] = (u_char) (s), \ + (p) + sizeof(uint16_t)) + +#define ngx_quic_write_uint32(p, s) \ + ((p)[0] = (u_char) ((s) >> 24), \ + (p)[1] = (u_char) ((s) >> 16), \ + (p)[2] = (u_char) ((s) >> 8), \ + (p)[3] = (u_char) (s), \ + (p) + sizeof(uint32_t)) + +#endif + + +#define ngx_quic_write_uint16_aligned(p, s) \ + (*(uint16_t *) (p) = htons((uint16_t) (s)), (p) + sizeof(uint16_t)) + +#define ngx_quic_write_uint32_aligned(p, s) \ + (*(uint32_t *) (p) = htonl((uint32_t) (s)), (p) + sizeof(uint32_t)) + + +/* TODO: real states, these are stubs */ +typedef enum { + NGX_QUIC_ST_INITIAL, + NGX_QUIC_ST_HANDSHAKE, + NGX_QUIC_ST_APP_DATA +} ngx_quic_state_t; + + +typedef struct { + ngx_str_t secret; + ngx_str_t key; + ngx_str_t iv; + ngx_str_t hp; +} ngx_quic_secret_t; + + +struct ngx_quic_connection_s { + + ngx_quic_state_t state; + ngx_ssl_t *ssl; + + ngx_str_t out; // stub for some kind of output queue + + ngx_str_t scid; + ngx_str_t dcid; + ngx_str_t token; + + ngx_quic_secret_t client_in; + ngx_quic_secret_t client_hs; + ngx_quic_secret_t client_ad; + ngx_quic_secret_t server_in; + ngx_quic_secret_t server_hs; + ngx_quic_secret_t server_ad; +}; + + +static ngx_int_t ngx_quic_new_connection(ngx_connection_t *c, ngx_ssl_t *ssl, + ngx_buf_t *b); +static ngx_int_t ngx_quic_handshake_input(ngx_connection_t *c, ngx_buf_t *b); + +static int ngx_quic_set_encryption_secrets(ngx_ssl_conn_t *ssl_conn, + enum ssl_encryption_level_t level, const uint8_t *read_secret, + const uint8_t *write_secret, size_t secret_len); +static int ngx_quic_add_handshake_data(ngx_ssl_conn_t *ssl_conn, + enum ssl_encryption_level_t level, const uint8_t *data, size_t len); +static int ngx_quic_flush_flight(ngx_ssl_conn_t *ssl_conn); +static int ngx_quic_send_alert(ngx_ssl_conn_t *ssl_conn, + enum ssl_encryption_level_t level, uint8_t alert); + +static uint64_t ngx_quic_parse_pn(u_char **pos, ngx_int_t len, u_char *mask); +static uint64_t ngx_quic_parse_int(u_char **pos); +static void ngx_quic_build_int(u_char **pos, uint64_t value); + +static ngx_int_t ngx_hkdf_extract(u_char *out_key, size_t *out_len, + const EVP_MD *digest, const u_char *secret, size_t secret_len, + const u_char *salt, size_t salt_len); +static ngx_int_t ngx_hkdf_expand(u_char *out_key, size_t out_len, + const EVP_MD *digest, const u_char *prk, size_t prk_len, + const u_char *info, size_t info_len); + +static ngx_int_t ngx_quic_hkdf_expand(ngx_connection_t *c, const EVP_MD *digest, + ngx_str_t *out, ngx_str_t *label, const uint8_t *prk, size_t prk_len); + +static ngx_int_t ngx_quic_tls_open(ngx_connection_t *c, + const EVP_CIPHER *cipher, ngx_quic_secret_t *s, ngx_str_t *out, + u_char *nonce, ngx_str_t *in, ngx_str_t *ad); +static ngx_int_t ngx_quic_tls_seal(ngx_connection_t *c, + const EVP_CIPHER *cipher, ngx_quic_secret_t *s, ngx_str_t *out, + u_char *nonce, ngx_str_t *in, ngx_str_t *ad); + +static ngx_int_t ngx_quic_tls_hp(ngx_connection_t *c, const EVP_CIPHER *cipher, + ngx_quic_secret_t *s, u_char *out, u_char *in); + + +static SSL_QUIC_METHOD quic_method = { + ngx_quic_set_encryption_secrets, + ngx_quic_add_handshake_data, + ngx_quic_flush_flight, + ngx_quic_send_alert, +}; + + +void +ngx_quic_init_ssl_methods(SSL_CTX* ctx) +{ + SSL_CTX_set_quic_method(ctx, &quic_method); +} + + +ngx_int_t +ngx_quic_input(ngx_connection_t *c, ngx_ssl_t *ssl, ngx_buf_t *b) +{ + if (c->quic == NULL) { + return ngx_quic_new_connection(c, ssl, b); //TODO: change state by results + } + + switch (c->quic->state) { + case NGX_QUIC_ST_INITIAL: + case NGX_QUIC_ST_HANDSHAKE: + return ngx_quic_handshake_input(c, b); + default: + /* application data */ + break; + } + + return NGX_OK; +} + + +ngx_int_t +ngx_quic_output(ngx_connection_t *c) +{ + /* stub for processing output queue */ + + if (c->quic->out.data) { + c->send(c, c->quic->out.data, c->quic->out.len); + c->quic->out.data = NULL; + } + + return NGX_OK; +} + + +static int +ngx_quic_set_encryption_secrets(ngx_ssl_conn_t *ssl_conn, + enum ssl_encryption_level_t level, const uint8_t *read_secret, + const uint8_t *write_secret, size_t secret_len) +{ + u_char *name; + ngx_uint_t i; + const EVP_MD *digest; + const EVP_CIPHER *cipher; + ngx_connection_t *c; + ngx_quic_secret_t *client, *server; + + c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn); + + //ngx_ssl_handshake_log(c); // TODO: enable again + +#if (NGX_DEBUG) + if (c->log->log_level & NGX_LOG_DEBUG_EVENT) { + u_char buf[64]; + size_t m; + + m = ngx_hex_dump(buf, (u_char *) read_secret, secret_len) - buf; + ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0, + "set_encryption_secrets: read %*s, len: %uz, level:%d", + m, buf, secret_len, (int) level); + + m = ngx_hex_dump(buf, (u_char *) write_secret, secret_len) - buf; + ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0, + "set_encryption_secrets: write %*s, len: %uz, level:%d", + m, buf, secret_len, (int) level); + } +#endif + + name = (u_char *) SSL_get_cipher(ssl_conn); + + if (ngx_strcasecmp(name, (u_char *) "TLS_AES_128_GCM_SHA256") == 0 + || ngx_strcasecmp(name, (u_char *) "(NONE)") == 0) + { + cipher = EVP_aes_128_gcm(); + digest = EVP_sha256(); + + } else if (ngx_strcasecmp(name, (u_char *) "TLS_AES_256_GCM_SHA384") == 0) { + cipher = EVP_aes_256_gcm(); + digest = EVP_sha384(); + + } else { + ngx_ssl_error(NGX_LOG_INFO, c->log, 0, "unexpected cipher"); + return 0; + } + + switch (level) { + + case ssl_encryption_handshake: + client = &c->quic->client_hs; + server = &c->quic->server_hs; + + break; + + case ssl_encryption_application: + client = &c->quic->client_ad; + server = &c->quic->server_ad; + + break; + + default: + return 0; + } + + client->key.len = EVP_CIPHER_key_length(cipher); + server->key.len = EVP_CIPHER_key_length(cipher); + + client->iv.len = EVP_CIPHER_iv_length(cipher); + server->iv.len = EVP_CIPHER_iv_length(cipher); + + client->hp.len = EVP_CIPHER_key_length(cipher); + server->hp.len = EVP_CIPHER_key_length(cipher); + + struct { + ngx_str_t label; + ngx_str_t *key; + const uint8_t *secret; + } seq[] = { + { ngx_string("tls13 quic key"), &client->key, read_secret }, + { ngx_string("tls13 quic iv"), &client->iv, read_secret }, + { ngx_string("tls13 quic hp"), &client->hp, read_secret }, + { ngx_string("tls13 quic key"), &server->key, write_secret }, + { ngx_string("tls13 quic iv"), &server->iv, write_secret }, + { ngx_string("tls13 quic hp"), &server->hp, write_secret }, + }; + + for (i = 0; i < (sizeof(seq) / sizeof(seq[0])); i++) { + + if (ngx_quic_hkdf_expand(c, digest, seq[i].key, &seq[i].label, + seq[i].secret, secret_len) + != NGX_OK) + { + return 0; + } + } + + return 1; +} + + +static int +ngx_quic_add_handshake_data(ngx_ssl_conn_t *ssl_conn, + enum ssl_encryption_level_t level, const uint8_t *data, size_t len) +{ + u_char *p, *pnp, *name, *nonce, *sample; + ngx_int_t m; + ngx_str_t in, out, ad; + static int pn; + const EVP_CIPHER *cipher; + ngx_connection_t *c; + ngx_quic_secret_t *secret; + ngx_quic_connection_t *qc; + u_char buf[2048], mask[16]; + + c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn); + qc = c->quic; + + //ngx_ssl_handshake_log(c); // TODO: enable again + + switch (level) { + + case ssl_encryption_initial: + secret = &qc->server_in; + break; + + case ssl_encryption_handshake: + secret = &qc->server_hs; + break; + + default: + return 0; + } + + m = ngx_hex_dump(buf, (u_char *) data, ngx_min(len, 1024)) - buf; + ngx_log_debug5(NGX_LOG_DEBUG_EVENT, c->log, 0, + "ngx_quic_add_handshake_data: %*s%s, len: %uz, level:%d", + m, buf, len < 2048 ? "" : "...", len, (int) level); + + in.data = ngx_alloc(4 + len + 5 /*minimal ACK*/, c->log); + if (in.data == 0) { + return 0; + } + + p = in.data; + ngx_quic_build_int(&p, 6); // crypto frame + ngx_quic_build_int(&p, 0); + ngx_quic_build_int(&p, len); + p = ngx_cpymem(p, data, len); + + if (level == ssl_encryption_initial) { + ngx_quic_build_int(&p, 2); // ack frame + ngx_quic_build_int(&p, 0); + ngx_quic_build_int(&p, 0); + ngx_quic_build_int(&p, 0); + ngx_quic_build_int(&p, 0); + } + + in.len = p - in.data; + out.len = in.len + EVP_GCM_TLS_TAG_LEN; + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, + "ngx_quic_add_handshake_data: clear_len:%uz, ciphertext_len:%uz", + in.len, out.len); + + ad.data = ngx_alloc(346 /*max header*/, c->log); + if (ad.data == 0) { + return 0; + } + + p = ad.data; + if (level == ssl_encryption_initial) { + *p++ = 0xc0; // initial, packet number len + } else if (level == ssl_encryption_handshake) { + *p++ = 0xe0; // handshake, packet number len + } + p = ngx_quic_write_uint32(p, quic_version); + *p++ = qc->scid.len; + p = ngx_cpymem(p, qc->scid.data, qc->scid.len); + *p++ = qc->dcid.len; + p = ngx_cpymem(p, qc->dcid.data, qc->dcid.len); + if (level == ssl_encryption_initial) { + ngx_quic_build_int(&p, 0); // token length + } + ngx_quic_build_int(&p, out.len + 1); // length (inc. pnl) + pnp = p; + + if (level == ssl_encryption_initial) { + *p++ = 0; // packet number 0 + + } else if (level == ssl_encryption_handshake) { + *p++ = pn++; + } + + ad.len = p - ad.data; + + m = ngx_hex_dump(buf, ad.data, ad.len) - buf; + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, + "ngx_quic_add_handshake_data ad: %*s, len: %uz", + m, buf, ad.len); + + + name = (u_char *) SSL_get_cipher(ssl_conn); + + if (ngx_strcasecmp(name, (u_char *) "TLS_AES_128_GCM_SHA256") == 0 + || ngx_strcasecmp(name, (u_char *) "(NONE)") == 0) + { + cipher = EVP_aes_128_gcm(); + + } else if (ngx_strcasecmp(name, (u_char *) "TLS_AES_256_GCM_SHA384") == 0) { + cipher = EVP_aes_256_gcm(); + + } else { + return 0; + } + + nonce = ngx_pstrdup(c->pool, &secret->iv); + if (level == ssl_encryption_handshake) { + nonce[11] ^= (pn - 1); + } + + m = ngx_hex_dump(buf, (u_char *) secret->iv.data, 12) - buf; + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, + "ngx_quic_add_handshake_data sample: server_iv %*s", + m, buf); + m = ngx_hex_dump(buf, (u_char *) nonce, 12) - buf; + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, + "ngx_quic_add_handshake_data sample: n=%d nonce %*s", + pn - 1, m, buf); + + if (ngx_quic_tls_seal(c, cipher, secret, &out, nonce, &in, &ad) != NGX_OK) + { + return 0; + } + + sample = &out.data[3]; // pnl=0 + if (ngx_quic_tls_hp(c, EVP_aes_128_ecb(), secret, mask, sample) != NGX_OK) { + return 0; + } + + m = ngx_hex_dump(buf, (u_char *) sample, 16) - buf; + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, + "ngx_quic_add_handshake_data sample: %*s, len: %uz", + m, buf, 16); + + m = ngx_hex_dump(buf, (u_char *) mask, 16) - buf; + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, + "ngx_quic_add_handshake_data mask: %*s, len: %uz", + m, buf, 16); + + m = ngx_hex_dump(buf, (u_char *) secret->hp.data, 16) - buf; + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, + "ngx_quic_add_handshake_data hp_key: %*s, len: %uz", + m, buf, 16); + + // header protection, pnl = 0 + ad.data[0] ^= mask[0] & 0x0f; + *pnp ^= mask[1]; + + u_char *packet = ngx_alloc(ad.len + out.len, c->log); + if (packet == 0) { + return 0; + } + + p = ngx_cpymem(packet, ad.data, ad.len); + p = ngx_cpymem(p, out.data, out.len); + + m = ngx_hex_dump(buf, (u_char *) packet, ngx_min(1024, p - packet)) - buf; + ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0, + "ngx_quic_add_handshake_data packet: %*s%s, len: %uz", + m, buf, len < 2048 ? "" : "...", p - packet); + + // TODO: save state of data to send into qc (push into queue) + + qc->out.data = packet; + qc->out.len = p - packet; + + if (ngx_quic_output(c) != NGX_OK) { + return 0; + } + + return 1; +} + + +static int +ngx_quic_flush_flight(ngx_ssl_conn_t *ssl_conn) +{ + ngx_connection_t *c; + + c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn); + + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "ngx_quic_flush_flight()"); + + return 1; +} + + +static int +ngx_quic_send_alert(ngx_ssl_conn_t *ssl_conn, enum ssl_encryption_level_t level, + uint8_t alert) +{ + ngx_connection_t *c; + + c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn); + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, + "ngx_quic_send_alert(), lvl=%d, alert=%d", + (int) level, (int) alert); + + return 1; +} + + +static ngx_int_t +ngx_quic_new_connection(ngx_connection_t *c, ngx_ssl_t *ssl, ngx_buf_t *b) +{ + int n, sslerr; +#if (NGX_DEBUG) + u_char buf[512]; + size_t m; +#endif + + ngx_quic_connection_t *qc; + + if ((b->pos[0] & 0xf0) != 0xc0) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, "invalid initial packet"); + return NGX_ERROR; + } + + if (ngx_buf_size(b) < 1200) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, "too small UDP datagram"); + return NGX_ERROR; + } + + ngx_int_t flags = *b->pos++; + uint32_t version = ngx_quic_parse_uint32(b->pos); + b->pos += sizeof(uint32_t); + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic flags:%xi version:%xD", flags, version); + + if (version != quic_version) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, "unsupported quic version"); + return NGX_ERROR; + } + + qc = ngx_pcalloc(c->pool, sizeof(ngx_quic_connection_t)); + if (qc == NULL) { + return NGX_ERROR; + } + + c->quic = qc; + + qc->dcid.len = *b->pos++; + qc->dcid.data = ngx_pnalloc(c->pool, qc->dcid.len); + if (qc->dcid.data == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(qc->dcid.data, b->pos, qc->dcid.len); + b->pos += qc->dcid.len; + + qc->scid.len = *b->pos++; + qc->scid.data = ngx_pnalloc(c->pool, qc->scid.len); + if (qc->scid.data == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(qc->scid.data, b->pos, qc->scid.len); + b->pos += qc->scid.len; + + qc->token.len = ngx_quic_parse_int(&b->pos); + qc->token.data = ngx_pnalloc(c->pool, qc->token.len); + if (qc->token.data == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(qc->token.data, b->pos, qc->token.len); + b->pos += qc->token.len; + + ngx_int_t plen = ngx_quic_parse_int(&b->pos); + + if (plen > b->last - b->pos) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, "truncated initial packet"); + return NGX_ERROR; + } + + /* draft-ietf-quic-tls-23#section-5.4.2: + * the Packet Number field is assumed to be 4 bytes long + * draft-ietf-quic-tls-23#section-5.4.[34]: + * AES-Based and ChaCha20-Based header protections sample 16 bytes + */ + u_char *sample = b->pos + 4; + +#if (NGX_DEBUG) + if (c->log->log_level & NGX_LOG_DEBUG_EVENT) { + m = ngx_hex_dump(buf, qc->dcid.data, qc->dcid.len) - buf; + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic DCID: %*s, len: %uz", m, buf, qc->dcid.len); + + m = ngx_hex_dump(buf, qc->scid.data, qc->scid.len) - buf; + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic SCID: %*s, len: %uz", m, buf, qc->scid.len); + + m = ngx_hex_dump(buf, qc->token.data, qc->token.len) - buf; + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic token: %*s, len: %uz", m, buf, qc->token.len); + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic packet length: %d", plen); + + m = ngx_hex_dump(buf, sample, 16) - buf; + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic sample: %*s", m, buf); + } +#endif + +// initial secret + + size_t is_len; + uint8_t is[SHA256_DIGEST_LENGTH]; + ngx_uint_t i; + const EVP_MD *digest; + const EVP_CIPHER *cipher; + static const uint8_t salt[20] = + "\xc3\xee\xf7\x12\xc7\x2e\xbb\x5a\x11\xa7" + "\xd2\x43\x2b\xb4\x63\x65\xbe\xf9\xf5\x02"; + + /* AEAD_AES_128_GCM prior to handshake, quic-tls-23#section-5.3 */ + + cipher = EVP_aes_128_gcm(); + digest = EVP_sha256(); + + if (ngx_hkdf_extract(is, &is_len, digest, qc->dcid.data, qc->dcid.len, + salt, sizeof(salt)) + != NGX_OK) + { + return NGX_ERROR; + } + + ngx_str_t iss = { + .data = is, + .len = is_len + }; + +#if (NGX_DEBUG) + if (c->log->log_level & NGX_LOG_DEBUG_EVENT) { + m = ngx_hex_dump(buf, (uint8_t *) salt, sizeof(salt)) - buf; + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic salt: %*s, len: %uz", m, buf, sizeof(salt)); + + m = ngx_hex_dump(buf, is, is_len) - buf; + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic initial secret: %*s, len: %uz", m, buf, is_len); + } +#endif + + /* draft-ietf-quic-tls-23#section-5.2 */ + qc->client_in.secret.len = SHA256_DIGEST_LENGTH; + qc->server_in.secret.len = SHA256_DIGEST_LENGTH; + + qc->client_in.key.len = EVP_CIPHER_key_length(cipher); + qc->server_in.key.len = EVP_CIPHER_key_length(cipher); + + qc->client_in.hp.len = EVP_CIPHER_key_length(cipher); + qc->server_in.hp.len = EVP_CIPHER_key_length(cipher); + + qc->client_in.iv.len = EVP_CIPHER_iv_length(cipher); + qc->server_in.iv.len = EVP_CIPHER_iv_length(cipher); + + struct { + ngx_str_t label; + ngx_str_t *key; + ngx_str_t *prk; + } seq[] = { + + /* draft-ietf-quic-tls-23#section-5.2 */ + { ngx_string("tls13 client in"), &qc->client_in.secret, &iss }, + { + ngx_string("tls13 quic key"), + &qc->client_in.key, + &qc->client_in.secret, + }, + { + ngx_string("tls13 quic iv"), + &qc->client_in.iv, + &qc->client_in.secret, + }, + { + /* AEAD_AES_128_GCM prior to handshake, quic-tls-23#section-5.4.1 */ + ngx_string("tls13 quic hp"), + &qc->client_in.hp, + &qc->client_in.secret, + }, + { ngx_string("tls13 server in"), &qc->server_in.secret, &iss }, + { + /* AEAD_AES_128_GCM prior to handshake, quic-tls-23#section-5.3 */ + ngx_string("tls13 quic key"), + &qc->server_in.key, + &qc->server_in.secret, + }, + { + ngx_string("tls13 quic iv"), + &qc->server_in.iv, + &qc->server_in.secret, + }, + { + /* AEAD_AES_128_GCM prior to handshake, quic-tls-23#section-5.4.1 */ + ngx_string("tls13 quic hp"), + &qc->server_in.hp, + &qc->server_in.secret, + }, + + }; + + for (i = 0; i < (sizeof(seq) / sizeof(seq[0])); i++) { + + if (ngx_quic_hkdf_expand(c, digest, seq[i].key, &seq[i].label, + seq[i].prk->data, seq[i].prk->len) + != NGX_OK) + { + return NGX_ERROR; + } + } + +// header protection + + uint8_t mask[16]; + if (ngx_quic_tls_hp(c, EVP_aes_128_ecb(), &qc->client_in, mask, sample) + != NGX_OK) + { + return NGX_ERROR; + } + + u_char clearflags = flags ^ (mask[0] & 0x0f); + ngx_int_t pnl = (clearflags & 0x03) + 1; + uint64_t pn = ngx_quic_parse_pn(&b->pos, pnl, &mask[1]); + +#if (NGX_DEBUG) + if (c->log->log_level & NGX_LOG_DEBUG_EVENT) { + m = ngx_hex_dump(buf, sample, 16) - buf; + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic sample: %*s", m, buf); + + m = ngx_hex_dump(buf, mask, 5) - buf; + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic mask: %*s", m, buf); + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic packet number: %uL, len: %xi", pn, pnl); + } +#endif + +// packet protection + + ngx_str_t in; + in.data = b->pos; + in.len = plen - pnl; + + ngx_str_t ad; + ad.len = b->pos - b->start; + ad.data = ngx_pnalloc(c->pool, ad.len); + if (ad.data == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(ad.data, b->start, ad.len); + ad.data[0] = clearflags; + ad.data[ad.len - pnl] = (u_char)pn; + + uint8_t *nonce = ngx_pstrdup(c->pool, &qc->client_in.iv); + nonce[11] ^= pn; + +#if (NGX_DEBUG) + if (c->log->log_level & NGX_LOG_DEBUG_EVENT) { + m = ngx_hex_dump(buf, nonce, 12) - buf; + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic nonce: %*s, len: %uz", m, buf, 12); + + m = ngx_hex_dump(buf, ad.data, ad.len) - buf; + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic ad: %*s, len: %uz", m, buf, ad.len); + } +#endif + + ngx_str_t out; + + if (ngx_quic_tls_open(c, EVP_aes_128_gcm(), &qc->client_in, &out, nonce, + &in, &ad) + != NGX_OK) + { + return NGX_ERROR; + } + +#if (NGX_DEBUG) + if (c->log->log_level & NGX_LOG_DEBUG_EVENT) { + m = ngx_hex_dump(buf, out.data, ngx_min(out.len, 256)) - buf; + ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic packet payload: %*s%s, len: %uz", + m, buf, m < 512 ? "" : "...", out.len); + } +#endif + + if (out.data[0] != 0x06) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "unexpected frame in initial packet"); + return NGX_ERROR; + } + + if (out.data[1] != 0x00) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "unexpected CRYPTO offset in initial packet"); + return NGX_ERROR; + } + + uint8_t *crypto = &out.data[2]; + uint64_t crypto_len = ngx_quic_parse_int(&crypto); + + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic initial packet CRYPTO length: %uL pp:%p:%p", + crypto_len, out.data, crypto); + + if (ngx_ssl_create_connection(ssl, c, NGX_SSL_BUFFER) != NGX_OK) { + return NGX_ERROR; + } + + static const uint8_t params[12] = "\x00\x0a\x00\x3a\x00\x01\x00\x00\x09\x00\x01\x03"; + + if (SSL_set_quic_transport_params(c->ssl->connection, params, + sizeof(params)) == 0) + { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "SSL_set_quic_transport_params() failed"); + return NGX_ERROR; + } + + n = SSL_do_handshake(c->ssl->connection); + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_do_handshake: %d", n); + + if (n == -1) { + sslerr = SSL_get_error(c->ssl->connection, n); + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_get_error: %d", + sslerr); + } + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, + "SSL_quic_read_level: %d, SSL_quic_write_level: %d", + (int) SSL_quic_read_level(c->ssl->connection), + (int) SSL_quic_write_level(c->ssl->connection)); + + if (!SSL_provide_quic_data(c->ssl->connection, + SSL_quic_read_level(c->ssl->connection), + crypto, crypto_len)) + { + ngx_ssl_error(NGX_LOG_INFO, c->log, 0, + "SSL_provide_quic_data() failed"); + return NGX_ERROR; + } + + n = SSL_do_handshake(c->ssl->connection); + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_do_handshake: %d", n); + + if (n == -1) { + sslerr = SSL_get_error(c->ssl->connection, n); + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_get_error: %d", + sslerr); + + if (sslerr == SSL_ERROR_SSL) { + ngx_ssl_error(NGX_LOG_ERR, c->log, 0, "SSL_do_handshake() failed"); + } + } + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, + "SSL_quic_read_level: %d, SSL_quic_write_level: %d", + (int) SSL_quic_read_level(c->ssl->connection), + (int) SSL_quic_write_level(c->ssl->connection)); + + return NGX_OK; +} + + +static ngx_int_t +ngx_quic_handshake_input(ngx_connection_t *c, ngx_buf_t *bb) +{ + size_t m; + ssize_t n; + ngx_str_t out; + const EVP_CIPHER *cipher; + ngx_quic_connection_t *qc; + u_char buf[4096], *p, *b; + + qc = c->quic; + + n = bb->last - bb->pos; + p = bb->pos; + b = bb->start; + + m = ngx_hex_dump(buf, b, n) - buf; + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic handshake handler: %*s, len: %uz", m, buf, n); + + if ((p[0] & 0xf0) != 0xe0) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, "invalid packet type"); + return NGX_ERROR; + } + + ngx_int_t flags = *p++; + uint32_t version = ngx_quic_parse_uint32(p); + p += sizeof(uint32_t); + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic flags:%xi version:%xD", flags, version); + + if (version != quic_version) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, "unsupported quic version"); + return NGX_ERROR; + } + + if (*p++ != qc->dcid.len) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, "unexpected quic dcidl"); + return NGX_ERROR; + } + + if (ngx_memcmp(p, qc->dcid.data, qc->dcid.len) != 0) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, "unexpected quic dcid"); + return NGX_ERROR; + } + + p += qc->dcid.len; + + if (*p++ != qc->scid.len) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, "unexpected quic scidl"); + return NGX_ERROR; + } + + if (ngx_memcmp(p, qc->scid.data, qc->scid.len) != 0) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, "unexpected quic scid"); + return NGX_ERROR; + } + + p += qc->scid.len; + + ngx_int_t plen = ngx_quic_parse_int(&p); + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic packet length: %d", plen); + + if (plen > b + n - p) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, "truncated handshake packet"); + return NGX_ERROR; + } + + u_char *sample = p + 4; + + m = ngx_hex_dump(buf, sample, 16) - buf; + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, "quic sample: %*s", m, buf); + +// header protection + + uint8_t mask[16]; + if (ngx_quic_tls_hp(c, EVP_aes_128_ecb(), &qc->client_hs, mask, sample) + != NGX_OK) + { + return NGX_ERROR; + } + + u_char clearflags = flags ^ (mask[0] & 0x0f); + ngx_int_t pnl = (clearflags & 0x03) + 1; + uint64_t pn = ngx_quic_parse_pn(&p, pnl, &mask[1]); + +#if (NGX_DEBUG) + if (c->log->log_level & NGX_LOG_DEBUG_EVENT) { + m = ngx_hex_dump(buf, mask, 5) - buf; + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic mask: %*s", m, buf); + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic clear flags: %xi", clearflags); + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic packet number: %uL, len: %xi", pn, pnl); + } +#endif + +// packet protection + + ngx_str_t in; + in.data = p; + in.len = plen - pnl; + + ngx_str_t ad; + ad.len = p - b; + ad.data = ngx_pnalloc(c->pool, ad.len); + if (ad.data == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(ad.data, b, ad.len); + ad.data[0] = clearflags; + ad.data[ad.len - pnl] = (u_char)pn; + + uint8_t *nonce = ngx_pstrdup(c->pool, &qc->client_hs.iv); + nonce[11] ^= pn; + +#if (NGX_DEBUG) + if (c->log->log_level & NGX_LOG_DEBUG_EVENT) { + m = ngx_hex_dump(buf, nonce, 12) - buf; + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic nonce: %*s, len: %uz", m, buf, 12); + + m = ngx_hex_dump(buf, ad.data, ad.len) - buf; + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic ad: %*s, len: %uz", m, buf, ad.len); + } +#endif + + u_char *name = (u_char *) SSL_get_cipher(c->ssl->connection); + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic ssl cipher: %s", name); + + if (ngx_strcasecmp(name, (u_char *) "TLS_AES_128_GCM_SHA256") == 0 + || ngx_strcasecmp(name, (u_char *) "(NONE)") == 0) + { + cipher = EVP_aes_128_gcm(); + + } else if (ngx_strcasecmp(name, (u_char *) "TLS_AES_256_GCM_SHA384") == 0) { + cipher = EVP_aes_256_gcm(); + + } else { + ngx_ssl_error(NGX_LOG_INFO, c->log, 0, "unexpected cipher"); + return NGX_ERROR; + } + + if (ngx_quic_tls_open(c, cipher, &qc->client_hs, &out, nonce, &in, &ad) + != NGX_OK) + { + return NGX_ERROR; + } + +#if (NGX_DEBUG) + if (c->log->log_level & NGX_LOG_DEBUG_EVENT) { + m = ngx_hex_dump(buf, out.data, ngx_min(out.len, 256)) - buf; + ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic packet payload: %*s%s, len: %uz", + m, buf, m < 512 ? "" : "...", out.len); + } +#endif + + return NGX_OK; +} uint64_t @@ -53,7 +1078,7 @@ ngx_quic_build_int(u_char **pos, uint64_ } -uint64_t +static uint64_t ngx_quic_parse_pn(u_char **pos, ngx_int_t len, u_char *mask) { u_char *p; @@ -71,7 +1096,7 @@ ngx_quic_parse_pn(u_char **pos, ngx_int_ } -ngx_int_t +static ngx_int_t ngx_hkdf_extract(u_char *out_key, size_t *out_len, const EVP_MD *digest, const u_char *secret, size_t secret_len, const u_char *salt, size_t salt_len) @@ -119,7 +1144,7 @@ ngx_hkdf_extract(u_char *out_key, size_t } -ngx_int_t +static ngx_int_t ngx_quic_hkdf_expand(ngx_connection_t *c, const EVP_MD *digest, ngx_str_t *out, ngx_str_t *label, const uint8_t *prk, size_t prk_len) { @@ -168,7 +1193,7 @@ ngx_quic_hkdf_expand(ngx_connection_t *c } -ngx_int_t +static ngx_int_t ngx_hkdf_expand(u_char *out_key, size_t out_len, const EVP_MD *digest, const uint8_t *prk, size_t prk_len, const u_char *info, size_t info_len) { @@ -214,7 +1239,7 @@ ngx_hkdf_expand(u_char *out_key, size_t } -ngx_int_t +static ngx_int_t ngx_quic_tls_open(ngx_connection_t *c, const EVP_CIPHER *cipher, ngx_quic_secret_t *s, ngx_str_t *out, u_char *nonce, ngx_str_t *in, ngx_str_t *ad) @@ -319,7 +1344,7 @@ ngx_quic_tls_open(ngx_connection_t *c, c } -ngx_int_t +static ngx_int_t ngx_quic_tls_seal(ngx_connection_t *c, const EVP_CIPHER *cipher, ngx_quic_secret_t *s, ngx_str_t *out, u_char *nonce, ngx_str_t *in, ngx_str_t *ad) @@ -422,7 +1447,7 @@ ngx_quic_tls_seal(ngx_connection_t *c, c } -ngx_int_t +static ngx_int_t ngx_quic_tls_hp(ngx_connection_t *c, const EVP_CIPHER *cipher, ngx_quic_secret_t *s, u_char *out, u_char *in) { diff --git a/src/event/ngx_event_quic.h b/src/event/ngx_event_quic.h --- a/src/event/ngx_event_quic.h +++ b/src/event/ngx_event_quic.h @@ -1,6 +1,6 @@ /* - * + * Copyright (C) Nginx, Inc. */ @@ -10,95 +10,11 @@ #include -#define quic_version 0xff000018 - - -typedef struct { - ngx_str_t secret; - ngx_str_t key; - ngx_str_t iv; - ngx_str_t hp; -} ngx_quic_secret_t; - +/* TODO: get rid somehow of ssl argument? */ +ngx_int_t ngx_quic_input(ngx_connection_t *c, ngx_ssl_t *ssl, ngx_buf_t *b); +ngx_int_t ngx_quic_output(ngx_connection_t *c); -struct ngx_quic_connection_s { - ngx_str_t scid; - ngx_str_t dcid; - ngx_str_t token; - - ngx_quic_secret_t client_in; - ngx_quic_secret_t client_hs; - ngx_quic_secret_t client_ad; - ngx_quic_secret_t server_in; - ngx_quic_secret_t server_hs; - ngx_quic_secret_t server_ad; -}; +void ngx_quic_init_ssl_methods(SSL_CTX* ctx); -uint64_t ngx_quic_parse_pn(u_char **pos, ngx_int_t len, u_char *mask); -uint64_t ngx_quic_parse_int(u_char **pos); -void ngx_quic_build_int(u_char **pos, uint64_t value); - -ngx_int_t ngx_hkdf_extract(u_char *out_key, size_t *out_len, - const EVP_MD *digest, const u_char *secret, size_t secret_len, - const u_char *salt, size_t salt_len); -ngx_int_t ngx_hkdf_expand(u_char *out_key, size_t out_len, - const EVP_MD *digest, const u_char *prk, size_t prk_len, - const u_char *info, size_t info_len); - -ngx_int_t ngx_quic_hkdf_expand(ngx_connection_t *c, const EVP_MD *digest, - ngx_str_t *out, ngx_str_t *label, const uint8_t *prk, size_t prk_len); - -ngx_int_t ngx_quic_tls_open(ngx_connection_t *c, - const EVP_CIPHER *cipher, ngx_quic_secret_t *s, ngx_str_t *out, - u_char *nonce, ngx_str_t *in, ngx_str_t *ad); -ngx_int_t ngx_quic_tls_seal(ngx_connection_t *c, - const EVP_CIPHER *cipher, ngx_quic_secret_t *s, ngx_str_t *out, - u_char *nonce, ngx_str_t *in, ngx_str_t *ad); - -ngx_int_t -ngx_quic_tls_hp(ngx_connection_t *c, const EVP_CIPHER *cipher, - ngx_quic_secret_t *s, u_char *out, u_char *in); - - -#if (NGX_HAVE_NONALIGNED) - -#define ngx_quic_parse_uint16(p) ntohs(*(uint16_t *) (p)) -#define ngx_quic_parse_uint32(p) ntohl(*(uint32_t *) (p)) - -#else - -#define ngx_quic_parse_uint16(p) ((p)[0] << 8 | (p)[1]) -#define ngx_quic_parse_uint32(p) \ - ((uint32_t) (p)[0] << 24 | (p)[1] << 16 | (p)[2] << 8 | (p)[3]) - -#endif - - -#define ngx_quic_write_uint16_aligned(p, s) \ - (*(uint16_t *) (p) = htons((uint16_t) (s)), (p) + sizeof(uint16_t)) -#define ngx_quic_write_uint32_aligned(p, s) \ - (*(uint32_t *) (p) = htonl((uint32_t) (s)), (p) + sizeof(uint32_t)) - -#if (NGX_HAVE_NONALIGNED) - -#define ngx_quic_write_uint16 ngx_quic_write_uint16_aligned -#define ngx_quic_write_uint32 ngx_quic_write_uint32_aligned - -#else - -#define ngx_quic_write_uint16(p, s) \ - ((p)[0] = (u_char) ((s) >> 8), \ - (p)[1] = (u_char) (s), \ - (p) + sizeof(uint16_t)) - -#define ngx_quic_write_uint32(p, s) \ - ((p)[0] = (u_char) ((s) >> 24), \ - (p)[1] = (u_char) ((s) >> 16), \ - (p)[2] = (u_char) ((s) >> 8), \ - (p)[3] = (u_char) (s), \ - (p) + sizeof(uint32_t)) - -#endif - #endif /* _NGX_EVENT_QUIC_H_INCLUDED_ */ diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -661,402 +661,22 @@ ngx_http_alloc_request(ngx_connection_t static void ngx_http_quic_handshake(ngx_event_t *rev) { - int n, sslerr; -#if (NGX_DEBUG) - u_char buf[512]; - size_t m; -#endif - ngx_buf_t *b; ngx_connection_t *c; ngx_http_connection_t *hc; - ngx_quic_connection_t *qc; ngx_http_ssl_srv_conf_t *sscf; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "quic handshake"); c = rev->data; hc = c->data; - b = c->buffer; - - if ((b->pos[0] & 0xf0) != 0xc0) { - ngx_log_error(NGX_LOG_INFO, rev->log, 0, "invalid initial packet"); - ngx_http_close_connection(c); - return; - } - - if (ngx_buf_size(b) < 1200) { - ngx_log_error(NGX_LOG_INFO, rev->log, 0, "too small UDP datagram"); - ngx_http_close_connection(c); - return; - } - - ngx_int_t flags = *b->pos++; - uint32_t version = ngx_quic_parse_uint32(b->pos); - b->pos += sizeof(uint32_t); - - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic flags:%xi version:%xD", flags, version); - - if (version != quic_version) { - ngx_log_error(NGX_LOG_INFO, rev->log, 0, "unsupported quic version"); - ngx_http_close_connection(c); - return; - } - - qc = ngx_pcalloc(c->pool, sizeof(ngx_quic_connection_t)); - if (qc == NULL) { - ngx_http_close_connection(c); - return; - } - - c->quic = qc; - - qc->dcid.len = *b->pos++; - qc->dcid.data = ngx_pnalloc(c->pool, qc->dcid.len); - if (qc->dcid.data == NULL) { - ngx_http_close_connection(c); - return; - } - - ngx_memcpy(qc->dcid.data, b->pos, qc->dcid.len); - b->pos += qc->dcid.len; - - qc->scid.len = *b->pos++; - qc->scid.data = ngx_pnalloc(c->pool, qc->scid.len); - if (qc->scid.data == NULL) { - ngx_http_close_connection(c); - return; - } - - ngx_memcpy(qc->scid.data, b->pos, qc->scid.len); - b->pos += qc->scid.len; - - qc->token.len = ngx_quic_parse_int(&b->pos); - qc->token.data = ngx_pnalloc(c->pool, qc->token.len); - if (qc->token.data == NULL) { - ngx_http_close_connection(c); - return; - } - - ngx_memcpy(qc->token.data, b->pos, qc->token.len); - b->pos += qc->token.len; - - ngx_int_t plen = ngx_quic_parse_int(&b->pos); - - if (plen > b->last - b->pos) { - ngx_log_error(NGX_LOG_INFO, rev->log, 0, "truncated initial packet"); - ngx_http_close_connection(c); - return; - } - - /* draft-ietf-quic-tls-23#section-5.4.2: - * the Packet Number field is assumed to be 4 bytes long - * draft-ietf-quic-tls-23#section-5.4.[34]: - * AES-Based and ChaCha20-Based header protections sample 16 bytes - */ - u_char *sample = b->pos + 4; - -#if (NGX_DEBUG) - if (c->log->log_level & NGX_LOG_DEBUG_EVENT) { - m = ngx_hex_dump(buf, qc->dcid.data, qc->dcid.len) - buf; - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic DCID: %*s, len: %uz", m, buf, qc->dcid.len); - - m = ngx_hex_dump(buf, qc->scid.data, qc->scid.len) - buf; - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic SCID: %*s, len: %uz", m, buf, qc->scid.len); - - m = ngx_hex_dump(buf, qc->token.data, qc->token.len) - buf; - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic token: %*s, len: %uz", m, buf, qc->token.len); - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic packet length: %d", plen); - - m = ngx_hex_dump(buf, sample, 16) - buf; - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic sample: %*s", m, buf); - } -#endif - -// initial secret - - size_t is_len; - uint8_t is[SHA256_DIGEST_LENGTH]; - ngx_uint_t i; - const EVP_MD *digest; - const EVP_CIPHER *cipher; - static const uint8_t salt[20] = - "\xc3\xee\xf7\x12\xc7\x2e\xbb\x5a\x11\xa7" - "\xd2\x43\x2b\xb4\x63\x65\xbe\xf9\xf5\x02"; - - /* AEAD_AES_128_GCM prior to handshake, quic-tls-23#section-5.3 */ - - cipher = EVP_aes_128_gcm(); - digest = EVP_sha256(); - - if (ngx_hkdf_extract(is, &is_len, digest, qc->dcid.data, qc->dcid.len, - salt, sizeof(salt)) - != NGX_OK) - { + + sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_ssl_module); + + if (ngx_quic_input(c, &sscf->ssl, c->buffer) != NGX_OK) { ngx_http_close_connection(c); return; } - ngx_str_t iss = { - .data = is, - .len = is_len - }; - -#if (NGX_DEBUG) - if (c->log->log_level & NGX_LOG_DEBUG_EVENT) { - m = ngx_hex_dump(buf, (uint8_t *) salt, sizeof(salt)) - buf; - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic salt: %*s, len: %uz", m, buf, sizeof(salt)); - - m = ngx_hex_dump(buf, is, is_len) - buf; - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic initial secret: %*s, len: %uz", m, buf, is_len); - } -#endif - - /* draft-ietf-quic-tls-23#section-5.2 */ - qc->client_in.secret.len = SHA256_DIGEST_LENGTH; - qc->server_in.secret.len = SHA256_DIGEST_LENGTH; - - qc->client_in.key.len = EVP_CIPHER_key_length(cipher); - qc->server_in.key.len = EVP_CIPHER_key_length(cipher); - - qc->client_in.hp.len = EVP_CIPHER_key_length(cipher); - qc->server_in.hp.len = EVP_CIPHER_key_length(cipher); - - qc->client_in.iv.len = EVP_CIPHER_iv_length(cipher); - qc->server_in.iv.len = EVP_CIPHER_iv_length(cipher); - - struct { - ngx_str_t label; - ngx_str_t *key; - ngx_str_t *prk; - } seq[] = { - - /* draft-ietf-quic-tls-23#section-5.2 */ - { ngx_string("tls13 client in"), &qc->client_in.secret, &iss }, - { - ngx_string("tls13 quic key"), - &qc->client_in.key, - &qc->client_in.secret, - }, - { - ngx_string("tls13 quic iv"), - &qc->client_in.iv, - &qc->client_in.secret, - }, - { - /* AEAD_AES_128_GCM prior to handshake, quic-tls-23#section-5.4.1 */ - ngx_string("tls13 quic hp"), - &qc->client_in.hp, - &qc->client_in.secret, - }, - { ngx_string("tls13 server in"), &qc->server_in.secret, &iss }, - { - /* AEAD_AES_128_GCM prior to handshake, quic-tls-23#section-5.3 */ - ngx_string("tls13 quic key"), - &qc->server_in.key, - &qc->server_in.secret, - }, - { - ngx_string("tls13 quic iv"), - &qc->server_in.iv, - &qc->server_in.secret, - }, - { - /* AEAD_AES_128_GCM prior to handshake, quic-tls-23#section-5.4.1 */ - ngx_string("tls13 quic hp"), - &qc->server_in.hp, - &qc->server_in.secret, - }, - - }; - - for (i = 0; i < (sizeof(seq) / sizeof(seq[0])); i++) { - - if (ngx_quic_hkdf_expand(c, digest, seq[i].key, &seq[i].label, - seq[i].prk->data, seq[i].prk->len) - != NGX_OK) - { - ngx_http_close_connection(c); - return; - } - } - -// header protection - - uint8_t mask[16]; - if (ngx_quic_tls_hp(c, EVP_aes_128_ecb(), &qc->client_in, mask, sample) - != NGX_OK) - { - ngx_http_close_connection(c); - return; - } - - u_char clearflags = flags ^ (mask[0] & 0x0f); - ngx_int_t pnl = (clearflags & 0x03) + 1; - uint64_t pn = ngx_quic_parse_pn(&b->pos, pnl, &mask[1]); - -#if (NGX_DEBUG) - if (c->log->log_level & NGX_LOG_DEBUG_EVENT) { - m = ngx_hex_dump(buf, sample, 16) - buf; - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic sample: %*s", m, buf); - - m = ngx_hex_dump(buf, mask, 5) - buf; - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic mask: %*s", m, buf); - - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic packet number: %uL, len: %xi", pn, pnl); - } -#endif - -// packet protection - - ngx_str_t in; - in.data = b->pos; - in.len = plen - pnl; - - ngx_str_t ad; - ad.len = b->pos - b->start; - ad.data = ngx_pnalloc(c->pool, ad.len); - if (ad.data == NULL) { - ngx_http_close_connection(c); - return; - } - - ngx_memcpy(ad.data, b->start, ad.len); - ad.data[0] = clearflags; - ad.data[ad.len - pnl] = (u_char)pn; - - uint8_t *nonce = ngx_pstrdup(c->pool, &qc->client_in.iv); - nonce[11] ^= pn; - -#if (NGX_DEBUG) - if (c->log->log_level & NGX_LOG_DEBUG_EVENT) { - m = ngx_hex_dump(buf, nonce, 12) - buf; - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic nonce: %*s, len: %uz", m, buf, 12); - - m = ngx_hex_dump(buf, ad.data, ad.len) - buf; - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic ad: %*s, len: %uz", m, buf, ad.len); - } -#endif - - ngx_str_t out; - - if (ngx_quic_tls_open(c, EVP_aes_128_gcm(), &qc->client_in, &out, nonce, - &in, &ad) - != NGX_OK) - { - ngx_http_close_connection(c); - return; - } - -#if (NGX_DEBUG) - if (c->log->log_level & NGX_LOG_DEBUG_EVENT) { - m = ngx_hex_dump(buf, out.data, ngx_min(out.len, 256)) - buf; - ngx_log_debug4(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic packet payload: %*s%s, len: %uz", - m, buf, m < 512 ? "" : "...", out.len); - } -#endif - - if (out.data[0] != 0x06) { - ngx_log_error(NGX_LOG_INFO, rev->log, 0, - "unexpected frame in initial packet"); - ngx_http_close_connection(c); - return; - } - - if (out.data[1] != 0x00) { - ngx_log_error(NGX_LOG_INFO, rev->log, 0, - "unexpected CRYPTO offset in initial packet"); - ngx_http_close_connection(c); - return; - } - - uint8_t *crypto = &out.data[2]; - uint64_t crypto_len = ngx_quic_parse_int(&crypto); - - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic initial packet CRYPTO length: %uL pp:%p:%p", - crypto_len, out.data, crypto); - - sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_ssl_module); - - if (ngx_ssl_create_connection(&sscf->ssl, c, NGX_SSL_BUFFER) - != NGX_OK) - { - ngx_http_close_connection(c); - return; - } - - static const uint8_t params[12] = "\x00\x0a\x00\x3a\x00\x01\x00\x00\x09\x00\x01\x03"; - - if (SSL_set_quic_transport_params(c->ssl->connection, params, - sizeof(params)) == 0) - { - ngx_log_error(NGX_LOG_INFO, rev->log, 0, - "SSL_set_quic_transport_params() failed"); - ngx_http_close_connection(c); - return; - } - - n = SSL_do_handshake(c->ssl->connection); - - ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_do_handshake: %d", n); - - if (n == -1) { - sslerr = SSL_get_error(c->ssl->connection, n); - - ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_get_error: %d", - sslerr); - } - - ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, - "SSL_quic_read_level: %d, SSL_quic_write_level: %d", - (int) SSL_quic_read_level(c->ssl->connection), - (int) SSL_quic_write_level(c->ssl->connection)); - - if (!SSL_provide_quic_data(c->ssl->connection, - SSL_quic_read_level(c->ssl->connection), - crypto, crypto_len)) - { - ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, - "SSL_provide_quic_data() failed"); - ngx_http_close_connection(c); - return; - } - - n = SSL_do_handshake(c->ssl->connection); - - ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_do_handshake: %d", n); - - if (n == -1) { - sslerr = SSL_get_error(c->ssl->connection, n); - - ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_get_error: %d", - sslerr); - - if (sslerr == SSL_ERROR_SSL) { - ngx_ssl_error(NGX_LOG_ERR, c->log, 0, "SSL_do_handshake() failed"); - } - } - - ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, - "SSL_quic_read_level: %d, SSL_quic_write_level: %d", - (int) SSL_quic_read_level(c->ssl->connection), - (int) SSL_quic_write_level(c->ssl->connection)); - if (!rev->timer_set) { ngx_add_timer(rev, c->listening->post_accept_timeout); } @@ -1069,17 +689,16 @@ ngx_http_quic_handshake(ngx_event_t *rev static void ngx_http_quic_handshake_handler(ngx_event_t *rev) { - size_t m; ssize_t n; - ngx_str_t out; ngx_connection_t *c; - const EVP_CIPHER *cipher; - ngx_quic_connection_t *qc; - u_char buf[4096], b[512], *p; + u_char buf[512]; + ngx_buf_t b; + + b.start = buf; + b.end = buf + 512; + b.pos = b.last = b.start; c = rev->data; - qc = c->quic; - p = b; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "quic handshake handler"); @@ -1094,7 +713,7 @@ ngx_http_quic_handshake_handler(ngx_even return; } - n = c->recv(c, b, sizeof(b)); + n = c->recv(c, b.start, b.end - b.start); if (n == NGX_AGAIN) { return; @@ -1106,166 +725,12 @@ ngx_http_quic_handshake_handler(ngx_even return; } - m = ngx_hex_dump(buf, b, n) - buf; - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic handshake handler: %*s, len: %uz", m, buf, n); - - if ((p[0] & 0xf0) != 0xe0) { - ngx_log_error(NGX_LOG_INFO, rev->log, 0, "invalid packet type"); - ngx_http_close_connection(c); - return; - } - - ngx_int_t flags = *p++; - uint32_t version = ngx_quic_parse_uint32(p); - p += sizeof(uint32_t); - - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic flags:%xi version:%xD", flags, version); - - if (version != quic_version) { - ngx_log_error(NGX_LOG_INFO, rev->log, 0, "unsupported quic version"); - ngx_http_close_connection(c); - return; - } - - if (*p++ != qc->dcid.len) { - ngx_log_error(NGX_LOG_INFO, rev->log, 0, "unexpected quic dcidl"); - ngx_http_close_connection(c); - return; - } - - if (ngx_memcmp(p, qc->dcid.data, qc->dcid.len) != 0) { - ngx_log_error(NGX_LOG_INFO, rev->log, 0, "unexpected quic dcid"); - ngx_http_close_connection(c); - return; - } - - p += qc->dcid.len; - - if (*p++ != qc->scid.len) { - ngx_log_error(NGX_LOG_INFO, rev->log, 0, "unexpected quic scidl"); - ngx_http_close_connection(c); - return; - } - - if (ngx_memcmp(p, qc->scid.data, qc->scid.len) != 0) { - ngx_log_error(NGX_LOG_INFO, rev->log, 0, "unexpected quic scid"); - ngx_http_close_connection(c); - return; - } - - p += qc->scid.len; - - ngx_int_t plen = ngx_quic_parse_int(&p); - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic packet length: %d", plen); - - if (plen > b + n - p) { - ngx_log_error(NGX_LOG_INFO, rev->log, 0, "truncated handshake packet"); - ngx_http_close_connection(c); - return; - } - - u_char *sample = p + 4; - - m = ngx_hex_dump(buf, sample, 16) - buf; - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, rev->log, 0, "quic sample: %*s", m, buf); - -// header protection - - uint8_t mask[16]; - if (ngx_quic_tls_hp(c, EVP_aes_128_ecb(), &qc->client_hs, mask, sample) - != NGX_OK) - { + b.last += n; + + if (ngx_quic_input(c, NULL, &b) != NGX_OK) { ngx_http_close_connection(c); return; } - - u_char clearflags = flags ^ (mask[0] & 0x0f); - ngx_int_t pnl = (clearflags & 0x03) + 1; - uint64_t pn = ngx_quic_parse_pn(&p, pnl, &mask[1]); - -#if (NGX_DEBUG) - if (c->log->log_level & NGX_LOG_DEBUG_EVENT) { - m = ngx_hex_dump(buf, mask, 5) - buf; - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic mask: %*s", m, buf); - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic clear flags: %xi", clearflags); - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic packet number: %uL, len: %xi", pn, pnl); - } -#endif - -// packet protection - - ngx_str_t in; - in.data = p; - in.len = plen - pnl; - - ngx_str_t ad; - ad.len = p - b; - ad.data = ngx_pnalloc(c->pool, ad.len); - if (ad.data == NULL) { - ngx_http_close_connection(c); - return; - } - - ngx_memcpy(ad.data, b, ad.len); - ad.data[0] = clearflags; - ad.data[ad.len - pnl] = (u_char)pn; - - uint8_t *nonce = ngx_pstrdup(c->pool, &qc->client_hs.iv); - nonce[11] ^= pn; - -#if (NGX_DEBUG) - if (c->log->log_level & NGX_LOG_DEBUG_EVENT) { - m = ngx_hex_dump(buf, nonce, 12) - buf; - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic nonce: %*s, len: %uz", m, buf, 12); - - m = ngx_hex_dump(buf, ad.data, ad.len) - buf; - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic ad: %*s, len: %uz", m, buf, ad.len); - } -#endif - - u_char *name = (u_char *) SSL_get_cipher(c->ssl->connection); - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic ssl cipher: %s", name); - - if (ngx_strcasecmp(name, (u_char *) "TLS_AES_128_GCM_SHA256") == 0 - || ngx_strcasecmp(name, (u_char *) "(NONE)") == 0) - { - cipher = EVP_aes_128_gcm(); - - } else if (ngx_strcasecmp(name, (u_char *) "TLS_AES_256_GCM_SHA384") == 0) { - cipher = EVP_aes_256_gcm(); - - } else { - ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, "unexpected cipher"); - ngx_http_close_connection(c); - return; - } - - if (ngx_quic_tls_open(c, cipher, &qc->client_hs, &out, nonce, &in, &ad) - != NGX_OK) - { - ngx_http_close_connection(c); - return; - } - -#if (NGX_DEBUG) - if (c->log->log_level & NGX_LOG_DEBUG_EVENT) { - m = ngx_hex_dump(buf, out.data, ngx_min(out.len, 256)) - buf; - ngx_log_debug4(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "quic packet payload: %*s%s, len: %uz", - m, buf, m < 512 ? "" : "...", out.len); - } -#endif - }