Initial QUIC support in http.

This commit is contained in:
Sergey Kandaurov 2020-02-28 13:09:51 +03:00
parent e92cb24f40
commit 26ac1c73f0
13 changed files with 766 additions and 27 deletions

View File

@ -1242,7 +1242,8 @@ if [ $USE_OPENSSL = YES ]; then
ngx_module_type=CORE
ngx_module_name=ngx_openssl_module
ngx_module_incs=
ngx_module_deps=src/event/ngx_event_openssl.h
ngx_module_deps="src/event/ngx_event_openssl.h \
src/event/ngx_event_quic.h"
ngx_module_srcs="src/event/ngx_event_openssl.c
src/event/ngx_event_openssl_stapling.c"
ngx_module_libs=

View File

@ -150,6 +150,7 @@ struct ngx_connection_s {
ngx_proxy_protocol_t *proxy_protocol;
#if (NGX_SSL || NGX_COMPAT)
ngx_quic_connection_t *quic;
ngx_ssl_connection_t *ssl;
#endif

View File

@ -27,6 +27,7 @@ typedef struct ngx_connection_s ngx_connection_t;
typedef struct ngx_thread_task_s ngx_thread_task_t;
typedef struct ngx_ssl_s ngx_ssl_t;
typedef struct ngx_proxy_protocol_s ngx_proxy_protocol_t;
typedef struct ngx_quic_connection_s ngx_quic_connection_t;
typedef struct ngx_ssl_connection_s ngx_ssl_connection_t;
typedef struct ngx_udp_connection_s ngx_udp_connection_t;
@ -82,6 +83,7 @@ typedef void (*ngx_connection_handler_pt)(ngx_connection_t *c);
#include <ngx_resolver.h>
#if (NGX_OPENSSL)
#include <ngx_event_openssl.h>
#include <ngx_event_quic.h>
#endif
#include <ngx_process_cycle.h>
#include <ngx_conf_file.h>

View File

@ -89,6 +89,126 @@ static void *ngx_openssl_create_conf(ngx_cycle_t *cycle);
static char *ngx_openssl_engine(ngx_conf_t *cf, ngx_command_t *cmd, void *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)
{
size_t *len;
uint8_t **rsec, **wsec;
ngx_connection_t *c;
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: %*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: %*s, len: %uz, level:%d",
m, buf, secret_len, (int) level);
}
#endif
switch (level) {
case ssl_encryption_handshake:
len = &c->quic->handshake_secret_len;
rsec = &c->quic->handshake_read_secret;
wsec = &c->quic->handshake_write_secret;
break;
case ssl_encryption_application:
len = &c->quic->application_secret_len;
rsec = &c->quic->application_read_secret;
wsec = &c->quic->application_write_secret;
break;
default:
return 0;
}
*len = secret_len;
*rsec = ngx_pnalloc(c->pool, secret_len);
if (*rsec == NULL) {
return NGX_ERROR;
}
ngx_memcpy(*rsec, read_secret, secret_len);
*wsec = ngx_pnalloc(c->pool, secret_len);
if (*wsec == NULL) {
return NGX_ERROR;
}
ngx_memcpy(*wsec, write_secret, secret_len);
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 buf[512];
ngx_int_t m;
ngx_connection_t *c;
c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);
m = ngx_hex_dump(buf, (u_char *) data, ngx_min(len, 256)) - buf;
ngx_log_debug5(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic_add_handshake_data: %*s%s, len: %uz, level:%d",
m, buf, len < 512 ? "" : "...", len, (int) level);
if (!(SSL_provide_quic_data(ssl_conn, level, data, len))) {
ERR_print_errors_fp(stderr);
return 0;
}
return 1;
}
static int
quic_flush_flight(ngx_ssl_conn_t *ssl_conn)
{
printf("quic_flush_flight()\n");
return 1;
}
static int
quic_send_alert(ngx_ssl_conn_t *ssl_conn, enum ssl_encryption_level_t level,
uint8_t alert)
{
printf("quic_send_alert(), lvl=%d, alert=%d\n", level, 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[] = {
@ -1459,6 +1579,29 @@ ngx_ssl_early_data(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_uint_t enable)
}
ngx_int_t
ngx_ssl_quic(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_uint_t enable)
{
if (!enable) {
return NGX_OK;
}
#if NGX_OPENSSL_QUIC
SSL_CTX_set_quic_method(ssl->ctx, &quic_method);
printf("%s\n", __func__);
return NGX_OK;
#else
ngx_log_error(NGX_LOG_WARN, ssl->log, 0,
"\"ssl_quic\" is not supported on this platform");
return NGX_ERROR;
#endif
}
ngx_int_t
ngx_ssl_client_session_cache(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_uint_t enable)
{

View File

@ -14,6 +14,7 @@
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/aes.h>
#include <openssl/bn.h>
#include <openssl/conf.h>
#include <openssl/crypto.h>
@ -22,6 +23,7 @@
#include <openssl/engine.h>
#endif
#include <openssl/evp.h>
#include <openssl/hkdf.h>
#include <openssl/hmac.h>
#ifndef OPENSSL_NO_OCSP
#include <openssl/ocsp.h>
@ -189,6 +191,7 @@ ngx_int_t ngx_ssl_dhparam(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file);
ngx_int_t ngx_ssl_ecdh_curve(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *name);
ngx_int_t ngx_ssl_early_data(ngx_conf_t *cf, ngx_ssl_t *ssl,
ngx_uint_t enable);
ngx_int_t ngx_ssl_quic(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_uint_t enable);
ngx_int_t ngx_ssl_client_session_cache(ngx_conf_t *cf, ngx_ssl_t *ssl,
ngx_uint_t enable);
ngx_int_t ngx_ssl_session_cache(ngx_ssl_t *ssl, ngx_str_t *sess_ctx,

View File

@ -0,0 +1,31 @@
/*
*
*/
#ifndef _NGX_EVENT_QUIC_H_INCLUDED_
#define _NGX_EVENT_QUIC_H_INCLUDED_
struct ngx_quic_connection_s {
ngx_str_t scid;
ngx_str_t dcid;
ngx_str_t token;
ngx_str_t client_in;
ngx_str_t client_in_key;
ngx_str_t client_in_iv;
ngx_str_t client_in_hp;
size_t handshake_secret_len;
uint8_t *handshake_read_secret;
uint8_t *handshake_write_secret;
size_t application_secret_len;
uint8_t *application_read_secret;
uint8_t *application_write_secret;
};
#endif /* _NGX_EVENT_QUIC_H_INCLUDED_ */

View File

@ -249,6 +249,13 @@ static ngx_command_t ngx_http_ssl_commands[] = {
offsetof(ngx_http_ssl_srv_conf_t, early_data),
NULL },
{ ngx_string("ssl_quic"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_SRV_CONF_OFFSET,
offsetof(ngx_http_ssl_srv_conf_t, quic),
NULL },
ngx_null_command
};
@ -568,6 +575,7 @@ ngx_http_ssl_create_srv_conf(ngx_conf_t *cf)
sscf->enable = NGX_CONF_UNSET;
sscf->prefer_server_ciphers = NGX_CONF_UNSET;
sscf->early_data = NGX_CONF_UNSET;
sscf->quic = NGX_CONF_UNSET;
sscf->buffer_size = NGX_CONF_UNSET_SIZE;
sscf->verify = NGX_CONF_UNSET_UINT;
sscf->verify_depth = NGX_CONF_UNSET_UINT;
@ -612,6 +620,8 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
ngx_conf_merge_value(conf->early_data, prev->early_data, 0);
ngx_conf_merge_value(conf->quic, prev->quic, 0);
ngx_conf_merge_bitmask_value(conf->protocols, prev->protocols,
(NGX_CONF_BITMASK_SET|NGX_SSL_TLSv1
|NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2));
@ -696,6 +706,7 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
}
}
printf("ngx_ssl_create\n");
if (ngx_ssl_create(&conf->ssl, conf->protocols, conf) != NGX_OK) {
return NGX_CONF_ERROR;
}
@ -857,6 +868,10 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
return NGX_CONF_ERROR;
}
if (ngx_ssl_quic(cf, &conf->ssl, conf->quic) != NGX_OK) {
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
@ -1141,13 +1156,15 @@ ngx_http_ssl_init(ngx_conf_t *cf)
addr = port[p].addrs.elts;
for (a = 0; a < port[p].addrs.nelts; a++) {
printf("ssl %d http3 %d\n", addr[a].opt.ssl, addr[a].opt.http3);
if (!addr[a].opt.ssl) {
if (!addr[a].opt.ssl && !addr[a].opt.http3) {
continue;
}
cscf = addr[a].default_server;
sscf = cscf->ctx->srv_conf[ngx_http_ssl_module.ctx_index];
printf("sscf->protocols %lx\n", sscf->protocols);
if (sscf->certificates == NULL) {
ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
@ -1156,6 +1173,14 @@ ngx_http_ssl_init(ngx_conf_t *cf)
cscf->file_name, cscf->line);
return NGX_ERROR;
}
if (addr[a].opt.http3 && !(sscf->protocols & NGX_SSL_TLSv1_3)) {
ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
"\"ssl_protocols\" did not enable TLSv1.3 for "
"the \"listen ... http3\" directive in %s:%ui",
cscf->file_name, cscf->line);
return NGX_ERROR;
}
}
}

View File

@ -21,6 +21,7 @@ typedef struct {
ngx_flag_t prefer_server_ciphers;
ngx_flag_t early_data;
ngx_flag_t quic;
ngx_uint_t protocols;

View File

@ -1203,6 +1203,9 @@ ngx_http_add_addresses(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,
#if (NGX_HTTP_V2)
ngx_uint_t http2;
#endif
#if (NGX_HTTP_SSL)
ngx_uint_t http3;
#endif
/*
* we cannot compare whole sockaddr struct's as kernel
@ -1238,6 +1241,9 @@ ngx_http_add_addresses(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,
#if (NGX_HTTP_V2)
http2 = lsopt->http2 || addr[i].opt.http2;
#endif
#if (NGX_HTTP_SSL)
http3 = lsopt->http3 || addr[i].opt.http3;
#endif
if (lsopt->set) {
@ -1274,6 +1280,9 @@ ngx_http_add_addresses(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,
#if (NGX_HTTP_V2)
addr[i].opt.http2 = http2;
#endif
#if (NGX_HTTP_SSL)
addr[i].opt.http3 = http3;
#endif
return NGX_OK;
}
@ -1315,6 +1324,17 @@ ngx_http_add_address(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,
&lsopt->addr_text);
}
#endif
#if (NGX_HTTP_SSL && !defined NGX_OPENSSL_QUIC)
if (lsopt->http3) {
ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
"nginx was built with OpenSSL that lacks QUIC "
"support, HTTP/3 is not enabled for %V",
&lsopt->addr_text);
}
#endif
addr = ngx_array_push(&port->addrs);
@ -1806,6 +1826,9 @@ ngx_http_add_addrs(ngx_conf_t *cf, ngx_http_port_t *hport,
#endif
#if (NGX_HTTP_V2)
addrs[i].conf.http2 = addr[i].opt.http2;
#endif
#if (NGX_HTTP_SSL)
addrs[i].conf.http3 = addr[i].opt.http3;
#endif
addrs[i].conf.proxy_protocol = addr[i].opt.proxy_protocol;
@ -1871,6 +1894,9 @@ ngx_http_add_addrs6(ngx_conf_t *cf, ngx_http_port_t *hport,
#endif
#if (NGX_HTTP_V2)
addrs6[i].conf.http2 = addr[i].opt.http2;
#endif
#if (NGX_HTTP_SSL)
addrs6[i].conf.http3 = addr[i].opt.http3;
#endif
addrs6[i].conf.proxy_protocol = addr[i].opt.proxy_protocol;

View File

@ -3822,11 +3822,6 @@ ngx_http_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
continue;
}
if (ngx_strcmp(value[n].data, "quic") == 0) {
lsopt.type = SOCK_DGRAM;
continue;
}
if (ngx_strcmp(value[n].data, "bind") == 0) {
lsopt.set = 1;
lsopt.bind = 1;
@ -4004,6 +3999,19 @@ ngx_http_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
#endif
}
if (ngx_strcmp(value[n].data, "http3") == 0) {
#if (NGX_HTTP_SSL)
lsopt.http3 = 1;
lsopt.type = SOCK_DGRAM;
continue;
#else
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"the \"http3\" parameter requires "
"ngx_http_ssl_module");
return NGX_CONF_ERROR;
#endif
}
if (ngx_strcmp(value[n].data, "spdy") == 0) {
ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
"invalid parameter \"spdy\": "

View File

@ -75,6 +75,7 @@ typedef struct {
unsigned wildcard:1;
unsigned ssl:1;
unsigned http2:1;
unsigned http3:1;
#if (NGX_HAVE_INET6)
unsigned ipv6only:1;
#endif
@ -238,6 +239,7 @@ struct ngx_http_addr_conf_s {
unsigned ssl:1;
unsigned http2:1;
unsigned http3:1;
unsigned proxy_protocol:1;
};

View File

@ -62,6 +62,8 @@ static u_char *ngx_http_log_error_handler(ngx_http_request_t *r,
#if (NGX_HTTP_SSL)
static void ngx_http_ssl_handshake(ngx_event_t *rev);
static void ngx_http_ssl_handshake_handler(ngx_connection_t *c);
static void ngx_http_quic_handshake(ngx_event_t *rev);
#endif
@ -328,6 +330,14 @@ ngx_http_init_connection(ngx_connection_t *c)
rev->ready = 1;
}
#if (NGX_HTTP_SSL)
if (hc->addr_conf->http3) {
hc->quic = 1;
c->log->action = "QUIC handshaking";
rev->handler = ngx_http_quic_handshake;
}
#endif
#if (NGX_HTTP_V2)
if (hc->addr_conf->http2) {
rev->handler = ngx_http_v2_init;
@ -647,6 +657,491 @@ ngx_http_alloc_request(ngx_connection_t *c)
#if (NGX_HTTP_SSL)
static uint64_t
ngx_quic_parse_int(u_char **pos)
{
u_char *p;
uint64_t value;
ngx_uint_t len;
p = *pos;
len = 1 << ((*p & 0xc0) >> 6);
value = *p++ & 0x3f;
while (--len) {
value = (value << 8) + *p++;
}
*pos = p;
return value;
}
static uint64_t
ngx_quic_parse_pn(u_char **pos, ngx_int_t len, u_char *mask)
{
u_char *p;
uint64_t value;
p = *pos;
value = *p++ ^ *mask++;
while (--len) {
value = (value << 8) + (*p++ ^ *mask++);
}
*pos = p;
return value;
}
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;
qc = ngx_pcalloc(c->pool, sizeof(ngx_quic_connection_t));
if (qc == NULL) {
ngx_http_close_connection(c);
return;
}
c->quic = qc;
printf("buffer %p %p:%p:%p:%p \n", b, b->start, b->pos, b->last, b->end);
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_http_v2_parse_uint32(b->pos);
b->pos += 4;
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, rev->log, 0,
"quic flags:%xi version:%xD", flags, version);
if (version != 0xff000017) {
ngx_log_error(NGX_LOG_INFO, rev->log, 0, "unsupported quic version");
ngx_http_close_connection(c);
return;
}
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;
uint64_t plen = ngx_quic_parse_int(&b->pos);
/* 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.3:
* AES-Based header protection samples 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];
const EVP_MD *digest;
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";
digest = EVP_sha256();
HKDF_extract(is, &is_len, digest, qc->dcid.data, qc->dcid.len, salt,
sizeof(salt));
#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
size_t hkdfl_len;
uint8_t hkdfl[20];
uint8_t *p;
/* draft-ietf-quic-tls-23#section-5.2 */
qc->client_in.len = SHA256_DIGEST_LENGTH;
qc->client_in.data = ngx_pnalloc(c->pool, qc->client_in.len);
if (qc->client_in.data == NULL) {
ngx_http_close_connection(c);
return;
}
hkdfl_len = 2 + 1 + sizeof("tls13 client in") - 1 + 1;
hkdfl[0] = 0;
hkdfl[1] = qc->client_in.len;
hkdfl[2] = sizeof("tls13 client in") - 1;
p = ngx_cpymem(&hkdfl[3], "tls13 client in",
sizeof("tls13 client in") - 1);
*p = '\0';
if (HKDF_expand(qc->client_in.data, qc->client_in.len,
digest, is, is_len, hkdfl, hkdfl_len)
== 0)
{
ngx_ssl_error(NGX_LOG_INFO, rev->log, 0,
"HKDF_expand(client_in) failed");
ngx_http_close_connection(c);
return;
}
ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0,
"quic EVP key:%d tag:%d nonce:%d",
EVP_AEAD_key_length(EVP_aead_aes_128_gcm()),
EVP_AEAD_max_tag_len(EVP_aead_aes_128_gcm()),
EVP_AEAD_nonce_length(EVP_aead_aes_128_gcm()));
/* AEAD_AES_128_GCM prior to handshake, quic-tls-23#section-5.3 */
qc->client_in_key.len = EVP_AEAD_key_length(EVP_aead_aes_128_gcm());
qc->client_in_key.data = ngx_pnalloc(c->pool, qc->client_in_key.len);
if (qc->client_in_key.data == NULL) {
ngx_http_close_connection(c);
return;
}
hkdfl_len = 2 + 1 + sizeof("tls13 quic key") - 1 + 1;
hkdfl[1] = qc->client_in_key.len;
hkdfl[2] = sizeof("tls13 quic key") - 1;
p = ngx_cpymem(&hkdfl[3], "tls13 quic key",
sizeof("tls13 quic key") - 1);
*p = '\0';
if (HKDF_expand(qc->client_in_key.data, qc->client_in_key.len,
digest, qc->client_in.data, qc->client_in.len,
hkdfl, hkdfl_len)
== 0)
{
ngx_ssl_error(NGX_LOG_INFO, rev->log, 0,
"HKDF_expand(client_in_key) failed");
ngx_http_close_connection(c);
return;
}
qc->client_in_iv.len = EVP_AEAD_nonce_length(EVP_aead_aes_128_gcm());
qc->client_in_iv.data = ngx_pnalloc(c->pool, qc->client_in_iv.len);
if (qc->client_in_iv.data == NULL) {
ngx_http_close_connection(c);
return;
}
hkdfl_len = 2 + 1 + sizeof("tls13 quic iv") - 1 + 1;
hkdfl[1] = qc->client_in_iv.len;
hkdfl[2] = sizeof("tls13 quic iv") - 1;
p = ngx_cpymem(&hkdfl[3], "tls13 quic iv", sizeof("tls13 quic iv") - 1);
*p = '\0';
if (HKDF_expand(qc->client_in_iv.data, qc->client_in_iv.len, digest,
qc->client_in.data, qc->client_in.len, hkdfl, hkdfl_len)
== 0)
{
ngx_ssl_error(NGX_LOG_INFO, rev->log, 0,
"HKDF_expand(client_in_iv) failed");
ngx_http_close_connection(c);
return;
}
/* AEAD_AES_128_GCM prior to handshake, quic-tls-23#section-5.4.1 */
qc->client_in_hp.len = EVP_AEAD_key_length(EVP_aead_aes_128_gcm());
qc->client_in_hp.data = ngx_pnalloc(c->pool, qc->client_in_hp.len);
if (qc->client_in_hp.data == NULL) {
ngx_http_close_connection(c);
return;
}
hkdfl_len = 2 + 1 + sizeof("tls13 quic hp") - 1 + 1;
hkdfl[1] = qc->client_in_hp.len;
hkdfl[2] = sizeof("tls13 quic hp") - 1;
p = ngx_cpymem(&hkdfl[3], "tls13 quic hp", sizeof("tls13 quic hp") - 1);
*p = '\0';
if (HKDF_expand(qc->client_in_hp.data, qc->client_in_hp.len, digest,
qc->client_in.data, qc->client_in.len, hkdfl, hkdfl_len)
== 0)
{
ngx_ssl_error(NGX_LOG_INFO, rev->log, 0,
"HKDF_expand(client_in_hp) failed");
ngx_http_close_connection(c);
return;
}
#if (NGX_DEBUG)
if (c->log->log_level & NGX_LOG_DEBUG_EVENT) {
m = ngx_hex_dump(buf, qc->client_in.data, qc->client_in.len) - buf;
ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0,
"quic client initial secret: %*s, len: %uz",
m, buf, qc->client_in.len);
m = ngx_hex_dump(buf, qc->client_in_key.data, qc->client_in_key.len)
- buf;
ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0,
"quic key: %*s, len: %uz",
m, buf, qc->client_in_key.len);
m = ngx_hex_dump(buf, qc->client_in_iv.data, qc->client_in_iv.len)
- buf;
ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0,
"quic iv: %*s, len: %uz", m, buf, qc->client_in_iv.len);
m = ngx_hex_dump(buf, qc->client_in_hp.data, qc->client_in_hp.len)
- buf;
ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0,
"quic hp: %*s, len: %uz", m, buf, qc->client_in_hp.len);
}
#endif
// header protection
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
uint8_t mask[16];
int outlen;
if (EVP_EncryptInit_ex(ctx, EVP_aes_128_ecb(), NULL,
qc->client_in_hp.data, NULL)
!= 1)
{
EVP_CIPHER_CTX_free(ctx);
ngx_ssl_error(NGX_LOG_INFO, rev->log, 0,
"EVP_EncryptInit_ex() failed");
ngx_http_close_connection(c);
return;
}
if (!EVP_EncryptUpdate(ctx, mask, &outlen, sample, 16)) {
EVP_CIPHER_CTX_free(ctx);
ngx_ssl_error(NGX_LOG_INFO, rev->log, 0,
"EVP_EncryptUpdate() failed");
ngx_http_close_connection(c);
return;
}
EVP_CIPHER_CTX_free(ctx);
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 ciphertext;
ciphertext.data = b->pos;
ciphertext.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
EVP_AEAD_CTX *aead = EVP_AEAD_CTX_new(EVP_aead_aes_128_gcm(),
qc->client_in_key.data,
qc->client_in_key.len,
EVP_AEAD_DEFAULT_TAG_LENGTH);
uint8_t cleartext[1600];
size_t cleartext_len = sizeof(cleartext);
if (EVP_AEAD_CTX_open(aead, cleartext, &cleartext_len, sizeof(cleartext),
nonce, qc->client_in_iv.len, ciphertext.data,
ciphertext.len, ad.data, ad.len)
!= 1)
{
EVP_AEAD_CTX_free(aead);
ngx_ssl_error(NGX_LOG_INFO, rev->log, 0,
"EVP_AEAD_CTX_open() failed");
ngx_http_close_connection(c);
return;
}
EVP_AEAD_CTX_free(aead);
#if (NGX_DEBUG)
if (c->log->log_level & NGX_LOG_DEBUG_EVENT) {
m = ngx_hex_dump(buf, cleartext, ngx_min(cleartext_len, 256)) - buf;
ngx_log_debug4(NGX_LOG_DEBUG_HTTP, rev->log, 0,
"quic packet: %*s%s, len: %uz",
m, buf, m < 512 ? "" : "...", cleartext_len);
}
#endif
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;
}
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),
&cleartext[4], cleartext_len - 4))
{
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));
ngx_http_close_connection(c);
return;
}
static void
ngx_http_ssl_handshake(ngx_event_t *rev)
{

View File

@ -323,6 +323,7 @@ typedef struct {
ngx_chain_t *free;
unsigned ssl:1;
unsigned quic:1;
unsigned proxy_protocol:1;
} ngx_http_connection_t;