changeset 8304:90f94413177e quic

TLS Early Data support.
author Sergey Kandaurov <pluknet@nginx.com>
date Wed, 01 Apr 2020 13:27:42 +0300
parents 2ac03e80d013
children e35f824f644d
files src/event/ngx_event_quic.c
diffstat 1 files changed, 98 insertions(+), 12 deletions(-) [+]
line wrap: on
line diff
--- a/src/event/ngx_event_quic.c
+++ b/src/event/ngx_event_quic.c
@@ -12,6 +12,7 @@
 typedef enum {
     NGX_QUIC_ST_INITIAL,     /* connection just created */
     NGX_QUIC_ST_HANDSHAKE,   /* handshake started */
+    NGX_QUIC_ST_EARLY_DATA,  /* handshake in progress */
     NGX_QUIC_ST_APPLICATION  /* handshake complete */
 } ngx_quic_state_t;
 
@@ -82,7 +83,8 @@ static int ngx_quic_send_alert(ngx_ssl_c
 
 
 static ngx_int_t ngx_quic_new_connection(ngx_connection_t *c, ngx_ssl_t *ssl,
-    ngx_quic_tp_t *tp, ngx_quic_header_t *pkt);
+    ngx_quic_tp_t *tp, ngx_quic_header_t *pkt,
+    ngx_connection_handler_pt handler);
 static ngx_int_t ngx_quic_init_connection(ngx_connection_t *c);
 static void ngx_quic_input_handler(ngx_event_t *rev);
 static void ngx_quic_close_connection(ngx_connection_t *c);
@@ -92,6 +94,8 @@ static ngx_int_t ngx_quic_initial_input(
     ngx_quic_header_t *pkt);
 static ngx_int_t ngx_quic_handshake_input(ngx_connection_t *c,
     ngx_quic_header_t *pkt);
+static ngx_int_t ngx_quic_early_input(ngx_connection_t *c,
+    ngx_quic_header_t *pkt);
 static ngx_int_t ngx_quic_app_input(ngx_connection_t *c,
     ngx_quic_header_t *pkt);
 static ngx_int_t ngx_quic_payload_handler(ngx_connection_t *c,
@@ -159,6 +163,10 @@ ngx_quic_set_read_secret(ngx_ssl_conn_t 
     ngx_quic_hexdump(c->log, "level:%d read secret",
                      rsecret, secret_len, level);
 
+    if (level == ssl_encryption_early_data) {
+        c->quic->state = NGX_QUIC_ST_EARLY_DATA;
+    }
+
     return ngx_quic_set_encryption_secret(c->pool, ssl_conn, level,
                                           rsecret, secret_len,
                                           &c->quic->secrets.client);
@@ -204,6 +212,7 @@ ngx_quic_set_encryption_secrets(ngx_ssl_
     }
 
     if (level == ssl_encryption_early_data) {
+        c->quic->state = NGX_QUIC_ST_EARLY_DATA;
         return 1;
     }
 
@@ -352,14 +361,11 @@ ngx_quic_run(ngx_connection_t *c, ngx_ss
     pkt.data = b->start;
     pkt.len = b->last - b->start;
 
-    if (ngx_quic_new_connection(c, ssl, tp, &pkt) != NGX_OK) {
+    if (ngx_quic_new_connection(c, ssl, tp, &pkt, handler) != NGX_OK) {
         ngx_quic_close_connection(c);
         return;
     }
 
-    // we don't need stream handler for initial packet processing
-    c->quic->streams.handler = handler;
-
     ngx_add_timer(c->read, c->quic->tp.max_idle_timeout);
 
     c->read->handler = ngx_quic_input_handler;
@@ -370,7 +376,7 @@ ngx_quic_run(ngx_connection_t *c, ngx_ss
 
 static ngx_int_t
 ngx_quic_new_connection(ngx_connection_t *c, ngx_ssl_t *ssl, ngx_quic_tp_t *tp,
-    ngx_quic_header_t *pkt)
+    ngx_quic_header_t *pkt, ngx_connection_handler_pt handler)
 {
     ngx_quic_tp_t          *ctp;
     ngx_quic_connection_t  *qc;
@@ -410,6 +416,7 @@ ngx_quic_new_connection(ngx_connection_t
     c->quic = qc;
     qc->ssl = ssl;
     qc->tp = *tp;
+    qc->streams.handler = handler;
 
     ctp = &qc->ctp;
     ctp->max_packet_size = NGX_QUIC_DEFAULT_MAX_PACKET_SIZE;
@@ -456,7 +463,14 @@ ngx_quic_new_connection(ngx_connection_t
         return NGX_ERROR;
     }
 
-    return ngx_quic_payload_handler(c, pkt);
+    if (ngx_quic_payload_handler(c, pkt) != NGX_OK) {
+        return NGX_ERROR;
+    }
+
+    /* pos is at header end, adjust by actual packet length */
+    pkt->raw->pos += pkt->len;
+
+    return ngx_quic_input(c, pkt->raw);
 }
 
 
@@ -483,6 +497,12 @@ ngx_quic_init_connection(ngx_connection_
         return NGX_ERROR;
     }
 
+#ifdef SSL_READ_EARLY_DATA_SUCCESS
+    if (SSL_CTX_get_max_early_data(qc->ssl->ctx)) {
+        SSL_set_quic_early_data_enabled(ssl_conn, 1);
+    }
+#endif
+
     len = ngx_quic_create_transport_params(NULL, NULL, &qc->tp);
     /* always succeeds */
 
@@ -666,9 +686,9 @@ ngx_quic_input(ngx_connection_t *c, ngx_
     ngx_int_t           rc;
     ngx_quic_header_t   pkt;
 
-    p = b->start;
+    p = b->pos;
 
-    do {
+    while (p < b->last) {
         c->log->action = "processing quic packet";
 
         ngx_memzero(&pkt, sizeof(ngx_quic_header_t));
@@ -693,6 +713,9 @@ ngx_quic_input(ngx_connection_t *c, ngx_
             } else if (ngx_quic_pkt_hs(pkt.flags)) {
                 rc = ngx_quic_handshake_input(c, &pkt);
 
+            } else if (ngx_quic_pkt_zrtt(pkt.flags)) {
+                rc = ngx_quic_early_input(c, &pkt);
+
             } else {
                 ngx_log_error(NGX_LOG_INFO, c->log, 0,
                               "BUG: unknown quic state");
@@ -710,8 +733,7 @@ ngx_quic_input(ngx_connection_t *c, ngx_
         /* b->pos is at header end, adjust by actual packet length */
         p = b->pos + pkt.len;
         b->pos = p;       /* reset b->pos to the next packet start */
-
-    } while (p < b->last);
+    }
 
     return NGX_OK;
 }
@@ -807,6 +829,68 @@ ngx_quic_handshake_input(ngx_connection_
 
 
 static ngx_int_t
+ngx_quic_early_input(ngx_connection_t *c, ngx_quic_header_t *pkt)
+{
+    ngx_quic_connection_t  *qc;
+    static u_char           buf[NGX_QUIC_DEFAULT_MAX_PACKET_SIZE];
+
+    c->log->action = "processing early data quic packet";
+
+    qc = c->quic;
+
+    /* extract cleartext data into pkt */
+    if (ngx_quic_parse_long_header(pkt) != NGX_OK) {
+        return NGX_ERROR;
+    }
+
+    if (pkt->dcid.len != qc->dcid.len) {
+        ngx_log_error(NGX_LOG_INFO, c->log, 0, "unexpected quic dcidl");
+        return NGX_ERROR;
+    }
+
+    if (ngx_memcmp(pkt->dcid.data, qc->dcid.data, qc->dcid.len) != 0) {
+        ngx_log_error(NGX_LOG_INFO, c->log, 0, "unexpected quic dcid");
+        return NGX_ERROR;
+    }
+
+    if (pkt->scid.len != qc->scid.len) {
+        ngx_log_error(NGX_LOG_INFO, c->log, 0, "unexpected quic scidl");
+        return NGX_ERROR;
+    }
+
+    if (ngx_memcmp(pkt->scid.data, qc->scid.data, qc->scid.len) != 0) {
+        ngx_log_error(NGX_LOG_INFO, c->log, 0, "unexpected quic scid");
+        return NGX_ERROR;
+    }
+
+    if (!ngx_quic_pkt_zrtt(pkt->flags)) {
+        ngx_log_error(NGX_LOG_INFO, c->log, 0,
+                      "invalid packet type: 0x%xi", pkt->flags);
+        return NGX_ERROR;
+    }
+
+    if (ngx_quic_parse_handshake_header(pkt) != NGX_OK) {
+        return NGX_ERROR;
+    }
+
+    if (c->quic->state != NGX_QUIC_ST_EARLY_DATA) {
+        ngx_log_error(NGX_LOG_INFO, c->log, 0, "unexpected 0-RTT packet");
+        return NGX_OK;
+    }
+
+    pkt->secret = &qc->secrets.client.ed;
+    pkt->level = ssl_encryption_early_data;
+    pkt->plaintext = buf;
+
+    if (ngx_quic_decrypt(pkt, c->ssl->connection) != NGX_OK) {
+        return NGX_ERROR;
+    }
+
+    return ngx_quic_payload_handler(c, pkt);
+}
+
+
+static ngx_int_t
 ngx_quic_app_input(ngx_connection_t *c, ngx_quic_header_t *pkt)
 {
     ngx_quic_connection_t  *qc;
@@ -1000,7 +1084,9 @@ ngx_quic_payload_handler(ngx_connection_
         return NGX_ERROR;
     }
 
-    ack_frame->level = pkt->level;
+    ack_frame->level = (pkt->level == ssl_encryption_early_data)
+                       ? ssl_encryption_application
+                       : pkt->level;
     ack_frame->type = NGX_QUIC_FT_ACK;
     ack_frame->u.ack.pn = pkt->pn;