mirror of
https://github.com/nginx/nginx.git
synced 2025-02-25 18:55:26 -06:00
QUIC: separate files for tokens related processing.
This commit is contained in:
parent
8df0b6bb2c
commit
e0b73cf6a2
@ -1347,6 +1347,7 @@ if [ $USE_OPENSSL$USE_OPENSSL_QUIC = YESYES ]; then
|
|||||||
src/event/quic/ngx_event_quic_connid.h \
|
src/event/quic/ngx_event_quic_connid.h \
|
||||||
src/event/quic/ngx_event_quic_migration.h \
|
src/event/quic/ngx_event_quic_migration.h \
|
||||||
src/event/quic/ngx_event_quic_streams.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_ack.h \
|
||||||
src/event/quic/ngx_event_quic_output.h"
|
src/event/quic/ngx_event_quic_output.h"
|
||||||
ngx_module_srcs="src/event/quic/ngx_event_quic.c \
|
ngx_module_srcs="src/event/quic/ngx_event_quic.c \
|
||||||
@ -1356,6 +1357,7 @@ if [ $USE_OPENSSL$USE_OPENSSL_QUIC = YESYES ]; then
|
|||||||
src/event/quic/ngx_event_quic_connid.c \
|
src/event/quic/ngx_event_quic_connid.c \
|
||||||
src/event/quic/ngx_event_quic_migration.c \
|
src/event/quic/ngx_event_quic_migration.c \
|
||||||
src/event/quic/ngx_event_quic_streams.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_ack.c \
|
||||||
src/event/quic/ngx_event_quic_output.c"
|
src/event/quic/ngx_event_quic_output.c"
|
||||||
|
|
||||||
|
@ -7,7 +7,6 @@
|
|||||||
#include <ngx_config.h>
|
#include <ngx_config.h>
|
||||||
#include <ngx_core.h>
|
#include <ngx_core.h>
|
||||||
#include <ngx_event.h>
|
#include <ngx_event.h>
|
||||||
#include <ngx_sha1.h>
|
|
||||||
#include <ngx_event_quic_connection.h>
|
#include <ngx_event_quic_connection.h>
|
||||||
|
|
||||||
|
|
||||||
@ -42,10 +41,6 @@ static ngx_quic_connection_t *ngx_quic_new_connection(ngx_connection_t *c,
|
|||||||
ngx_quic_conf_t *conf, ngx_quic_header_t *pkt);
|
ngx_quic_conf_t *conf, ngx_quic_header_t *pkt);
|
||||||
static ngx_int_t ngx_quic_process_stateless_reset(ngx_connection_t *c,
|
static ngx_int_t ngx_quic_process_stateless_reset(ngx_connection_t *c,
|
||||||
ngx_quic_header_t *pkt);
|
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 ngx_int_t ngx_quic_init_connection(ngx_connection_t *c);
|
||||||
static void ngx_quic_input_handler(ngx_event_t *rev);
|
static void ngx_quic_input_handler(ngx_event_t *rev);
|
||||||
|
|
||||||
@ -620,32 +615,6 @@ ngx_quic_new_connection(ngx_connection_t *c, ngx_quic_conf_t *conf,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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
|
static ngx_int_t
|
||||||
ngx_quic_process_stateless_reset(ngx_connection_t *c, ngx_quic_header_t *pkt)
|
ngx_quic_process_stateless_reset(ngx_connection_t *c, ngx_quic_header_t *pkt)
|
||||||
{
|
{
|
||||||
@ -690,253 +659,6 @@ ngx_quic_process_stateless_reset(ngx_connection_t *c, ngx_quic_header_t *pkt)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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
|
static ngx_int_t
|
||||||
ngx_quic_init_connection(ngx_connection_t *c)
|
ngx_quic_init_connection(ngx_connection_t *c)
|
||||||
{
|
{
|
||||||
|
@ -21,13 +21,11 @@ typedef struct ngx_quic_send_ctx_s ngx_quic_send_ctx_t;
|
|||||||
#include <ngx_event_quic_migration.h>
|
#include <ngx_event_quic_migration.h>
|
||||||
#include <ngx_event_quic_connid.h>
|
#include <ngx_event_quic_connid.h>
|
||||||
#include <ngx_event_quic_streams.h>
|
#include <ngx_event_quic_streams.h>
|
||||||
|
#include <ngx_event_quic_tokens.h>
|
||||||
#include <ngx_event_quic_ack.h>
|
#include <ngx_event_quic_ack.h>
|
||||||
#include <ngx_event_quic_output.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 */
|
/* quic-recovery, section 6.2.2, kInitialRtt */
|
||||||
#define NGX_QUIC_INITIAL_RTT 333 /* ms */
|
#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_close_connection(ngx_connection_t *c, ngx_int_t rc);
|
||||||
void ngx_quic_shutdown_quic(ngx_connection_t *c);
|
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 *************************************/
|
/********************************* DEBUG *************************************/
|
||||||
|
|
||||||
|
292
src/event/quic/ngx_event_quic_tokens.c
Normal file
292
src/event/quic/ngx_event_quic_tokens.c
Normal file
@ -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;
|
||||||
|
}
|
22
src/event/quic/ngx_event_quic_tokens.h
Normal file
22
src/event/quic/ngx_event_quic_tokens.h
Normal file
@ -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_ */
|
Loading…
Reference in New Issue
Block a user