changeset 8170:53a5cdbe500c quic

QUIC add_handshake_data callback, varint routines.
author Sergey Kandaurov <pluknet@nginx.com>
date Fri, 28 Feb 2020 13:09:51 +0300
parents bd006bd520a9
children 4daf03d2bd0a
files auto/modules src/event/ngx_event_openssl.c src/event/ngx_event_quic.h src/http/ngx_http_request.c
diffstat 4 files changed, 153 insertions(+), 45 deletions(-) [+]
line wrap: on
line diff
--- a/auto/modules
+++ b/auto/modules
@@ -1245,7 +1245,8 @@ if [ $USE_OPENSSL = YES ]; then
     ngx_module_deps="src/event/ngx_event_openssl.h \
                      src/event/ngx_event_quic.h"
     ngx_module_srcs="src/event/ngx_event_openssl.c
-                     src/event/ngx_event_openssl_stapling.c"
+                     src/event/ngx_event_openssl_stapling.c
+                     src/event/ngx_event_quic.c"
     ngx_module_libs=
     ngx_module_link=YES
     ngx_module_order=
--- a/src/event/ngx_event_openssl.c
+++ b/src/event/ngx_event_openssl.c
@@ -362,22 +362,159 @@ static int
 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             buf[512];
-    ngx_int_t          m;
-    ngx_connection_t  *c;
+    u_char                  buf[512], *p, *cipher, *clear, *ad;
+    size_t                  ad_len, clear_len;
+    ngx_int_t               m;
+    ngx_str_t              *server_key, *server_iv, *server_hp;
+    ngx_connection_t       *c;
+    ngx_quic_connection_t  *qc;
 
     c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);
+    qc = c->quic;
+
+    switch (level) {
+
+    case ssl_encryption_initial:
+        server_key = &qc->server_in_key;
+        server_iv = &qc->server_in_iv;
+        server_hp = &qc->server_in_hp;
+        break;
+
+    case ssl_encryption_handshake:
+        server_key = &qc->server_hs_key;
+        server_iv = &qc->server_hs_iv;
+        server_hp = &qc->server_hs_hp;
+        break;
+
+    default:
+        return 0;
+    }
 
     m = ngx_hex_dump(buf, (u_char *) data, ngx_min(len, 256)) - buf;
     ngx_log_debug5(NGX_LOG_DEBUG_EVENT, c->log, 0,
                    "quic_add_handshake_data: %*s%s, len: %uz, level:%d",
                    m, buf, len < 512 ? "" : "...", len, (int) level);
 
-    if (!(SSL_provide_quic_data(ssl_conn, level, data, len))) {
-        ERR_print_errors_fp(stderr);
+    clear = ngx_alloc(4 + len + 5 /*minimal ACK*/, c->log);
+    if (clear == 0) {
+        return 0;
+    }
+
+    p = clear;
+    ngx_quic_build_int(&p, 6);	// crypto frame
+    ngx_quic_build_int(&p, 0);
+    ngx_quic_build_int(&p, len);
+    p = ngx_cpymem(p, data, len);
+
+    ngx_quic_build_int(&p, 2);	// ack frame
+    ngx_quic_build_int(&p, 0);
+    ngx_quic_build_int(&p, 0);
+    ngx_quic_build_int(&p, 0);
+    ngx_quic_build_int(&p, 0);
+
+    clear_len = p - clear;
+    size_t cipher_len = clear_len + 16 /*expansion*/;
+
+    ad = ngx_alloc(346 /*max header*/, c->log);
+    if (ad == 0) {
+        return 0;
+    }
+
+    p = ad;
+    if (level == ssl_encryption_initial) {
+        *p++ = 0xc0;	// initial, packet number len
+    } else if (level == ssl_encryption_handshake) {
+        *p++ = 0xe0;	// handshake, packet number len
+    }
+    *p++ = 0xff;
+    *p++ = 0x00;
+    *p++ = 0x00;
+    *p++ = 0x17;
+    *p++ = qc->scid.len;
+    p = ngx_cpymem(p, qc->scid.data, qc->scid.len);
+    *p++ = qc->dcid.len;
+    p = ngx_cpymem(p, qc->dcid.data, qc->dcid.len);
+    ngx_quic_build_int(&p, 0);	// token length
+    ngx_quic_build_int(&p, cipher_len); // length
+    u_char *pnp = p;
+    *p++ = 0;	// packet number 0
+
+    ad_len = p - ad;
+
+    m = ngx_hex_dump(buf, (u_char *) ad, ad_len) - buf;
+    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                   "quic_add_handshake_data ad: %*s, len: %uz",
+                   m, buf, ad_len);
+
+    EVP_AEAD_CTX *aead = EVP_AEAD_CTX_new(EVP_aead_aes_128_gcm(),
+                                          server_key->data,
+                                          server_key->len,
+                                          EVP_AEAD_DEFAULT_TAG_LENGTH);
+
+    cipher = ngx_alloc(cipher_len, c->log);
+    if (cipher == 0) {
         return 0;
     }
 
+    size_t out_len;
+
+    if (EVP_AEAD_CTX_seal(aead, cipher, &out_len, cipher_len,
+                          server_iv->data, server_iv->len,
+                          clear, clear_len, ad, ad_len)
+        != 1)
+    {
+        EVP_AEAD_CTX_free(aead);
+        ngx_ssl_error(NGX_LOG_INFO, c->log, 0,
+                      "EVP_AEAD_CTX_seal() failed");
+        return 0;
+    }
+
+    EVP_AEAD_CTX_free(aead);
+
+    EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
+    u_char *sample = cipher + 3; // pnl=0
+    uint8_t mask[16];
+    int outlen;
+
+    if (EVP_EncryptInit_ex(ctx, EVP_aes_128_ecb(), NULL, server_hp->data, NULL)
+        != 1)
+    {
+        EVP_CIPHER_CTX_free(ctx);
+        ngx_ssl_error(NGX_LOG_INFO, c->log, 0,
+                      "EVP_EncryptInit_ex() failed");
+        return 0;
+    }
+
+    if (!EVP_EncryptUpdate(ctx, mask, &outlen, sample, 16)) {
+        EVP_CIPHER_CTX_free(ctx);
+        ngx_ssl_error(NGX_LOG_INFO, c->log, 0,
+                      "EVP_EncryptUpdate() failed");
+        return 0;
+    }
+
+    EVP_CIPHER_CTX_free(ctx);
+
+    // header protection, pnl = 0
+    ad[0] ^= mask[0] & 0x0f;
+    *pnp ^= mask[1];
+
+printf("cipher_len %ld out_len %ld ad_len %ld\n", cipher_len, out_len, ad_len);
+
+    u_char *packet = ngx_alloc(ad_len + out_len, c->log);
+    if (packet == 0) {
+        return 0;
+    }
+
+    p = ngx_cpymem(packet, ad, ad_len);
+    p = ngx_cpymem(p, cipher, out_len);
+
+    m = ngx_hex_dump(buf, (u_char *) packet, p - packet) - buf;
+    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                   "quic_add_handshake_data packet: %*s, len: %uz",
+                   m, buf, p - packet);
+
+    c->send(c, packet, p - packet);
+
     return 1;
 }
 
--- a/src/event/ngx_event_quic.h
+++ b/src/event/ngx_event_quic.h
@@ -8,6 +8,9 @@
 #define _NGX_EVENT_QUIC_H_INCLUDED_
 
 
+#include <ngx_event_openssl.h>
+
+
 struct ngx_quic_connection_s {
     ngx_str_t   scid;
     ngx_str_t   dcid;
@@ -45,4 +48,9 @@ struct ngx_quic_connection_s {
 };
 
 
+uint64_t ngx_quic_parse_pn(u_char **pos, ngx_int_t len, u_char *mask);
+uint64_t ngx_quic_parse_int(u_char **pos);
+void ngx_quic_build_int(u_char **pos, uint64_t value);
+
+
 #endif /* _NGX_EVENT_QUIC_H_INCLUDED_ */
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -657,44 +657,6 @@ ngx_http_alloc_request(ngx_connection_t 
 
 #if (NGX_HTTP_SSL)
 
-static uint64_t
-ngx_quic_parse_int(u_char **pos)
-{
-    u_char      *p;
-    uint64_t     value;
-    ngx_uint_t   len;
-
-    p = *pos;
-    len = 1 << ((*p & 0xc0) >> 6);
-    value = *p++ & 0x3f;
-
-    while (--len) {
-        value = (value << 8) + *p++;
-    }
-
-    *pos = p;
-    return value;
-}
-
-
-static uint64_t
-ngx_quic_parse_pn(u_char **pos, ngx_int_t len, u_char *mask)
-{
-    u_char      *p;
-    uint64_t     value;
-
-    p = *pos;
-    value = *p++ ^ *mask++;
-
-    while (--len) {
-        value = (value << 8) + (*p++ ^ *mask++);
-    }
-
-    *pos = p;
-    return value;
-}
-
-
 static void
 ngx_http_quic_handshake(ngx_event_t *rev)
 {
@@ -1210,7 +1172,7 @@ ngx_http_quic_handshake(ngx_event_t *rev
     if (c->log->log_level & NGX_LOG_DEBUG_EVENT) {
         m = ngx_hex_dump(buf, cleartext, ngx_min(cleartext_len, 256)) - buf;
         ngx_log_debug4(NGX_LOG_DEBUG_HTTP, rev->log, 0,
-                       "quic packet: %*s%s, len: %uz",
+                       "quic packet payload: %*s%s, len: %uz",
                        m, buf, m < 512 ? "" : "...", cleartext_len);
     }
 #endif