QUIC: OpenSSL compatibility layer.

The change allows to compile QUIC with OpenSSL which lacks BoringSSL QUIC API.

This implementation does not support 0-RTT.
This commit is contained in:
Roman Arutyunyan 2023-02-22 19:16:53 +04:00
parent 76adb91913
commit a36ebf7e95
11 changed files with 829 additions and 76 deletions

7
README
View File

@ -63,12 +63,17 @@ Experimental QUIC support for nginx
--with-http_v3_module - enable QUIC and HTTP/3
--with-stream_quic_module - enable QUIC in Stream
A library that provides QUIC support is required to build nginx, there
A library that provides QUIC support is recommended to build nginx, there
are several of those available on the market:
+ BoringSSL [4]
+ LibreSSL [5]
+ QuicTLS [6]
Alternatively, nginx can be configured with OpenSSL compatibility
layer, which emulates BoringSSL QUIC API for OpenSSL. This mode is
enabled by default if native QUIC support is not detected.
0-RTT is not supported in OpenSSL compatibility mode.
Clone the NGINX QUIC repository
$ hg clone -b quic https://hg.nginx.org/nginx-quic

View File

@ -10,6 +10,7 @@ if [ $OPENSSL != NONE ]; then
if [ $USE_OPENSSL_QUIC = YES ]; then
have=NGX_QUIC . auto/have
have=NGX_QUIC_OPENSSL_COMPAT . auto/have
fi
case "$CC" in
@ -124,6 +125,35 @@ else
CORE_INCS="$CORE_INCS $ngx_feature_path"
CORE_LIBS="$CORE_LIBS $ngx_feature_libs"
OPENSSL=YES
if [ $USE_OPENSSL_QUIC = YES ]; then
ngx_feature="OpenSSL QUIC support"
ngx_feature_name="NGX_QUIC"
ngx_feature_test="SSL_set_quic_method(NULL, NULL)"
. auto/feature
if [ $ngx_found = no ]; then
have=NGX_QUIC_OPENSSL_COMPAT . auto/have
ngx_feature="OpenSSL QUIC compatibility"
ngx_feature_test="SSL_CTX_add_custom_ext(NULL, 0, 0,
NULL, NULL, NULL, NULL, NULL)"
. auto/feature
fi
if [ $ngx_found = no ]; then
cat << END
$0: error: certain modules require OpenSSL QUIC support.
You can either do not enable the modules, or install the OpenSSL library with
QUIC support into the system, or build the OpenSSL library with QUIC support
statically from the source with nginx by using --with-openssl=<path> option.
END
exit 1
fi
fi
fi
fi
@ -140,28 +170,4 @@ END
exit 1
fi
if [ $USE_OPENSSL_QUIC = YES ]; then
ngx_feature="OpenSSL QUIC support"
ngx_feature_name="NGX_QUIC"
ngx_feature_run=no
ngx_feature_incs="#include <openssl/ssl.h>"
ngx_feature_path=
ngx_feature_libs="-lssl -lcrypto $NGX_LIBDL $NGX_LIBPTHREAD"
ngx_feature_test="SSL_set_quic_method(NULL, NULL)"
. auto/feature
if [ $ngx_found = no ]; then
cat << END
$0: error: certain modules require OpenSSL QUIC support.
You can either do not enable the modules, or install the OpenSSL library with
QUIC support into the system, or build the OpenSSL library with QUIC support
statically from the source with nginx by using --with-openssl=<path> option.
END
exit 1
fi
fi
fi

View File

@ -1342,7 +1342,8 @@ if [ $USE_OPENSSL_QUIC = YES ]; then
src/event/quic/ngx_event_quic_tokens.h \
src/event/quic/ngx_event_quic_ack.h \
src/event/quic/ngx_event_quic_output.h \
src/event/quic/ngx_event_quic_socket.h"
src/event/quic/ngx_event_quic_socket.h \
src/event/quic/ngx_event_quic_openssl_compat.h"
ngx_module_srcs="src/event/quic/ngx_event_quic.c \
src/event/quic/ngx_event_quic_udp.c \
src/event/quic/ngx_event_quic_transport.c \
@ -1355,7 +1356,8 @@ if [ $USE_OPENSSL_QUIC = YES ]; then
src/event/quic/ngx_event_quic_tokens.c \
src/event/quic/ngx_event_quic_ack.c \
src/event/quic/ngx_event_quic_output.c \
src/event/quic/ngx_event_quic_socket.c"
src/event/quic/ngx_event_quic_socket.c \
src/event/quic/ngx_event_quic_openssl_compat.c"
ngx_module_libs=
ngx_module_link=YES

View File

@ -25,6 +25,9 @@ typedef struct ngx_quic_socket_s ngx_quic_socket_t;
typedef struct ngx_quic_path_s ngx_quic_path_t;
typedef struct ngx_quic_keys_s ngx_quic_keys_t;
#if (NGX_QUIC_OPENSSL_COMPAT)
#include <ngx_event_quic_openssl_compat.h>
#endif
#include <ngx_event_quic_transport.h>
#include <ngx_event_quic_protection.h>
#include <ngx_event_quic_frames.h>
@ -236,6 +239,10 @@ struct ngx_quic_connection_s {
ngx_uint_t nshadowbufs;
#endif
#if (NGX_QUIC_OPENSSL_COMPAT)
ngx_quic_compat_t *compat;
#endif
ngx_quic_streams_t streams;
ngx_quic_congestion_t congestion;

View File

@ -0,0 +1,646 @@
/*
* Copyright (C) Nginx, Inc.
*/
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_event.h>
#include <ngx_event_quic_connection.h>
#if (NGX_QUIC_OPENSSL_COMPAT)
#define NGX_QUIC_COMPAT_RECORD_SIZE 1024
#define NGX_QUIC_COMPAT_SSL_TP_EXT 0x39
#define NGX_QUIC_COMPAT_CLIENT_HANDSHAKE "CLIENT_HANDSHAKE_TRAFFIC_SECRET"
#define NGX_QUIC_COMPAT_SERVER_HANDSHAKE "SERVER_HANDSHAKE_TRAFFIC_SECRET"
#define NGX_QUIC_COMPAT_CLIENT_APPLICATION "CLIENT_TRAFFIC_SECRET_0"
#define NGX_QUIC_COMPAT_SERVER_APPLICATION "SERVER_TRAFFIC_SECRET_0"
typedef struct {
ngx_quic_secret_t secret;
ngx_uint_t cipher;
} ngx_quic_compat_keys_t;
typedef struct {
ngx_log_t *log;
u_char type;
ngx_str_t payload;
uint64_t number;
ngx_quic_compat_keys_t *keys;
enum ssl_encryption_level_t level;
} ngx_quic_compat_record_t;
struct ngx_quic_compat_s {
const SSL_QUIC_METHOD *method;
enum ssl_encryption_level_t write_level;
enum ssl_encryption_level_t read_level;
uint64_t read_record;
ngx_quic_compat_keys_t keys;
ngx_str_t tp;
ngx_str_t ctp;
};
static void ngx_quic_compat_keylog_callback(const SSL *ssl, const char *line);
static ngx_int_t ngx_quic_compat_set_encryption_secret(ngx_log_t *log,
ngx_quic_compat_keys_t *keys, enum ssl_encryption_level_t level,
const SSL_CIPHER *cipher, const uint8_t *secret, size_t secret_len);
static int ngx_quic_compat_add_transport_params_callback(SSL *ssl,
unsigned int ext_type, unsigned int context, const unsigned char **out,
size_t *outlen, X509 *x, size_t chainidx, int *al, void *add_arg);
static int ngx_quic_compat_parse_transport_params_callback(SSL *ssl,
unsigned int ext_type, unsigned int context, const unsigned char *in,
size_t inlen, X509 *x, size_t chainidx, int *al, void *parse_arg);
static void ngx_quic_compat_message_callback(int write_p, int version,
int content_type, const void *buf, size_t len, SSL *ssl, void *arg);
static size_t ngx_quic_compat_create_header(ngx_quic_compat_record_t *rec,
u_char *out, ngx_uint_t plain);
static ngx_int_t ngx_quic_compat_create_record(ngx_quic_compat_record_t *rec,
ngx_str_t *res);
ngx_int_t
ngx_quic_compat_init(ngx_conf_t *cf, SSL_CTX *ctx)
{
SSL_CTX_set_keylog_callback(ctx, ngx_quic_compat_keylog_callback);
if (SSL_CTX_has_client_custom_ext(ctx, NGX_QUIC_COMPAT_SSL_TP_EXT)) {
return NGX_OK;
}
if (SSL_CTX_add_custom_ext(ctx, NGX_QUIC_COMPAT_SSL_TP_EXT,
SSL_EXT_CLIENT_HELLO
|SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS,
ngx_quic_compat_add_transport_params_callback,
NULL,
NULL,
ngx_quic_compat_parse_transport_params_callback,
NULL)
== 0)
{
ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
"SSL_CTX_add_custom_ext() failed");
return NGX_ERROR;
}
return NGX_OK;
}
static void
ngx_quic_compat_keylog_callback(const SSL *ssl, const char *line)
{
u_char ch, *p, *start, value;
size_t n;
ngx_uint_t write;
const SSL_CIPHER *cipher;
ngx_quic_compat_t *com;
ngx_connection_t *c;
ngx_quic_connection_t *qc;
enum ssl_encryption_level_t level;
u_char secret[EVP_MAX_MD_SIZE];
c = ngx_ssl_get_connection(ssl);
if (c->type != SOCK_DGRAM) {
return;
}
p = (u_char *) line;
for (start = p; *p && *p != ' '; p++);
n = p - start;
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic compat secret %*s", n, start);
if (n == sizeof(NGX_QUIC_COMPAT_CLIENT_HANDSHAKE) - 1
&& ngx_strncmp(start, NGX_QUIC_COMPAT_CLIENT_HANDSHAKE, n) == 0)
{
level = ssl_encryption_handshake;
write = 0;
} else if (n == sizeof(NGX_QUIC_COMPAT_SERVER_HANDSHAKE) - 1
&& ngx_strncmp(start, NGX_QUIC_COMPAT_SERVER_HANDSHAKE, n) == 0)
{
level = ssl_encryption_handshake;
write = 1;
} else if (n == sizeof(NGX_QUIC_COMPAT_CLIENT_APPLICATION) - 1
&& ngx_strncmp(start, NGX_QUIC_COMPAT_CLIENT_APPLICATION, n)
== 0)
{
level = ssl_encryption_application;
write = 0;
} else if (n == sizeof(NGX_QUIC_COMPAT_SERVER_APPLICATION) - 1
&& ngx_strncmp(start, NGX_QUIC_COMPAT_SERVER_APPLICATION, n)
== 0)
{
level = ssl_encryption_application;
write = 1;
} else {
return;
}
if (*p++ == '\0') {
return;
}
for ( /* void */ ; *p && *p != ' '; p++);
if (*p++ == '\0') {
return;
}
for (n = 0, start = p; *p; p++) {
ch = *p;
if (ch >= '0' && ch <= '9') {
value = ch - '0';
goto next;
}
ch = (u_char) (ch | 0x20);
if (ch >= 'a' && ch <= 'f') {
value = ch - 'a' + 10;
goto next;
}
ngx_log_error(NGX_LOG_EMERG, c->log, 0,
"invalid OpenSSL QUIC secret format");
return;
next:
if ((p - start) % 2) {
secret[n++] += value;
} else {
if (n >= EVP_MAX_MD_SIZE) {
ngx_log_error(NGX_LOG_EMERG, c->log, 0,
"too big OpenSSL QUIC secret");
return;
}
secret[n] = (value << 4);
}
}
qc = ngx_quic_get_connection(c);
com = qc->compat;
cipher = SSL_get_current_cipher(ssl);
if (write) {
com->method->set_write_secret((SSL *) ssl, level, cipher, secret, n);
com->write_level = level;
} else {
com->method->set_read_secret((SSL *) ssl, level, cipher, secret, n);
com->read_level = level;
com->read_record = 0;
(void) ngx_quic_compat_set_encryption_secret(c->log, &com->keys, level,
cipher, secret, n);
}
}
static ngx_int_t
ngx_quic_compat_set_encryption_secret(ngx_log_t *log,
ngx_quic_compat_keys_t *keys, enum ssl_encryption_level_t level,
const SSL_CIPHER *cipher, const uint8_t *secret, size_t secret_len)
{
ngx_int_t key_len;
ngx_str_t secret_str;
ngx_uint_t i;
ngx_quic_hkdf_t seq[2];
ngx_quic_secret_t *peer_secret;
ngx_quic_ciphers_t ciphers;
peer_secret = &keys->secret;
keys->cipher = SSL_CIPHER_get_id(cipher);
key_len = ngx_quic_ciphers(keys->cipher, &ciphers, level);
if (key_len == NGX_ERROR) {
ngx_ssl_error(NGX_LOG_INFO, log, 0, "unexpected cipher");
return NGX_ERROR;
}
if (sizeof(peer_secret->secret.data) < secret_len) {
ngx_log_error(NGX_LOG_ALERT, log, 0,
"unexpected secret len: %uz", secret_len);
return NGX_ERROR;
}
peer_secret->secret.len = secret_len;
ngx_memcpy(peer_secret->secret.data, secret, secret_len);
peer_secret->key.len = key_len;
peer_secret->iv.len = NGX_QUIC_IV_LEN;
secret_str.len = secret_len;
secret_str.data = (u_char *) secret;
ngx_quic_hkdf_set(&seq[0], "tls13 key", &peer_secret->key, &secret_str);
ngx_quic_hkdf_set(&seq[1], "tls13 iv", &peer_secret->iv, &secret_str);
for (i = 0; i < (sizeof(seq) / sizeof(seq[0])); i++) {
if (ngx_quic_hkdf_expand(&seq[i], ciphers.d, log) != NGX_OK) {
return NGX_ERROR;
}
}
return NGX_OK;
}
static int
ngx_quic_compat_add_transport_params_callback(SSL *ssl, unsigned int ext_type,
unsigned int context, const unsigned char **out, size_t *outlen, X509 *x,
size_t chainidx, int *al, void *add_arg)
{
ngx_connection_t *c;
ngx_quic_compat_t *com;
ngx_quic_connection_t *qc;
c = ngx_ssl_get_connection(ssl);
if (c->type != SOCK_DGRAM) {
return 0;
}
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic compat add transport params");
qc = ngx_quic_get_connection(c);
com = qc->compat;
*out = com->tp.data;
*outlen = com->tp.len;
return 1;
}
static int
ngx_quic_compat_parse_transport_params_callback(SSL *ssl, unsigned int ext_type,
unsigned int context, const unsigned char *in, size_t inlen, X509 *x,
size_t chainidx, int *al, void *parse_arg)
{
u_char *p;
ngx_connection_t *c;
ngx_quic_compat_t *com;
ngx_quic_connection_t *qc;
c = ngx_ssl_get_connection(ssl);
if (c->type != SOCK_DGRAM) {
return 0;
}
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic compat parse transport params");
qc = ngx_quic_get_connection(c);
com = qc->compat;
p = ngx_pnalloc(c->pool, inlen);
if (p == NULL) {
return 0;
}
ngx_memcpy(p, in, inlen);
com->ctp.data = p;
com->ctp.len = inlen;
return 1;
}
int
SSL_set_quic_method(SSL *ssl, const SSL_QUIC_METHOD *quic_method)
{
BIO *rbio, *wbio;
ngx_connection_t *c;
ngx_quic_compat_t *com;
ngx_quic_connection_t *qc;
c = ngx_ssl_get_connection(ssl);
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "quic compat set method");
qc = ngx_quic_get_connection(c);
qc->compat = ngx_pcalloc(c->pool, sizeof(ngx_quic_compat_t));
if (qc->compat == NULL) {
return 0;
}
com = qc->compat;
com->method = quic_method;
rbio = BIO_new(BIO_s_mem());
if (rbio == NULL) {
return 0;
}
wbio = BIO_new(BIO_s_null());
if (wbio == NULL) {
return 0;
}
SSL_set_bio(ssl, rbio, wbio);
SSL_set_msg_callback(ssl, ngx_quic_compat_message_callback);
/* early data is not supported */
SSL_set_max_early_data(ssl, 0);
return 1;
}
static void
ngx_quic_compat_message_callback(int write_p, int version, int content_type,
const void *buf, size_t len, SSL *ssl, void *arg)
{
ngx_uint_t alert;
ngx_connection_t *c;
ngx_quic_compat_t *com;
ngx_quic_connection_t *qc;
enum ssl_encryption_level_t level;
if (!write_p) {
return;
}
c = ngx_ssl_get_connection(ssl);
qc = ngx_quic_get_connection(c);
if (qc == NULL) {
/* closing */
return;
}
com = qc->compat;
level = com->write_level;
switch (content_type) {
case SSL3_RT_HANDSHAKE:
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic compat tx %s len:%uz ",
ngx_quic_level_name(level), len);
(void) com->method->add_handshake_data(ssl, level, buf, len);
break;
case SSL3_RT_ALERT:
if (len >= 2) {
alert = ((u_char *) buf)[1];
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic compat %s alert:%ui len:%uz ",
ngx_quic_level_name(level), alert, len);
(void) com->method->send_alert(ssl, level, alert);
}
break;
}
}
int
SSL_provide_quic_data(SSL *ssl, enum ssl_encryption_level_t level,
const uint8_t *data, size_t len)
{
BIO *rbio;
size_t n;
u_char *p;
ngx_str_t res;
ngx_connection_t *c;
ngx_quic_compat_t *com;
ngx_quic_connection_t *qc;
ngx_quic_compat_record_t rec;
u_char in[NGX_QUIC_COMPAT_RECORD_SIZE + 1];
u_char out[NGX_QUIC_COMPAT_RECORD_SIZE + 1
+ SSL3_RT_HEADER_LENGTH
+ EVP_GCM_TLS_TAG_LEN];
c = ngx_ssl_get_connection(ssl);
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, "quic compat rx %s len:%uz",
ngx_quic_level_name(level), len);
qc = ngx_quic_get_connection(c);
com = qc->compat;
rbio = SSL_get_rbio(ssl);
while (len) {
ngx_memzero(&rec, sizeof(ngx_quic_compat_record_t));
rec.type = SSL3_RT_HANDSHAKE;
rec.log = c->log;
rec.number = com->read_record++;
rec.keys = &com->keys;
if (level == ssl_encryption_initial) {
n = ngx_min(len, 65535);
rec.payload.len = n;
rec.payload.data = (u_char *) data;
ngx_quic_compat_create_header(&rec, out, 1);
BIO_write(rbio, out, SSL3_RT_HEADER_LENGTH);
BIO_write(rbio, data, n);
#if defined(NGX_QUIC_DEBUG_CRYPTO) && defined(NGX_QUIC_DEBUG_PACKETS)
ngx_log_debug5(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic compat record len:%uz %*xs%*xs",
n + SSL3_RT_HEADER_LENGTH,
(size_t) SSL3_RT_HEADER_LENGTH, out, n, data);
#endif
} else {
n = ngx_min(len, NGX_QUIC_COMPAT_RECORD_SIZE);
p = ngx_cpymem(in, data, n);
*p++ = SSL3_RT_HANDSHAKE;
rec.payload.len = p - in;
rec.payload.data = in;
res.data = out;
if (ngx_quic_compat_create_record(&rec, &res) != NGX_OK) {
return 0;
}
#if defined(NGX_QUIC_DEBUG_CRYPTO) && defined(NGX_QUIC_DEBUG_PACKETS)
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
"quic compat record len:%uz %xV", res.len, &res);
#endif
BIO_write(rbio, res.data, res.len);
}
data += n;
len -= n;
}
return 1;
}
static size_t
ngx_quic_compat_create_header(ngx_quic_compat_record_t *rec, u_char *out,
ngx_uint_t plain)
{
u_char type;
size_t len;
len = rec->payload.len;
if (plain) {
type = rec->type;
} else {
type = SSL3_RT_APPLICATION_DATA;
len += EVP_GCM_TLS_TAG_LEN;
}
out[0] = type;
out[1] = 0x03;
out[2] = 0x03;
out[3] = (len >> 8);
out[4] = len;
return 5;
}
static ngx_int_t
ngx_quic_compat_create_record(ngx_quic_compat_record_t *rec, ngx_str_t *res)
{
ngx_str_t ad, out;
ngx_quic_secret_t *secret;
ngx_quic_ciphers_t ciphers;
u_char nonce[NGX_QUIC_IV_LEN];
ad.data = res->data;
ad.len = ngx_quic_compat_create_header(rec, ad.data, 0);
out.len = rec->payload.len + EVP_GCM_TLS_TAG_LEN;
out.data = res->data + ad.len;
#ifdef NGX_QUIC_DEBUG_CRYPTO
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, rec->log, 0,
"quic compat ad len:%uz %xV", ad.len, &ad);
#endif
if (ngx_quic_ciphers(rec->keys->cipher, &ciphers, rec->level) == NGX_ERROR)
{
return NGX_ERROR;
}
secret = &rec->keys->secret;
ngx_memcpy(nonce, secret->iv.data, secret->iv.len);
ngx_quic_compute_nonce(nonce, sizeof(nonce), rec->number);
if (ngx_quic_tls_seal(ciphers.c, secret, &out,
nonce, &rec->payload, &ad, rec->log)
!= NGX_OK)
{
return NGX_ERROR;
}
res->len = ad.len + out.len;
return NGX_OK;
}
enum ssl_encryption_level_t
SSL_quic_read_level(const SSL *ssl)
{
ngx_connection_t *c;
ngx_quic_connection_t *qc;
c = ngx_ssl_get_connection(ssl);
qc = ngx_quic_get_connection(c);
return qc->compat->read_level;
}
enum ssl_encryption_level_t
SSL_quic_write_level(const SSL *ssl)
{
ngx_connection_t *c;
ngx_quic_connection_t *qc;
c = ngx_ssl_get_connection(ssl);
qc = ngx_quic_get_connection(c);
return qc->compat->write_level;
}
int
SSL_set_quic_transport_params(SSL *ssl, const uint8_t *params,
size_t params_len)
{
ngx_connection_t *c;
ngx_quic_compat_t *com;
ngx_quic_connection_t *qc;
c = ngx_ssl_get_connection(ssl);
qc = ngx_quic_get_connection(c);
com = qc->compat;
com->tp.len = params_len;
com->tp.data = (u_char *) params;
return 1;
}
void
SSL_get_peer_quic_transport_params(const SSL *ssl, const uint8_t **out_params,
size_t *out_params_len)
{
ngx_connection_t *c;
ngx_quic_compat_t *com;
ngx_quic_connection_t *qc;
c = ngx_ssl_get_connection(ssl);
qc = ngx_quic_get_connection(c);
com = qc->compat;
*out_params = com->ctp.data;
*out_params_len = com->ctp.len;
}
#endif /* NGX_QUIC_OPENSSL_COMPAT */

View File

@ -0,0 +1,60 @@
/*
* Copyright (C) Nginx, Inc.
*/
#ifndef _NGX_EVENT_QUIC_OPENSSL_COMPAT_H_INCLUDED_
#define _NGX_EVENT_QUIC_OPENSSL_COMPAT_H_INCLUDED_
#ifdef TLSEXT_TYPE_quic_transport_parameters
#undef NGX_QUIC_OPENSSL_COMPAT
#else
#include <ngx_config.h>
#include <ngx_core.h>
typedef struct ngx_quic_compat_s ngx_quic_compat_t;
enum ssl_encryption_level_t {
ssl_encryption_initial = 0,
ssl_encryption_early_data,
ssl_encryption_handshake,
ssl_encryption_application
};
typedef struct ssl_quic_method_st {
int (*set_read_secret)(SSL *ssl, enum ssl_encryption_level_t level,
const SSL_CIPHER *cipher,
const uint8_t *rsecret, size_t secret_len);
int (*set_write_secret)(SSL *ssl, enum ssl_encryption_level_t level,
const SSL_CIPHER *cipher,
const uint8_t *wsecret, size_t secret_len);
int (*add_handshake_data)(SSL *ssl, enum ssl_encryption_level_t level,
const uint8_t *data, size_t len);
int (*flush_flight)(SSL *ssl);
int (*send_alert)(SSL *ssl, enum ssl_encryption_level_t level,
uint8_t alert);
} SSL_QUIC_METHOD;
ngx_int_t ngx_quic_compat_init(ngx_conf_t *cf, SSL_CTX *ctx);
int SSL_set_quic_method(SSL *ssl, const SSL_QUIC_METHOD *quic_method);
int SSL_provide_quic_data(SSL *ssl, enum ssl_encryption_level_t level,
const uint8_t *data, size_t len);
enum ssl_encryption_level_t SSL_quic_read_level(const SSL *ssl);
enum ssl_encryption_level_t SSL_quic_write_level(const SSL *ssl);
int SSL_set_quic_transport_params(SSL *ssl, const uint8_t *params,
size_t params_len);
void SSL_get_peer_quic_transport_params(const SSL *ssl,
const uint8_t **out_params, size_t *out_params_len);
#endif /* TLSEXT_TYPE_quic_transport_parameters */
#endif /* _NGX_EVENT_QUIC_OPENSSL_COMPAT_H_INCLUDED_ */

View File

@ -23,37 +23,6 @@
#endif
#ifdef OPENSSL_IS_BORINGSSL
#define ngx_quic_cipher_t EVP_AEAD
#else
#define ngx_quic_cipher_t EVP_CIPHER
#endif
typedef struct {
const ngx_quic_cipher_t *c;
const EVP_CIPHER *hp;
const EVP_MD *d;
} ngx_quic_ciphers_t;
typedef struct {
size_t out_len;
u_char *out;
size_t prk_len;
const uint8_t *prk;
size_t label_len;
const u_char *label;
} ngx_quic_hkdf_t;
#define ngx_quic_hkdf_set(seq, _label, _out, _prk) \
(seq)->out_len = (_out)->len; (seq)->out = (_out)->data; \
(seq)->prk_len = (_prk)->len, (seq)->prk = (_prk)->data, \
(seq)->label_len = (sizeof(_label) - 1); (seq)->label = (u_char *)(_label);
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,
const u_char *info, size_t info_len);
@ -63,20 +32,12 @@ static ngx_int_t ngx_hkdf_extract(u_char *out_key, size_t *out_len,
static uint64_t ngx_quic_parse_pn(u_char **pos, ngx_int_t len, u_char *mask,
uint64_t *largest_pn);
static void ngx_quic_compute_nonce(u_char *nonce, size_t len, uint64_t pn);
static ngx_int_t ngx_quic_ciphers(ngx_uint_t id,
ngx_quic_ciphers_t *ciphers, enum ssl_encryption_level_t level);
static ngx_int_t ngx_quic_tls_open(const ngx_quic_cipher_t *cipher,
ngx_quic_secret_t *s, ngx_str_t *out, u_char *nonce, ngx_str_t *in,
ngx_str_t *ad, ngx_log_t *log);
static ngx_int_t ngx_quic_tls_seal(const ngx_quic_cipher_t *cipher,
ngx_quic_secret_t *s, ngx_str_t *out, u_char *nonce, ngx_str_t *in,
ngx_str_t *ad, ngx_log_t *log);
static ngx_int_t ngx_quic_tls_hp(ngx_log_t *log, const EVP_CIPHER *cipher,
ngx_quic_secret_t *s, u_char *out, u_char *in);
static ngx_int_t ngx_quic_hkdf_expand(ngx_quic_hkdf_t *hkdf,
const EVP_MD *digest, ngx_log_t *log);
static ngx_int_t ngx_quic_create_packet(ngx_quic_header_t *pkt,
ngx_str_t *res);
@ -84,7 +45,7 @@ static ngx_int_t ngx_quic_create_retry_packet(ngx_quic_header_t *pkt,
ngx_str_t *res);
static ngx_int_t
ngx_int_t
ngx_quic_ciphers(ngx_uint_t id, ngx_quic_ciphers_t *ciphers,
enum ssl_encryption_level_t level)
{
@ -221,7 +182,7 @@ ngx_quic_keys_set_initial_secret(ngx_quic_keys_t *keys, ngx_str_t *secret,
}
static ngx_int_t
ngx_int_t
ngx_quic_hkdf_expand(ngx_quic_hkdf_t *h, const EVP_MD *digest, ngx_log_t *log)
{
size_t info_len;
@ -480,7 +441,7 @@ ngx_quic_tls_open(const ngx_quic_cipher_t *cipher, ngx_quic_secret_t *s,
}
static ngx_int_t
ngx_int_t
ngx_quic_tls_seal(const ngx_quic_cipher_t *cipher, ngx_quic_secret_t *s,
ngx_str_t *out, u_char *nonce, ngx_str_t *in, ngx_str_t *ad, ngx_log_t *log)
{
@ -961,7 +922,7 @@ ngx_quic_parse_pn(u_char **pos, ngx_int_t len, u_char *mask,
}
static void
void
ngx_quic_compute_nonce(u_char *nonce, size_t len, uint64_t pn)
{
nonce[len - 8] ^= (pn >> 56) & 0x3f;

View File

@ -23,6 +23,13 @@
#define NGX_QUIC_MAX_MD_SIZE 48
#ifdef OPENSSL_IS_BORINGSSL
#define ngx_quic_cipher_t EVP_AEAD
#else
#define ngx_quic_cipher_t EVP_CIPHER
#endif
typedef struct {
size_t len;
u_char data[NGX_QUIC_MAX_MD_SIZE];
@ -56,6 +63,30 @@ struct ngx_quic_keys_s {
};
typedef struct {
const ngx_quic_cipher_t *c;
const EVP_CIPHER *hp;
const EVP_MD *d;
} ngx_quic_ciphers_t;
typedef struct {
size_t out_len;
u_char *out;
size_t prk_len;
const uint8_t *prk;
size_t label_len;
const u_char *label;
} ngx_quic_hkdf_t;
#define ngx_quic_hkdf_set(seq, _label, _out, _prk) \
(seq)->out_len = (_out)->len; (seq)->out = (_out)->data; \
(seq)->prk_len = (_prk)->len, (seq)->prk = (_prk)->data, \
(seq)->label_len = (sizeof(_label) - 1); (seq)->label = (u_char *)(_label);
ngx_int_t ngx_quic_keys_set_initial_secret(ngx_quic_keys_t *keys,
ngx_str_t *secret, ngx_log_t *log);
ngx_int_t ngx_quic_keys_set_encryption_secret(ngx_log_t *log,
@ -70,6 +101,14 @@ void ngx_quic_keys_switch(ngx_connection_t *c, ngx_quic_keys_t *keys);
ngx_int_t ngx_quic_keys_update(ngx_connection_t *c, ngx_quic_keys_t *keys);
ngx_int_t ngx_quic_encrypt(ngx_quic_header_t *pkt, ngx_str_t *res);
ngx_int_t ngx_quic_decrypt(ngx_quic_header_t *pkt, uint64_t *largest_pn);
void ngx_quic_compute_nonce(u_char *nonce, size_t len, uint64_t pn);
ngx_int_t ngx_quic_ciphers(ngx_uint_t id, ngx_quic_ciphers_t *ciphers,
enum ssl_encryption_level_t level);
ngx_int_t ngx_quic_tls_seal(const ngx_quic_cipher_t *cipher,
ngx_quic_secret_t *s, ngx_str_t *out, u_char *nonce, ngx_str_t *in,
ngx_str_t *ad, ngx_log_t *log);
ngx_int_t ngx_quic_hkdf_expand(ngx_quic_hkdf_t *hkdf, const EVP_MD *digest,
ngx_log_t *log);
#endif /* _NGX_EVENT_QUIC_PROTECTION_H_INCLUDED_ */

View File

@ -10,6 +10,13 @@
#include <ngx_event_quic_connection.h>
#if defined OPENSSL_IS_BORINGSSL \
|| defined LIBRESSL_VERSION_NUMBER \
|| NGX_QUIC_OPENSSL_COMPAT
#define NGX_QUIC_BORINGSSL_API 1
#endif
/*
* RFC 9000, 7.5. Cryptographic Message Buffering
*
@ -18,7 +25,7 @@
#define NGX_QUIC_MAX_BUFFERED 65535
#if defined OPENSSL_IS_BORINGSSL || defined LIBRESSL_VERSION_NUMBER
#if (NGX_QUIC_BORINGSSL_API)
static int ngx_quic_set_read_secret(ngx_ssl_conn_t *ssl_conn,
enum ssl_encryption_level_t level, const SSL_CIPHER *cipher,
const uint8_t *secret, size_t secret_len);
@ -39,7 +46,7 @@ static int ngx_quic_send_alert(ngx_ssl_conn_t *ssl_conn,
static ngx_int_t ngx_quic_crypto_input(ngx_connection_t *c, ngx_chain_t *data);
#if defined OPENSSL_IS_BORINGSSL || defined LIBRESSL_VERSION_NUMBER
#if (NGX_QUIC_BORINGSSL_API)
static int
ngx_quic_set_read_secret(ngx_ssl_conn_t *ssl_conn,
@ -523,7 +530,7 @@ ngx_quic_init_connection(ngx_connection_t *c)
ssl_conn = c->ssl->connection;
if (!quic_method.send_alert) {
#if defined OPENSSL_IS_BORINGSSL || defined LIBRESSL_VERSION_NUMBER
#if (NGX_QUIC_BORINGSSL_API)
quic_method.set_read_secret = ngx_quic_set_read_secret;
quic_method.set_write_secret = ngx_quic_set_write_secret;
#else

View File

@ -9,6 +9,10 @@
#include <ngx_core.h>
#include <ngx_http.h>
#if (NGX_QUIC_OPENSSL_COMPAT)
#include <ngx_event_quic_openssl_compat.h>
#endif
typedef ngx_int_t (*ngx_ssl_variable_handler_pt)(ngx_connection_t *c,
ngx_pool_t *pool, ngx_str_t *s);
@ -1317,16 +1321,22 @@ ngx_http_ssl_init(ngx_conf_t *cf)
continue;
}
cscf = addr[a].default_server;
sscf = cscf->ctx->srv_conf[ngx_http_ssl_module.ctx_index];
if (addr[a].opt.http3) {
name = "http3";
#if (NGX_QUIC_OPENSSL_COMPAT)
if (ngx_quic_compat_init(cf, sscf->ssl.ctx) != NGX_OK) {
return NGX_ERROR;
}
#endif
} else {
name = "ssl";
}
cscf = addr[a].default_server;
sscf = cscf->ctx->srv_conf[ngx_http_ssl_module.ctx_index];
if (sscf->certificates) {
if (addr[a].opt.http3 && !(sscf->protocols & NGX_SSL_TLSv1_3)) {

View File

@ -9,6 +9,10 @@
#include <ngx_core.h>
#include <ngx_stream.h>
#if (NGX_QUIC_OPENSSL_COMPAT)
#include <ngx_event_quic_openssl_compat.h>
#endif
typedef ngx_int_t (*ngx_ssl_variable_handler_pt)(ngx_connection_t *c,
ngx_pool_t *pool, ngx_str_t *s);
@ -1218,6 +1222,12 @@ ngx_stream_ssl_init(ngx_conf_t *cf)
scf = listen[i].ctx->srv_conf[ngx_stream_ssl_module.ctx_index];
#if (NGX_QUIC_OPENSSL_COMPAT)
if (ngx_quic_compat_init(cf, scf->ssl.ctx) != NGX_OK) {
return NGX_ERROR;
}
#endif
if (scf->certificates && !(scf->protocols & NGX_SSL_TLSv1_3)) {
ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
"\"ssl_protocols\" must enable TLSv1.3 for "