src/event/quic/ngx_event_quic_tokens.c - nginx source code

Functions defined

Source code


  1. /*
  2. * Copyright (C) Nginx, Inc.
  3. */


  4. #include <ngx_config.h>
  5. #include <ngx_core.h>
  6. #include <ngx_event.h>
  7. #include <ngx_sha1.h>
  8. #include <ngx_event_quic_connection.h>


  9. static void ngx_quic_address_hash(struct sockaddr *sockaddr, socklen_t socklen,
  10.     ngx_uint_t no_port, u_char buf[20]);


  11. ngx_int_t
  12. ngx_quic_new_sr_token(ngx_connection_t *c, ngx_str_t *cid, u_char *secret,
  13.     u_char *token)
  14. {
  15.     ngx_str_t  tmp;

  16.     tmp.data = secret;
  17.     tmp.len = NGX_QUIC_SR_KEY_LEN;

  18.     if (ngx_quic_derive_key(c->log, "sr_token_key", &tmp, cid, token,
  19.                             NGX_QUIC_SR_TOKEN_LEN)
  20.         != NGX_OK)
  21.     {
  22.         return NGX_ERROR;
  23.     }

  24.     ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
  25.                    "quic stateless reset token %*xs",
  26.                     (size_t) NGX_QUIC_SR_TOKEN_LEN, token);

  27.     return NGX_OK;
  28. }


  29. ngx_int_t
  30. ngx_quic_new_token(ngx_log_t *log, struct sockaddr *sockaddr,
  31.     socklen_t socklen, u_char *key, ngx_str_t *token, ngx_str_t *odcid,
  32.     time_t exp, ngx_uint_t is_retry)
  33. {
  34.     int                len, iv_len;
  35.     u_char            *p, *iv;
  36.     EVP_CIPHER_CTX    *ctx;
  37.     const EVP_CIPHER  *cipher;

  38.     u_char             in[NGX_QUIC_MAX_TOKEN_SIZE];

  39.     ngx_quic_address_hash(sockaddr, socklen, !is_retry, in);

  40.     p = in + 20;

  41.     p = ngx_cpymem(p, &exp, sizeof(time_t));

  42.     *p++ = is_retry ? 1 : 0;

  43.     if (odcid) {
  44.         *p++ = odcid->len;
  45.         p = ngx_cpymem(p, odcid->data, odcid->len);

  46.     } else {
  47.         *p++ = 0;
  48.     }

  49.     len = p - in;

  50.     cipher = EVP_aes_256_gcm();
  51.     iv_len = NGX_QUIC_AES_256_GCM_IV_LEN;

  52.     if ((size_t) (iv_len + len + NGX_QUIC_AES_256_GCM_TAG_LEN) > token->len) {
  53.         ngx_log_error(NGX_LOG_ALERT, log, 0, "quic token buffer is too small");
  54.         return NGX_ERROR;
  55.     }

  56.     ctx = EVP_CIPHER_CTX_new();
  57.     if (ctx == NULL) {
  58.         return NGX_ERROR;
  59.     }

  60.     iv = token->data;

  61.     if (RAND_bytes(iv, iv_len) <= 0
  62.         || !EVP_EncryptInit_ex(ctx, cipher, NULL, key, iv))
  63.     {
  64.         EVP_CIPHER_CTX_free(ctx);
  65.         return NGX_ERROR;
  66.     }

  67.     token->len = iv_len;

  68.     if (EVP_EncryptUpdate(ctx, token->data + token->len, &len, in, len) != 1) {
  69.         EVP_CIPHER_CTX_free(ctx);
  70.         return NGX_ERROR;
  71.     }

  72.     token->len += len;

  73.     if (EVP_EncryptFinal_ex(ctx, token->data + token->len, &len) <= 0) {
  74.         EVP_CIPHER_CTX_free(ctx);
  75.         return NGX_ERROR;
  76.     }

  77.     token->len += len;

  78.     if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG,
  79.                             NGX_QUIC_AES_256_GCM_TAG_LEN,
  80.                             token->data + token->len)
  81.         == 0)
  82.     {
  83.         EVP_CIPHER_CTX_free(ctx);
  84.         return NGX_ERROR;
  85.     }

  86.     token->len += NGX_QUIC_AES_256_GCM_TAG_LEN;

  87.     EVP_CIPHER_CTX_free(ctx);

  88. #ifdef NGX_QUIC_DEBUG_PACKETS
  89.     ngx_log_debug2(NGX_LOG_DEBUG_EVENT, log, 0,
  90.                    "quic new token len:%uz %xV", token->len, token);
  91. #endif

  92.     return NGX_OK;
  93. }


  94. static void
  95. ngx_quic_address_hash(struct sockaddr *sockaddr, socklen_t socklen,
  96.     ngx_uint_t no_port, u_char buf[20])
  97. {
  98.     size_t                len;
  99.     u_char               *data;
  100.     ngx_sha1_t            sha1;
  101.     struct sockaddr_in   *sin;
  102. #if (NGX_HAVE_INET6)
  103.     struct sockaddr_in6  *sin6;
  104. #endif

  105.     len = (size_t) socklen;
  106.     data = (u_char *) sockaddr;

  107.     if (no_port) {
  108.         switch (sockaddr->sa_family) {

  109. #if (NGX_HAVE_INET6)
  110.         case AF_INET6:
  111.             sin6 = (struct sockaddr_in6 *) sockaddr;

  112.             len = sizeof(struct in6_addr);
  113.             data = sin6->sin6_addr.s6_addr;

  114.             break;
  115. #endif

  116.         case AF_INET:
  117.             sin = (struct sockaddr_in *) sockaddr;

  118.             len = sizeof(in_addr_t);
  119.             data = (u_char *) &sin->sin_addr;

  120.             break;
  121.         }
  122.     }

  123.     ngx_sha1_init(&sha1);
  124.     ngx_sha1_update(&sha1, data, len);
  125.     ngx_sha1_final(buf, &sha1);
  126. }


  127. ngx_int_t
  128. ngx_quic_validate_token(ngx_connection_t *c, u_char *key,
  129.     ngx_quic_header_t *pkt)
  130. {
  131.     int                len, tlen, iv_len;
  132.     u_char            *iv, *p;
  133.     time_t             now, exp;
  134.     size_t             total;
  135.     ngx_str_t          odcid;
  136.     EVP_CIPHER_CTX    *ctx;
  137.     const EVP_CIPHER  *cipher;

  138.     u_char             addr_hash[20];
  139.     u_char             tdec[NGX_QUIC_MAX_TOKEN_SIZE];

  140. #if NGX_SUPPRESS_WARN
  141.     ngx_str_null(&odcid);
  142. #endif

  143.     /* Retry token or NEW_TOKEN in a previous connection */

  144.     cipher = EVP_aes_256_gcm();
  145.     iv = pkt->token.data;
  146.     iv_len = NGX_QUIC_AES_256_GCM_IV_LEN;

  147.     /* sanity checks */

  148.     if (pkt->token.len < (size_t) iv_len + NGX_QUIC_AES_256_GCM_TAG_LEN) {
  149.         goto garbage;
  150.     }

  151.     if (pkt->token.len > (size_t) iv_len + NGX_QUIC_MAX_TOKEN_SIZE
  152.                          + NGX_QUIC_AES_256_GCM_TAG_LEN)
  153.     {
  154.         goto garbage;
  155.     }

  156.     ctx = EVP_CIPHER_CTX_new();
  157.     if (ctx == NULL) {
  158.         return NGX_ERROR;
  159.     }

  160.     if (!EVP_DecryptInit_ex(ctx, cipher, NULL, key, iv)) {
  161.         EVP_CIPHER_CTX_free(ctx);
  162.         return NGX_ERROR;
  163.     }

  164.     p = pkt->token.data + iv_len;
  165.     len = pkt->token.len - iv_len - NGX_QUIC_AES_256_GCM_TAG_LEN;

  166.     if (EVP_DecryptUpdate(ctx, tdec, &tlen, p, len) != 1) {
  167.         EVP_CIPHER_CTX_free(ctx);
  168.         goto garbage;
  169.     }
  170.     total = tlen;

  171.     if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG,
  172.                             NGX_QUIC_AES_256_GCM_TAG_LEN, p + len)
  173.         == 0)
  174.     {
  175.         EVP_CIPHER_CTX_free(ctx);
  176.         goto garbage;
  177.     }

  178.     if (EVP_DecryptFinal_ex(ctx, tdec + tlen, &tlen) <= 0) {
  179.         EVP_CIPHER_CTX_free(ctx);
  180.         goto garbage;
  181.     }
  182.     total += tlen;

  183.     EVP_CIPHER_CTX_free(ctx);

  184.     if (total < (20 + sizeof(time_t) + 2)) {
  185.         goto garbage;
  186.     }

  187.     p = tdec + 20;

  188.     ngx_memcpy(&exp, p, sizeof(time_t));
  189.     p += sizeof(time_t);

  190.     pkt->retried = (*p++ == 1);

  191.     ngx_quic_address_hash(c->sockaddr, c->socklen, !pkt->retried, addr_hash);

  192.     if (ngx_memcmp(tdec, addr_hash, 20) != 0) {
  193.         goto bad_token;
  194.     }

  195.     odcid.len = *p++;
  196.     if (odcid.len) {
  197.         if (odcid.len > NGX_QUIC_MAX_CID_LEN) {
  198.             goto bad_token;
  199.         }

  200.         if ((size_t)(tdec + total - p) < odcid.len) {
  201.             goto bad_token;
  202.         }

  203.         odcid.data = p;
  204.     }

  205.     now = ngx_time();

  206.     if (now > exp) {
  207.         ngx_log_error(NGX_LOG_INFO, c->log, 0, "quic expired token");
  208.         return NGX_DECLINED;
  209.     }

  210.     if (odcid.len) {
  211.         pkt->odcid.len = odcid.len;
  212.         pkt->odcid.data = pkt->odcid_buf;
  213.         ngx_memcpy(pkt->odcid.data, odcid.data, odcid.len);

  214.     } else {
  215.         pkt->odcid = pkt->dcid;
  216.     }

  217.     pkt->validated = 1;

  218.     return NGX_OK;

  219. garbage:

  220.     ngx_log_error(NGX_LOG_INFO, c->log, 0, "quic garbage token");

  221.     return NGX_ABORT;

  222. bad_token:

  223.     ngx_log_error(NGX_LOG_INFO, c->log, 0, "quic invalid token");

  224.     return NGX_DECLINED;
  225. }