src: Enable HTTP/3 with boringssl

This commit is contained in:
Tatsuhiro Tsujikawa 2021-10-15 19:52:01 +09:00
parent c790ee64a4
commit 7055501efd
8 changed files with 282 additions and 33 deletions

View File

@ -474,6 +474,7 @@ if test "x${request_openssl}" != "xno"; then
CFLAGS="$OPENSSL_CFLAGS $CFLAGS"
LIBS="$OPENSSL_LIBS $LIBS"
# quictls/openssl has SSL_is_quic.
have_ssl_is_quic=no
AC_MSG_CHECKING([for SSL_is_quic])
AC_LINK_IFELSE([AC_LANG_PROGRAM([[
@ -485,6 +486,17 @@ if test "x${request_openssl}" != "xno"; then
[AC_MSG_RESULT([yes]); have_ssl_is_quic=yes],
[AC_MSG_RESULT([no]); have_ssl_is_quic=no])
# boringssl has SSL_set_quic_early_data_context.
AC_MSG_CHECKING([for SSL_set_quic_early_data_context])
AC_LINK_IFELSE([AC_LANG_PROGRAM([[
#include <openssl/ssl.h>
]], [[
SSL *ssl = NULL;
SSL_set_quic_early_data_context(ssl, NULL, 0);
]])],
[AC_MSG_RESULT([yes]); have_boringssl_quic=yes],
[AC_MSG_RESULT([no]); have_boringssl_quic=no])
CFLAGS="$save_CFLAGS"
LIBS="$save_LIBS"
fi
@ -527,21 +539,48 @@ fi
# ngtcp2_crypto_openssl (for src)
have_libngtcp2_crypto_openssl=no
if test "x${request_libngtcp2}" != "xno"; then
if test "x${have_ssl_is_quic}" = "xyes" &&
test "x${request_libngtcp2}" != "xno"; then
PKG_CHECK_MODULES([LIBNGTCP2_CRYPTO_OPENSSL],
[libngtcp2_crypto_openssl >= 0.0.0],
[have_libngtcp2_crypto_openssl=yes],
[have_libngtcp2_crypto_openssl=no])
if test "x${have_libngtcp2_crypto_openssl}" = "xno"; then
AC_MSG_NOTICE($LIBNGTCP2_CRYPTO_OPENSSL_PKG_ERRORS)
else
AC_DEFINE([HAVE_LIBNGTCP2_CRYPTO_OPENSSL], [1],
[Define to 1 if you have `libngtcp2_crypto_openssl` library.])
fi
fi
if test "x${request_libngtcp2}" = "xyes" &&
if test "x${have_ssl_is_quic}" = "xyes" &&
test "x${request_libngtcp2}" = "xyes" &&
test "x${have_libngtcp2_crypto_openssl}" != "xyes"; then
AC_MSG_ERROR([libngtcp2_crypto_openssl was requested (--with-libngtcp2) but not found])
fi
# ngtcp2_crypto_boringssl (for src)
have_libngtcp2_crypto_boringssl=no
if test "x${have_boringssl_quic}" = "xyes" &&
test "x${request_libngtcp2}" != "xno"; then
PKG_CHECK_MODULES([LIBNGTCP2_CRYPTO_BORINGSSL],
[libngtcp2_crypto_boringssl >= 0.0.0],
[have_libngtcp2_crypto_boringssl=yes],
[have_libngtcp2_crypto_boringssl=no])
if test "x${have_libngtcp2_crypto_boringssl}" = "xno"; then
AC_MSG_NOTICE($LIBNGTCP2_CRYPTO_BORINGSSL_PKG_ERRORS)
else
AC_DEFINE([HAVE_LIBNGTCP2_CRYPTO_BORINGSSL], [1],
[Define to 1 if you have `libngtcp2_crypto_boringssl` library.])
fi
fi
if test "x${have_boringssl_quic}" = "xyes" &&
test "x${request_libngtcp2}" = "xyes" &&
test "x${have_libngtcp2_crypto_boringssl}" != "xyes"; then
AC_MSG_ERROR([libngtcp2_crypto_boringssl was requested (--with-libngtcp2) but not found])
fi
# nghttp3 (for src)
have_libnghttp3=no
if test "x${request_libnghttp3}" != "xno"; then
@ -749,9 +788,11 @@ AM_CONDITIONAL([ENABLE_APP], [ test "x${enable_app}" = "xyes" ])
# Check HTTP/3 support
enable_http3=no
if test "x${request_http3}" != "xno" &&
test "x${have_ssl_is_quic}" = "xyes" &&
(test "x${have_ssl_is_quic}" = "xyes" ||
test "x${have_boringssl_quic}" = "xyes") &&
test "x${have_libngtcp2}" = "xyes" &&
test "x${have_libngtcp2_crypto_openssl}" = "xyes" &&
(test "x${have_libngtcp2_crypto_openssl}" = "xyes" ||
test "x${have_libngtcp2_crypto_boringssl}" = "xyes") &&
test "x${have_libnghttp3}" = "xyes"; then
enable_http3=yes
AC_DEFINE([ENABLE_HTTP3], [1], [Define to 1 if HTTP/3 is enabled.])
@ -1184,6 +1225,7 @@ AC_MSG_NOTICE([summary of build options:
Libc-ares: ${have_libcares} (CFLAGS='${LIBCARES_CFLAGS}' LIBS='${LIBCARES_LIBS}')
libngtcp2: ${have_libngtcp2} (CFLAGS='${LIBNGTCP2_CFLAGS}' LIBS='${LIBNGTCP2_LIBS}')
libngtcp2_crypto_openssl: ${have_libngtcp2_crypto_openssl} (CFLAGS='${LIBNGTCP2_CRYPTO_OPENSSL_CFLAGS}' LIBS='${LIBNGTCP2_CRYPTO_OPENSSL_LIBS}')
libngtcp2_crypto_boringssl: ${have_libngtcp2_crypto_boringssl} (CFLAGS='${LIBNGTCP2_CRYPTO_BORINGSSL_CFLAGS}' LIBS='${LIBNGTCP2_CRYPTO_BORINGSSL_LIBS}')
libnghttp3: ${have_libnghttp3} (CFLAGS='${LIBNGHTTP3_CFLAGS}' LIBS='${LIBNGHTTP3_LIBS}')
libbpf: ${have_libbpf} (CFLAGS='${LIBBPF_CFLAGS}' LIBS='${LIBBPF_LIBS}')
Libevent(SSL): ${have_libevent_openssl} (CFLAGS='${LIBEVENT_OPENSSL_CFLAGS}' LIBS='${LIBEVENT_OPENSSL_LIBS}')

View File

@ -47,6 +47,7 @@ AM_CPPFLAGS = \
@LIBEV_CFLAGS@ \
@LIBNGHTTP3_CFLAGS@ \
@LIBNGTCP2_CRYPTO_OPENSSL_CFLAGS@ \
@LIBNGTCP2_CRYPTO_BORINGSSL_CFLAGS@ \
@LIBNGTCP2_CFLAGS@ \
@OPENSSL_CFLAGS@ \
@LIBCARES_CFLAGS@ \
@ -65,6 +66,7 @@ LDADD = $(top_builddir)/lib/libnghttp2.la \
@LIBEV_LIBS@ \
@LIBNGHTTP3_LIBS@ \
@LIBNGTCP2_CRYPTO_OPENSSL_LIBS@ \
@LIBNGTCP2_CRYPTO_BORINGSSL_LIBS@ \
@LIBNGTCP2_LIBS@ \
@OPENSSL_LIBS@ \
@LIBCARES_LIBS@ \

View File

@ -474,8 +474,10 @@ struct Client {
int quic_stream_stop_sending(int64_t stream_id, uint64_t app_error_code);
int quic_extend_max_local_streams();
int quic_on_key(ngtcp2_crypto_level level, const uint8_t *rx_secret,
const uint8_t *tx_secret, size_t secretlen);
int quic_on_rx_secret(ngtcp2_crypto_level level, const uint8_t *secret,
size_t secretlen);
int quic_on_tx_secret(ngtcp2_crypto_level level, const uint8_t *secret,
size_t secretlen);
void quic_set_tls_alert(uint8_t alert);
void quic_write_client_handshake(ngtcp2_crypto_level level,

View File

@ -28,7 +28,12 @@
#include <iostream>
#include <ngtcp2/ngtcp2_crypto_openssl.h>
#ifdef HAVE_LIBNGTCP2_CRYPTO_OPENSSL
# include <ngtcp2/ngtcp2_crypto_openssl.h>
#endif // HAVE_LIBNGTCP2_CRYPTO_OPENSSL
#ifdef HAVE_LIBNGTCP2_CRYPTO_BORINGSSL
# include <ngtcp2/ngtcp2_crypto_boringssl.h>
#endif // HAVE_LIBNGTCP2_CRYPTO_BORINGSSL
#include <openssl/err.h>
@ -236,15 +241,19 @@ ngtcp2_tstamp timestamp(struct ev_loop *loop) {
}
} // namespace
#ifdef HAVE_LIBNGTCP2_CRYPTO_OPENSSL
namespace {
int set_encryption_secrets(SSL *ssl, OSSL_ENCRYPTION_LEVEL ossl_level,
const uint8_t *rx_secret, const uint8_t *tx_secret,
size_t secret_len) {
auto c = static_cast<Client *>(SSL_get_app_data(ssl));
auto level = ngtcp2_crypto_openssl_from_ossl_encryption_level(ossl_level);
if (c->quic_on_key(
ngtcp2_crypto_openssl_from_ossl_encryption_level(ossl_level),
rx_secret, tx_secret, secret_len) != 0) {
if (c->quic_on_rx_secret(level, rx_secret, secret_len) != 0) {
return 0;
}
if (c->quic_on_tx_secret(level, tx_secret, secret_len) != 0) {
return 0;
}
@ -282,6 +291,70 @@ auto quic_method = SSL_QUIC_METHOD{
send_alert,
};
} // namespace
#endif // HAVE_LIBNGTCP2_CRYPTO_OPENSSL
#ifdef HAVE_LIBNGTCP2_CRYPTO_BORINGSSL
namespace {
int set_read_secret(SSL *ssl, ssl_encryption_level_t ssl_level,
const SSL_CIPHER *cipher, const uint8_t *secret,
size_t secretlen) {
auto c = static_cast<Client *>(SSL_get_app_data(ssl));
if (c->quic_on_rx_secret(
ngtcp2_crypto_boringssl_from_ssl_encryption_level(ssl_level), secret,
secretlen) != 0) {
return 0;
}
return 1;
}
} // namespace
namespace {
int set_write_secret(SSL *ssl, ssl_encryption_level_t ssl_level,
const SSL_CIPHER *cipher, const uint8_t *secret,
size_t secretlen) {
auto c = static_cast<Client *>(SSL_get_app_data(ssl));
if (c->quic_on_tx_secret(
ngtcp2_crypto_boringssl_from_ssl_encryption_level(ssl_level), secret,
secretlen) != 0) {
return 0;
}
return 1;
}
} // namespace
namespace {
int add_handshake_data(SSL *ssl, ssl_encryption_level_t ssl_level,
const uint8_t *data, size_t len) {
auto c = static_cast<Client *>(SSL_get_app_data(ssl));
c->quic_write_client_handshake(
ngtcp2_crypto_boringssl_from_ssl_encryption_level(ssl_level), data, len);
return 1;
}
} // namespace
namespace {
int flush_flight(SSL *ssl) { return 1; }
} // namespace
namespace {
int send_alert(SSL *ssl, ssl_encryption_level_t level, uint8_t alert) {
auto c = static_cast<Client *>(SSL_get_app_data(ssl));
c->quic_set_tls_alert(alert);
return 1;
}
} // namespace
namespace {
auto quic_method = SSL_QUIC_METHOD{
set_read_secret, set_write_secret, add_handshake_data,
flush_flight, send_alert,
};
} // namespace
#endif // HAVE_LIBNGTCP2_CRYPTO_BORINGSSL
// qlog write callback -- excerpted from ngtcp2/examples/client_base.cc
namespace {
@ -462,24 +535,16 @@ void Client::quic_close_connection() {
ps.path.remote.addrlen, buf.data(), nwrite, 0);
}
int Client::quic_on_key(ngtcp2_crypto_level level, const uint8_t *rx_secret,
const uint8_t *tx_secret, size_t secretlen) {
int Client::quic_on_rx_secret(ngtcp2_crypto_level level, const uint8_t *secret,
size_t secretlen) {
if (ngtcp2_crypto_derive_and_install_rx_key(quic.conn, nullptr, nullptr,
nullptr, level, rx_secret,
nullptr, level, secret,
secretlen) != 0) {
std::cerr << "ngtcp2_crypto_derive_and_install_rx_key() failed"
<< std::endl;
return -1;
}
if (ngtcp2_crypto_derive_and_install_tx_key(quic.conn, nullptr, nullptr,
nullptr, level, tx_secret,
secretlen) != 0) {
std::cerr << "ngtcp2_crypto_derive_and_install_tx_key() failed"
<< std::endl;
return -1;
}
if (level == NGTCP2_CRYPTO_LEVEL_APPLICATION) {
auto s = std::make_unique<Http3Session>(this);
if (s->init_conn() == -1) {
@ -491,6 +556,19 @@ int Client::quic_on_key(ngtcp2_crypto_level level, const uint8_t *rx_secret,
return 0;
}
int Client::quic_on_tx_secret(ngtcp2_crypto_level level, const uint8_t *secret,
size_t secretlen) {
if (ngtcp2_crypto_derive_and_install_tx_key(quic.conn, nullptr, nullptr,
nullptr, level, secret,
secretlen) != 0) {
std::cerr << "ngtcp2_crypto_derive_and_install_tx_key() failed"
<< std::endl;
return -1;
}
return 0;
}
void Client::quic_set_tls_alert(uint8_t alert) {
quic.last_error = quic::err_transport_tls(alert);
}

View File

@ -740,9 +740,9 @@ void ConnectionHandler::handle_ocsp_complete() {
// that case we get nullptr.
auto quic_ssl_ctx = quic_all_ssl_ctx_[ocsp_.next];
if (quic_ssl_ctx) {
# ifndef OPENSSL_IS_BORINGSSL
auto quic_tls_ctx_data = static_cast<tls::TLSContextData *>(
SSL_CTX_get_app_data(quic_ssl_ctx));
# ifndef OPENSSL_IS_BORINGSSL
# ifdef HAVE_ATOMIC_STD_SHARED_PTR
std::atomic_store_explicit(
&quic_tls_ctx_data->ocsp_data,

View File

@ -613,6 +613,40 @@ int Http3Upstream::init(const UpstreamAddr *faddr, const Address &remote_addr,
params.max_idle_timeout = static_cast<ngtcp2_tstamp>(
quicconf.upstream.timeout.idle * NGTCP2_SECONDS);
#ifdef OPENSSL_IS_BORINGSSL
if (quicconf.upstream.early_data) {
ngtcp2_transport_params early_data_params{
.initial_max_stream_data_bidi_local =
params.initial_max_stream_data_bidi_local,
.initial_max_stream_data_bidi_remote =
params.initial_max_stream_data_bidi_remote,
.initial_max_stream_data_uni = params.initial_max_stream_data_uni,
.initial_max_data = params.initial_max_data,
.initial_max_streams_bidi = params.initial_max_streams_bidi,
.initial_max_streams_uni = params.initial_max_streams_uni,
};
// TODO include HTTP/3 SETTINGS
std::array<uint8_t, 128> quic_early_data_ctx;
auto quic_early_data_ctxlen = ngtcp2_encode_transport_params(
quic_early_data_ctx.data(), quic_early_data_ctx.size(),
NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS, &early_data_params);
assert(quic_early_data_ctxlen > 0);
assert(static_cast<size_t>(quic_early_data_ctxlen) <=
quic_early_data_ctx.size());
if (SSL_set_quic_early_data_context(handler_->get_ssl(),
quic_early_data_ctx.data(),
quic_early_data_ctxlen) != 1) {
ULOG(ERROR, this) << "SSL_set_quic_early_data_context failed";
return -1;
}
}
#endif // OPENSSL_IS_BORINGSSL
if (odcid) {
params.original_dcid = *odcid;
params.retry_scid = initial_hd.dcid;

View File

@ -369,7 +369,9 @@ ClientHandler *QUICConnectionHandler::handle_new_connection(
return nullptr;
}
#if OPENSSL_1_1_1_API && !defined(OPENSSL_IS_BORINGSSL)
assert(SSL_is_quic(ssl));
#endif // OPENSSL_1_1_1_API && !defined(OPENSSL_IS_BORINGSSL)
SSL_set_accept_state(ssl);
@ -377,7 +379,11 @@ ClientHandler *QUICConnectionHandler::handle_new_connection(
auto &quicconf = config->quic;
if (quicconf.upstream.early_data) {
#if OPENSSL_1_1_1_API && !defined(OPENSSL_IS_BORINGSSL)
SSL_set_quic_early_data_enabled(ssl, 1);
#else // !(OPENSSL_1_1_1_API && !defined(OPENSSL_IS_BORINGSSL))
SSL_set_early_data_enabled(ssl, 1);
#endif // !(OPENSSL_1_1_1_API && !defined(OPENSSL_IS_BORINGSSL))
}
// Disable TLS session ticket if we don't have working ticket

View File

@ -61,7 +61,12 @@
#ifdef ENABLE_HTTP3
# include <ngtcp2/ngtcp2.h>
# include <ngtcp2/ngtcp2_crypto.h>
# ifdef HAVE_LIBNGTCP2_CRYPTO_OPENSSL
# include <ngtcp2/ngtcp2_crypto_openssl.h>
# endif // HAVE_LIBNGTCP2_CRYPTO_OPENSSL
# ifdef HAVE_LIBNGTCP2_CRYPTO_BORINGSSL
# include <ngtcp2/ngtcp2_crypto_boringssl.h>
# endif // HAVE_LIBNGTCP2_CRYPTO_BORINGSSL
#endif // ENABLE_HTTP3
#include "shrpx_log.h"
@ -196,7 +201,8 @@ int servername_callback(SSL *ssl, int *al, void *arg) {
auto hostname = StringRef{std::begin(buf), end_buf};
#ifdef ENABLE_HTTP3
auto cert_tree = SSL_is_quic(ssl) ? worker->get_quic_cert_lookup_tree()
auto cert_tree = conn->proto == Proto::HTTP3
? worker->get_quic_cert_lookup_tree()
: worker->get_cert_lookup_tree();
#else // !ENABLE_HTTP3
auto cert_tree = worker->get_cert_lookup_tree();
@ -212,7 +218,7 @@ int servername_callback(SSL *ssl, int *al, void *arg) {
auto conn_handler = worker->get_connection_handler();
#ifdef ENABLE_HTTP3
const auto &ssl_ctx_list = SSL_is_quic(ssl)
const auto &ssl_ctx_list = conn->proto == Proto::HTTP3
? conn_handler->get_quic_indexed_ssl_ctx(idx)
: conn_handler->get_indexed_ssl_ctx(idx);
#else // !ENABLE_HTTP3
@ -1196,6 +1202,7 @@ SSL_CTX *create_ssl_context(const char *private_key_file, const char *cert_file,
}
#ifdef ENABLE_HTTP3
# ifdef HAVE_LIBNGTCP2_CRYPTO_OPENSSL
namespace {
int quic_set_encryption_secrets(SSL *ssl, OSSL_ENCRYPTION_LEVEL ossl_level,
const uint8_t *rx_secret,
@ -1259,6 +1266,84 @@ auto quic_method = SSL_QUIC_METHOD{
quic_send_alert,
};
} // namespace
# endif // HAVE_LIBNGTCP2_CRYPTO_OPENSSL
# ifdef HAVE_LIBNGTCP2_CRYPTO_BORINGSSL
namespace {
int quic_set_read_secret(SSL *ssl, ssl_encryption_level_t ssl_level,
const SSL_CIPHER *cipher, const uint8_t *secret,
size_t secretlen) {
auto conn = static_cast<Connection *>(SSL_get_app_data(ssl));
auto handler = static_cast<ClientHandler *>(conn->data);
auto upstream = static_cast<Http3Upstream *>(handler->get_upstream());
auto level = ngtcp2_crypto_boringssl_from_ssl_encryption_level(ssl_level);
if (upstream->on_rx_secret(level, secret, secretlen) != 0) {
return 0;
}
return 1;
}
} // namespace
namespace {
int quic_set_write_secret(SSL *ssl, ssl_encryption_level_t ssl_level,
const SSL_CIPHER *cipher, const uint8_t *secret,
size_t secretlen) {
auto conn = static_cast<Connection *>(SSL_get_app_data(ssl));
auto handler = static_cast<ClientHandler *>(conn->data);
auto upstream = static_cast<Http3Upstream *>(handler->get_upstream());
auto level = ngtcp2_crypto_boringssl_from_ssl_encryption_level(ssl_level);
if (upstream->on_tx_secret(level, secret, secretlen) != 0) {
return 0;
}
return 1;
}
} // namespace
namespace {
int quic_add_handshake_data(SSL *ssl, ssl_encryption_level_t ssl_level,
const uint8_t *data, size_t len) {
auto conn = static_cast<Connection *>(SSL_get_app_data(ssl));
auto handler = static_cast<ClientHandler *>(conn->data);
auto upstream = static_cast<Http3Upstream *>(handler->get_upstream());
auto level = ngtcp2_crypto_boringssl_from_ssl_encryption_level(ssl_level);
upstream->add_crypto_data(level, data, len);
return 1;
}
} // namespace
namespace {
int quic_flush_flight(SSL *ssl) { return 1; }
} // namespace
namespace {
int quic_send_alert(SSL *ssl, ssl_encryption_level_t level, uint8_t alert) {
auto conn = static_cast<Connection *>(SSL_get_app_data(ssl));
auto handler = static_cast<ClientHandler *>(conn->data);
auto upstream = static_cast<Http3Upstream *>(handler->get_upstream());
if (!upstream) {
return 1;
}
upstream->set_tls_alert(alert);
return 1;
}
} // namespace
namespace {
auto quic_method = SSL_QUIC_METHOD{
quic_set_read_secret, quic_set_write_secret, quic_add_handshake_data,
quic_flush_flight, quic_send_alert,
};
} // namespace
# endif // HAVE_LIBNGTCP2_CRYPTO_BORINGSSL
SSL_CTX *create_quic_ssl_context(const char *private_key_file,
const char *cert_file,
@ -1279,14 +1364,14 @@ SSL_CTX *create_quic_ssl_context(const char *private_key_file,
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION | SSL_OP_SINGLE_ECDH_USE |
SSL_OP_SINGLE_DH_USE |
SSL_OP_CIPHER_SERVER_PREFERENCE
# if OPENSSL_1_1_1_API
# if OPENSSL_1_1_1_API && !defined(OPENSSL_IS_BORINGSSL)
// The reason for disabling built-in anti-replay in OpenSSL is
// that it only works if client gets back to the same server.
// The freshness check described in
// https://tools.ietf.org/html/rfc8446#section-8.3 is still
// performed.
| SSL_OP_NO_ANTI_REPLAY
# endif // OPENSSL_1_1_1_API
# endif // OPENSSL_1_1_1_API && !defined(OPENSSL_IS_BORINGSSL)
;
auto config = mod_config();
@ -1309,13 +1394,13 @@ SSL_CTX *create_quic_ssl_context(const char *private_key_file,
DIE();
}
# if OPENSSL_1_1_1_API
# if OPENSSL_1_1_1_API && !defined(OPENSSL_IS_BORINGSSL)
if (SSL_CTX_set_ciphersuites(ssl_ctx, tlsconf.tls13_ciphers.c_str()) == 0) {
LOG(FATAL) << "SSL_CTX_set_ciphersuites " << tlsconf.tls13_ciphers
<< " failed: " << ERR_error_string(ERR_get_error(), nullptr);
DIE();
}
# endif // OPENSSL_1_1_1_API
# endif // OPENSSL_1_1_1_API && !defined(OPENSSL_IS_BORINGSSL)
# ifndef OPENSSL_NO_EC
# if !LIBRESSL_LEGACY_API && OPENSSL_VERSION_NUMBER >= 0x10002000L
@ -1503,7 +1588,7 @@ SSL_CTX *create_quic_ssl_context(const char *private_key_file,
# endif // !LIBRESSL_IN_USE && OPENSSL_VERSION_NUMBER >= 0x10002000L &&
// !defined(OPENSSL_IS_BORINGSSL)
# if OPENSSL_1_1_1_API
# if OPENSSL_1_1_1_API && !defined(OPENSSL_IS_BORINGSSL)
auto &quicconf = config->quic;
if (quicconf.upstream.early_data &&
@ -1513,7 +1598,7 @@ SSL_CTX *create_quic_ssl_context(const char *private_key_file,
<< ERR_error_string(ERR_get_error(), nullptr);
DIE();
}
# endif // OPENSSL_1_1_1_API
# endif // OPENSSL_1_1_1_API && !defined(OPENSSL_IS_BORINGSSL)
# ifndef OPENSSL_NO_PSK
SSL_CTX_set_psk_server_callback(ssl_ctx, psk_server_cb);