[PATCH] QUIC: OpenSSL compatibility layer

Roman Arutyunyan arut at nginx.com
Mon Feb 6 14:27:01 UTC 2023


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 at 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 at nginx.org
> https://mailman.nginx.org/mailman/listinfo/nginx-devel

--
Roman Arutyunyan
-------------- next part --------------
# HG changeset patch
# User Roman Arutyunyan <arut at 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 "


More information about the nginx-devel mailing list