changeset 8339:aba84d9ab256 quic

Parsing of truncated packet numbers. For sample decoding algorithm, see quic-transport-27#appendix-A.
author Sergey Kandaurov <pluknet@nginx.com>
date Thu, 16 Apr 2020 12:46:48 +0300
parents 0f9e9786b90d
children a8fc0ab54cea
files src/event/ngx_event_quic.c src/event/ngx_event_quic_protection.c src/event/ngx_event_quic_protection.h
diffstat 3 files changed, 63 insertions(+), 18 deletions(-) [+]
line wrap: on
line diff
--- a/src/event/ngx_event_quic.c
+++ b/src/event/ngx_event_quic.c
@@ -65,8 +65,9 @@ typedef struct {
     ngx_quic_secret_t                 client_secret;
     ngx_quic_secret_t                 server_secret;
 
-    uint64_t                          pnum;
+    uint64_t                          pnum;        /* packet number to send     */
     uint64_t                          largest_ack; /* number received from peer */
+    uint64_t                          largest_pn;  /* number received from peer */
 
     ngx_queue_t                       frames;
     ngx_queue_t                       sent;
@@ -473,6 +474,7 @@ ngx_quic_new_connection(ngx_connection_t
     ngx_uint_t              i;
     ngx_quic_tp_t          *ctp;
     ngx_quic_secrets_t     *keys;
+    ngx_quic_send_ctx_t    *ctx;
     ngx_quic_connection_t  *qc;
     static u_char           buf[NGX_QUIC_DEFAULT_MAX_PACKET_SIZE];
 
@@ -510,6 +512,7 @@ ngx_quic_new_connection(ngx_connection_t
     for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) {
         ngx_queue_init(&qc->send_ctx[i].frames);
         ngx_queue_init(&qc->send_ctx[i].sent);
+        qc->send_ctx[i].largest_pn = (uint64_t) -1;
     }
 
     for (i = 0; i < NGX_QUIC_ENCRYPTION_LAST; i++) {
@@ -574,7 +577,9 @@ ngx_quic_new_connection(ngx_connection_t
     pkt->level = ssl_encryption_initial;
     pkt->plaintext = buf;
 
-    if (ngx_quic_decrypt(pkt, NULL) != NGX_OK) {
+    ctx = ngx_quic_get_send_ctx(qc, pkt->level);
+
+    if (ngx_quic_decrypt(pkt, NULL, &ctx->largest_pn) != NGX_OK) {
         return NGX_ERROR;
     }
 
@@ -907,9 +912,10 @@ ngx_quic_input(ngx_connection_t *c, ngx_
 static ngx_int_t
 ngx_quic_initial_input(ngx_connection_t *c, ngx_quic_header_t *pkt)
 {
-    ngx_ssl_conn_t      *ssl_conn;
-    ngx_quic_secrets_t  *keys;
-    static u_char        buf[NGX_QUIC_DEFAULT_MAX_PACKET_SIZE];
+    ngx_ssl_conn_t       *ssl_conn;
+    ngx_quic_secrets_t   *keys;
+    ngx_quic_send_ctx_t  *ctx;
+    static u_char         buf[NGX_QUIC_DEFAULT_MAX_PACKET_SIZE];
 
     c->log->action = "processing initial quic packet";
 
@@ -929,7 +935,9 @@ ngx_quic_initial_input(ngx_connection_t 
     pkt->level = ssl_encryption_initial;
     pkt->plaintext = buf;
 
-    if (ngx_quic_decrypt(pkt, ssl_conn) != NGX_OK) {
+    ctx = ngx_quic_get_send_ctx(c->quic, pkt->level);
+
+    if (ngx_quic_decrypt(pkt, ssl_conn, &ctx->largest_pn) != NGX_OK) {
         return NGX_ERROR;
     }
 
@@ -941,6 +949,7 @@ static ngx_int_t
 ngx_quic_handshake_input(ngx_connection_t *c, ngx_quic_header_t *pkt)
 {
     ngx_quic_secrets_t     *keys;
+    ngx_quic_send_ctx_t    *ctx;
     ngx_quic_connection_t  *qc;
     static u_char           buf[NGX_QUIC_DEFAULT_MAX_PACKET_SIZE];
 
@@ -995,7 +1004,9 @@ ngx_quic_handshake_input(ngx_connection_
     pkt->level = ssl_encryption_handshake;
     pkt->plaintext = buf;
 
-    if (ngx_quic_decrypt(pkt, c->ssl->connection) != NGX_OK) {
+    ctx = ngx_quic_get_send_ctx(qc, pkt->level);
+
+    if (ngx_quic_decrypt(pkt, c->ssl->connection, &ctx->largest_pn) != NGX_OK) {
         return NGX_ERROR;
     }
 
@@ -1007,6 +1018,7 @@ static ngx_int_t
 ngx_quic_early_input(ngx_connection_t *c, ngx_quic_header_t *pkt)
 {
     ngx_quic_secrets_t     *keys;
+    ngx_quic_send_ctx_t    *ctx;
     ngx_quic_connection_t  *qc;
     static u_char           buf[NGX_QUIC_DEFAULT_MAX_PACKET_SIZE];
 
@@ -1060,7 +1072,9 @@ ngx_quic_early_input(ngx_connection_t *c
     pkt->level = ssl_encryption_early_data;
     pkt->plaintext = buf;
 
-    if (ngx_quic_decrypt(pkt, c->ssl->connection) != NGX_OK) {
+    ctx = ngx_quic_get_send_ctx(qc, pkt->level);
+
+    if (ngx_quic_decrypt(pkt, c->ssl->connection, &ctx->largest_pn) != NGX_OK) {
         return NGX_ERROR;
     }
 
@@ -1073,6 +1087,7 @@ ngx_quic_app_input(ngx_connection_t *c, 
 {
     ngx_int_t               rc;
     ngx_quic_secrets_t     *keys, *next, tmp;
+    ngx_quic_send_ctx_t    *ctx;
     ngx_quic_connection_t  *qc;
     static u_char           buf[NGX_QUIC_DEFAULT_MAX_PACKET_SIZE];
 
@@ -1099,7 +1114,9 @@ ngx_quic_app_input(ngx_connection_t *c, 
     pkt->level = ssl_encryption_application;
     pkt->plaintext = buf;
 
-    if (ngx_quic_decrypt(pkt, c->ssl->connection) != NGX_OK) {
+    ctx = ngx_quic_get_send_ctx(qc, pkt->level);
+
+    if (ngx_quic_decrypt(pkt, c->ssl->connection, &ctx->largest_pn) != NGX_OK) {
         return NGX_ERROR;
     }
 
--- a/src/event/ngx_event_quic_protection.c
+++ b/src/event/ngx_event_quic_protection.c
@@ -36,7 +36,8 @@ static ngx_int_t ngx_hkdf_extract(u_char
     const EVP_MD *digest, const u_char *secret, size_t secret_len,
     const u_char *salt, size_t salt_len);
 
-static uint64_t ngx_quic_parse_pn(u_char **pos, ngx_int_t len, u_char *mask);
+static uint64_t ngx_quic_parse_pn(u_char **pos, ngx_int_t len, u_char *mask,
+    uint64_t *largest_pn);
 static void ngx_quic_compute_nonce(u_char *nonce, size_t len, uint64_t pn);
 static ngx_int_t ngx_quic_ciphers(ngx_ssl_conn_t *ssl_conn,
     ngx_quic_ciphers_t *ciphers, enum ssl_encryption_level_t level);
@@ -870,20 +871,45 @@ ngx_quic_create_short_packet(ngx_quic_he
 
 
 static uint64_t
-ngx_quic_parse_pn(u_char **pos, ngx_int_t len, u_char *mask)
+ngx_quic_parse_pn(u_char **pos, ngx_int_t len, u_char *mask,
+    uint64_t *largest_pn)
 {
     u_char    *p;
-    uint64_t   value;
+    uint64_t   truncated_pn, expected_pn, candidate_pn;
+    uint64_t   pn_nbits, pn_win, pn_hwin, pn_mask;
+
+    pn_nbits = ngx_min(len * 8, 62);
 
     p = *pos;
-    value = *p++ ^ *mask++;
+    truncated_pn = *p++ ^ *mask++;
 
     while (--len) {
-        value = (value << 8) + (*p++ ^ *mask++);
+        truncated_pn = (truncated_pn << 8) + (*p++ ^ *mask++);
     }
 
     *pos = p;
-    return value;
+
+    expected_pn = *largest_pn + 1;
+    pn_win = 1 << pn_nbits;
+    pn_hwin = pn_win / 2;
+    pn_mask = pn_win - 1;
+
+    candidate_pn = (expected_pn & ~pn_mask) | truncated_pn;
+
+    if ((int64_t) candidate_pn <= (int64_t) (expected_pn - pn_hwin)
+        && candidate_pn < (1ULL << 62) - pn_win)
+    {
+        candidate_pn += pn_win;
+
+    } else if (candidate_pn > expected_pn + pn_hwin
+               && candidate_pn >= pn_win)
+    {
+        candidate_pn -= pn_win;
+    }
+
+    *largest_pn = ngx_max((int64_t) *largest_pn, (int64_t) candidate_pn);
+
+    return candidate_pn;
 }
 
 
@@ -910,7 +936,8 @@ ngx_quic_encrypt(ngx_quic_header_t *pkt,
 
 
 ngx_int_t
-ngx_quic_decrypt(ngx_quic_header_t *pkt, ngx_ssl_conn_t *ssl_conn)
+ngx_quic_decrypt(ngx_quic_header_t *pkt, ngx_ssl_conn_t *ssl_conn,
+    uint64_t *largest_pn)
 {
     u_char               clearflags, *p, *sample;
     uint64_t             pn;
@@ -960,7 +987,7 @@ ngx_quic_decrypt(ngx_quic_header_t *pkt,
     }
 
     pnl = (clearflags & 0x03) + 1;
-    pn = ngx_quic_parse_pn(&p, pnl, &mask[1]);
+    pn = ngx_quic_parse_pn(&p, pnl, &mask[1], largest_pn);
 
     pkt->pn = pn;
 
--- a/src/event/ngx_event_quic_protection.h
+++ b/src/event/ngx_event_quic_protection.h
@@ -39,7 +39,8 @@ ngx_int_t ngx_quic_key_update(ngx_connec
 ssize_t ngx_quic_encrypt(ngx_quic_header_t *pkt, ngx_ssl_conn_t *ssl_conn,
      ngx_str_t *res);
 
-ngx_int_t ngx_quic_decrypt(ngx_quic_header_t *pkt, ngx_ssl_conn_t *ssl_conn);
+ngx_int_t ngx_quic_decrypt(ngx_quic_header_t *pkt, ngx_ssl_conn_t *ssl_conn,
+     uint64_t *largest_pn);
 
 
 #endif /* _NGX_EVENT_QUIC_PROTECTION_H_INCLUDED_ */