Welcome! Log In Create A New Profile

Advanced

Re: [PATCH] QUIC: OpenSSL compatibility layer

Roman Arutyunyan
February 06, 2023 09:28AM
Hi,

On Thu, Feb 02, 2023 at 10:35:22PM +0400, Sergey Kandaurov wrote:
> On Mon, Jan 09, 2023 at 03:13:16PM +0400, Roman Arutyunyan wrote:
> > # HG changeset patch
> > # User Roman Arutyunyan <arut@nginx.com>
> > # Date 1673262402 -14400
> > # Mon Jan 09 15:06:42 2023 +0400
> > # Branch quic
> > # Node ID 4e5dfe13c84fe50bec639f1b7dcc81604378a42b
> > # Parent aaa2a3831eefe4315dfb8a9be7178c79ff67f163
> > QUIC: OpenSSL compatibility layer.
> >
> > The change allows to compile QUIC with OpenSSL which lacks BoringSSL QUIC API.
> >
> > This implementation does not support 0-RTT.
> >
> > diff --git a/README b/README
> > --- a/README
> > +++ b/README
> > @@ -53,7 +53,7 @@ 1. Introduction
> >
> > 2. Installing
> >
> > - 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]
> > @@ -85,6 +85,10 @@ 2. Installing
> > --with-cc-opt="-I../libressl/build/include" \
> > --with-ld-opt="-L../libressl/build/lib"
> >
> > + 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.
> > +
> > When configuring nginx, it's possible to enable QUIC and HTTP/3
> > using the following new configuration options:
> >
> > diff --git a/auto/lib/openssl/conf b/auto/lib/openssl/conf
> > --- a/auto/lib/openssl/conf
> > +++ b/auto/lib/openssl/conf
> > @@ -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
>
> This won't build with QuicTLS sources specified in --with-openssl,
> due to type/function redefinitions. The patch to address this:
>
> diff --git a/src/event/quic/ngx_event_quic_openssl_compat.h b/src/event/quic/ngx_event_quic_openssl_compat.h
> --- a/src/event/quic/ngx_event_quic_openssl_compat.h
> +++ b/src/event/quic/ngx_event_quic_openssl_compat.h
> @@ -7,6 +7,10 @@
> #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

Good point, thanks.

> +
>
> #include <ngx_config.h>
> #include <ngx_core.h>
> @@ -48,4 +52,6 @@ void SSL_get_peer_quic_transport_params(
> const uint8_t **out_params, size_t *out_params_len);
>
>
> +#endif /* TLSEXT_TYPE_quic_transport_parameters */
> +
> #endif /* _NGX_EVENT_QUIC_OPENSSL_COMPAT_H_INCLUDED_ */
>
>
> > fi
> >
> > case "$CC" in
> > @@ -124,6 +125,45 @@ 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_OPENSSL_QUIC"
> > + ngx_feature_run=no
> > + ngx_feature_incs="#include <openssl/ssl.h>"
> > + ngx_feature_test="SSL_set_quic_method(NULL, NULL)"
> > + . auto/feature
> > +
> > + if [ $ngx_found = no ]; then
>
> bad indent

OK, thanks.

> > +
> > + ngx_feature="OpenSSL QUIC compatibility"
> > + ngx_feature_name="NGX_QUIC_OPENSSL_COMPAT"
> > + ngx_feature_run=no
> > + ngx_feature_incs="#include <openssl/ssl.h>"
> > + ngx_feature_test="
> > + SSL_set_max_early_data(NULL, TLS1_3_VERSION);
>
> While practicaly useful to check for TLSv1.3-aware library,
> this abuses SSL_set_max_early_data() API.
>
> > + SSL_CTX_set_msg_callback(NULL, NULL);
> > + SSL_CTX_set_keylog_callback(NULL, NULL);
> > + SSL_CTX_add_custom_ext(NULL, 0, 0, NULL, NULL,
> > + NULL, NULL, NULL)"
>
> Just using SSL_CTX_add_custom_ext() seems to be sufficient to ensure
> this is OpenSSL version 1.1.1+.

OK, left only SSL_CTX_add_custom_ext() and TLS1_3_VERSION.

> > + . 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
> > +
> > + have=NGX_QUIC . auto/have
> > + fi
> > fi
> > fi
> >
> > @@ -139,29 +179,4 @@ with nginx by using --with-openssl=<path
> > 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
> > diff --git a/auto/modules b/auto/modules
> > --- a/auto/modules
> > +++ b/auto/modules
> > @@ -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
> > diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c
> > --- a/src/event/ngx_event_openssl.c
> > +++ b/src/event/ngx_event_openssl.c
> > @@ -9,6 +9,10 @@
> > #include <ngx_core.h>
> > #include <ngx_event.h>
> >
> > +#if (NGX_QUIC_OPENSSL_COMPAT)
> > +#include <ngx_event_quic_openssl_compat.h>
> > +#endif
> > +
> >
> > #define NGX_SSL_PASSWORD_BUFFER_SIZE 4096
> >
> > @@ -392,6 +396,10 @@ ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_
> >
> > SSL_CTX_set_info_callback(ssl->ctx, ngx_ssl_info_callback);
> >
> > +#if (NGX_QUIC_OPENSSL_COMPAT)
> > + ngx_quic_compat_init(ssl->ctx);
> > +#endif
> > +
>
> This enables compatibility unconditionally for all TLS versions and
> consumers, including such modules as the mail module and connections
> to upstream. While SSL_CTX_add_custom_ext() to enable the transport
> parameters extension in TLS < 1.3 seems to be harmless, and enabling
> the keylog callback just leads to redundant function calls, I propose
> to move this close to consumers, e.g.:
>
> diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c
> --- a/src/http/modules/ngx_http_ssl_module.c
> +++ b/src/http/modules/ngx_http_ssl_module.c
> @@ -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,19 @@ 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)
> + ngx_quic_compat_init(sscf->ssl.ctx);

This can be called several times per SSL_CTX, which will end up with an error
in SSL_CTX_add_custom_ext(). Added a check for existing transport parameter
in ngx_quic_compat_init() and logging. Also, added a check for error from
ngx_quic_compat_init() here.

> +#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)) {
>
> (with similar change in the stream module)

Yes, added.

> So we could remove SOCK_DGRAM checks.

Not sure about this. A single SSL_CTX can be shared between TCP and QUIC.

> > return NGX_OK;
> > }
> >
> > diff --git a/src/event/quic/ngx_event_quic_connection.h b/src/event/quic/ngx_event_quic_connection.h
> > --- a/src/event/quic/ngx_event_quic_connection.h
> > +++ b/src/event/quic/ngx_event_quic_connection.h
> > @@ -24,6 +24,9 @@ typedef struct ngx_quic_send_ctx_s ng
> > 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)
> > +typedef struct ngx_quic_compat_s ngx_quic_compat_t;
> > +#endif
> >
> > #include <ngx_event_quic_transport.h>
> > #include <ngx_event_quic_protection.h>
> > @@ -36,6 +39,9 @@ typedef struct ngx_quic_keys_s ng
> > #include <ngx_event_quic_ack.h>
> > #include <ngx_event_quic_output.h>
> > #include <ngx_event_quic_socket.h>
> > +#if (NGX_QUIC_OPENSSL_COMPAT)
> > +#include <ngx_event_quic_openssl_compat.h>
> > +#endif
> >
> >
> > /* RFC 9002, 6.2.2. Handshakes and New Paths: kInitialRtt */
> > @@ -236,6 +242,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;
> >
> > diff --git a/src/event/quic/ngx_event_quic_openssl_compat.c b/src/event/quic/ngx_event_quic_openssl_compat.c
> > new file mode 100644
> > --- /dev/null
> > +++ b/src/event/quic/ngx_event_quic_openssl_compat.c
> > @@ -0,0 +1,656 @@
> > +
> > +/*
> > + * 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
>
> For the record:
> SSL library sources have the TLSEXT_TYPE_quic_transport_parameters
> extension value written in the decimal form, so could we.
> An exception is QuicTLS, which seems to derive hex form from draft days.

In RFC 8446 TLS 1.3, extensions are specified as decimal numbers.
In RFC 9001 QUIC TLS, transport parameter extension is specified as 0x39.
The code follows those definitions literally :)

> > +#define NGX_QUIC_COMPAT_CLIENT_EARLY "CLIENT_EARLY_TRAFFIC_SECRET"
> > +#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(SSL_CTX *ctx)
> > +{
> > + SSL_CTX_set_keylog_callback(ctx, ngx_quic_compat_keylog_callback);
> > +
> > + 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)
> > + {
> > + 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_EARLY) - 1
> > + && ngx_strncmp(start, NGX_QUIC_COMPAT_CLIENT_EARLY, n) == 0)
>
> For the record, with the current approach, this condition will never match:

Thanks, removed this one from this patch.

> > + {
> > + level = ssl_encryption_early_data;
> > + write = 0;
> > +
> > + } else 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;
> > + }
> > +
> > + /* fall through */
> > +
> > + default:
> > + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
> > + "quic compat %s ignore msg:%d len:%uz ",
> > + ngx_quic_level_name(level), content_type, len);
>
> Other content_type values are typically SSL3_RT_HEADER with its short header,
> and SSL3_RT_INNER_CONTENT_TYPE pseudo content type used to preface the inner
> TLSv1.3 content type.
> As such, I think such log should be removed for brevity as typically useless.

OK, removed.

> > +
> > + 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 */
> > diff --git a/src/event/quic/ngx_event_quic_openssl_compat.h b/src/event/quic/ngx_event_quic_openssl_compat.h
> > new file mode 100644
> > --- /dev/null
> > +++ b/src/event/quic/ngx_event_quic_openssl_compat.h
> > @@ -0,0 +1,51 @@
> > +
> > +/*
> > + * Copyright (C) Nginx, Inc.
> > + */
> > +
> > +
> > +#ifndef _NGX_EVENT_QUIC_OPENSSL_COMPAT_H_INCLUDED_
> > +#define _NGX_EVENT_QUIC_OPENSSL_COMPAT_H_INCLUDED_
> > +
> > +
> > +#include <ngx_config.h>
> > +#include <ngx_core.h>
> > +
> > +
> > +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);
>
> bad indent

Yes, thanks.

> > + 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(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 /* _NGX_EVENT_QUIC_OPENSSL_COMPAT_H_INCLUDED_ */
> > diff --git a/src/event/quic/ngx_event_quic_protection.c b/src/event/quic/ngx_event_quic_protection.c
> > --- a/src/event/quic/ngx_event_quic_protection.c
> > +++ b/src/event/quic/ngx_event_quic_protection.c
> > @@ -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
> >
> > 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_p
> > 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_qui
> > }
> >
> >
> > -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_
> > }
> >
> >
> > -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_
> > }
> >
> >
> > -static void
> > +void
> > ngx_quic_compute_nonce(u_char *nonce, size_t len, uint64_t pn)
> > {
> > nonce[len - 8] ^= (pn >> 56) & 0x3f;
> > diff --git a/src/event/quic/ngx_event_quic_protection.h b/src/event/quic/ngx_event_quic_protection.h
> > --- a/src/event/quic/ngx_event_quic_protection.h
> > +++ b/src/event/quic/ngx_event_quic_protection.h
> > @@ -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
> > 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_ */
> > diff --git a/src/event/quic/ngx_event_quic_ssl.c b/src/event/quic/ngx_event_quic_ssl.c
> > --- a/src/event/quic/ngx_event_quic_ssl.c
> > +++ b/src/event/quic/ngx_event_quic_ssl.c
> > @@ -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_c
> > 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,
> > @@ -67,12 +74,6 @@ ngx_quic_set_read_secret(ngx_ssl_conn_t
> > return 0;
> > }
> >
> > - if (level == ssl_encryption_early_data) {
> > - if (ngx_quic_init_streams(c) != NGX_OK) {
> > - return 0;
> > - }
> > - }
> > -
> > return 1;
> > }
> >
> > @@ -138,10 +139,6 @@ ngx_quic_set_encryption_secrets(ngx_ssl_
> > }
> >
> > if (level == ssl_encryption_early_data) {
> > - if (ngx_quic_init_streams(c) != NGX_OK) {
> > - return 0;
> > - }
> > -
> > return 1;
> > }
> >
> > @@ -456,6 +453,12 @@ ngx_quic_crypto_input(ngx_connection_t *
> > return NGX_ERROR;
> > }
> >
> > + if (ngx_quic_keys_available(qc->keys, ssl_encryption_early_data)) {
> > + if (ngx_quic_init_streams(c) != NGX_OK) {
> > + return NGX_ERROR;
> > + }
> > + }
> > +
> > return NGX_OK;
> > }
> >
>
> For the record, these chunks were merged.
>
> > @@ -540,7 +543,7 @@ ngx_quic_init_connection(ngx_connection_
> > 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
> > diff --git a/src/event/quic/ngx_event_quic_transport.h b/src/event/quic/ngx_event_quic_transport.h
> > --- a/src/event/quic/ngx_event_quic_transport.h
> > +++ b/src/event/quic/ngx_event_quic_transport.h
> > @@ -11,6 +11,10 @@
> > #include <ngx_config.h>
> > #include <ngx_core.h>
> >
> > +#if (NGX_QUIC_OPENSSL_COMPAT)
> > +#include <ngx_event_quic_openssl_compat.h>
> > +#endif
> > +
> >
> > /*
> > * RFC 9000, 17.2. Long Header Packets
>
> I think it make sense to adjust the compat header inclusion in
> ngx_event_quic_connection.h, in order to make this part unnecessary,
> and for more consistency:
>
> diff --git a/src/event/quic/ngx_event_quic_connection.h b/src/event/quic/ngx_eve
> nt_quic_connection.h
> --- a/src/event/quic/ngx_event_quic_connection.h
> +++ b/src/event/quic/ngx_event_quic_connection.h
> @@ -28,6 +28,9 @@ typedef struct ngx_quic_keys_s ng
> typedef struct ngx_quic_compat_s ngx_quic_compat_t;

Considering the following, moved ngx_quic_compat_t definition to
ngx_event_quic_openssl_compat.h. If NGX_QUIC_OPENSSL_COMPAT is undefined,
the definition does not make sense.

> #endif
>
> +#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>
> @@ -39,9 +42,6 @@ typedef struct ngx_quic_compat_s ng
> #include <ngx_event_quic_ack.h>
> #include <ngx_event_quic_output.h>
> #include <ngx_event_quic_socket.h>
> -#if (NGX_QUIC_OPENSSL_COMPAT)
> -#include <ngx_event_quic_openssl_compat.h>
> -#endif

OK.

> /* RFC 9002, 6.2.2. Handshakes and New Paths: kInitialRtt */
> diff --git a/src/event/quic/ngx_event_quic_transport.h b/src/event/quic/ngx_event_quic_transport.h
> --- a/src/event/quic/ngx_event_quic_transport.h
> +++ b/src/event/quic/ngx_event_quic_transport.h
> @@ -11,10 +11,6 @@
> #include <ngx_config.h>
> #include <ngx_core.h>
>
> -#if (NGX_QUIC_OPENSSL_COMPAT)
> -#include <ngx_event_quic_openssl_compat.h>
> -#endif
> -
>
> /*
> * RFC 9000, 17.2. Long Header Packets
>
>
> In other news, I looked how to obtain 0-RTT keys without interacting
> with SSL_read_early_data(), which enters the OpenSSL handshake state
> machine into a special mode and causes TLSv1.3/QUIC handshake message
> mismatches. This approach uses the SSL_SESSION_get_master_key() API.
> In TLSv1.3, it returns PSK, which accoring to TLSv1.3 key schedule
> is used as the input secret to extract Early Secret, then to derive
> client_early_traffic_secret with ClientHello message transcript digest,
> which we also control, so obtaining 0-RTT keys is relatively easy.
> Still, OpenSSL knows nothing about we accept 0-RTT, and as such
> it won't set the required early_data extension in ServerHello,
> which means we rejected it, even if 0-RTT was read and acknowledged.
> So the only way to switch the state seems to call SSL_read_early_data()
> with all its hard-ways.
> _______________________________________________
> nginx-devel mailing list
> nginx-devel@nginx.org
> https://mailman.nginx.org/mailman/listinfo/nginx-devel

--
Roman Arutyunyan
# HG changeset patch
# User Roman Arutyunyan <arut@nginx.com>
# Date 1675427689 -14400
# Fri Feb 03 16:34:49 2023 +0400
# Branch quic
# Node ID 9cf1fc42260e7e0e19fe5707f1b054d6499a4157
# Parent def8e398d7c50131f8dac844814fff729da5c86c
QUIC: OpenSSL compatibility layer.

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

This implementation does not support 0-RTT.

diff --git a/README b/README
--- a/README
+++ b/README
@@ -53,7 +53,7 @@ 1. Introduction

2. Installing

- 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]
@@ -85,6 +85,10 @@ 2. Installing
--with-cc-opt="-I../libressl/build/include" \
--with-ld-opt="-L../libressl/build/lib"

+ 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.
+
When configuring nginx, it's possible to enable QUIC and HTTP/3
using the following new configuration options:

diff --git a/auto/lib/openssl/conf b/auto/lib/openssl/conf
--- a/auto/lib/openssl/conf
+++ b/auto/lib/openssl/conf
@@ -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,43 @@ 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_OPENSSL_QUIC"
+ ngx_feature_run=no
+ ngx_feature_incs="#include <openssl/ssl.h>"
+ ngx_feature_test="SSL_set_quic_method(NULL, NULL)"
+ . auto/feature
+
+ if [ $ngx_found = no ]; then
+
+ ngx_feature="OpenSSL QUIC compatibility"
+ ngx_feature_name="NGX_QUIC_OPENSSL_COMPAT"
+ ngx_feature_run=no
+ ngx_feature_incs="#include <openssl/ssl.h>"
+ ngx_feature_test="
+ (void) TLS1_3_VERSION;
+ 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
+
+ have=NGX_QUIC . auto/have
+ fi
fi
fi

@@ -139,29 +177,4 @@ with nginx by using --with-openssl=<path
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
diff --git a/auto/modules b/auto/modules
--- a/auto/modules
+++ b/auto/modules
@@ -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
diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c
--- a/src/event/ngx_event_openssl.c
+++ b/src/event/ngx_event_openssl.c
@@ -9,6 +9,10 @@
#include <ngx_core.h>
#include <ngx_event.h>

+#if (NGX_QUIC_OPENSSL_COMPAT)
+#include <ngx_event_quic_openssl_compat.h>
+#endif
+

#define NGX_SSL_PASSWORD_BUFFER_SIZE 4096

diff --git a/src/event/quic/ngx_event_quic_connection.h b/src/event/quic/ngx_event_quic_connection.h
--- a/src/event/quic/ngx_event_quic_connection.h
+++ b/src/event/quic/ngx_event_quic_connection.h
@@ -25,6 +25,9 @@ typedef struct ngx_quic_socket_s ng
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;

diff --git a/src/event/quic/ngx_event_quic_openssl_compat.c b/src/event/quic/ngx_event_quic_openssl_compat.c
new file mode 100644
--- /dev/null
+++ b/src/event/quic/ngx_event_quic_openssl_compat.c
@@ -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 */
diff --git a/src/event/quic/ngx_event_quic_openssl_compat.h b/src/event/quic/ngx_event_quic_openssl_compat.h
new file mode 100644
--- /dev/null
+++ b/src/event/quic/ngx_event_quic_openssl_compat.h
@@ -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_ */
diff --git a/src/event/quic/ngx_event_quic_protection.c b/src/event/quic/ngx_event_quic_protection.c
--- a/src/event/quic/ngx_event_quic_protection.c
+++ b/src/event/quic/ngx_event_quic_protection.c
@@ -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

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_p
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_qui
}


-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_
}


-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_
}


-static void
+void
ngx_quic_compute_nonce(u_char *nonce, size_t len, uint64_t pn)
{
nonce[len - 8] ^= (pn >> 56) & 0x3f;
diff --git a/src/event/quic/ngx_event_quic_protection.h b/src/event/quic/ngx_event_quic_protection.h
--- a/src/event/quic/ngx_event_quic_protection.h
+++ b/src/event/quic/ngx_event_quic_protection.h
@@ -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
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_ */
diff --git a/src/event/quic/ngx_event_quic_ssl.c b/src/event/quic/ngx_event_quic_ssl.c
--- a/src/event/quic/ngx_event_quic_ssl.c
+++ b/src/event/quic/ngx_event_quic_ssl.c
@@ -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_c
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,
@@ -517,7 +524,7 @@ ngx_quic_init_connection(ngx_connection_
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
diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c
--- a/src/http/modules/ngx_http_ssl_module.c
+++ b/src/http/modules/ngx_http_ssl_module.c
@@ -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)) {
diff --git a/src/stream/ngx_stream_ssl_module.c b/src/stream/ngx_stream_ssl_module.c
--- a/src/stream/ngx_stream_ssl_module.c
+++ b/src/stream/ngx_stream_ssl_module.c
@@ -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 "
_______________________________________________
nginx-devel mailing list
nginx-devel@nginx.org
https://mailman.nginx.org/mailman/listinfo/nginx-devel
Subject Author Views Posted

[PATCH] QUIC: OpenSSL compatibility layer

Roman Arutyunyan 1207 December 21, 2022 02:08AM

Re: [PATCH] QUIC: OpenSSL compatibility layer

Roman Arutyunyan 132 January 09, 2023 06:14AM

Re: [PATCH] QUIC: OpenSSL compatibility layer

Sergey Kandaurov 138 February 02, 2023 01:36PM

Re: [PATCH] QUIC: OpenSSL compatibility layer

Roman Arutyunyan 118 February 06, 2023 09:28AM

Re: [PATCH] QUIC: OpenSSL compatibility layer

Sergey Kandaurov 120 February 08, 2023 07:30AM

Re: [PATCH] QUIC: OpenSSL compatibility layer

Roman Arutyunyan 123 February 08, 2023 09:42AM

Re: [PATCH] QUIC: OpenSSL compatibility layer

Sergey Kandaurov 129 February 08, 2023 10:18AM

Re: [PATCH] QUIC: OpenSSL compatibility layer

Roman Arutyunyan 109 February 22, 2023 09:22AM

Re: [PATCH] QUIC: OpenSSL compatibility layer

Sergey Kandaurov 99 March 23, 2023 09:20AM

Re: [PATCH] QUIC: OpenSSL compatibility layer

Roman Arutyunyan 127 March 23, 2023 09:54AM



Sorry, you do not have permission to post/reply in this forum.

Online Users

Guests: 223
Record Number of Users: 8 on April 13, 2023
Record Number of Guests: 421 on December 02, 2018
Powered by nginx      Powered by FreeBSD      PHP Powered      Powered by MariaDB      ipv6 ready