mirror of
https://github.com/nginx/nginx.git
synced 2024-12-13 11:09:06 +08:00
QUIC handshake final bits.
Added handling of client Finished, both feeding and acknowledgement. This includes sending NST in 1-RTT triggered by a handshake process.
This commit is contained in:
parent
d2deb6d6b4
commit
3c17072122
@ -174,6 +174,9 @@ static int ngx_quic_add_handshake_data(ngx_ssl_conn_t *ssl_conn,
|
||||
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 ngx_int_t ngx_quic_create_short_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_send_alert(ngx_ssl_conn_t *ssl_conn,
|
||||
enum ssl_encryption_level_t level, uint8_t alert);
|
||||
@ -441,14 +444,110 @@ ngx_quic_create_long_packet(ngx_connection_t *c, ngx_ssl_conn_t *ssl_conn,
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_quic_create_short_packet(ngx_connection_t *c, ngx_ssl_conn_t *ssl_conn,
|
||||
ngx_quic_header_t *pkt, ngx_str_t *payload, ngx_str_t *res)
|
||||
{
|
||||
u_char *p, *pnp, *name, *nonce, *sample, *packet;
|
||||
ngx_str_t ad, out;
|
||||
const EVP_CIPHER *cipher;
|
||||
ngx_quic_connection_t *qc;
|
||||
|
||||
u_char mask[16];
|
||||
|
||||
qc = c->quic;
|
||||
|
||||
out.len = payload->len + EVP_GCM_TLS_TAG_LEN;
|
||||
|
||||
ad.data = ngx_alloc(25 /*max header*/, c->log);
|
||||
if (ad.data == 0) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
p = ad.data;
|
||||
|
||||
*p++ = 0x40;
|
||||
|
||||
p = ngx_cpymem(p, qc->scid.data, qc->scid.len);
|
||||
|
||||
pnp = p;
|
||||
|
||||
*p++ = (*pkt->number)++;
|
||||
|
||||
ad.len = p - ad.data;
|
||||
|
||||
ngx_quic_hexdump0(c->log, "ad", ad.data, 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 NGX_ERROR;
|
||||
}
|
||||
|
||||
nonce = ngx_pstrdup(c->pool, &pkt->secret->iv);
|
||||
if (pkt->level == ssl_encryption_handshake) {
|
||||
nonce[11] ^= (*pkt->number - 1);
|
||||
}
|
||||
|
||||
ngx_quic_hexdump0(c->log, "server_iv", pkt->secret->iv.data, 12);
|
||||
ngx_quic_hexdump0(c->log, "nonce", nonce, 12);
|
||||
|
||||
if (ngx_quic_tls_seal(c, cipher, pkt->secret, &out, nonce, payload, &ad)
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_quic_hexdump0(c->log, "out", out.data, out.len);
|
||||
|
||||
sample = &out.data[3]; // pnl=0
|
||||
if (ngx_quic_tls_hp(c, EVP_aes_128_ecb(), pkt->secret, mask, sample)
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_quic_hexdump0(c->log, "sample", sample, 16);
|
||||
ngx_quic_hexdump0(c->log, "mask", mask, 16);
|
||||
ngx_quic_hexdump0(c->log, "hp_key", pkt->secret->hp.data, 16);
|
||||
|
||||
// header protection, pnl = 0
|
||||
ad.data[0] ^= mask[0] & 0x1f;
|
||||
*pnp ^= mask[1];
|
||||
|
||||
packet = ngx_alloc(ad.len + out.len, c->log);
|
||||
if (packet == 0) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
p = ngx_cpymem(packet, ad.data, ad.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_create_ack(u_char **p, uint64_t num)
|
||||
{
|
||||
ngx_quic_build_int(p, NGX_QUIC_FT_ACK);
|
||||
ngx_quic_build_int(p, num);
|
||||
ngx_quic_build_int(p, 0);
|
||||
ngx_quic_build_int(p, 0);
|
||||
ngx_quic_build_int(p, 0);
|
||||
ngx_quic_build_int(p, 0);
|
||||
ngx_quic_build_int(p, num);
|
||||
}
|
||||
|
||||
|
||||
@ -493,7 +592,7 @@ ngx_quic_add_handshake_data(ngx_ssl_conn_t *ssl_conn,
|
||||
ngx_quic_create_crypto(&p, (u_char *) data, len);
|
||||
|
||||
if (level == ssl_encryption_initial) {
|
||||
ngx_quic_create_ack(&p);
|
||||
ngx_quic_create_ack(&p, 0);
|
||||
|
||||
pkt.number = &qc->initial_pn;
|
||||
pkt.flags = NGX_QUIC_PKT_INITIAL;
|
||||
@ -510,6 +609,7 @@ ngx_quic_add_handshake_data(ngx_ssl_conn_t *ssl_conn,
|
||||
|
||||
} else {
|
||||
pkt.number = &qc->appdata_pn;
|
||||
pkt.secret = &qc->server_ad;
|
||||
}
|
||||
|
||||
payload.len = p - payload.data;
|
||||
@ -518,10 +618,21 @@ ngx_quic_add_handshake_data(ngx_ssl_conn_t *ssl_conn,
|
||||
"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;
|
||||
if (level == ssl_encryption_application) {
|
||||
|
||||
if (ngx_quic_create_short_packet(c, ssl_conn, &pkt, &payload, &res)
|
||||
!= NGX_OK)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
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)
|
||||
@ -884,11 +995,12 @@ 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 *bb)
|
||||
{
|
||||
int sslerr;
|
||||
ssize_t n;
|
||||
ngx_str_t out;
|
||||
const EVP_CIPHER *cipher;
|
||||
ngx_quic_connection_t *qc;
|
||||
u_char *p, *b;
|
||||
u_char *p, *b;
|
||||
|
||||
qc = c->quic;
|
||||
|
||||
@ -1020,6 +1132,92 @@ ngx_quic_handshake_input(ngx_connection_t *c, ngx_buf_t *bb)
|
||||
|
||||
ngx_quic_hexdump0(c->log, "packet payload", out.data, out.len);
|
||||
|
||||
if (out.data[0] != 0x06) {
|
||||
ngx_log_error(NGX_LOG_INFO, c->log, 0,
|
||||
"non-CRYPTO frame in HS packet, skipping");
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
if (out.data[1] != 0x00) {
|
||||
ngx_log_error(NGX_LOG_INFO, c->log, 0,
|
||||
"not yet supported 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 Handshake packet CRYPTO length: %uL pp:%p:%p",
|
||||
crypto_len, out.data, crypto);
|
||||
|
||||
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));
|
||||
|
||||
// ACK Client Finished
|
||||
|
||||
ngx_str_t payload, res;
|
||||
ngx_quic_header_t pkt;
|
||||
ngx_memzero(&pkt, sizeof(ngx_quic_header_t));
|
||||
|
||||
pkt.level = ssl_encryption_handshake;
|
||||
pkt.number = &qc->handshake_pn;
|
||||
pkt.flags = NGX_QUIC_PKT_HANDSHAKE;
|
||||
pkt.secret = &qc->server_hs;
|
||||
|
||||
payload.data = ngx_alloc(5 /*minimal ACK*/, c->log);
|
||||
if (payload.data == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
p = payload.data;
|
||||
ngx_quic_create_ack(&p, pn);
|
||||
|
||||
payload.len = p - payload.data;
|
||||
|
||||
if (ngx_quic_create_long_packet(c, c->ssl->connection, &pkt, &payload, &res)
|
||||
!= NGX_OK)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
qc->out = res;
|
||||
|
||||
if (ngx_quic_output(c) != NGX_OK) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user