src/event/quic/ngx_event_quic_tokens.c - nginx-1.31.3 nginx/ @ 42f8df65b

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_event_quic_connection.h>


  8. ngx_int_t
  9. ngx_quic_new_sr_token(ngx_connection_t *c, ngx_str_t *cid, u_char *secret,
  10.     u_char *token)
  11. {
  12.     ngx_str_t  tmp;
  13.     u_char     buf[NGX_QUIC_SR_KEY_LEN + sizeof(ngx_uint_t)];

  14.     ngx_memcpy(buf, secret, NGX_QUIC_SR_KEY_LEN);
  15.     ngx_memcpy(buf + NGX_QUIC_SR_KEY_LEN, &ngx_worker, sizeof(ngx_uint_t));

  16.     tmp.data = buf;
  17.     tmp.len = sizeof(buf);

  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, NULL, 0, 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. ngx_int_t
  95. ngx_quic_validate_token(ngx_connection_t *c, u_char *key,
  96.     ngx_quic_header_t *pkt)
  97. {
  98.     int                len, tlen, iv_len;
  99.     u_char            *iv, *p;
  100.     time_t             now, exp;
  101.     size_t             total;
  102.     ngx_str_t          odcid;
  103.     EVP_CIPHER_CTX    *ctx;
  104.     const EVP_CIPHER  *cipher;

  105.     u_char             addr_hash[20];
  106.     u_char             tdec[NGX_QUIC_MAX_TOKEN_SIZE];

  107. #if NGX_SUPPRESS_WARN
  108.     ngx_str_null(&odcid);
  109. #endif

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

  111.     cipher = EVP_aes_256_gcm();
  112.     iv = pkt->token.data;
  113.     iv_len = NGX_QUIC_AES_256_GCM_IV_LEN;

  114.     /* sanity checks */

  115.     if (pkt->token.len < (size_t) iv_len + NGX_QUIC_AES_256_GCM_TAG_LEN) {
  116.         goto garbage;
  117.     }

  118.     if (pkt->token.len > (size_t) iv_len + NGX_QUIC_MAX_TOKEN_SIZE
  119.                          + NGX_QUIC_AES_256_GCM_TAG_LEN)
  120.     {
  121.         goto garbage;
  122.     }

  123.     ctx = EVP_CIPHER_CTX_new();
  124.     if (ctx == NULL) {
  125.         return NGX_ERROR;
  126.     }

  127.     if (!EVP_DecryptInit_ex(ctx, cipher, NULL, key, iv)) {
  128.         EVP_CIPHER_CTX_free(ctx);
  129.         return NGX_ERROR;
  130.     }

  131.     p = pkt->token.data + iv_len;
  132.     len = pkt->token.len - iv_len - NGX_QUIC_AES_256_GCM_TAG_LEN;

  133.     if (EVP_DecryptUpdate(ctx, tdec, &tlen, p, len) != 1) {
  134.         EVP_CIPHER_CTX_free(ctx);
  135.         goto garbage;
  136.     }
  137.     total = tlen;

  138.     if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG,
  139.                             NGX_QUIC_AES_256_GCM_TAG_LEN, p + len)
  140.         == 0)
  141.     {
  142.         EVP_CIPHER_CTX_free(ctx);
  143.         goto garbage;
  144.     }

  145.     if (EVP_DecryptFinal_ex(ctx, tdec + tlen, &tlen) <= 0) {
  146.         EVP_CIPHER_CTX_free(ctx);
  147.         goto garbage;
  148.     }
  149.     total += tlen;

  150.     EVP_CIPHER_CTX_free(ctx);

  151.     if (total < (20 + sizeof(time_t) + 2)) {
  152.         goto garbage;
  153.     }

  154.     p = tdec + 20;

  155.     ngx_memcpy(&exp, p, sizeof(time_t));
  156.     p += sizeof(time_t);

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

  158.     ngx_quic_address_hash(c->sockaddr, c->socklen, !pkt->retried, NULL, 0,
  159.                           addr_hash);

  160.     if (ngx_memcmp(tdec, addr_hash, 20) != 0) {
  161.         goto bad_token;
  162.     }

  163.     odcid.len = *p++;
  164.     if (odcid.len) {
  165.         if (odcid.len > NGX_QUIC_MAX_CID_LEN) {
  166.             goto bad_token;
  167.         }

  168.         if ((size_t)(tdec + total - p) < odcid.len) {
  169.             goto bad_token;
  170.         }

  171.         odcid.data = p;
  172.     }

  173.     now = ngx_time();

  174.     if (now > exp) {
  175.         ngx_log_error(NGX_LOG_INFO, c->log, 0, "quic expired token");
  176.         return NGX_DECLINED;
  177.     }

  178.     if (odcid.len) {
  179.         pkt->odcid.len = odcid.len;
  180.         pkt->odcid.data = pkt->odcid_buf;
  181.         ngx_memcpy(pkt->odcid.data, odcid.data, odcid.len);

  182.     } else {
  183.         pkt->odcid = pkt->dcid;
  184.     }

  185.     pkt->validated = 1;

  186.     return NGX_OK;

  187. garbage:

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

  189.     return NGX_ABORT;

  190. bad_token:

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

  192.     return NGX_DECLINED;
  193. }