diff src/event/ngx_event_openssl.c @ 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
line wrap: on
line diff
--- 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;
 }