mirror of
https://github.com/nginx/nginx.git
synced 2025-06-22 22:10:45 +08:00
TLS Key Update in QUIC.
Old keys retention is yet to be implemented.
This commit is contained in:
parent
3b77fb705b
commit
1381982aaa
@ -68,6 +68,7 @@ struct ngx_quic_connection_s {
|
|||||||
|
|
||||||
ngx_quic_namespace_t ns[NGX_QUIC_NAMESPACE_LAST];
|
ngx_quic_namespace_t ns[NGX_QUIC_NAMESPACE_LAST];
|
||||||
ngx_quic_secrets_t keys[NGX_QUIC_ENCRYPTION_LAST];
|
ngx_quic_secrets_t keys[NGX_QUIC_ENCRYPTION_LAST];
|
||||||
|
ngx_quic_secrets_t next_key;
|
||||||
uint64_t crypto_offset[NGX_QUIC_ENCRYPTION_LAST];
|
uint64_t crypto_offset[NGX_QUIC_ENCRYPTION_LAST];
|
||||||
|
|
||||||
ngx_ssl_t *ssl;
|
ngx_ssl_t *ssl;
|
||||||
@ -88,6 +89,7 @@ struct ngx_quic_connection_s {
|
|||||||
|
|
||||||
unsigned send_timer_set:1;
|
unsigned send_timer_set:1;
|
||||||
unsigned closing:1;
|
unsigned closing:1;
|
||||||
|
unsigned key_phase:1;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -979,7 +981,8 @@ ngx_quic_early_input(ngx_connection_t *c, ngx_quic_header_t *pkt)
|
|||||||
static ngx_int_t
|
static ngx_int_t
|
||||||
ngx_quic_app_input(ngx_connection_t *c, ngx_quic_header_t *pkt)
|
ngx_quic_app_input(ngx_connection_t *c, ngx_quic_header_t *pkt)
|
||||||
{
|
{
|
||||||
ngx_quic_secrets_t *keys;
|
ngx_int_t rc;
|
||||||
|
ngx_quic_secrets_t *keys, *next, tmp;
|
||||||
ngx_quic_connection_t *qc;
|
ngx_quic_connection_t *qc;
|
||||||
static u_char buf[NGX_QUIC_DEFAULT_MAX_PACKET_SIZE];
|
static u_char buf[NGX_QUIC_DEFAULT_MAX_PACKET_SIZE];
|
||||||
|
|
||||||
@ -988,6 +991,7 @@ ngx_quic_app_input(ngx_connection_t *c, ngx_quic_header_t *pkt)
|
|||||||
qc = c->quic;
|
qc = c->quic;
|
||||||
|
|
||||||
keys = &c->quic->keys[ssl_encryption_application];
|
keys = &c->quic->keys[ssl_encryption_application];
|
||||||
|
next = &c->quic->next_key;
|
||||||
|
|
||||||
if (keys->client.key.len == 0) {
|
if (keys->client.key.len == 0) {
|
||||||
ngx_log_error(NGX_LOG_INFO, c->log, 0,
|
ngx_log_error(NGX_LOG_INFO, c->log, 0,
|
||||||
@ -1000,6 +1004,8 @@ ngx_quic_app_input(ngx_connection_t *c, ngx_quic_header_t *pkt)
|
|||||||
}
|
}
|
||||||
|
|
||||||
pkt->secret = &keys->client;
|
pkt->secret = &keys->client;
|
||||||
|
pkt->next = &next->client;
|
||||||
|
pkt->key_phase = c->quic->key_phase;
|
||||||
pkt->level = ssl_encryption_application;
|
pkt->level = ssl_encryption_application;
|
||||||
pkt->plaintext = buf;
|
pkt->plaintext = buf;
|
||||||
|
|
||||||
@ -1007,7 +1013,31 @@ ngx_quic_app_input(ngx_connection_t *c, ngx_quic_header_t *pkt)
|
|||||||
return NGX_ERROR;
|
return NGX_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ngx_quic_payload_handler(c, pkt);
|
/* switch keys on Key Phase change */
|
||||||
|
|
||||||
|
if (pkt->key_update) {
|
||||||
|
c->quic->key_phase ^= 1;
|
||||||
|
|
||||||
|
tmp = *keys;
|
||||||
|
*keys = *next;
|
||||||
|
*next = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = ngx_quic_payload_handler(c, pkt);
|
||||||
|
|
||||||
|
if (rc == NGX_ERROR) {
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* generate next keys */
|
||||||
|
|
||||||
|
if (pkt->key_update) {
|
||||||
|
if (ngx_quic_key_update(c, keys, next) != NGX_OK) {
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1330,6 +1360,18 @@ ngx_quic_handle_crypto_frame(ngx_connection_t *c, ngx_quic_header_t *pkt,
|
|||||||
ngx_quic_queue_frame(c->quic, frame);
|
ngx_quic_queue_frame(c->quic, frame);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Generating next keys before a key update is received.
|
||||||
|
* See quic-tls 9.4 Header Protection Timing Side-Channels.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (ngx_quic_key_update(c, &c->quic->keys[ssl_encryption_application],
|
||||||
|
&c->quic->next_key)
|
||||||
|
!= NGX_OK)
|
||||||
|
{
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||||
@ -1756,7 +1798,8 @@ ngx_quic_send_frames(ngx_connection_t *c, ngx_queue_t *frames)
|
|||||||
pkt.flags = NGX_QUIC_PKT_HANDSHAKE;
|
pkt.flags = NGX_QUIC_PKT_HANDSHAKE;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
pkt.flags = 0x40; // TODO: macro, set FIXED bit
|
// TODO: macro, set FIXED bit
|
||||||
|
pkt.flags = 0x40 | (c->quic->key_phase ? NGX_QUIC_PKT_KPHASE : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
ngx_quic_set_packet_number(&pkt, ns);
|
ngx_quic_set_packet_number(&pkt, ns);
|
||||||
|
@ -232,10 +232,12 @@ ngx_quic_hkdf_expand(ngx_pool_t *pool, const EVP_MD *digest, ngx_str_t *out,
|
|||||||
uint8_t *p;
|
uint8_t *p;
|
||||||
uint8_t info[20];
|
uint8_t info[20];
|
||||||
|
|
||||||
|
if (out->data == NULL) {
|
||||||
out->data = ngx_pnalloc(pool, out->len);
|
out->data = ngx_pnalloc(pool, out->len);
|
||||||
if (out->data == NULL) {
|
if (out->data == NULL) {
|
||||||
return NGX_ERROR;
|
return NGX_ERROR;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
info_len = 2 + 1 + label->len + 1;
|
info_len = 2 + 1 + label->len + 1;
|
||||||
|
|
||||||
@ -622,6 +624,14 @@ ngx_quic_set_encryption_secret(ngx_pool_t *pool, ngx_ssl_conn_t *ssl_conn,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
peer_secret->secret.data = ngx_pnalloc(pool, secret_len);
|
||||||
|
if (peer_secret->secret.data == NULL) {
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
peer_secret->secret.len = secret_len;
|
||||||
|
ngx_memcpy(peer_secret->secret.data, secret, secret_len);
|
||||||
|
|
||||||
peer_secret->key.len = key_len;
|
peer_secret->key.len = key_len;
|
||||||
peer_secret->iv.len = NGX_QUIC_IV_LEN;
|
peer_secret->iv.len = NGX_QUIC_IV_LEN;
|
||||||
peer_secret->hp.len = key_len;
|
peer_secret->hp.len = key_len;
|
||||||
@ -650,6 +660,83 @@ ngx_quic_set_encryption_secret(ngx_pool_t *pool, ngx_ssl_conn_t *ssl_conn,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ngx_int_t
|
||||||
|
ngx_quic_key_update(ngx_connection_t *c, ngx_quic_secrets_t *current,
|
||||||
|
ngx_quic_secrets_t *next)
|
||||||
|
{
|
||||||
|
ngx_uint_t i;
|
||||||
|
ngx_quic_ciphers_t ciphers;
|
||||||
|
|
||||||
|
ngx_log_debug(NGX_LOG_DEBUG_EVENT, c->log, 0, "quic key update");
|
||||||
|
|
||||||
|
if (ngx_quic_ciphers(c->ssl->connection, &ciphers,
|
||||||
|
ssl_encryption_application)
|
||||||
|
== NGX_ERROR)
|
||||||
|
{
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
next->client.secret.len = current->client.secret.len;
|
||||||
|
next->client.key.len = current->client.key.len;
|
||||||
|
next->client.iv.len = current->client.iv.len;
|
||||||
|
next->client.hp = current->client.hp;
|
||||||
|
|
||||||
|
next->server.secret.len = current->server.secret.len;
|
||||||
|
next->server.key.len = current->server.key.len;
|
||||||
|
next->server.iv.len = current->server.iv.len;
|
||||||
|
next->server.hp = current->server.hp;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
ngx_str_t label;
|
||||||
|
ngx_str_t *key;
|
||||||
|
ngx_str_t *secret;
|
||||||
|
} seq[] = {
|
||||||
|
{
|
||||||
|
ngx_string("tls13 quic ku"),
|
||||||
|
&next->client.secret,
|
||||||
|
¤t->client.secret,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ngx_string("tls13 quic key"),
|
||||||
|
&next->client.key,
|
||||||
|
&next->client.secret,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ngx_string("tls13 quic iv"),
|
||||||
|
&next->client.iv,
|
||||||
|
&next->client.secret,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ngx_string("tls13 quic ku"),
|
||||||
|
&next->server.secret,
|
||||||
|
¤t->server.secret,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ngx_string("tls13 quic key"),
|
||||||
|
&next->server.key,
|
||||||
|
&next->server.secret,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ngx_string("tls13 quic iv"),
|
||||||
|
&next->server.iv,
|
||||||
|
&next->server.secret,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
for (i = 0; i < (sizeof(seq) / sizeof(seq[0])); i++) {
|
||||||
|
|
||||||
|
if (ngx_quic_hkdf_expand(c->pool, ciphers.d, seq[i].key, &seq[i].label,
|
||||||
|
seq[i].secret->data, seq[i].secret->len)
|
||||||
|
!= NGX_OK)
|
||||||
|
{
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static ssize_t
|
static ssize_t
|
||||||
ngx_quic_create_long_packet(ngx_quic_header_t *pkt, ngx_ssl_conn_t *ssl_conn,
|
ngx_quic_create_long_packet(ngx_quic_header_t *pkt, ngx_ssl_conn_t *ssl_conn,
|
||||||
ngx_str_t *res)
|
ngx_str_t *res)
|
||||||
@ -821,8 +908,9 @@ ngx_quic_decrypt(ngx_quic_header_t *pkt, ngx_ssl_conn_t *ssl_conn)
|
|||||||
{
|
{
|
||||||
u_char clearflags, *p, *sample;
|
u_char clearflags, *p, *sample;
|
||||||
uint64_t pn;
|
uint64_t pn;
|
||||||
ngx_int_t pnl, rc;
|
ngx_int_t pnl, rc, key_phase;
|
||||||
ngx_str_t in, ad;
|
ngx_str_t in, ad;
|
||||||
|
ngx_quic_secret_t *secret;
|
||||||
ngx_quic_ciphers_t ciphers;
|
ngx_quic_ciphers_t ciphers;
|
||||||
uint8_t mask[16], nonce[12];
|
uint8_t mask[16], nonce[12];
|
||||||
|
|
||||||
@ -830,6 +918,8 @@ ngx_quic_decrypt(ngx_quic_header_t *pkt, ngx_ssl_conn_t *ssl_conn)
|
|||||||
return NGX_ERROR;
|
return NGX_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
secret = pkt->secret;
|
||||||
|
|
||||||
p = pkt->raw->pos;
|
p = pkt->raw->pos;
|
||||||
|
|
||||||
/* draft-ietf-quic-tls-23#section-5.4.2:
|
/* draft-ietf-quic-tls-23#section-5.4.2:
|
||||||
@ -844,7 +934,7 @@ ngx_quic_decrypt(ngx_quic_header_t *pkt, ngx_ssl_conn_t *ssl_conn)
|
|||||||
|
|
||||||
/* header protection */
|
/* header protection */
|
||||||
|
|
||||||
if (ngx_quic_tls_hp(pkt->log, ciphers.hp, pkt->secret, mask, sample)
|
if (ngx_quic_tls_hp(pkt->log, ciphers.hp, secret, mask, sample)
|
||||||
!= NGX_OK)
|
!= NGX_OK)
|
||||||
{
|
{
|
||||||
return NGX_ERROR;
|
return NGX_ERROR;
|
||||||
@ -855,6 +945,12 @@ ngx_quic_decrypt(ngx_quic_header_t *pkt, ngx_ssl_conn_t *ssl_conn)
|
|||||||
|
|
||||||
} else {
|
} else {
|
||||||
clearflags = pkt->flags ^ (mask[0] & 0x1f);
|
clearflags = pkt->flags ^ (mask[0] & 0x1f);
|
||||||
|
key_phase = (clearflags & NGX_QUIC_PKT_KPHASE) != 0;
|
||||||
|
|
||||||
|
if (key_phase != pkt->key_phase) {
|
||||||
|
secret = pkt->next;
|
||||||
|
pkt->key_update = 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pnl = (clearflags & 0x03) + 1;
|
pnl = (clearflags & 0x03) + 1;
|
||||||
@ -889,7 +985,7 @@ ngx_quic_decrypt(ngx_quic_header_t *pkt, ngx_ssl_conn_t *ssl_conn)
|
|||||||
ad.data[ad.len - pnl] = pn >> (8 * (pnl - 1)) % 256;
|
ad.data[ad.len - pnl] = pn >> (8 * (pnl - 1)) % 256;
|
||||||
} while (--pnl);
|
} while (--pnl);
|
||||||
|
|
||||||
ngx_memcpy(nonce, pkt->secret->iv.data, pkt->secret->iv.len);
|
ngx_memcpy(nonce, secret->iv.data, secret->iv.len);
|
||||||
ngx_quic_compute_nonce(nonce, sizeof(nonce), pn);
|
ngx_quic_compute_nonce(nonce, sizeof(nonce), pn);
|
||||||
|
|
||||||
ngx_quic_hexdump0(pkt->log, "nonce", nonce, 12);
|
ngx_quic_hexdump0(pkt->log, "nonce", nonce, 12);
|
||||||
@ -903,7 +999,7 @@ ngx_quic_decrypt(ngx_quic_header_t *pkt, ngx_ssl_conn_t *ssl_conn)
|
|||||||
|
|
||||||
pkt->payload.data = pkt->plaintext + ad.len;
|
pkt->payload.data = pkt->plaintext + ad.len;
|
||||||
|
|
||||||
rc = ngx_quic_tls_open(ciphers.c, pkt->secret, &pkt->payload,
|
rc = ngx_quic_tls_open(ciphers.c, secret, &pkt->payload,
|
||||||
nonce, &in, &ad, pkt->log);
|
nonce, &in, &ad, pkt->log);
|
||||||
|
|
||||||
ngx_quic_hexdump0(pkt->log, "packet payload",
|
ngx_quic_hexdump0(pkt->log, "packet payload",
|
||||||
|
@ -33,6 +33,9 @@ int ngx_quic_set_encryption_secret(ngx_pool_t *pool, ngx_ssl_conn_t *ssl_conn,
|
|||||||
enum ssl_encryption_level_t level, const uint8_t *secret, size_t secret_len,
|
enum ssl_encryption_level_t level, const uint8_t *secret, size_t secret_len,
|
||||||
ngx_quic_secret_t *peer_secret);
|
ngx_quic_secret_t *peer_secret);
|
||||||
|
|
||||||
|
ngx_int_t ngx_quic_key_update(ngx_connection_t *c,
|
||||||
|
ngx_quic_secrets_t *current, ngx_quic_secrets_t *next);
|
||||||
|
|
||||||
ssize_t ngx_quic_encrypt(ngx_quic_header_t *pkt, ngx_ssl_conn_t *ssl_conn,
|
ssize_t ngx_quic_encrypt(ngx_quic_header_t *pkt, ngx_ssl_conn_t *ssl_conn,
|
||||||
ngx_str_t *res);
|
ngx_str_t *res);
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#define NGX_QUIC_PKT_ZRTT 0xD0 /* 17.2.3 */
|
#define NGX_QUIC_PKT_ZRTT 0xD0 /* 17.2.3 */
|
||||||
#define NGX_QUIC_PKT_HANDSHAKE 0xE0 /* 17.2.4 */
|
#define NGX_QUIC_PKT_HANDSHAKE 0xE0 /* 17.2.4 */
|
||||||
#define NGX_QUIC_PKT_RETRY 0xF0 /* 17.2.5 */
|
#define NGX_QUIC_PKT_RETRY 0xF0 /* 17.2.5 */
|
||||||
|
#define NGX_QUIC_PKT_KPHASE 0x04 /* 17.3 */
|
||||||
|
|
||||||
#define ngx_quic_pkt_in(flags) (((flags) & 0xF0) == NGX_QUIC_PKT_INITIAL)
|
#define ngx_quic_pkt_in(flags) (((flags) & 0xF0) == NGX_QUIC_PKT_INITIAL)
|
||||||
#define ngx_quic_pkt_zrtt(flags) (((flags) & 0xF0) == NGX_QUIC_PKT_ZRTT)
|
#define ngx_quic_pkt_zrtt(flags) (((flags) & 0xF0) == NGX_QUIC_PKT_ZRTT)
|
||||||
@ -237,6 +238,7 @@ typedef struct {
|
|||||||
ngx_log_t *log;
|
ngx_log_t *log;
|
||||||
|
|
||||||
struct ngx_quic_secret_s *secret;
|
struct ngx_quic_secret_s *secret;
|
||||||
|
struct ngx_quic_secret_s *next;
|
||||||
uint64_t number;
|
uint64_t number;
|
||||||
uint8_t num_len;
|
uint8_t num_len;
|
||||||
uint32_t trunc;
|
uint32_t trunc;
|
||||||
@ -258,8 +260,9 @@ typedef struct {
|
|||||||
u_char *plaintext;
|
u_char *plaintext;
|
||||||
ngx_str_t payload; /* decrypted data */
|
ngx_str_t payload; /* decrypted data */
|
||||||
|
|
||||||
ngx_uint_t need_ack;
|
unsigned need_ack:1;
|
||||||
/* unsigned need_ack:1; */
|
unsigned key_phase:1;
|
||||||
|
unsigned key_update:1;
|
||||||
} ngx_quic_header_t;
|
} ngx_quic_header_t;
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user