changeset 8752:e19723c40d28 quic

QUIC: separate files for tokens related processing.
author Vladimir Homutov <vl@nginx.com>
date Tue, 13 Apr 2021 14:41:52 +0300
parents bc910a5ec737
children 46161c610919
files auto/modules src/event/quic/ngx_event_quic.c src/event/quic/ngx_event_quic_connection.h src/event/quic/ngx_event_quic_tokens.c src/event/quic/ngx_event_quic_tokens.h
diffstat 5 files changed, 317 insertions(+), 285 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_tokens.h \
                      src/event/quic/ngx_event_quic_ack.h \
                      src/event/quic/ngx_event_quic_output.h"
     ngx_module_srcs="src/event/quic/ngx_event_quic.c \
@@ -1356,6 +1357,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_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
@@ -7,7 +7,6 @@
 #include <ngx_config.h>
 #include <ngx_core.h>
 #include <ngx_event.h>
-#include <ngx_sha1.h>
 #include <ngx_event_quic_connection.h>
 
 
@@ -42,10 +41,6 @@ static ngx_quic_connection_t *ngx_quic_n
     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 void ngx_quic_address_hash(ngx_connection_t *c, ngx_uint_t no_port,
-    u_char buf[20]);
-static ngx_int_t ngx_quic_validate_token(ngx_connection_t *c,
-    u_char *key, 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);
 
@@ -620,32 +615,6 @@ ngx_quic_new_connection(ngx_connection_t
 }
 
 
-ngx_int_t
-ngx_quic_new_sr_token(ngx_connection_t *c, ngx_str_t *cid, u_char *secret,
-    u_char *token)
-{
-    ngx_str_t tmp;
-
-    tmp.data = secret;
-    tmp.len = NGX_QUIC_SR_KEY_LEN;
-
-    if (ngx_quic_derive_key(c->log, "sr_token_key", &tmp, cid, token,
-                            NGX_QUIC_SR_TOKEN_LEN)
-        != NGX_OK)
-    {
-        return NGX_ERROR;
-    }
-
-#if (NGX_DEBUG)
-    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
-                   "quic stateless reset token %*xs",
-                    (size_t) NGX_QUIC_SR_TOKEN_LEN, token);
-#endif
-
-    return NGX_OK;
-}
-
-
 static ngx_int_t
 ngx_quic_process_stateless_reset(ngx_connection_t *c, ngx_quic_header_t *pkt)
 {
@@ -690,253 +659,6 @@ ngx_quic_process_stateless_reset(ngx_con
 }
 
 
-ngx_int_t
-ngx_quic_new_token(ngx_connection_t *c, u_char *key, ngx_str_t *token,
-    ngx_str_t *odcid, time_t exp, ngx_uint_t is_retry)
-{
-    int                len, iv_len;
-    u_char            *p, *iv;
-    EVP_CIPHER_CTX    *ctx;
-    const EVP_CIPHER  *cipher;
-
-    u_char             in[NGX_QUIC_MAX_TOKEN_SIZE];
-
-    ngx_quic_address_hash(c, !is_retry, in);
-
-    p = in + 20;
-
-    p = ngx_cpymem(p, &exp, sizeof(time_t));
-
-    *p++ = is_retry ? 1 : 0;
-
-    if (odcid) {
-        *p++ = odcid->len;
-        p = ngx_cpymem(p, odcid->data, odcid->len);
-
-    } else {
-        *p++ = 0;
-    }
-
-    len = p - in;
-
-    cipher = EVP_aes_256_cbc();
-    iv_len = EVP_CIPHER_iv_length(cipher);
-
-    token->len = iv_len + len + EVP_CIPHER_block_size(cipher);
-    token->data = ngx_pnalloc(c->pool, token->len);
-    if (token->data == NULL) {
-        return NGX_ERROR;
-    }
-
-    ctx = EVP_CIPHER_CTX_new();
-    if (ctx == NULL) {
-        return NGX_ERROR;
-    }
-
-    iv = token->data;
-
-    if (RAND_bytes(iv, iv_len) <= 0
-        || !EVP_EncryptInit_ex(ctx, cipher, NULL, key, iv))
-    {
-        EVP_CIPHER_CTX_free(ctx);
-        return NGX_ERROR;
-    }
-
-    token->len = iv_len;
-
-    if (EVP_EncryptUpdate(ctx, token->data + token->len, &len, in, len) != 1) {
-        EVP_CIPHER_CTX_free(ctx);
-        return NGX_ERROR;
-    }
-
-    token->len += len;
-
-    if (EVP_EncryptFinal_ex(ctx, token->data + token->len, &len) <= 0) {
-        EVP_CIPHER_CTX_free(ctx);
-        return NGX_ERROR;
-    }
-
-    token->len += len;
-
-    EVP_CIPHER_CTX_free(ctx);
-
-#ifdef NGX_QUIC_DEBUG_PACKETS
-    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
-                   "quic new token len:%uz %xV", token->len, token);
-#endif
-
-    return NGX_OK;
-}
-
-
-static void
-ngx_quic_address_hash(ngx_connection_t *c, ngx_uint_t no_port, u_char buf[20])
-{
-    size_t                len;
-    u_char               *data;
-    ngx_sha1_t            sha1;
-    struct sockaddr_in   *sin;
-#if (NGX_HAVE_INET6)
-    struct sockaddr_in6  *sin6;
-#endif
-
-    len = (size_t) c->socklen;
-    data = (u_char *) c->sockaddr;
-
-    if (no_port) {
-        switch (c->sockaddr->sa_family) {
-
-#if (NGX_HAVE_INET6)
-        case AF_INET6:
-            sin6 = (struct sockaddr_in6 *) c->sockaddr;
-
-            len = sizeof(struct in6_addr);
-            data = sin6->sin6_addr.s6_addr;
-
-            break;
-#endif
-
-        case AF_INET:
-            sin = (struct sockaddr_in *) c->sockaddr;
-
-            len = sizeof(in_addr_t);
-            data = (u_char *) &sin->sin_addr;
-
-            break;
-        }
-    }
-
-    ngx_sha1_init(&sha1);
-    ngx_sha1_update(&sha1, data, len);
-    ngx_sha1_final(buf, &sha1);
-}
-
-
-static ngx_int_t
-ngx_quic_validate_token(ngx_connection_t *c, u_char *key,
-    ngx_quic_header_t *pkt)
-{
-    int                len, tlen, iv_len;
-    u_char            *iv, *p;
-    time_t             now, exp;
-    size_t             total;
-    ngx_str_t          odcid;
-    EVP_CIPHER_CTX    *ctx;
-    const EVP_CIPHER  *cipher;
-
-    u_char             addr_hash[20];
-    u_char             tdec[NGX_QUIC_MAX_TOKEN_SIZE];
-
-    /* Retry token or NEW_TOKEN in a previous connection */
-
-    cipher = EVP_aes_256_cbc();
-    iv = pkt->token.data;
-    iv_len = EVP_CIPHER_iv_length(cipher);
-
-    /* sanity checks */
-
-    if (pkt->token.len < (size_t) iv_len + EVP_CIPHER_block_size(cipher)) {
-        goto garbage;
-    }
-
-    if (pkt->token.len > (size_t) iv_len + NGX_QUIC_MAX_TOKEN_SIZE) {
-        goto garbage;
-    }
-
-    ctx = EVP_CIPHER_CTX_new();
-    if (ctx == NULL) {
-        return NGX_ERROR;
-    }
-
-    if (!EVP_DecryptInit_ex(ctx, cipher, NULL, key, iv)) {
-        EVP_CIPHER_CTX_free(ctx);
-        return NGX_ERROR;
-    }
-
-    p = pkt->token.data + iv_len;
-    len = pkt->token.len - iv_len;
-
-    if (EVP_DecryptUpdate(ctx, tdec, &len, p, len) != 1) {
-        EVP_CIPHER_CTX_free(ctx);
-        goto garbage;
-    }
-    total = len;
-
-    if (EVP_DecryptFinal_ex(ctx, tdec + len, &tlen) <= 0) {
-        EVP_CIPHER_CTX_free(ctx);
-        goto garbage;
-    }
-    total += tlen;
-
-    EVP_CIPHER_CTX_free(ctx);
-
-    if (total < (20 + sizeof(time_t) + 2)) {
-        goto garbage;
-    }
-
-    p = tdec + 20;
-
-    ngx_memcpy(&exp, p, sizeof(time_t));
-    p += sizeof(time_t);
-
-    pkt->retried = (*p++ == 1);
-
-    ngx_quic_address_hash(c, !pkt->retried, addr_hash);
-
-    if (ngx_memcmp(tdec, addr_hash, 20) != 0) {
-        goto bad_token;
-    }
-
-    odcid.len = *p++;
-    if (odcid.len) {
-        if (odcid.len > NGX_QUIC_MAX_CID_LEN) {
-            goto bad_token;
-        }
-
-        if ((size_t)(tdec + total - p) < odcid.len) {
-            goto bad_token;
-        }
-
-        odcid.data = p;
-        p += odcid.len;
-    }
-
-    now = ngx_time();
-
-    if (now > exp) {
-        ngx_log_error(NGX_LOG_INFO, c->log, 0, "quic expired token");
-        return NGX_DECLINED;
-    }
-
-    if (odcid.len) {
-        pkt->odcid.len = odcid.len;
-        pkt->odcid.data = ngx_pstrdup(c->pool, &odcid);
-        if (pkt->odcid.data == NULL) {
-            return NGX_ERROR;
-        }
-
-    } else {
-        pkt->odcid = pkt->dcid;
-    }
-
-    pkt->validated = 1;
-
-    return NGX_OK;
-
-garbage:
-
-    ngx_log_error(NGX_LOG_INFO, c->log, 0, "quic garbage token");
-
-    return NGX_ABORT;
-
-bad_token:
-
-    ngx_log_error(NGX_LOG_INFO, c->log, 0, "quic invalid token");
-
-    return NGX_DECLINED;
-}
-
-
 static ngx_int_t
 ngx_quic_init_connection(ngx_connection_t *c)
 {
--- a/src/event/quic/ngx_event_quic_connection.h
+++ b/src/event/quic/ngx_event_quic_connection.h
@@ -21,13 +21,11 @@ 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_tokens.h>
 #include <ngx_event_quic_ack.h>
 #include <ngx_event_quic_output.h>
 
 
-#define NGX_QUIC_MAX_TOKEN_SIZE              64
-    /* SHA-1(addr)=20 + sizeof(time_t) + retry(1) + odcid.len(1) + odcid */
-
 /* quic-recovery, section 6.2.2, kInitialRtt */
 #define NGX_QUIC_INITIAL_RTT                 333 /* ms */
 
@@ -206,10 +204,6 @@ struct ngx_quic_connection_s {
 void ngx_quic_close_connection(ngx_connection_t *c, ngx_int_t rc);
 void ngx_quic_shutdown_quic(ngx_connection_t *c);
 
-ngx_int_t ngx_quic_new_sr_token(ngx_connection_t *c, ngx_str_t *cid,
-    u_char *secret, u_char *token);
-ngx_int_t ngx_quic_new_token(ngx_connection_t *c, u_char *key,
-    ngx_str_t *token, ngx_str_t *odcid, time_t expires, ngx_uint_t is_retry);
 
 /********************************* DEBUG *************************************/
 
new file mode 100644
--- /dev/null
+++ b/src/event/quic/ngx_event_quic_tokens.c
@@ -0,0 +1,292 @@
+
+/*
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_sha1.h>
+#include <ngx_event_quic_protection.h>
+
+
+#define NGX_QUIC_MAX_TOKEN_SIZE              64
+    /* SHA-1(addr)=20 + sizeof(time_t) + retry(1) + odcid.len(1) + odcid */
+
+
+static void ngx_quic_address_hash(ngx_connection_t *c, ngx_uint_t no_port,
+    u_char buf[20]);
+
+
+ngx_int_t
+ngx_quic_new_sr_token(ngx_connection_t *c, ngx_str_t *cid, u_char *secret,
+    u_char *token)
+{
+    ngx_str_t tmp;
+
+    tmp.data = secret;
+    tmp.len = NGX_QUIC_SR_KEY_LEN;
+
+    if (ngx_quic_derive_key(c->log, "sr_token_key", &tmp, cid, token,
+                            NGX_QUIC_SR_TOKEN_LEN)
+        != NGX_OK)
+    {
+        return NGX_ERROR;
+    }
+
+#if (NGX_DEBUG)
+    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                   "quic stateless reset token %*xs",
+                    (size_t) NGX_QUIC_SR_TOKEN_LEN, token);
+#endif
+
+    return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_quic_new_token(ngx_connection_t *c, u_char *key, ngx_str_t *token,
+    ngx_str_t *odcid, time_t exp, ngx_uint_t is_retry)
+{
+    int                len, iv_len;
+    u_char            *p, *iv;
+    EVP_CIPHER_CTX    *ctx;
+    const EVP_CIPHER  *cipher;
+
+    u_char             in[NGX_QUIC_MAX_TOKEN_SIZE];
+
+    ngx_quic_address_hash(c, !is_retry, in);
+
+    p = in + 20;
+
+    p = ngx_cpymem(p, &exp, sizeof(time_t));
+
+    *p++ = is_retry ? 1 : 0;
+
+    if (odcid) {
+        *p++ = odcid->len;
+        p = ngx_cpymem(p, odcid->data, odcid->len);
+
+    } else {
+        *p++ = 0;
+    }
+
+    len = p - in;
+
+    cipher = EVP_aes_256_cbc();
+    iv_len = EVP_CIPHER_iv_length(cipher);
+
+    token->len = iv_len + len + EVP_CIPHER_block_size(cipher);
+    token->data = ngx_pnalloc(c->pool, token->len);
+    if (token->data == NULL) {
+        return NGX_ERROR;
+    }
+
+    ctx = EVP_CIPHER_CTX_new();
+    if (ctx == NULL) {
+        return NGX_ERROR;
+    }
+
+    iv = token->data;
+
+    if (RAND_bytes(iv, iv_len) <= 0
+        || !EVP_EncryptInit_ex(ctx, cipher, NULL, key, iv))
+    {
+        EVP_CIPHER_CTX_free(ctx);
+        return NGX_ERROR;
+    }
+
+    token->len = iv_len;
+
+    if (EVP_EncryptUpdate(ctx, token->data + token->len, &len, in, len) != 1) {
+        EVP_CIPHER_CTX_free(ctx);
+        return NGX_ERROR;
+    }
+
+    token->len += len;
+
+    if (EVP_EncryptFinal_ex(ctx, token->data + token->len, &len) <= 0) {
+        EVP_CIPHER_CTX_free(ctx);
+        return NGX_ERROR;
+    }
+
+    token->len += len;
+
+    EVP_CIPHER_CTX_free(ctx);
+
+#ifdef NGX_QUIC_DEBUG_PACKETS
+    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                   "quic new token len:%uz %xV", token->len, token);
+#endif
+
+    return NGX_OK;
+}
+
+
+static void
+ngx_quic_address_hash(ngx_connection_t *c, ngx_uint_t no_port, u_char buf[20])
+{
+    size_t                len;
+    u_char               *data;
+    ngx_sha1_t            sha1;
+    struct sockaddr_in   *sin;
+#if (NGX_HAVE_INET6)
+    struct sockaddr_in6  *sin6;
+#endif
+
+    len = (size_t) c->socklen;
+    data = (u_char *) c->sockaddr;
+
+    if (no_port) {
+        switch (c->sockaddr->sa_family) {
+
+#if (NGX_HAVE_INET6)
+        case AF_INET6:
+            sin6 = (struct sockaddr_in6 *) c->sockaddr;
+
+            len = sizeof(struct in6_addr);
+            data = sin6->sin6_addr.s6_addr;
+
+            break;
+#endif
+
+        case AF_INET:
+            sin = (struct sockaddr_in *) c->sockaddr;
+
+            len = sizeof(in_addr_t);
+            data = (u_char *) &sin->sin_addr;
+
+            break;
+        }
+    }
+
+    ngx_sha1_init(&sha1);
+    ngx_sha1_update(&sha1, data, len);
+    ngx_sha1_final(buf, &sha1);
+}
+
+
+ngx_int_t
+ngx_quic_validate_token(ngx_connection_t *c, u_char *key,
+    ngx_quic_header_t *pkt)
+{
+    int                len, tlen, iv_len;
+    u_char            *iv, *p;
+    time_t             now, exp;
+    size_t             total;
+    ngx_str_t          odcid;
+    EVP_CIPHER_CTX    *ctx;
+    const EVP_CIPHER  *cipher;
+
+    u_char             addr_hash[20];
+    u_char             tdec[NGX_QUIC_MAX_TOKEN_SIZE];
+
+    /* Retry token or NEW_TOKEN in a previous connection */
+
+    cipher = EVP_aes_256_cbc();
+    iv = pkt->token.data;
+    iv_len = EVP_CIPHER_iv_length(cipher);
+
+    /* sanity checks */
+
+    if (pkt->token.len < (size_t) iv_len + EVP_CIPHER_block_size(cipher)) {
+        goto garbage;
+    }
+
+    if (pkt->token.len > (size_t) iv_len + NGX_QUIC_MAX_TOKEN_SIZE) {
+        goto garbage;
+    }
+
+    ctx = EVP_CIPHER_CTX_new();
+    if (ctx == NULL) {
+        return NGX_ERROR;
+    }
+
+    if (!EVP_DecryptInit_ex(ctx, cipher, NULL, key, iv)) {
+        EVP_CIPHER_CTX_free(ctx);
+        return NGX_ERROR;
+    }
+
+    p = pkt->token.data + iv_len;
+    len = pkt->token.len - iv_len;
+
+    if (EVP_DecryptUpdate(ctx, tdec, &len, p, len) != 1) {
+        EVP_CIPHER_CTX_free(ctx);
+        goto garbage;
+    }
+    total = len;
+
+    if (EVP_DecryptFinal_ex(ctx, tdec + len, &tlen) <= 0) {
+        EVP_CIPHER_CTX_free(ctx);
+        goto garbage;
+    }
+    total += tlen;
+
+    EVP_CIPHER_CTX_free(ctx);
+
+    if (total < (20 + sizeof(time_t) + 2)) {
+        goto garbage;
+    }
+
+    p = tdec + 20;
+
+    ngx_memcpy(&exp, p, sizeof(time_t));
+    p += sizeof(time_t);
+
+    pkt->retried = (*p++ == 1);
+
+    ngx_quic_address_hash(c, !pkt->retried, addr_hash);
+
+    if (ngx_memcmp(tdec, addr_hash, 20) != 0) {
+        goto bad_token;
+    }
+
+    odcid.len = *p++;
+    if (odcid.len) {
+        if (odcid.len > NGX_QUIC_MAX_CID_LEN) {
+            goto bad_token;
+        }
+
+        if ((size_t)(tdec + total - p) < odcid.len) {
+            goto bad_token;
+        }
+
+        odcid.data = p;
+        p += odcid.len;
+    }
+
+    now = ngx_time();
+
+    if (now > exp) {
+        ngx_log_error(NGX_LOG_INFO, c->log, 0, "quic expired token");
+        return NGX_DECLINED;
+    }
+
+    if (odcid.len) {
+        pkt->odcid.len = odcid.len;
+        pkt->odcid.data = ngx_pstrdup(c->pool, &odcid);
+        if (pkt->odcid.data == NULL) {
+            return NGX_ERROR;
+        }
+
+    } else {
+        pkt->odcid = pkt->dcid;
+    }
+
+    pkt->validated = 1;
+
+    return NGX_OK;
+
+garbage:
+
+    ngx_log_error(NGX_LOG_INFO, c->log, 0, "quic garbage token");
+
+    return NGX_ABORT;
+
+bad_token:
+
+    ngx_log_error(NGX_LOG_INFO, c->log, 0, "quic invalid token");
+
+    return NGX_DECLINED;
+}
new file mode 100644
--- /dev/null
+++ b/src/event/quic/ngx_event_quic_tokens.h
@@ -0,0 +1,22 @@
+
+/*
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_EVENT_QUIC_TOKENS_H_INCLUDED_
+#define _NGX_EVENT_QUIC_TOKENS_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+ngx_int_t ngx_quic_new_sr_token(ngx_connection_t *c, ngx_str_t *cid,
+    u_char *secret, u_char *token);
+ngx_int_t ngx_quic_new_token(ngx_connection_t *c, u_char *key,
+    ngx_str_t *token, ngx_str_t *odcid, time_t expires, ngx_uint_t is_retry);
+ngx_int_t ngx_quic_validate_token(ngx_connection_t *c,
+    u_char *key, ngx_quic_header_t *pkt);
+
+#endif /* _NGX_EVENT_QUIC_TOKENS_H_INCLUDED_ */