Split frame and packet generation into separate steps.

While there, a number of QUIC constants from spec defined and magic numbers
were replaced.
This commit is contained in:
Vladimir Homutov 2020-03-03 13:30:30 +03:00
parent 820be1b846
commit d2deb6d6b4

View File

@ -74,6 +74,39 @@ do { \
ngx_quic_hexdump(log, fmt "%s", data, len, "") \ ngx_quic_hexdump(log, fmt "%s", data, len, "") \
/* 17.2. Long Header Packets */
#define NGX_QUIC_PKT_LONG 0x80
#define NGX_QUIC_PKT_INITIAL 0xc0
#define NGX_QUIC_PKT_HANDSHAKE 0xe0
/* 12.4. Frames and Frame Types */
#define NGX_QUIC_FT_PADDING 0x00
#define NGX_QUIC_FT_PING 0x01
#define NGX_QUIC_FT_ACK 0x02
#define NGX_QUIC_FT_ACK_ECN 0x03
#define NGX_QUIC_FT_RESET_STREAM 0x04
#define NGX_QUIC_FT_STOP_SENDING 0x05
#define NGX_QUIC_FT_CRYPTO 0x06
#define NGX_QUIC_FT_NEW_TOKEN 0x07
#define NGX_QUIC_FT_STREAM 0x08 // - 0x0f
#define NGX_QUIC_FT_MAX_DATA 0x10
#define NGX_QUIC_FT_MAX_STREAM_DATA 0x11
#define NGX_QUIC_FT_MAX_STREAMS 0x12
#define NGX_QUIC_FT_MAX_STREAMS2 0x13 // XXX
#define NGX_QUIC_FT_DATA_BLOCKED 0x14
#define NGX_QUIC_FT_STREAM_DATA_BLOCKED 0x15
#define NGX_QUIC_FT_STREAMS_BLOCKED 0x16
#define NGX_QUIC_FT_STREAMS_BLOCKED2 0x17 // XXX
#define NGX_QUIC_FT_NEW_CONNECTION_ID 0x18
#define NGX_QUIC_FT_RETIRE_CONNECTION_ID 0x19
#define NGX_QUIC_FT_PATH_CHALLENGE 0x1a
#define NGX_QUIC_FT_PATH_RESPONSE 0x1b
#define NGX_QUIC_FT_CONNECTION_CLOSE 0x1c
#define NGX_QUIC_FT_CONNECTION_CLOSE2 0x1d // XXX
#define NGX_QUIC_FT_HANDSHAKE_DONE 0x1e
/* TODO: real states, these are stubs */ /* TODO: real states, these are stubs */
typedef enum { typedef enum {
@ -102,6 +135,11 @@ struct ngx_quic_connection_s {
ngx_str_t dcid; ngx_str_t dcid;
ngx_str_t token; ngx_str_t token;
/* current packet numbers for each namespace */
ngx_uint_t initial_pn;
ngx_uint_t handshake_pn;
ngx_uint_t appdata_pn;
ngx_quic_secret_t client_in; ngx_quic_secret_t client_in;
ngx_quic_secret_t client_hs; ngx_quic_secret_t client_hs;
ngx_quic_secret_t client_ad; ngx_quic_secret_t client_ad;
@ -111,6 +149,19 @@ struct ngx_quic_connection_s {
}; };
typedef enum ssl_encryption_level_t ngx_quic_level_t;
typedef struct {
ngx_quic_secret_t *secret;
ngx_uint_t type;
ngx_uint_t *number;
ngx_uint_t flags;
ngx_uint_t version;
ngx_str_t *token;
ngx_quic_level_t level;
} ngx_quic_header_t;
static ngx_int_t ngx_quic_new_connection(ngx_connection_t *c, ngx_ssl_t *ssl, static ngx_int_t ngx_quic_new_connection(ngx_connection_t *c, ngx_ssl_t *ssl,
ngx_buf_t *b); ngx_buf_t *b);
static ngx_int_t ngx_quic_handshake_input(ngx_connection_t *c, ngx_buf_t *b); static ngx_int_t ngx_quic_handshake_input(ngx_connection_t *c, ngx_buf_t *b);
@ -120,6 +171,9 @@ static int ngx_quic_set_encryption_secrets(ngx_ssl_conn_t *ssl_conn,
const uint8_t *write_secret, size_t secret_len); const uint8_t *write_secret, size_t secret_len);
static int ngx_quic_add_handshake_data(ngx_ssl_conn_t *ssl_conn, 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); enum ssl_encryption_level_t level, const uint8_t *data, size_t len);
static ngx_int_t ngx_quic_create_long_packet(ngx_connection_t *c,
ngx_ssl_conn_t *ssl_conn, ngx_quic_header_t *pkt, ngx_str_t *in,
ngx_str_t *res);
static int ngx_quic_flush_flight(ngx_ssl_conn_t *ssl_conn); static int ngx_quic_flush_flight(ngx_ssl_conn_t *ssl_conn);
static int ngx_quic_send_alert(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); enum ssl_encryption_level_t level, uint8_t alert);
@ -288,98 +342,50 @@ ngx_quic_set_encryption_secrets(ngx_ssl_conn_t *ssl_conn,
} }
static int static ngx_int_t
ngx_quic_add_handshake_data(ngx_ssl_conn_t *ssl_conn, ngx_quic_create_long_packet(ngx_connection_t *c, ngx_ssl_conn_t *ssl_conn,
enum ssl_encryption_level_t level, const uint8_t *data, size_t len) ngx_quic_header_t *pkt, ngx_str_t *payload, ngx_str_t *res)
{ {
u_char *p, *pnp, *name, *nonce, *sample; u_char *p, *pnp, *name, *nonce, *sample, *packet;
ngx_str_t in, out, ad; ngx_str_t ad, out;
static int pn; const EVP_CIPHER *cipher;
const EVP_CIPHER *cipher; ngx_quic_connection_t *qc;
ngx_connection_t *c;
ngx_quic_secret_t *secret; u_char mask[16];
ngx_quic_connection_t *qc;
u_char mask[16];
c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);
qc = c->quic; qc = c->quic;
//ngx_ssl_handshake_log(c); // TODO: enable again out.len = payload->len + EVP_GCM_TLS_TAG_LEN;
switch (level) {
case ssl_encryption_initial:
secret = &qc->server_in;
break;
case ssl_encryption_handshake:
secret = &qc->server_hs;
break;
default:
return 0;
}
ngx_quic_hexdump(c->log, "level:%d read", data, len, 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); ad.data = ngx_alloc(346 /*max header*/, c->log);
if (ad.data == 0) { if (ad.data == 0) {
return 0; return NGX_ERROR;
} }
p = ad.data; p = ad.data;
if (level == ssl_encryption_initial) {
*p++ = 0xc0; // initial, packet number len *p++ = pkt->flags;
} else if (level == ssl_encryption_handshake) {
*p++ = 0xe0; // handshake, packet number len
}
p = ngx_quic_write_uint32(p, quic_version); p = ngx_quic_write_uint32(p, quic_version);
*p++ = qc->scid.len; *p++ = qc->scid.len;
p = ngx_cpymem(p, qc->scid.data, qc->scid.len); p = ngx_cpymem(p, qc->scid.data, qc->scid.len);
*p++ = qc->dcid.len; *p++ = qc->dcid.len;
p = ngx_cpymem(p, qc->dcid.data, 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 if (pkt->token) { // if pkt->flags & initial ?
ngx_quic_build_int(&p, pkt->token->len);
} }
ngx_quic_build_int(&p, out.len + 1); // length (inc. pnl) ngx_quic_build_int(&p, out.len + 1); // length (inc. pnl)
pnp = p; pnp = p;
if (level == ssl_encryption_initial) { *p++ = (*pkt->number)++;
*p++ = 0; // packet number 0
} else if (level == ssl_encryption_handshake) {
*p++ = pn++;
}
ad.len = p - ad.data; ad.len = p - ad.data;
ngx_quic_hexdump0(c->log, "ad", data, len); ngx_quic_hexdump0(c->log, "ad", ad.data, ad.len);
name = (u_char *) SSL_get_cipher(ssl_conn); name = (u_char *) SSL_get_cipher(ssl_conn);
@ -392,49 +398,134 @@ ngx_quic_add_handshake_data(ngx_ssl_conn_t *ssl_conn,
cipher = EVP_aes_256_gcm(); cipher = EVP_aes_256_gcm();
} else { } else {
return 0; return NGX_ERROR;
} }
nonce = ngx_pstrdup(c->pool, &secret->iv); nonce = ngx_pstrdup(c->pool, &pkt->secret->iv);
if (level == ssl_encryption_handshake) { if (pkt->level == ssl_encryption_handshake) {
nonce[11] ^= (pn - 1); nonce[11] ^= (*pkt->number - 1);
} }
ngx_quic_hexdump0(c->log, "server_iv", secret->iv.data, 12); ngx_quic_hexdump0(c->log, "server_iv", pkt->secret->iv.data, 12);
ngx_quic_hexdump(c->log, "sample: n=%d nonce", nonce, 12, pn - 1); ngx_quic_hexdump0(c->log, "nonce", nonce, 12);
if (ngx_quic_tls_seal(c, cipher, secret, &out, nonce, &in, &ad) != NGX_OK) if (ngx_quic_tls_seal(c, cipher, pkt->secret, &out, nonce, payload, &ad) != NGX_OK) {
{ return NGX_ERROR;
return 0;
} }
sample = &out.data[3]; // pnl=0 sample = &out.data[3]; // pnl=0
if (ngx_quic_tls_hp(c, EVP_aes_128_ecb(), secret, mask, sample) != NGX_OK) { if (ngx_quic_tls_hp(c, EVP_aes_128_ecb(), pkt->secret, mask, sample) != NGX_OK) {
return 0; return NGX_ERROR;
} }
ngx_quic_hexdump0(c->log, "sample", sample, 16); ngx_quic_hexdump0(c->log, "sample", sample, 16);
ngx_quic_hexdump0(c->log, "mask", mask, 16); ngx_quic_hexdump0(c->log, "mask", mask, 16);
ngx_quic_hexdump0(c->log, "hp_key", secret->hp.data, 16); ngx_quic_hexdump0(c->log, "hp_key", pkt->secret->hp.data, 16);
// header protection, pnl = 0 // header protection, pnl = 0
ad.data[0] ^= mask[0] & 0x0f; ad.data[0] ^= mask[0] & 0x0f;
*pnp ^= mask[1]; *pnp ^= mask[1];
u_char *packet = ngx_alloc(ad.len + out.len, c->log); packet = ngx_alloc(ad.len + out.len, c->log);
if (packet == 0) { if (packet == 0) {
return 0; return NGX_ERROR;
} }
p = ngx_cpymem(packet, ad.data, ad.len); p = ngx_cpymem(packet, ad.data, ad.len);
p = ngx_cpymem(p, out.data, out.len); p = ngx_cpymem(p, out.data, out.len);
ngx_quic_hexdump0(c->log, "packet", packet, p - packet); res->data = packet;
res->len = p - packet;
return NGX_OK;
}
static void
ngx_quic_create_ack(u_char **p)
{
ngx_quic_build_int(p, NGX_QUIC_FT_ACK);
ngx_quic_build_int(p, 0);
ngx_quic_build_int(p, 0);
ngx_quic_build_int(p, 0);
ngx_quic_build_int(p, 0);
}
static void
ngx_quic_create_crypto(u_char **p, u_char *data, size_t len)
{
ngx_quic_build_int(p, NGX_QUIC_FT_CRYPTO);
ngx_quic_build_int(p, 0);
ngx_quic_build_int(p, len);
*p = ngx_cpymem(*p, data, 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)
{
u_char *p;
ngx_str_t payload, res;
ngx_connection_t *c;
ngx_quic_header_t pkt;
ngx_quic_connection_t *qc;
ngx_str_t initial_token = ngx_null_string;
c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);
qc = c->quic;
//ngx_ssl_handshake_log(c); // TODO: enable again
ngx_memzero(&pkt, sizeof(ngx_quic_header_t));
pkt.level = level;
payload.data = ngx_alloc(4 + len + 5 /*minimal ACK*/, c->log);
if (payload.data == 0) {
return 0;
}
p = payload.data;
ngx_quic_create_crypto(&p, (u_char *) data, len);
if (level == ssl_encryption_initial) {
ngx_quic_create_ack(&p);
pkt.number = &qc->initial_pn;
pkt.flags = NGX_QUIC_PKT_INITIAL;
pkt.secret = &qc->server_in;
pkt.token = &initial_token;
} else if (level == ssl_encryption_handshake) {
pkt.number = &qc->handshake_pn;
pkt.flags = NGX_QUIC_PKT_HANDSHAKE;
pkt.secret = &qc->server_hs;
pkt.token = NULL;
} else {
pkt.number = &qc->appdata_pn;
}
payload.len = p - payload.data;
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
"ngx_quic_add_handshake_data: clear_len:%uz",
payload.len);
if (ngx_quic_create_long_packet(c, ssl_conn, &pkt, &payload, &res)
!= NGX_OK)
{
return 0;
}
// TODO: save state of data to send into qc (push into queue) // TODO: save state of data to send into qc (push into queue)
qc->out = res;
qc->out.data = packet;
qc->out.len = p - packet;
if (ngx_quic_output(c) != NGX_OK) { if (ngx_quic_output(c) != NGX_OK) {
return 0; return 0;
@ -479,7 +570,7 @@ ngx_quic_new_connection(ngx_connection_t *c, ngx_ssl_t *ssl, ngx_buf_t *b)
int n, sslerr; int n, sslerr;
ngx_quic_connection_t *qc; ngx_quic_connection_t *qc;
if ((b->pos[0] & 0xf0) != 0xc0) { if ((b->pos[0] & 0xf0) != NGX_QUIC_PKT_INITIAL) {
ngx_log_error(NGX_LOG_INFO, c->log, 0, "invalid initial packet"); ngx_log_error(NGX_LOG_INFO, c->log, 0, "invalid initial packet");
return NGX_ERROR; return NGX_ERROR;
} }
@ -807,7 +898,7 @@ ngx_quic_handshake_input(ngx_connection_t *c, ngx_buf_t *bb)
ngx_quic_hexdump0(c->log, "input", buf, n); ngx_quic_hexdump0(c->log, "input", buf, n);
if ((p[0] & 0xf0) != 0xe0) { if ((p[0] & 0xf0) != NGX_QUIC_PKT_HANDSHAKE) {
ngx_log_error(NGX_LOG_INFO, c->log, 0, "invalid packet type"); ngx_log_error(NGX_LOG_INFO, c->log, 0, "invalid packet type");
return NGX_ERROR; return NGX_ERROR;
} }