[PATCH 5 of 8] QUIC: reusing crypto contexts for packet protection

Sergey Kandaurov pluknet at nginx.com
Mon Oct 23 22:37:58 UTC 2023


> On 13 Oct 2023, at 19:13, Sergey Kandaurov <pluknet at nginx.com> wrote:
> 
> [..]
> 
> I was pondering on reusing a static crypto context
> to make generating Retry packets more lightweight.
> Known fixed values for key and nonce make it possible to create
> a single context and reuse it over all Retry packets. 
> 
> Note that the context memory is kept for reuse after the first
> retry, it will be freed eventually on process exit,
> the operating system will take care of it.
> Not sure though this is a good solution.
> 
> diff --git a/src/event/quic/ngx_event_quic_protection.c b/src/event/quic/ngx_event_quic_protection.c
> --- a/src/event/quic/ngx_event_quic_protection.c
> +++ b/src/event/quic/ngx_event_quic_protection.c
> @@ -872,7 +872,6 @@ ngx_quic_create_retry_packet(ngx_quic_he
> {
>     u_char              *start;
>     ngx_str_t            ad, itag;
> -    ngx_quic_secret_t    secret;
>     ngx_quic_ciphers_t   ciphers;
> 
>     /* 5.8.  Retry Packet Integrity */
> @@ -882,6 +881,8 @@ ngx_quic_create_retry_packet(ngx_quic_he
>         "\x46\x15\x99\xd3\x5d\x63\x2b\xf2\x23\x98\x25\xbb";
>     static ngx_str_t  in = ngx_string("");
> 
> +    static ngx_quic_secret_t  secret;
> +
>     ad.data = res->data;
>     ad.len = ngx_quic_create_retry_itag(pkt, ad.data, &start);
> 
> @@ -893,6 +894,10 @@ ngx_quic_create_retry_packet(ngx_quic_he
>                    "quic retry itag len:%uz %xV", ad.len, &ad);
> #endif
> 
> +    if (secret.ctx) {
> +        goto seal;
> +    }
> +
>     if (ngx_quic_ciphers(0, &ciphers, pkt->level) == NGX_ERROR) {
>         return NGX_ERROR;
>     }
> @@ -905,14 +910,14 @@ ngx_quic_create_retry_packet(ngx_quic_he
>         return NGX_ERROR;
>     }
> 
> +seal:
> +
>     if (ngx_quic_crypto_seal(&secret, &itag, nonce, &in, &ad, pkt->log)
>         != NGX_OK)
>     {
>         return NGX_ERROR;
>     }
> 
> -    ngx_quic_crypto_cleanup(&secret);
> -
>     res->len = itag.data + itag.len - start;
>     res->data = start;
> 
> 

Another approach is to create a single context in the master process,
used to cleanup the context in explicit manner on process shutdown.
Note that ngx_quic_conf_t is already taken, had to pick a new one.

# HG changeset patch
# User Sergey Kandaurov <pluknet at nginx.com>
# Date 1698099112 -14400
#      Tue Oct 24 02:11:52 2023 +0400
# Node ID acdb54a32cdebf5cd987cf343b5e836e12d50967
# Parent  af5ab04c7037f33960efc595cd76b4f4a0bf4a86
QUIC: reusing crypto context for Retry packets.

diff --git a/src/event/quic/ngx_event_quic.c b/src/event/quic/ngx_event_quic.c
--- a/src/event/quic/ngx_event_quic.c
+++ b/src/event/quic/ngx_event_quic.c
@@ -30,10 +30,13 @@ static ngx_int_t ngx_quic_handle_frames(
 
 static void ngx_quic_push_handler(ngx_event_t *ev);
 
+static void *ngx_quic_create_conf(ngx_cycle_t *cycle);
+static void ngx_quic_cleanup(void *data);
+
 
 static ngx_core_module_t  ngx_quic_module_ctx = {
     ngx_string("quic"),
-    NULL,
+    ngx_quic_create_conf,
     NULL
 };
 
@@ -1454,3 +1457,52 @@ ngx_quic_shutdown_quic(ngx_connection_t 
         ngx_quic_finalize_connection(c, qc->shutdown_code, qc->shutdown_reason);
     }
 }
+
+
+static void *
+ngx_quic_create_conf(ngx_cycle_t *cycle)
+{
+    ngx_quic_ciphers_t       ciphers;
+    ngx_pool_cleanup_t      *cln;
+    ngx_quic_module_conf_t  *qcf;
+
+    /* RFC 9001, 5.8.  Retry Packet Integrity */
+    static ngx_quic_md_t  key = {
+        NGX_QUIC_AES_128_KEY_LEN,
+        "\xbe\x0c\x69\x0b\x9f\x66\x57\x5a\x1d\x76\x6b\x54\xe3\x68\xc8\x4e"
+    };
+
+    qcf = ngx_pcalloc(cycle->pool, sizeof(ngx_quic_module_conf_t));
+    if (qcf == NULL) {
+        return NULL;
+    }
+
+    if (ngx_quic_ciphers(NGX_QUIC_INITIAL_CIPHER, &ciphers) == NGX_ERROR) {
+        return NULL;
+    }
+
+    cln = ngx_pool_cleanup_add(cycle->pool, 0);
+    if (cln == NULL) {
+        return NULL;
+    }
+
+    cln->handler = ngx_quic_cleanup;
+    cln->data = qcf;
+
+    if (ngx_quic_crypto_init(ciphers.c, &qcf->retry, &key, 1, cycle->log)
+        == NGX_ERROR)
+    {
+        return NULL;
+    }
+
+    return qcf;
+}
+
+
+static void
+ngx_quic_cleanup(void *data)
+{
+    ngx_quic_module_conf_t *qcf = data;
+
+    ngx_quic_crypto_cleanup(&qcf->retry);
+}
diff --git a/src/event/quic/ngx_event_quic_connection.h b/src/event/quic/ngx_event_quic_connection.h
--- a/src/event/quic/ngx_event_quic_connection.h
+++ b/src/event/quic/ngx_event_quic_connection.h
@@ -41,6 +41,11 @@ typedef struct ngx_quic_keys_s        ng
 #include <ngx_event_quic_socket.h>
 
 
+typedef struct {
+    ngx_quic_secret_t  retry;
+} ngx_quic_module_conf_t;
+
+
 /* RFC 9002, 6.2.2.  Handshakes and New Paths: kInitialRtt */
 #define NGX_QUIC_INITIAL_RTT                 333 /* ms */
 
@@ -293,4 +298,8 @@ void ngx_quic_connstate_dbg(ngx_connecti
 #define ngx_quic_connstate_dbg(c)
 #endif
 
+
+extern ngx_module_t ngx_quic_module;
+
+
 #endif /* _NGX_EVENT_QUIC_CONNECTION_H_INCLUDED_ */
diff --git a/src/event/quic/ngx_event_quic_protection.c b/src/event/quic/ngx_event_quic_protection.c
--- a/src/event/quic/ngx_event_quic_protection.c
+++ b/src/event/quic/ngx_event_quic_protection.c
@@ -13,10 +13,6 @@
 /* RFC 9001, 5.4.1.  Header Protection Application: 5-byte mask */
 #define NGX_QUIC_HP_LEN               5
 
-#define NGX_QUIC_AES_128_KEY_LEN      16
-
-#define NGX_QUIC_INITIAL_CIPHER       TLS1_3_CK_AES_128_GCM_SHA256
-
 
 static ngx_int_t ngx_hkdf_expand(u_char *out_key, size_t out_len,
     const EVP_MD *digest, const u_char *prk, size_t prk_len,
@@ -929,15 +925,11 @@ ngx_quic_create_packet(ngx_quic_header_t
 static ngx_int_t
 ngx_quic_create_retry_packet(ngx_quic_header_t *pkt, ngx_str_t *res)
 {
-    u_char              *start;
-    ngx_str_t            ad, itag;
-    ngx_quic_md_t        key;
-    ngx_quic_secret_t    secret;
-    ngx_quic_ciphers_t   ciphers;
+    u_char                  *start;
+    ngx_str_t                ad, itag;
+    ngx_quic_module_conf_t  *qcf;
 
-    /* 5.8.  Retry Packet Integrity */
-    static u_char     key_data[16] =
-        "\xbe\x0c\x69\x0b\x9f\x66\x57\x5a\x1d\x76\x6b\x54\xe3\x68\xc8\x4e";
+    /* RFC 9001, 5.8.  Retry Packet Integrity */
     static u_char     nonce[NGX_QUIC_IV_LEN] =
         "\x46\x15\x99\xd3\x5d\x63\x2b\xf2\x23\x98\x25\xbb";
     static ngx_str_t  in = ngx_string("");
@@ -953,28 +945,15 @@ ngx_quic_create_retry_packet(ngx_quic_he
                    "quic retry itag len:%uz %xV", ad.len, &ad);
 #endif
 
-    if (ngx_quic_ciphers(NGX_QUIC_INITIAL_CIPHER, &ciphers) == NGX_ERROR) {
-        return NGX_ERROR;
-    }
+    qcf = (ngx_quic_module_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,
+                                                  ngx_quic_module);
 
-    key.len = sizeof(key_data);
-    ngx_memcpy(key.data, key_data, sizeof(key_data));
-
-    if (ngx_quic_crypto_init(ciphers.c, &secret, &key, 1, pkt->log)
-        == NGX_ERROR)
+    if (ngx_quic_crypto_seal(&qcf->retry, &itag, nonce, &in, &ad, pkt->log)
+        != NGX_OK)
     {
         return NGX_ERROR;
     }
 
-    if (ngx_quic_crypto_seal(&secret, &itag, nonce, &in, &ad, pkt->log)
-        != NGX_OK)
-    {
-        ngx_quic_crypto_cleanup(&secret);
-        return NGX_ERROR;
-    }
-
-    ngx_quic_crypto_cleanup(&secret);
-
     res->len = itag.data + itag.len - start;
     res->data = start;
 
diff --git a/src/event/quic/ngx_event_quic_protection.h b/src/event/quic/ngx_event_quic_protection.h
--- a/src/event/quic/ngx_event_quic_protection.h
+++ b/src/event/quic/ngx_event_quic_protection.h
@@ -23,6 +23,10 @@
 /* largest hash used in TLS is SHA-384 */
 #define NGX_QUIC_MAX_MD_SIZE          48
 
+#define NGX_QUIC_AES_128_KEY_LEN      16
+
+#define NGX_QUIC_INITIAL_CIPHER       TLS1_3_CK_AES_128_GCM_SHA256
+
 
 #ifdef OPENSSL_IS_BORINGSSL
 #define ngx_quic_cipher_t             EVP_AEAD

-- 
Sergey Kandaurov


More information about the nginx-devel mailing list