changeset 8753:46161c610919 quic

QUIC: separate files for SSL library interfaces.
author Vladimir Homutov <vl@nginx.com>
date Wed, 14 Apr 2021 14:47:04 +0300
parents e19723c40d28
children 915c2f7092ed
files auto/modules src/event/quic/ngx_event_quic.c src/event/quic/ngx_event_quic_connection.h src/event/quic/ngx_event_quic_frames.c src/event/quic/ngx_event_quic_ssl.c src/event/quic/ngx_event_quic_ssl.h
diffstat 6 files changed, 531 insertions(+), 507 deletions(-) [+]
line wrap: on
line diff
--- a/auto/modules
+++ b/auto/modules
@@ -1347,6 +1347,7 @@ if [ $USE_OPENSSL$USE_OPENSSL_QUIC = YES
                      src/event/quic/ngx_event_quic_connid.h \
                      src/event/quic/ngx_event_quic_migration.h \
                      src/event/quic/ngx_event_quic_streams.h \
+                     src/event/quic/ngx_event_quic_ssl.h \
                      src/event/quic/ngx_event_quic_tokens.h \
                      src/event/quic/ngx_event_quic_ack.h \
                      src/event/quic/ngx_event_quic_output.h"
@@ -1357,6 +1358,7 @@ if [ $USE_OPENSSL$USE_OPENSSL_QUIC = YES
                      src/event/quic/ngx_event_quic_connid.c \
                      src/event/quic/ngx_event_quic_migration.c \
                      src/event/quic/ngx_event_quic_streams.c \
+                     src/event/quic/ngx_event_quic_ssl.c \
                      src/event/quic/ngx_event_quic_tokens.c \
                      src/event/quic/ngx_event_quic_ack.c \
                      src/event/quic/ngx_event_quic_output.c"
--- a/src/event/quic/ngx_event_quic.c
+++ b/src/event/quic/ngx_event_quic.c
@@ -10,38 +10,10 @@
 #include <ngx_event_quic_connection.h>
 
 
-/*
- * 7.4.  Cryptographic Message Buffering
- *       Implementations MUST support buffering at least 4096 bytes of data
- */
-#define NGX_QUIC_MAX_BUFFERED    65535
-
-
-#if BORINGSSL_API_VERSION >= 10
-static int ngx_quic_set_read_secret(ngx_ssl_conn_t *ssl_conn,
-    enum ssl_encryption_level_t level, const SSL_CIPHER *cipher,
-    const uint8_t *secret, size_t secret_len);
-static int ngx_quic_set_write_secret(ngx_ssl_conn_t *ssl_conn,
-    enum ssl_encryption_level_t level, const SSL_CIPHER *cipher,
-    const uint8_t *secret, size_t secret_len);
-#else
-static int ngx_quic_set_encryption_secrets(ngx_ssl_conn_t *ssl_conn,
-    enum ssl_encryption_level_t level, const uint8_t *read_secret,
-    const uint8_t *write_secret, size_t secret_len);
-#endif
-
-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);
-static int ngx_quic_flush_flight(ngx_ssl_conn_t *ssl_conn);
-
-
-static ngx_int_t ngx_quic_apply_transport_params(ngx_connection_t *c,
-    ngx_quic_tp_t *ctp);
 static ngx_quic_connection_t *ngx_quic_new_connection(ngx_connection_t *c,
     ngx_quic_conf_t *conf, ngx_quic_header_t *pkt);
 static ngx_int_t ngx_quic_process_stateless_reset(ngx_connection_t *c,
     ngx_quic_header_t *pkt);
-static ngx_int_t ngx_quic_init_connection(ngx_connection_t *c);
 static void ngx_quic_input_handler(ngx_event_t *rev);
 
 static ngx_int_t ngx_quic_close_quic(ngx_connection_t *c, ngx_int_t rc);
@@ -53,19 +25,11 @@ static ngx_int_t ngx_quic_process_packet
     ngx_quic_conf_t *conf, ngx_quic_header_t *pkt);
 static ngx_int_t ngx_quic_process_payload(ngx_connection_t *c,
     ngx_quic_header_t *pkt);
-static void ngx_quic_discard_ctx(ngx_connection_t *c,
-    enum ssl_encryption_level_t level);
 static ngx_int_t ngx_quic_check_csid(ngx_quic_connection_t *qc,
     ngx_quic_header_t *pkt);
 static ngx_int_t ngx_quic_handle_frames(ngx_connection_t *c,
     ngx_quic_header_t *pkt);
 
-
-static ngx_int_t ngx_quic_handle_crypto_frame(ngx_connection_t *c,
-    ngx_quic_header_t *pkt, ngx_quic_frame_t *frame);
-ngx_int_t ngx_quic_crypto_input(ngx_connection_t *c,
-    ngx_quic_frame_t *frame, void *data);
-
 static void ngx_quic_push_handler(ngx_event_t *ev);
 
 
@@ -92,19 +56,6 @@ ngx_module_t  ngx_quic_module = {
 };
 
 
-static SSL_QUIC_METHOD quic_method = {
-#if BORINGSSL_API_VERSION >= 10
-    ngx_quic_set_read_secret,
-    ngx_quic_set_write_secret,
-#else
-    ngx_quic_set_encryption_secrets,
-#endif
-    ngx_quic_add_handshake_data,
-    ngx_quic_flush_flight,
-    ngx_quic_send_alert,
-};
-
-
 #if (NGX_DEBUG)
 
 void
@@ -173,227 +124,7 @@ ngx_quic_connstate_dbg(ngx_connection_t 
 #endif
 
 
-#if BORINGSSL_API_VERSION >= 10
-
-static int
-ngx_quic_set_read_secret(ngx_ssl_conn_t *ssl_conn,
-    enum ssl_encryption_level_t level, const SSL_CIPHER *cipher,
-    const uint8_t *rsecret, size_t secret_len)
-{
-    ngx_connection_t       *c;
-    ngx_quic_connection_t  *qc;
-
-    c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);
-    qc = ngx_quic_get_connection(c);
-
-    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
-                   "quic ngx_quic_set_read_secret() level:%d", level);
-#ifdef NGX_QUIC_DEBUG_CRYPTO
-    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
-                   "quic read secret len:%uz %*xs", secret_len,
-                   secret_len, rsecret);
-#endif
-
-    return ngx_quic_keys_set_encryption_secret(c->pool, 0, qc->keys, level,
-                                               cipher, rsecret, secret_len);
-}
-
-
-static int
-ngx_quic_set_write_secret(ngx_ssl_conn_t *ssl_conn,
-    enum ssl_encryption_level_t level, const SSL_CIPHER *cipher,
-    const uint8_t *wsecret, size_t secret_len)
-{
-    ngx_connection_t       *c;
-    ngx_quic_connection_t  *qc;
-
-    c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);
-    qc = ngx_quic_get_connection(c);
-
-    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
-                   "quic ngx_quic_set_write_secret() level:%d", level);
-#ifdef NGX_QUIC_DEBUG_CRYPTO
-    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
-                   "quic write secret len:%uz %*xs", secret_len,
-                   secret_len, wsecret);
-#endif
-
-    return ngx_quic_keys_set_encryption_secret(c->pool, 1, qc->keys, level,
-                                               cipher, wsecret, secret_len);
-}
-
-#else
-
-static int
-ngx_quic_set_encryption_secrets(ngx_ssl_conn_t *ssl_conn,
-    enum ssl_encryption_level_t level, const uint8_t *rsecret,
-    const uint8_t *wsecret, size_t secret_len)
-{
-    ngx_connection_t       *c;
-    const SSL_CIPHER       *cipher;
-    ngx_quic_connection_t  *qc;
-
-    c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);
-    qc = ngx_quic_get_connection(c);
-
-    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
-                   "quic ngx_quic_set_encryption_secrets() level:%d", level);
-#ifdef NGX_QUIC_DEBUG_CRYPTO
-    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
-                   "quic read secret len:%uz %*xs", secret_len,
-                   secret_len, rsecret);
-#endif
-
-    cipher = SSL_get_current_cipher(ssl_conn);
-
-    if (ngx_quic_keys_set_encryption_secret(c->pool, 0, qc->keys, level,
-                                            cipher, rsecret, secret_len)
-        != 1)
-    {
-        return 0;
-    }
-
-    if (level == ssl_encryption_early_data) {
-        return 1;
-    }
-
-#ifdef NGX_QUIC_DEBUG_CRYPTO
-    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
-                   "quic write secret len:%uz %*xs", secret_len,
-                   secret_len, wsecret);
-#endif
-
-    return ngx_quic_keys_set_encryption_secret(c->pool, 1, qc->keys, level,
-                                               cipher, wsecret, secret_len);
-}
-
-#endif
-
-
-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, *end;
-    size_t                     client_params_len;
-    const uint8_t             *client_params;
-    ngx_quic_tp_t              ctp;
-    ngx_quic_frame_t          *frame;
-    ngx_connection_t          *c;
-    ngx_quic_connection_t     *qc;
-    ngx_quic_frames_stream_t  *fs;
-
-    c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);
-    qc = ngx_quic_get_connection(c);
-
-    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
-                   "quic ngx_quic_add_handshake_data");
-
-    if (!qc->client_tp_done) {
-        /*
-         * things to do once during handshake: check ALPN and transport
-         * parameters; we want to break handshake if something is wrong
-         * here;
-         */
-
-#if defined(TLSEXT_TYPE_application_layer_protocol_negotiation)
-        if (qc->conf->require_alpn) {
-            unsigned int          len;
-            const unsigned char  *data;
-
-            SSL_get0_alpn_selected(ssl_conn, &data, &len);
-
-            if (len == 0) {
-                qc->error = 0x100 + SSL_AD_NO_APPLICATION_PROTOCOL;
-                qc->error_reason = "unsupported protocol in ALPN extension";
-
-                ngx_log_error(NGX_LOG_INFO, c->log, 0,
-                              "quic unsupported protocol in ALPN extension");
-                return 0;
-            }
-        }
-#endif
-
-        SSL_get_peer_quic_transport_params(ssl_conn, &client_params,
-                                           &client_params_len);
-
-        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
-                       "quic SSL_get_peer_quic_transport_params():"
-                       " params_len:%ui", client_params_len);
-
-        if (client_params_len == 0) {
-            /* quic-tls 8.2 */
-            qc->error = NGX_QUIC_ERR_CRYPTO(SSL_AD_MISSING_EXTENSION);
-            qc->error_reason = "missing transport parameters";
-
-            ngx_log_error(NGX_LOG_INFO, c->log, 0,
-                          "missing transport parameters");
-            return 0;
-        }
-
-        p = (u_char *) client_params;
-        end = p + client_params_len;
-
-        /* defaults for parameters not sent by client */
-        ngx_memcpy(&ctp, &qc->ctp, sizeof(ngx_quic_tp_t));
-
-        if (ngx_quic_parse_transport_params(p, end, &ctp, c->log)
-            != NGX_OK)
-        {
-            qc->error = NGX_QUIC_ERR_TRANSPORT_PARAMETER_ERROR;
-            qc->error_reason = "failed to process transport parameters";
-
-            return 0;
-        }
-
-        if (ngx_quic_apply_transport_params(c, &ctp) != NGX_OK) {
-            return 0;
-        }
-
-        qc->client_tp_done = 1;
-    }
-
-    fs = &qc->crypto[level];
-
-    frame = ngx_quic_alloc_frame(c);
-    if (frame == NULL) {
-        return 0;
-    }
-
-    frame->data = ngx_quic_copy_buf(c, (u_char *) data, len);
-    if (frame->data == NGX_CHAIN_ERROR) {
-        return 0;
-    }
-
-    frame->level = level;
-    frame->type = NGX_QUIC_FT_CRYPTO;
-    frame->u.crypto.offset = fs->sent;
-    frame->u.crypto.length = len;
-
-    fs->sent += len;
-
-    ngx_quic_queue_frame(qc, frame);
-
-    return 1;
-}
-
-
-static int
-ngx_quic_flush_flight(ngx_ssl_conn_t *ssl_conn)
-{
-#if (NGX_DEBUG)
-    ngx_connection_t  *c;
-
-    c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);
-
-    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
-                   "quic ngx_quic_flush_flight()");
-#endif
-    return 1;
-}
-
-
-static ngx_int_t
+ngx_int_t
 ngx_quic_apply_transport_params(ngx_connection_t *c, ngx_quic_tp_t *ctp)
 {
     ngx_quic_connection_t  *qc;
@@ -659,88 +390,6 @@ ngx_quic_process_stateless_reset(ngx_con
 }
 
 
-static ngx_int_t
-ngx_quic_init_connection(ngx_connection_t *c)
-{
-    u_char                 *p;
-    size_t                  clen;
-    ssize_t                 len;
-    ngx_ssl_conn_t         *ssl_conn;
-    ngx_quic_connection_t  *qc;
-
-    qc = ngx_quic_get_connection(c);
-
-    if (ngx_ssl_create_connection(qc->conf->ssl, c, NGX_SSL_BUFFER) != NGX_OK) {
-        return NGX_ERROR;
-    }
-
-    c->ssl->no_wait_shutdown = 1;
-
-    ssl_conn = c->ssl->connection;
-
-    if (SSL_set_quic_method(ssl_conn, &quic_method) == 0) {
-        ngx_log_error(NGX_LOG_INFO, c->log, 0,
-                      "quic SSL_set_quic_method() failed");
-        return NGX_ERROR;
-    }
-
-#ifdef SSL_READ_EARLY_DATA_SUCCESS
-    if (SSL_CTX_get_max_early_data(qc->conf->ssl->ctx)) {
-        SSL_set_quic_early_data_enabled(ssl_conn, 1);
-    }
-#endif
-
-#if BORINGSSL_API_VERSION >= 13
-    SSL_set_quic_use_legacy_codepoint(ssl_conn, qc->version != 1);
-#endif
-
-    if (ngx_quic_new_sr_token(c, &qc->dcid, qc->conf->sr_token_key,
-                              qc->tp.sr_token)
-        != NGX_OK)
-    {
-        return NGX_ERROR;
-    }
-
-    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
-                   "quic stateless reset token %*xs",
-                   (size_t) NGX_QUIC_SR_TOKEN_LEN, qc->tp.sr_token);
-
-    len = ngx_quic_create_transport_params(NULL, NULL, &qc->tp, &clen);
-    /* always succeeds */
-
-    p = ngx_pnalloc(c->pool, len);
-    if (p == NULL) {
-        return NGX_ERROR;
-    }
-
-    len = ngx_quic_create_transport_params(p, p + len, &qc->tp, NULL);
-    if (len < 0) {
-        return NGX_ERROR;
-    }
-
-#ifdef NGX_QUIC_DEBUG_PACKETS
-    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
-                   "quic transport parameters len:%uz %*xs", len, len, p);
-#endif
-
-    if (SSL_set_quic_transport_params(ssl_conn, p, len) == 0) {
-        ngx_log_error(NGX_LOG_INFO, c->log, 0,
-                      "quic SSL_set_quic_transport_params() failed");
-        return NGX_ERROR;
-    }
-
-#if NGX_OPENSSL_QUIC_ZRTT_CTX
-    if (SSL_set_quic_early_data_context(ssl_conn, p, clen) == 0) {
-        ngx_log_error(NGX_LOG_INFO, c->log, 0,
-                      "quic SSL_set_quic_early_data_context() failed");
-        return NGX_ERROR;
-    }
-#endif
-
-    return NGX_OK;
-}
-
-
 static void
 ngx_quic_input_handler(ngx_event_t *rev)
 {
@@ -1361,7 +1010,7 @@ ngx_quic_process_payload(ngx_connection_
 }
 
 
-static void
+void
 ngx_quic_discard_ctx(ngx_connection_t *c, enum ssl_encryption_level_t level)
 {
     ngx_queue_t            *q;
@@ -1672,156 +1321,6 @@ ngx_quic_handle_frames(ngx_connection_t 
 }
 
 
-static ngx_int_t
-ngx_quic_handle_crypto_frame(ngx_connection_t *c, ngx_quic_header_t *pkt,
-    ngx_quic_frame_t *frame)
-{
-    uint64_t                   last;
-    ngx_int_t                  rc;
-    ngx_quic_send_ctx_t       *ctx;
-    ngx_quic_connection_t     *qc;
-    ngx_quic_crypto_frame_t   *f;
-    ngx_quic_frames_stream_t  *fs;
-
-    qc = ngx_quic_get_connection(c);
-    fs = &qc->crypto[pkt->level];
-    f = &frame->u.crypto;
-
-    /* no overflow since both values are 62-bit */
-    last = f->offset + f->length;
-
-    if (last > fs->received && last - fs->received > NGX_QUIC_MAX_BUFFERED) {
-        qc->error = NGX_QUIC_ERR_CRYPTO_BUFFER_EXCEEDED;
-        return NGX_ERROR;
-    }
-
-    rc = ngx_quic_handle_ordered_frame(c, fs, frame, ngx_quic_crypto_input,
-                                       NULL);
-    if (rc != NGX_DECLINED) {
-        return rc;
-    }
-
-    /* speeding up handshake completion */
-
-    if (pkt->level == ssl_encryption_initial) {
-        ctx = ngx_quic_get_send_ctx(qc, pkt->level);
-
-        if (!ngx_queue_empty(&ctx->sent)) {
-            ngx_quic_resend_frames(c, ctx);
-
-            ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_handshake);
-            while (!ngx_queue_empty(&ctx->sent)) {
-                ngx_quic_resend_frames(c, ctx);
-            }
-        }
-    }
-
-    return NGX_OK;
-}
-
-
-ngx_int_t
-ngx_quic_crypto_input(ngx_connection_t *c, ngx_quic_frame_t *frame, void *data)
-{
-    int                     n, sslerr;
-    ngx_buf_t              *b;
-    ngx_chain_t            *cl;
-    ngx_ssl_conn_t         *ssl_conn;
-    ngx_quic_connection_t  *qc;
-
-    qc = ngx_quic_get_connection(c);
-
-    ssl_conn = c->ssl->connection;
-
-    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
-                   "quic SSL_quic_read_level:%d SSL_quic_write_level:%d",
-                   (int) SSL_quic_read_level(ssl_conn),
-                   (int) SSL_quic_write_level(ssl_conn));
-
-    for (cl = frame->data; cl; cl = cl->next) {
-        b = cl->buf;
-
-        if (!SSL_provide_quic_data(ssl_conn, SSL_quic_read_level(ssl_conn),
-                                   b->pos, b->last - b->pos))
-        {
-            ngx_ssl_error(NGX_LOG_INFO, c->log, 0,
-                          "SSL_provide_quic_data() failed");
-            return NGX_ERROR;
-        }
-    }
-
-    n = SSL_do_handshake(ssl_conn);
-
-    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
-                   "quic SSL_quic_read_level:%d SSL_quic_write_level:%d",
-                   (int) SSL_quic_read_level(ssl_conn),
-                   (int) SSL_quic_write_level(ssl_conn));
-
-    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_do_handshake: %d", n);
-
-    if (n <= 0) {
-        sslerr = SSL_get_error(ssl_conn, n);
-
-        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_get_error: %d",
-                       sslerr);
-
-        if (sslerr != SSL_ERROR_WANT_READ) {
-            ngx_ssl_error(NGX_LOG_ERR, c->log, 0, "SSL_do_handshake() failed");
-            return NGX_ERROR;
-        }
-
-        return NGX_OK;
-    }
-
-    if (SSL_in_init(ssl_conn)) {
-        return NGX_OK;
-    }
-
-    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
-                   "quic ssl cipher:%s", SSL_get_cipher(ssl_conn));
-
-    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
-                   "quic handshake completed successfully");
-
-    c->ssl->handshaked = 1;
-
-    frame = ngx_quic_alloc_frame(c);
-    if (frame == NULL) {
-        return NGX_ERROR;
-    }
-
-    /* 12.4 Frames and frame types, figure 8 */
-    frame->level = ssl_encryption_application;
-    frame->type = NGX_QUIC_FT_HANDSHAKE_DONE;
-    ngx_quic_queue_frame(qc, frame);
-
-    if (ngx_quic_send_new_token(c) != NGX_OK) {
-        return NGX_ERROR;
-    }
-
-    /*
-     * Generating next keys before a key update is received.
-     * See quic-tls 9.4 Header Protection Timing Side-Channels.
-     */
-
-    if (ngx_quic_keys_update(c, qc->keys) != NGX_OK) {
-        return NGX_ERROR;
-    }
-
-    /*
-     * 4.10.2 An endpoint MUST discard its handshake keys
-     * when the TLS handshake is confirmed
-     */
-    ngx_quic_discard_ctx(c, ssl_encryption_handshake);
-
-    if (ngx_quic_issue_server_ids(c) != NGX_OK) {
-        return NGX_ERROR;
-    }
-
-    return NGX_OK;
-}
-
-
 static void
 ngx_quic_push_handler(ngx_event_t *ev)
 {
--- a/src/event/quic/ngx_event_quic_connection.h
+++ b/src/event/quic/ngx_event_quic_connection.h
@@ -21,6 +21,7 @@ typedef struct ngx_quic_send_ctx_s    ng
 #include <ngx_event_quic_migration.h>
 #include <ngx_event_quic_connid.h>
 #include <ngx_event_quic_streams.h>
+#include <ngx_event_quic_ssl.h>
 #include <ngx_event_quic_tokens.h>
 #include <ngx_event_quic_ack.h>
 #include <ngx_event_quic_output.h>
@@ -201,6 +202,10 @@ struct ngx_quic_connection_s {
 };
 
 
+ngx_int_t ngx_quic_apply_transport_params(ngx_connection_t *c,
+    ngx_quic_tp_t *ctp);
+void ngx_quic_discard_ctx(ngx_connection_t *c,
+    enum ssl_encryption_level_t level);
 void ngx_quic_close_connection(ngx_connection_t *c, ngx_int_t rc);
 void ngx_quic_shutdown_quic(ngx_connection_t *c);
 
--- a/src/event/quic/ngx_event_quic_frames.c
+++ b/src/event/quic/ngx_event_quic_frames.c
@@ -469,10 +469,6 @@ done:
 }
 
 
-ngx_int_t ngx_quic_crypto_input(ngx_connection_t *c,
-    ngx_quic_frame_t *frame, void *data);
-
-
 ngx_int_t
 ngx_quic_handle_ordered_frame(ngx_connection_t *c, ngx_quic_frames_stream_t *fs,
     ngx_quic_frame_t *frame, ngx_quic_frame_handler_pt handler, void *data)
new file mode 100644
--- /dev/null
+++ b/src/event/quic/ngx_event_quic_ssl.c
@@ -0,0 +1,500 @@
+
+/*
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_event_quic_connection.h>
+
+
+/*
+ * 7.4.  Cryptographic Message Buffering
+ *       Implementations MUST support buffering at least 4096 bytes of data
+ */
+#define NGX_QUIC_MAX_BUFFERED    65535
+
+
+#if BORINGSSL_API_VERSION >= 10
+static int ngx_quic_set_read_secret(ngx_ssl_conn_t *ssl_conn,
+    enum ssl_encryption_level_t level, const SSL_CIPHER *cipher,
+    const uint8_t *secret, size_t secret_len);
+static int ngx_quic_set_write_secret(ngx_ssl_conn_t *ssl_conn,
+    enum ssl_encryption_level_t level, const SSL_CIPHER *cipher,
+    const uint8_t *secret, size_t secret_len);
+#else
+static int ngx_quic_set_encryption_secrets(ngx_ssl_conn_t *ssl_conn,
+    enum ssl_encryption_level_t level, const uint8_t *read_secret,
+    const uint8_t *write_secret, size_t secret_len);
+#endif
+
+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);
+static int ngx_quic_flush_flight(ngx_ssl_conn_t *ssl_conn);
+
+
+static SSL_QUIC_METHOD quic_method = {
+#if BORINGSSL_API_VERSION >= 10
+    ngx_quic_set_read_secret,
+    ngx_quic_set_write_secret,
+#else
+    ngx_quic_set_encryption_secrets,
+#endif
+    ngx_quic_add_handshake_data,
+    ngx_quic_flush_flight,
+    ngx_quic_send_alert,
+};
+
+
+#if BORINGSSL_API_VERSION >= 10
+
+static int
+ngx_quic_set_read_secret(ngx_ssl_conn_t *ssl_conn,
+    enum ssl_encryption_level_t level, const SSL_CIPHER *cipher,
+    const uint8_t *rsecret, size_t secret_len)
+{
+    ngx_connection_t       *c;
+    ngx_quic_connection_t  *qc;
+
+    c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);
+    qc = ngx_quic_get_connection(c);
+
+    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                   "quic ngx_quic_set_read_secret() level:%d", level);
+#ifdef NGX_QUIC_DEBUG_CRYPTO
+    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                   "quic read secret len:%uz %*xs", secret_len,
+                   secret_len, rsecret);
+#endif
+
+    return ngx_quic_keys_set_encryption_secret(c->pool, 0, qc->keys, level,
+                                               cipher, rsecret, secret_len);
+}
+
+
+static int
+ngx_quic_set_write_secret(ngx_ssl_conn_t *ssl_conn,
+    enum ssl_encryption_level_t level, const SSL_CIPHER *cipher,
+    const uint8_t *wsecret, size_t secret_len)
+{
+    ngx_connection_t       *c;
+    ngx_quic_connection_t  *qc;
+
+    c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);
+    qc = ngx_quic_get_connection(c);
+
+    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                   "quic ngx_quic_set_write_secret() level:%d", level);
+#ifdef NGX_QUIC_DEBUG_CRYPTO
+    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                   "quic write secret len:%uz %*xs", secret_len,
+                   secret_len, wsecret);
+#endif
+
+    return ngx_quic_keys_set_encryption_secret(c->pool, 1, qc->keys, level,
+                                               cipher, wsecret, secret_len);
+}
+
+#else
+
+static int
+ngx_quic_set_encryption_secrets(ngx_ssl_conn_t *ssl_conn,
+    enum ssl_encryption_level_t level, const uint8_t *rsecret,
+    const uint8_t *wsecret, size_t secret_len)
+{
+    ngx_connection_t       *c;
+    const SSL_CIPHER       *cipher;
+    ngx_quic_connection_t  *qc;
+
+    c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);
+    qc = ngx_quic_get_connection(c);
+
+    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                   "quic ngx_quic_set_encryption_secrets() level:%d", level);
+#ifdef NGX_QUIC_DEBUG_CRYPTO
+    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                   "quic read secret len:%uz %*xs", secret_len,
+                   secret_len, rsecret);
+#endif
+
+    cipher = SSL_get_current_cipher(ssl_conn);
+
+    if (ngx_quic_keys_set_encryption_secret(c->pool, 0, qc->keys, level,
+                                            cipher, rsecret, secret_len)
+        != 1)
+    {
+        return 0;
+    }
+
+    if (level == ssl_encryption_early_data) {
+        return 1;
+    }
+
+#ifdef NGX_QUIC_DEBUG_CRYPTO
+    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                   "quic write secret len:%uz %*xs", secret_len,
+                   secret_len, wsecret);
+#endif
+
+    return ngx_quic_keys_set_encryption_secret(c->pool, 1, qc->keys, level,
+                                               cipher, wsecret, secret_len);
+}
+
+#endif
+
+
+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, *end;
+    size_t                     client_params_len;
+    const uint8_t             *client_params;
+    ngx_quic_tp_t              ctp;
+    ngx_quic_frame_t          *frame;
+    ngx_connection_t          *c;
+    ngx_quic_connection_t     *qc;
+    ngx_quic_frames_stream_t  *fs;
+
+    c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);
+    qc = ngx_quic_get_connection(c);
+
+    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                   "quic ngx_quic_add_handshake_data");
+
+    if (!qc->client_tp_done) {
+        /*
+         * things to do once during handshake: check ALPN and transport
+         * parameters; we want to break handshake if something is wrong
+         * here;
+         */
+
+#if defined(TLSEXT_TYPE_application_layer_protocol_negotiation)
+        if (qc->conf->require_alpn) {
+            unsigned int          len;
+            const unsigned char  *data;
+
+            SSL_get0_alpn_selected(ssl_conn, &data, &len);
+
+            if (len == 0) {
+                qc->error = 0x100 + SSL_AD_NO_APPLICATION_PROTOCOL;
+                qc->error_reason = "unsupported protocol in ALPN extension";
+
+                ngx_log_error(NGX_LOG_INFO, c->log, 0,
+                              "quic unsupported protocol in ALPN extension");
+                return 0;
+            }
+        }
+#endif
+
+        SSL_get_peer_quic_transport_params(ssl_conn, &client_params,
+                                           &client_params_len);
+
+        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                       "quic SSL_get_peer_quic_transport_params():"
+                       " params_len:%ui", client_params_len);
+
+        if (client_params_len == 0) {
+            /* quic-tls 8.2 */
+            qc->error = NGX_QUIC_ERR_CRYPTO(SSL_AD_MISSING_EXTENSION);
+            qc->error_reason = "missing transport parameters";
+
+            ngx_log_error(NGX_LOG_INFO, c->log, 0,
+                          "missing transport parameters");
+            return 0;
+        }
+
+        p = (u_char *) client_params;
+        end = p + client_params_len;
+
+        /* defaults for parameters not sent by client */
+        ngx_memcpy(&ctp, &qc->ctp, sizeof(ngx_quic_tp_t));
+
+        if (ngx_quic_parse_transport_params(p, end, &ctp, c->log)
+            != NGX_OK)
+        {
+            qc->error = NGX_QUIC_ERR_TRANSPORT_PARAMETER_ERROR;
+            qc->error_reason = "failed to process transport parameters";
+
+            return 0;
+        }
+
+        if (ngx_quic_apply_transport_params(c, &ctp) != NGX_OK) {
+            return 0;
+        }
+
+        qc->client_tp_done = 1;
+    }
+
+    fs = &qc->crypto[level];
+
+    frame = ngx_quic_alloc_frame(c);
+    if (frame == NULL) {
+        return 0;
+    }
+
+    frame->data = ngx_quic_copy_buf(c, (u_char *) data, len);
+    if (frame->data == NGX_CHAIN_ERROR) {
+        return 0;
+    }
+
+    frame->level = level;
+    frame->type = NGX_QUIC_FT_CRYPTO;
+    frame->u.crypto.offset = fs->sent;
+    frame->u.crypto.length = len;
+
+    fs->sent += len;
+
+    ngx_quic_queue_frame(qc, frame);
+
+    return 1;
+}
+
+
+static int
+ngx_quic_flush_flight(ngx_ssl_conn_t *ssl_conn)
+{
+#if (NGX_DEBUG)
+    ngx_connection_t  *c;
+
+    c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);
+
+    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                   "quic ngx_quic_flush_flight()");
+#endif
+    return 1;
+}
+
+
+ngx_int_t
+ngx_quic_handle_crypto_frame(ngx_connection_t *c, ngx_quic_header_t *pkt,
+    ngx_quic_frame_t *frame)
+{
+    uint64_t                   last;
+    ngx_int_t                  rc;
+    ngx_quic_send_ctx_t       *ctx;
+    ngx_quic_connection_t     *qc;
+    ngx_quic_crypto_frame_t   *f;
+    ngx_quic_frames_stream_t  *fs;
+
+    qc = ngx_quic_get_connection(c);
+    fs = &qc->crypto[pkt->level];
+    f = &frame->u.crypto;
+
+    /* no overflow since both values are 62-bit */
+    last = f->offset + f->length;
+
+    if (last > fs->received && last - fs->received > NGX_QUIC_MAX_BUFFERED) {
+        qc->error = NGX_QUIC_ERR_CRYPTO_BUFFER_EXCEEDED;
+        return NGX_ERROR;
+    }
+
+    rc = ngx_quic_handle_ordered_frame(c, fs, frame, ngx_quic_crypto_input,
+                                       NULL);
+    if (rc != NGX_DECLINED) {
+        return rc;
+    }
+
+    /* speeding up handshake completion */
+
+    if (pkt->level == ssl_encryption_initial) {
+        ctx = ngx_quic_get_send_ctx(qc, pkt->level);
+
+        if (!ngx_queue_empty(&ctx->sent)) {
+            ngx_quic_resend_frames(c, ctx);
+
+            ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_handshake);
+            while (!ngx_queue_empty(&ctx->sent)) {
+                ngx_quic_resend_frames(c, ctx);
+            }
+        }
+    }
+
+    return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_quic_crypto_input(ngx_connection_t *c, ngx_quic_frame_t *frame, void *data)
+{
+    int                     n, sslerr;
+    ngx_buf_t              *b;
+    ngx_chain_t            *cl;
+    ngx_ssl_conn_t         *ssl_conn;
+    ngx_quic_connection_t  *qc;
+
+    qc = ngx_quic_get_connection(c);
+
+    ssl_conn = c->ssl->connection;
+
+    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                   "quic SSL_quic_read_level:%d SSL_quic_write_level:%d",
+                   (int) SSL_quic_read_level(ssl_conn),
+                   (int) SSL_quic_write_level(ssl_conn));
+
+    for (cl = frame->data; cl; cl = cl->next) {
+        b = cl->buf;
+
+        if (!SSL_provide_quic_data(ssl_conn, SSL_quic_read_level(ssl_conn),
+                                   b->pos, b->last - b->pos))
+        {
+            ngx_ssl_error(NGX_LOG_INFO, c->log, 0,
+                          "SSL_provide_quic_data() failed");
+            return NGX_ERROR;
+        }
+    }
+
+    n = SSL_do_handshake(ssl_conn);
+
+    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                   "quic SSL_quic_read_level:%d SSL_quic_write_level:%d",
+                   (int) SSL_quic_read_level(ssl_conn),
+                   (int) SSL_quic_write_level(ssl_conn));
+
+    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_do_handshake: %d", n);
+
+    if (n <= 0) {
+        sslerr = SSL_get_error(ssl_conn, n);
+
+        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_get_error: %d",
+                       sslerr);
+
+        if (sslerr != SSL_ERROR_WANT_READ) {
+            ngx_ssl_error(NGX_LOG_ERR, c->log, 0, "SSL_do_handshake() failed");
+            return NGX_ERROR;
+        }
+
+        return NGX_OK;
+    }
+
+    if (SSL_in_init(ssl_conn)) {
+        return NGX_OK;
+    }
+
+    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                   "quic ssl cipher:%s", SSL_get_cipher(ssl_conn));
+
+    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                   "quic handshake completed successfully");
+
+    c->ssl->handshaked = 1;
+
+    frame = ngx_quic_alloc_frame(c);
+    if (frame == NULL) {
+        return NGX_ERROR;
+    }
+
+    /* 12.4 Frames and frame types, figure 8 */
+    frame->level = ssl_encryption_application;
+    frame->type = NGX_QUIC_FT_HANDSHAKE_DONE;
+    ngx_quic_queue_frame(qc, frame);
+
+    if (ngx_quic_send_new_token(c) != NGX_OK) {
+        return NGX_ERROR;
+    }
+
+    /*
+     * Generating next keys before a key update is received.
+     * See quic-tls 9.4 Header Protection Timing Side-Channels.
+     */
+
+    if (ngx_quic_keys_update(c, qc->keys) != NGX_OK) {
+        return NGX_ERROR;
+    }
+
+    /*
+     * 4.10.2 An endpoint MUST discard its handshake keys
+     * when the TLS handshake is confirmed
+     */
+    ngx_quic_discard_ctx(c, ssl_encryption_handshake);
+
+    if (ngx_quic_issue_server_ids(c) != NGX_OK) {
+        return NGX_ERROR;
+    }
+
+    return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_quic_init_connection(ngx_connection_t *c)
+{
+    u_char                 *p;
+    size_t                  clen;
+    ssize_t                 len;
+    ngx_ssl_conn_t         *ssl_conn;
+    ngx_quic_connection_t  *qc;
+
+    qc = ngx_quic_get_connection(c);
+
+    if (ngx_ssl_create_connection(qc->conf->ssl, c, NGX_SSL_BUFFER) != NGX_OK) {
+        return NGX_ERROR;
+    }
+
+    c->ssl->no_wait_shutdown = 1;
+
+    ssl_conn = c->ssl->connection;
+
+    if (SSL_set_quic_method(ssl_conn, &quic_method) == 0) {
+        ngx_log_error(NGX_LOG_INFO, c->log, 0,
+                      "quic SSL_set_quic_method() failed");
+        return NGX_ERROR;
+    }
+
+#ifdef SSL_READ_EARLY_DATA_SUCCESS
+    if (SSL_CTX_get_max_early_data(qc->conf->ssl->ctx)) {
+        SSL_set_quic_early_data_enabled(ssl_conn, 1);
+    }
+#endif
+
+#if BORINGSSL_API_VERSION >= 13
+    SSL_set_quic_use_legacy_codepoint(ssl_conn, qc->version != 1);
+#endif
+
+    if (ngx_quic_new_sr_token(c, &qc->dcid, qc->conf->sr_token_key,
+                              qc->tp.sr_token)
+        != NGX_OK)
+    {
+        return NGX_ERROR;
+    }
+
+    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                   "quic stateless reset token %*xs",
+                   (size_t) NGX_QUIC_SR_TOKEN_LEN, qc->tp.sr_token);
+
+    len = ngx_quic_create_transport_params(NULL, NULL, &qc->tp, &clen);
+    /* always succeeds */
+
+    p = ngx_pnalloc(c->pool, len);
+    if (p == NULL) {
+        return NGX_ERROR;
+    }
+
+    len = ngx_quic_create_transport_params(p, p + len, &qc->tp, NULL);
+    if (len < 0) {
+        return NGX_ERROR;
+    }
+
+#ifdef NGX_QUIC_DEBUG_PACKETS
+    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                   "quic transport parameters len:%uz %*xs", len, len, p);
+#endif
+
+    if (SSL_set_quic_transport_params(ssl_conn, p, len) == 0) {
+        ngx_log_error(NGX_LOG_INFO, c->log, 0,
+                      "quic SSL_set_quic_transport_params() failed");
+        return NGX_ERROR;
+    }
+
+#if NGX_OPENSSL_QUIC_ZRTT_CTX
+    if (SSL_set_quic_early_data_context(ssl_conn, p, clen) == 0) {
+        ngx_log_error(NGX_LOG_INFO, c->log, 0,
+                      "quic SSL_set_quic_early_data_context() failed");
+        return NGX_ERROR;
+    }
+#endif
+
+    return NGX_OK;
+}
new file mode 100644
--- /dev/null
+++ b/src/event/quic/ngx_event_quic_ssl.h
@@ -0,0 +1,22 @@
+
+/*
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_EVENT_QUIC_SSL_H_INCLUDED_
+#define _NGX_EVENT_QUIC_SSL_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+ngx_int_t ngx_quic_init_connection(ngx_connection_t *c);
+
+ngx_int_t ngx_quic_handle_crypto_frame(ngx_connection_t *c,
+    ngx_quic_header_t *pkt, ngx_quic_frame_t *frame);
+
+ngx_int_t ngx_quic_crypto_input(ngx_connection_t *c,
+    ngx_quic_frame_t *frame, void *data);
+
+#endif /* _NGX_EVENT_QUIC_SSL_H_INCLUDED_ */