changeset 8285:f85749b60e58 quic

Removed memory allocations from encryption code. + ngx_quic_encrypt(): - no longer accepts pool as argument - pkt is 1st arg - payload is passed as pkt->payload - performs encryption to the specified static buffer + ngx_quic_create_long/short_packet() functions: - single buffer for everything, allocated by caller - buffer layout is: [ ad | payload | TAG ] the result is in the beginning of buffer with proper length - nonce is calculated on stack - log is passed explicitly, pkt is 1st arg - no more allocations inside + ngx_quic_create_long_header(): - args changed: no need to pass str_t + added ngx_quic_create_short_header()
author Vladimir Homutov <vl@nginx.com>
date Thu, 26 Mar 2020 12:11:50 +0300
parents 2935a11c55b6
children c7185bc5b4d9
files src/event/ngx_event_quic.c src/event/ngx_event_quic_protection.c src/event/ngx_event_quic_protection.h src/event/ngx_event_quic_transport.c src/event/ngx_event_quic_transport.h
diffstat 5 files changed, 101 insertions(+), 118 deletions(-) [+]
line wrap: on
line diff
--- a/src/event/ngx_event_quic.c
+++ b/src/event/ngx_event_quic.c
@@ -1365,8 +1365,9 @@ static ngx_int_t
 ngx_quic_send_packet(ngx_connection_t *c, ngx_quic_connection_t *qc,
     enum ssl_encryption_level_t level, ngx_str_t *payload)
 {
-    ngx_str_t         res;
-    ngx_quic_header_t pkt;
+    ngx_str_t          res;
+    ngx_quic_header_t  pkt;
+    static u_char      buf[65535];
 
     static ngx_str_t  initial_token = ngx_null_string;
 
@@ -1377,6 +1378,7 @@ ngx_quic_send_packet(ngx_connection_t *c
     pkt.level = level;
     pkt.dcid = qc->dcid;
     pkt.scid = qc->scid;
+    pkt.payload = *payload;
 
     if (level == ssl_encryption_initial) {
         pkt.number = &qc->initial_pn;
@@ -1394,9 +1396,12 @@ ngx_quic_send_packet(ngx_connection_t *c
         pkt.secret = &qc->secrets.server.ad;
     }
 
-    if (ngx_quic_encrypt(c->pool, c->ssl->connection, &pkt, payload, &res)
-        != NGX_OK)
-    {
+    // TODO: ensure header size + payload.len + crypto tail fits into packet
+    //       (i.e. limit payload while pushing frames to < 65k)
+
+    res.data = buf;
+
+    if (ngx_quic_encrypt(&pkt, c->ssl->connection, &res) != NGX_OK) {
         return NGX_ERROR;
     }
 
--- a/src/event/ngx_event_quic_protection.c
+++ b/src/event/ngx_event_quic_protection.c
@@ -43,21 +43,19 @@ static ngx_int_t ngx_quic_ciphers(ngx_ss
 static ngx_int_t ngx_quic_tls_open(ngx_pool_t *pool, const ngx_quic_cipher_t *cipher,
     ngx_quic_secret_t *s, ngx_str_t *out, u_char *nonce, ngx_str_t *in,
     ngx_str_t *ad);
-static ngx_int_t ngx_quic_tls_seal(ngx_pool_t *pool,
-    const ngx_quic_cipher_t *cipher, ngx_quic_secret_t *s, ngx_str_t *out,
-    u_char *nonce, ngx_str_t *in, ngx_str_t *ad);
+static ngx_int_t ngx_quic_tls_seal(const ngx_quic_cipher_t *cipher,
+    ngx_quic_secret_t *s, ngx_str_t *out, u_char *nonce, ngx_str_t *in,
+    ngx_str_t *ad, ngx_log_t *log);
 static ngx_int_t ngx_quic_tls_hp(ngx_log_t *log, const EVP_CIPHER *cipher,
     ngx_quic_secret_t *s, u_char *out, u_char *in);
 static ngx_int_t ngx_quic_hkdf_expand(ngx_pool_t *pool, const EVP_MD *digest,
     ngx_str_t *out, ngx_str_t *label, const uint8_t *prk, size_t prk_len);
 
-static ngx_int_t ngx_quic_create_long_packet(ngx_pool_t *pool,
-    ngx_ssl_conn_t *ssl_conn, ngx_quic_header_t *pkt, ngx_str_t *in,
-    ngx_str_t *res);
+static ssize_t ngx_quic_create_long_packet(ngx_quic_header_t *pkt,
+    ngx_ssl_conn_t *ssl_conn, ngx_str_t *res);
 
-static ngx_int_t ngx_quic_create_short_packet(ngx_pool_t *pool,
-    ngx_ssl_conn_t *ssl_conn, ngx_quic_header_t *pkt, ngx_str_t *in,
-    ngx_str_t *res);
+static ssize_t ngx_quic_create_short_packet(ngx_quic_header_t *pkt,
+    ngx_ssl_conn_t *ssl_conn,  ngx_str_t *res);
 
 
 static ngx_int_t
@@ -467,19 +465,9 @@ ngx_quic_tls_open(ngx_pool_t *pool, cons
 
 
 static ngx_int_t
-ngx_quic_tls_seal(ngx_pool_t *pool, const ngx_quic_cipher_t *cipher,
-    ngx_quic_secret_t *s, ngx_str_t *out, u_char *nonce, ngx_str_t *in,
-    ngx_str_t *ad)
+ngx_quic_tls_seal(const ngx_quic_cipher_t *cipher, ngx_quic_secret_t *s,
+    ngx_str_t *out, u_char *nonce, ngx_str_t *in, ngx_str_t *ad, ngx_log_t *log)
 {
-    ngx_log_t  *log;
-
-    log = pool->log; // TODO: pass log ?
-
-    out->len = in->len + EVP_GCM_TLS_TAG_LEN;
-    out->data = ngx_pnalloc(pool, out->len);
-    if (out->data == NULL) {
-        return NGX_ERROR;
-    }
 
 #ifdef OPENSSL_IS_BORINGSSL
     EVP_AEAD_CTX  *ctx;
@@ -682,161 +670,128 @@ ngx_quic_set_encryption_secret(ngx_pool_
 }
 
 
-static ngx_int_t
-ngx_quic_create_long_packet(ngx_pool_t *pool, ngx_ssl_conn_t *ssl_conn,
-    ngx_quic_header_t *pkt, ngx_str_t *payload, ngx_str_t *res)
+static ssize_t
+ngx_quic_create_long_packet(ngx_quic_header_t *pkt, ngx_ssl_conn_t *ssl_conn,
+    ngx_str_t *res)
 {
-    u_char              *p, *pnp, *nonce, *sample, *packet;
+    u_char              *pnp, *sample;
     uint64_t             pn;
-    ngx_log_t           *log;
     ngx_str_t            ad, out;
     ngx_quic_ciphers_t   ciphers;
-    u_char               mask[16];
+    u_char               nonce[12], mask[16];
 
-    log = pool->log;
-
-    out.len = payload->len + EVP_GCM_TLS_TAG_LEN;
+    out.len = pkt->payload.len + EVP_GCM_TLS_TAG_LEN;
 
-    ad.data = ngx_alloc(NGX_QUIC_MAX_LONG_HEADER, log);
-    if (ad.data == 0) {
-        return NGX_ERROR;
-    }
+    ad.data = res->data;
+    ad.len = ngx_quic_create_long_header(pkt, ad.data, out.len, &pnp);
 
-    ad.len = ngx_quic_create_long_header(pkt, &ad, out.len, &pnp);
+    out.data = res->data + ad.len;
 
-    ngx_quic_hexdump0(log, "ad", ad.data, ad.len);
+    ngx_quic_hexdump0(pkt->log, "ad", ad.data, ad.len);
 
     if (ngx_quic_ciphers(ssl_conn, &ciphers, pkt->level) == NGX_ERROR) {
         return NGX_ERROR;
     }
 
-    nonce = ngx_pstrdup(pool, &pkt->secret->iv);
+    ngx_memcpy(nonce, pkt->secret->iv.data, pkt->secret->iv.len);
     pn = *pkt->number;
     nonce[11] ^= pn;
 
-    ngx_quic_hexdump0(log, "server_iv", pkt->secret->iv.data, 12);
-    ngx_quic_hexdump0(log, "nonce", nonce, 12);
+    ngx_quic_hexdump0(pkt->log, "server_iv", pkt->secret->iv.data, 12);
+    ngx_quic_hexdump0(pkt->log, "nonce", nonce, 12);
 
-    if (ngx_quic_tls_seal(pool, ciphers.c, pkt->secret, &out,
-                          nonce, payload, &ad)
+    if (ngx_quic_tls_seal(ciphers.c, pkt->secret, &out,
+                          nonce, &pkt->payload, &ad, pkt->log)
         != NGX_OK)
     {
         return NGX_ERROR;
     }
 
     sample = &out.data[3]; // pnl=0
-    if (ngx_quic_tls_hp(log, ciphers.hp, pkt->secret, mask, sample) != NGX_OK) {
+    if (ngx_quic_tls_hp(pkt->log, ciphers.hp, pkt->secret, mask, sample)
+        != NGX_OK)
+    {
         return NGX_ERROR;
     }
 
-    ngx_quic_hexdump0(log, "sample", sample, 16);
-    ngx_quic_hexdump0(log, "mask", mask, 16);
-    ngx_quic_hexdump0(log, "hp_key", pkt->secret->hp.data, 16);
+    ngx_quic_hexdump0(pkt->log, "sample", sample, 16);
+    ngx_quic_hexdump0(pkt->log, "mask", mask, 16);
+    ngx_quic_hexdump0(pkt->log, "hp_key", pkt->secret->hp.data, 16);
 
     // header protection, pnl = 0
     ad.data[0] ^= mask[0] & 0x0f;
     *pnp ^= mask[1];
 
-    packet = ngx_alloc(ad.len + out.len, log);
-    if (packet == 0) {
-        return NGX_ERROR;
-    }
-
-    p = ngx_cpymem(packet, ad.data, ad.len);
-    p = ngx_cpymem(p, out.data, out.len);
-
-    res->data = packet;
-    res->len = p - packet;
+    res->len = ad.len + out.len;
 
     return NGX_OK;
 }
 
 
-static ngx_int_t
-ngx_quic_create_short_packet(ngx_pool_t *pool, ngx_ssl_conn_t *ssl_conn,
-    ngx_quic_header_t *pkt, ngx_str_t *payload, ngx_str_t *res)
+static ssize_t
+ngx_quic_create_short_packet(ngx_quic_header_t *pkt, ngx_ssl_conn_t *ssl_conn,
+    ngx_str_t *res)
 {
-    u_char              *p, *pnp, *nonce, *sample, *packet;
-    ngx_log_t           *log;
+    u_char              *pnp, *sample;
     ngx_str_t            ad, out;
     ngx_quic_ciphers_t   ciphers;
-    u_char               mask[16];
-
-    log = pool->log;
+    u_char               nonce[12], mask[16];
 
-    out.len = payload->len + EVP_GCM_TLS_TAG_LEN;
-
-    ad.data = ngx_alloc(NGX_QUIC_MAX_SHORT_HEADER, log);
-    if (ad.data == 0) {
-        return NGX_ERROR;
-    }
+    out.len = pkt->payload.len + EVP_GCM_TLS_TAG_LEN;
 
-    p = ad.data;
-
-    *p++ = 0x40;
-
-    p = ngx_cpymem(p, pkt->scid.data, pkt->scid.len);
+    ad.data = res->data;
+    ad.len = ngx_quic_create_short_header(pkt, ad.data, out.len, &pnp);
 
-    pnp = p;
-
-    *p++ = (*pkt->number);
-
-    ad.len = p - ad.data;
-
-    ngx_quic_hexdump0(log, "ad", ad.data, ad.len);
+    ngx_quic_hexdump0(pkt->log, "ad", ad.data, ad.len);
 
     if (ngx_quic_ciphers(ssl_conn, &ciphers, pkt->level) == NGX_ERROR) {
         return NGX_ERROR;
     }
 
-    nonce = ngx_pstrdup(pool, &pkt->secret->iv);
+    ngx_memcpy(nonce, pkt->secret->iv.data, pkt->secret->iv.len);
     if (pkt->level == ssl_encryption_handshake
         || pkt->level == ssl_encryption_application)
     {
         nonce[11] ^= *pkt->number;
     }
 
-    ngx_quic_hexdump0(log, "server_iv", pkt->secret->iv.data, 12);
-    ngx_quic_hexdump0(log, "nonce", nonce, 12);
+    ngx_quic_hexdump0(pkt->log, "server_iv", pkt->secret->iv.data, 12);
+    ngx_quic_hexdump0(pkt->log, "nonce", nonce, 12);
 
-    if (ngx_quic_tls_seal(pool, ciphers.c, pkt->secret, &out,
-                          nonce, payload, &ad)
+    out.data = res->data + ad.len;
+
+    if (ngx_quic_tls_seal(ciphers.c, pkt->secret, &out, nonce, &pkt->payload,
+                          &ad, pkt->log)
         != NGX_OK)
     {
         return NGX_ERROR;
     }
 
-    ngx_quic_hexdump0(log, "out", out.data, out.len);
+    ngx_quic_hexdump0(pkt->log, "out", out.data, out.len);
 
     sample = &out.data[3]; // pnl=0
-    if (ngx_quic_tls_hp(log, ciphers.hp, pkt->secret, mask, sample) != NGX_OK) {
+    if (ngx_quic_tls_hp(pkt->log, ciphers.hp, pkt->secret, mask, sample)
+        != NGX_OK)
+    {
         return NGX_ERROR;
     }
 
-    ngx_quic_hexdump0(log, "sample", sample, 16);
-    ngx_quic_hexdump0(log, "mask", mask, 16);
-    ngx_quic_hexdump0(log, "hp_key", pkt->secret->hp.data, 16);
+    ngx_quic_hexdump0(pkt->log, "sample", sample, 16);
+    ngx_quic_hexdump0(pkt->log, "mask", mask, 16);
+    ngx_quic_hexdump0(pkt->log, "hp_key", pkt->secret->hp.data, 16);
 
     // header protection, pnl = 0
     ad.data[0] ^= mask[0] & 0x1f;
     *pnp ^= mask[1];
 
-    packet = ngx_alloc(ad.len + out.len, log);
-    if (packet == 0) {
-        return NGX_ERROR;
-    }
+    res->len = ad.len + out.len;
 
-    p = ngx_cpymem(packet, ad.data, ad.len);
-    p = ngx_cpymem(p, out.data, out.len);
-
-    ngx_quic_hexdump0(log, "packet", packet, p - packet);
-
-    res->data = packet;
-    res->len = p - packet;
+    ngx_quic_hexdump0(pkt->log, "packet", res->data, res->len);
 
     return NGX_OK;
 }
 
+
 static uint64_t
 ngx_quic_parse_pn(u_char **pos, ngx_int_t len, u_char *mask)
 {
@@ -855,15 +810,15 @@ ngx_quic_parse_pn(u_char **pos, ngx_int_
 }
 
 
-ngx_int_t
-ngx_quic_encrypt(ngx_pool_t *pool, ngx_ssl_conn_t *ssl_conn,
-    ngx_quic_header_t *pkt, ngx_str_t *payload, ngx_str_t *res)
+ssize_t
+ngx_quic_encrypt(ngx_quic_header_t *pkt, ngx_ssl_conn_t *ssl_conn,
+    ngx_str_t *res)
 {
     if (pkt->level == ssl_encryption_application) {
-        return ngx_quic_create_short_packet(pool, ssl_conn, pkt, payload, res);
+        return ngx_quic_create_short_packet(pkt, ssl_conn, res);
     }
 
-    return ngx_quic_create_long_packet(pool, ssl_conn, pkt, payload, res);
+    return ngx_quic_create_long_packet(pkt, ssl_conn, res);
 }
 
 
--- a/src/event/ngx_event_quic_protection.h
+++ b/src/event/ngx_event_quic_protection.h
@@ -36,8 +36,8 @@ int ngx_quic_set_encryption_secret(ngx_p
     enum ssl_encryption_level_t level, const uint8_t *secret, size_t secret_len,
     ngx_quic_peer_secrets_t *qsec);
 
-ngx_int_t ngx_quic_encrypt(ngx_pool_t *pool, ngx_ssl_conn_t *ssl_conn,
-    ngx_quic_header_t *pkt, ngx_str_t *payload, ngx_str_t *res);
+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_pool_t *pool, ngx_ssl_conn_t *ssl_conn,
     ngx_quic_header_t *pkt);
--- a/src/event/ngx_event_quic_transport.c
+++ b/src/event/ngx_event_quic_transport.c
@@ -341,12 +341,12 @@ ngx_quic_parse_long_header(ngx_quic_head
 
 
 size_t
-ngx_quic_create_long_header(ngx_quic_header_t *pkt, ngx_str_t *out,
+ngx_quic_create_long_header(ngx_quic_header_t *pkt, u_char *out,
     size_t pkt_len, u_char **pnp)
 {
-    u_char    *p, *start;
+    u_char  *p, *start;
 
-    p = start = out->data;
+    p = start = out;
 
     *p++ = pkt->flags;
 
@@ -372,6 +372,26 @@ ngx_quic_create_long_header(ngx_quic_hea
 }
 
 
+size_t
+ngx_quic_create_short_header(ngx_quic_header_t *pkt, u_char *out,
+    size_t pkt_len, u_char **pnp)
+{
+    u_char  *p, *start;
+
+    p = start = out;
+
+    *p++ = 0x40;
+
+    p = ngx_cpymem(p, pkt->scid.data, pkt->scid.len);
+
+    *pnp = p;
+
+    *p++ = (*pkt->number);
+
+    return p - start;
+}
+
+
 ngx_int_t
 ngx_quic_parse_short_header(ngx_quic_header_t *pkt, ngx_str_t *dcid)
 {
--- a/src/event/ngx_event_quic_transport.h
+++ b/src/event/ngx_event_quic_transport.h
@@ -255,11 +255,14 @@ typedef struct {
 u_char *ngx_quic_error_text(uint64_t error_code);
 
 ngx_int_t ngx_quic_parse_long_header(ngx_quic_header_t *pkt);
-size_t ngx_quic_create_long_header(ngx_quic_header_t *pkt, ngx_str_t *out,
+size_t ngx_quic_create_long_header(ngx_quic_header_t *pkt, u_char *out,
     size_t pkt_len, u_char **pnp);
 
 ngx_int_t ngx_quic_parse_short_header(ngx_quic_header_t *pkt,
     ngx_str_t *dcid);
+size_t ngx_quic_create_short_header(ngx_quic_header_t *pkt, u_char *out,
+    size_t pkt_len, u_char **pnp);
+
 ngx_int_t ngx_quic_parse_initial_header(ngx_quic_header_t *pkt);
 ngx_int_t ngx_quic_parse_handshake_header(ngx_quic_header_t *pkt);