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.
This commit is contained in:
Vladimir Homutov 2020-02-28 16:23:25 +03:00
parent 8993721298
commit b20ed8f7f1
4 changed files with 1049 additions and 965 deletions

View File

@ -90,328 +90,6 @@ 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)
{
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 *ssl, ngx_uint_t enable)
#if NGX_OPENSSL_QUIC
SSL_CTX_set_quic_method(ssl->ctx, &quic_method);
ngx_quic_init_ssl_methods(ssl->ctx);
return NGX_OK;
#else

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
/*
*
* Copyright (C) Nginx, Inc.
*/
@ -10,95 +10,11 @@
#include <ngx_event_openssl.h>
#define quic_version 0xff000018
/* 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);
void ngx_quic_init_ssl_methods(SSL_CTX* ctx);
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_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;
};
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_ */

View File

@ -661,402 +661,22 @@ ngx_http_alloc_request(ngx_connection_t *c)
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)
{
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)
{
if (ngx_quic_input(c, &sscf->ssl, c->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_event_t *rev)
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_event_t *rev)
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);
b.last += n;
if ((p[0] & 0xf0) != 0xe0) {
ngx_log_error(NGX_LOG_INFO, rev->log, 0, "invalid packet type");
if (ngx_quic_input(c, NULL, &b) != NGX_OK) {
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)
{
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
}