From a2e2e46af39a63e1b0b1bdab3ead93a2267533c5 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Wed, 8 Sep 2021 23:23:17 +0900 Subject: [PATCH] Build with OpenSSL v3.0.0 --- .github/workflows/build.yml | 21 +++++- examples/libevent-server.c | 29 ++++++--- src/HttpServer.cc | 41 ++++++++++-- src/h2load.cc | 11 ++++ src/shrpx_tls.cc | 126 +++++++++++++++++++++++++++++++++--- src/ssl_compat.h | 2 + 6 files changed, 204 insertions(+), 26 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9f148458..2456088a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,6 +12,12 @@ jobs: compiler: [gcc, clang] buildtool: [autotools, cmake] http3: [http3, no-http3] + openssl: [openssl1, openssl3] + exclude: + - os: macos-10.15 + openssl: openssl3 + - http3: no-http3 + openssl: openssl3 steps: - uses: actions/checkout@v2 @@ -88,14 +94,25 @@ jobs: EXTRA_AUTOTOOLS_OPTS="--with-libbpf" echo 'EXTRA_AUTOTOOLS_OPTS='"$EXTRA_AUTOTOOLS_OPTS" >> $GITHUB_ENV - - name: Build quictls/openssl - if: matrix.http3 == 'http3' + - name: Build quictls/openssl v1.1.1 + if: matrix.http3 == 'http3' && matrix.openssl == 'openssl1' run: | git clone --depth 1 -b OpenSSL_1_1_1l+quic https://github.com/quictls/openssl cd openssl ./config enable-tls1_3 --prefix=$PWD/build make -j$(nproc) make install_sw + - name: Build quictls/openssl v3.0.0 + if: matrix.http3 == 'http3' && matrix.openssl == 'openssl3' + run: | + unset CPPFLAGS + unset LDFLAGS + + git clone --depth 1 -b openssl-3.0.0+quic https://github.com/quictls/openssl + cd openssl + ./config enable-tls1_3 --prefix=$PWD/build --libdir=$PWD/build/lib + make -j$(nproc) + make install_sw - name: Build nghttp3 if: matrix.http3 == 'http3' run: | diff --git a/examples/libevent-server.c b/examples/libevent-server.c index a5536175..0465a785 100644 --- a/examples/libevent-server.c +++ b/examples/libevent-server.c @@ -142,7 +142,6 @@ static int alpn_select_proto_cb(SSL *ssl, const unsigned char **out, /* Create SSL_CTX. */ static SSL_CTX *create_ssl_ctx(const char *key_file, const char *cert_file) { SSL_CTX *ssl_ctx; - EC_KEY *ecdh; ssl_ctx = SSL_CTX_new(SSLv23_server_method()); if (!ssl_ctx) { @@ -153,14 +152,28 @@ static SSL_CTX *create_ssl_ctx(const char *key_file, const char *cert_file) { SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION | SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); - - ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); - if (!ecdh) { - errx(1, "EC_KEY_new_by_curv_name failed: %s", - ERR_error_string(ERR_get_error(), NULL)); +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + { + EVP_PKEY *ecdh; + ecdh = EVP_EC_gen("P-256"); + if (!ecdh) { + errx(1, "EVP_EC_gen failed: %s", ERR_error_string(ERR_get_error(), NULL)); + } + SSL_CTX_set_tmp_ecdh(ssl_ctx, ecdh); + EVP_PKEY_free(ecdh); } - SSL_CTX_set_tmp_ecdh(ssl_ctx, ecdh); - EC_KEY_free(ecdh); +#else /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */ + { + EC_KEY *ecdh; + ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); + if (!ecdh) { + errx(1, "EC_KEY_new_by_curv_name failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + } + SSL_CTX_set_tmp_ecdh(ssl_ctx, ecdh); + EC_KEY_free(ecdh); + } +#endif /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */ if (SSL_CTX_use_PrivateKey_file(ssl_ctx, key_file, SSL_FILETYPE_PEM) != 1) { errx(1, "Could not read private key file %s", key_file); diff --git a/src/HttpServer.cc b/src/HttpServer.cc index 5075bc95..67bc87aa 100644 --- a/src/HttpServer.cc +++ b/src/HttpServer.cc @@ -52,8 +52,13 @@ #include #include +#include "ssl_compat.h" + #include #include +#if OPENSSL_3_0_0_API +# include +#endif // OPENSSL_3_0_0_API #include @@ -2138,15 +2143,22 @@ int HttpServer::run() { SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_SERVER); #ifndef OPENSSL_NO_EC - // Disabled SSL_CTX_set_ecdh_auto, because computational cost of // chosen curve is much higher than P-256. - // #if OPENSSL_VERSION_NUMBER >= 0x10002000L // SSL_CTX_set_ecdh_auto(ssl_ctx, 1); - // #else // OPENSSL_VERSION_NUBMER < 0x10002000L // Use P-256, which is sufficiently secure at the time of this // writing. +# if OPENSSL_3_0_0_API + auto ecdh = EVP_EC_gen("P-256"); + if (ecdh == nullptr) { + std::cerr << "EC_KEY_new_by_curv_name failed: " + << ERR_error_string(ERR_get_error(), nullptr); + return -1; + } + SSL_CTX_set_tmp_ecdh(ssl_ctx, ecdh); + EVP_PKEY_free(ecdh); +# else // !OPENSSL_3_0_0_API auto ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); if (ecdh == nullptr) { std::cerr << "EC_KEY_new_by_curv_name failed: " @@ -2155,19 +2167,33 @@ int HttpServer::run() { } SSL_CTX_set_tmp_ecdh(ssl_ctx, ecdh); EC_KEY_free(ecdh); - // #endif // OPENSSL_VERSION_NUBMER < 0x10002000L - -#endif // OPENSSL_NO_EC +# endif // !OPENSSL_3_0_0_API +#endif // OPENSSL_NO_EC if (!config_->dh_param_file.empty()) { // Read DH parameters from file - auto bio = BIO_new_file(config_->dh_param_file.c_str(), "r"); + auto bio = BIO_new_file(config_->dh_param_file.c_str(), "rb"); if (bio == nullptr) { std::cerr << "BIO_new_file() failed: " << ERR_error_string(ERR_get_error(), nullptr) << std::endl; return -1; } +#if OPENSSL_3_0_0_API + EVP_PKEY *dh = nullptr; + auto dctx = OSSL_DECODER_CTX_new_for_pkey( + &dh, "PEM", nullptr, "DH", OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS, + nullptr, nullptr); + + if (!OSSL_DECODER_from_bio(dctx, bio)) { + std::cerr << "OSSL_DECODER_from_bio() failed: " + << ERR_error_string(ERR_get_error(), nullptr) << std::endl; + return -1; + } + + SSL_CTX_set_tmp_dh(ssl_ctx, dh); + EVP_PKEY_free(dh); +#else // !OPENSSL_3_0_0_API auto dh = PEM_read_bio_DHparams(bio, nullptr, nullptr, nullptr); if (dh == nullptr) { @@ -2178,6 +2204,7 @@ int HttpServer::run() { SSL_CTX_set_tmp_dh(ssl_ctx, dh); DH_free(dh); +#endif // !OPENSSL_3_0_0_API BIO_free(bio); } diff --git a/src/h2load.cc b/src/h2load.cc index a5edd3e0..ad37144e 100644 --- a/src/h2load.cc +++ b/src/h2load.cc @@ -827,6 +827,16 @@ void print_server_tmp_key(SSL *ssl) { std::cout << "DH " << EVP_PKEY_bits(key) << " bits" << std::endl; break; case EVP_PKEY_EC: { +# if OPENSSL_3_0_0_API + std::array curve_name; + const char *cname; + if (!EVP_PKEY_get_utf8_string_param(key, "group", curve_name.data(), + curve_name.size(), nullptr)) { + cname = ""; + } else { + cname = curve_name.data(); + } +# else // !OPENSSL_3_0_0_API auto ec = EVP_PKEY_get1_EC_KEY(key); auto ec_del = defer(EC_KEY_free, ec); auto nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec)); @@ -834,6 +844,7 @@ void print_server_tmp_key(SSL *ssl) { if (!cname) { cname = OBJ_nid2sn(nid); } +# endif // !OPENSSL_3_0_0_API std::cout << "ECDH " << cname << " " << EVP_PKEY_bits(key) << " bits" << std::endl; diff --git a/src/shrpx_tls.cc b/src/shrpx_tls.cc index b26bc482..cda9bbc3 100644 --- a/src/shrpx_tls.cc +++ b/src/shrpx_tls.cc @@ -40,6 +40,8 @@ #include +#include "ssl_compat.h" + #include #include #include @@ -48,6 +50,11 @@ #ifndef OPENSSL_NO_OCSP # include #endif // OPENSSL_NO_OCSP +#if OPENSSL_3_0_0_API +# include +# include +# include +#endif // OPENSSL_3_0_0_API #include @@ -72,7 +79,6 @@ #include "util.h" #include "tls.h" #include "template.h" -#include "ssl_compat.h" #include "timegm.h" using namespace nghttp2; @@ -246,6 +252,15 @@ int servername_callback(SSL *ssl, int *al, void *arg) { for (auto i = 0; i < num_shared_curves; ++i) { auto shared_curve = SSL_get_shared_curve(ssl, i); +# if OPENSSL_3_0_0_API + // It looks like only short name is defined in OpenSSL. No idea + // which one to use because it is unknown that which one + // EVP_PKEY_get_utf8_string_param("group") returns. + auto shared_curve_name = OBJ_nid2sn(shared_curve); + if (shared_curve_name == nullptr) { + continue; + } +# endif // OPENSSL_3_0_0_API for (auto ssl_ctx : ssl_ctx_list) { auto cert = SSL_CTX_get0_certificate(ssl_ctx); @@ -260,11 +275,23 @@ int servername_callback(SSL *ssl, int *al, void *arg) { continue; } -# if OPENSSL_1_1_API +# if OPENSSL_3_0_0_API + std::array curve_name; + if (!EVP_PKEY_get_utf8_string_param(pubkey, "group", curve_name.data(), + curve_name.size(), nullptr)) { + continue; + } + + if (strcmp(shared_curve_name, curve_name.data()) == 0) { + SSL_set_SSL_CTX(ssl, ssl_ctx); + return SSL_TLSEXT_ERR_OK; + } +# else // !OPENSSL_3_0_0_API +# if OPENSSL_1_1_API auto eckey = EVP_PKEY_get0_EC_KEY(pubkey); -# else // !OPENSSL_1_1_API +# else // !OPENSSL_1_1_API auto eckey = EVP_PKEY_get1_EC_KEY(pubkey); -# endif // !OPENSSL_1_1_API +# endif // !OPENSSL_1_1_API if (eckey == nullptr) { continue; @@ -273,15 +300,16 @@ int servername_callback(SSL *ssl, int *al, void *arg) { auto ecgroup = EC_KEY_get0_group(eckey); auto cert_curve = EC_GROUP_get_curve_name(ecgroup); -# if !OPENSSL_1_1_API +# if !OPENSSL_1_1_API EC_KEY_free(eckey); EVP_PKEY_free(pubkey); -# endif // !OPENSSL_1_1_API +# endif // !OPENSSL_1_1_API if (shared_curve == cert_curve) { SSL_set_SSL_CTX(ssl, ssl_ctx); return SSL_TLSEXT_ERR_OK; } +# endif // !OPENSSL_3_0_0_API } } #endif // !defined(OPENSSL_IS_BORINGSSL) && !LIBRESSL_IN_USE && @@ -495,7 +523,13 @@ SSL_SESSION *tls_session_get_cb(SSL *ssl, namespace { int ticket_key_cb(SSL *ssl, unsigned char *key_name, unsigned char *iv, - EVP_CIPHER_CTX *ctx, HMAC_CTX *hctx, int enc) { + EVP_CIPHER_CTX *ctx, +#if OPENSSL_3_0_0_API + EVP_MAC_CTX *hctx, +#else // !OPENSSL_3_0_0_API + HMAC_CTX *hctx, +#endif // !OPENSSL_3_0_0_API + int enc) { auto conn = static_cast(SSL_get_app_data(ssl)); auto handler = static_cast(conn->data); auto worker = handler->get_worker(); @@ -528,8 +562,25 @@ int ticket_key_cb(SSL *ssl, unsigned char *key_name, unsigned char *iv, EVP_EncryptInit_ex(ctx, get_config()->tls.ticket.cipher, nullptr, key.data.enc_key.data(), iv); +#if OPENSSL_3_0_0_API + std::array params{ + OSSL_PARAM_construct_octet_string( + OSSL_MAC_PARAM_KEY, key.data.hmac_key.data(), key.hmac_keylen), + OSSL_PARAM_construct_utf8_string( + OSSL_MAC_PARAM_DIGEST, + const_cast(EVP_MD_get0_name(key.hmac)), 0), + OSSL_PARAM_construct_end(), + }; + if (!EVP_MAC_CTX_set_params(hctx, params.data())) { + if (LOG_ENABLED(INFO)) { + CLOG(INFO, handler) << "EVP_MAC_CTX_set_params failed"; + } + return -1; + } +#else // !OPENSSL_3_0_0_API HMAC_Init_ex(hctx, key.data.hmac_key.data(), key.hmac_keylen, key.hmac, nullptr); +#endif // !OPENSSL_3_0_0_API return 1; } @@ -556,8 +607,25 @@ int ticket_key_cb(SSL *ssl, unsigned char *key_name, unsigned char *iv, } auto &key = keys[i]; +#if OPENSSL_3_0_0_API + std::array params{ + OSSL_PARAM_construct_octet_string( + OSSL_MAC_PARAM_KEY, key.data.hmac_key.data(), key.hmac_keylen), + OSSL_PARAM_construct_utf8_string( + OSSL_MAC_PARAM_DIGEST, const_cast(EVP_MD_get0_name(key.hmac)), + 0), + OSSL_PARAM_construct_end(), + }; + if (!EVP_MAC_CTX_set_params(hctx, params.data())) { + if (LOG_ENABLED(INFO)) { + CLOG(INFO, handler) << "EVP_MAC_CTX_set_params failed"; + } + return -1; + } +#else // !OPENSSL_3_0_0_API HMAC_Init_ex(hctx, key.data.hmac_key.data(), key.hmac_keylen, key.hmac, nullptr); +#endif // !OPENSSL_3_0_0_API EVP_DecryptInit_ex(ctx, key.cipher, nullptr, key.data.enc_key.data(), iv); #ifdef TLS1_3_VERSION @@ -931,12 +999,27 @@ SSL_CTX *create_ssl_context(const char *private_key_file, const char *cert_file, if (!tlsconf.dh_param_file.empty()) { // Read DH parameters from file - auto bio = BIO_new_file(tlsconf.dh_param_file.c_str(), "r"); + auto bio = BIO_new_file(tlsconf.dh_param_file.c_str(), "rb"); if (bio == nullptr) { LOG(FATAL) << "BIO_new_file() failed: " << ERR_error_string(ERR_get_error(), nullptr); DIE(); } +#if OPENSSL_3_0_0_API + EVP_PKEY *dh = nullptr; + auto dctx = OSSL_DECODER_CTX_new_for_pkey( + &dh, "PEM", nullptr, "DH", OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS, + nullptr, nullptr); + + if (!OSSL_DECODER_from_bio(dctx, bio)) { + LOG(FATAL) << "OSSL_DECODER_from_bio() failed: " + << ERR_error_string(ERR_get_error(), nullptr); + DIE(); + } + + SSL_CTX_set_tmp_dh(ssl_ctx, dh); + EVP_PKEY_free(dh); +#else // !OPENSSL_3_0_0_API auto dh = PEM_read_bio_DHparams(bio, nullptr, nullptr, nullptr); if (dh == nullptr) { LOG(FATAL) << "PEM_read_bio_DHparams() failed: " @@ -945,6 +1028,7 @@ SSL_CTX *create_ssl_context(const char *private_key_file, const char *cert_file, } SSL_CTX_set_tmp_dh(ssl_ctx, dh); DH_free(dh); +#endif // !OPENSSL_3_0_0_API BIO_free(bio); } @@ -1024,7 +1108,11 @@ SSL_CTX *create_ssl_context(const char *private_key_file, const char *cert_file, verify_callback); } SSL_CTX_set_tlsext_servername_callback(ssl_ctx, servername_callback); +#if OPENSSL_3_0_0_API + SSL_CTX_set_tlsext_ticket_key_evp_cb(ssl_ctx, ticket_key_cb); +#else // !OPENSSL_3_0_0_API SSL_CTX_set_tlsext_ticket_key_cb(ssl_ctx, ticket_key_cb); +#endif // !OPENSSL_3_0_0_API #ifndef OPENSSL_IS_BORINGSSL SSL_CTX_set_tlsext_status_cb(ssl_ctx, ocsp_resp_cb); #endif // OPENSSL_IS_BORINGSSL @@ -1249,12 +1337,27 @@ SSL_CTX *create_quic_ssl_context(const char *private_key_file, if (!tlsconf.dh_param_file.empty()) { // Read DH parameters from file - auto bio = BIO_new_file(tlsconf.dh_param_file.c_str(), "r"); + auto bio = BIO_new_file(tlsconf.dh_param_file.c_str(), "rb"); if (bio == nullptr) { LOG(FATAL) << "BIO_new_file() failed: " << ERR_error_string(ERR_get_error(), nullptr); DIE(); } +# if OPENSSL_3_0_0_API + EVP_PKEY *dh = nullptr; + auto dctx = OSSL_DECODER_CTX_new_for_pkey( + &dh, "PEM", nullptr, "DH", OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS, + nullptr, nullptr); + + if (!OSSL_DECODER_from_bio(dctx, bio)) { + LOG(FATAL) << "OSSL_DECODER_from_bio() failed: " + << ERR_error_string(ERR_get_error(), nullptr); + DIE(); + } + + SSL_CTX_set_tmp_dh(ssl_ctx, dh); + EVP_PKEY_free(dh); +# else // !OPENSSL_3_0_0_API auto dh = PEM_read_bio_DHparams(bio, nullptr, nullptr, nullptr); if (dh == nullptr) { LOG(FATAL) << "PEM_read_bio_DHparams() failed: " @@ -1263,6 +1366,7 @@ SSL_CTX *create_quic_ssl_context(const char *private_key_file, } SSL_CTX_set_tmp_dh(ssl_ctx, dh); DH_free(dh); +# endif // !OPENSSL_3_0_0_API BIO_free(bio); } @@ -1342,7 +1446,11 @@ SSL_CTX *create_quic_ssl_context(const char *private_key_file, verify_callback); } SSL_CTX_set_tlsext_servername_callback(ssl_ctx, servername_callback); +# if OPENSSL_3_0_0_API + SSL_CTX_set_tlsext_ticket_key_evp_cb(ssl_ctx, ticket_key_cb); +# else // !OPENSSL_3_0_0_API SSL_CTX_set_tlsext_ticket_key_cb(ssl_ctx, ticket_key_cb); +# endif // !OPENSSL_3_0_0_API # ifndef OPENSSL_IS_BORINGSSL SSL_CTX_set_tlsext_status_cb(ssl_ctx, ocsp_resp_cb); # endif // OPENSSL_IS_BORINGSSL diff --git a/src/ssl_compat.h b/src/ssl_compat.h index 9574b7cc..e1077eef 100644 --- a/src/ssl_compat.h +++ b/src/ssl_compat.h @@ -29,12 +29,14 @@ # if defined(LIBRESSL_VERSION_NUMBER) # define OPENSSL_1_1_API 0 # define OPENSSL_1_1_1_API 0 +# define OPENSSL_3_0_0_API 0 # define LIBRESSL_IN_USE 1 # define LIBRESSL_LEGACY_API (LIBRESSL_VERSION_NUMBER < 0x20700000L) # define LIBRESSL_2_7_API (LIBRESSL_VERSION_NUMBER >= 0x20700000L) # else // !defined(LIBRESSL_VERSION_NUMBER) # define OPENSSL_1_1_API (OPENSSL_VERSION_NUMBER >= 0x1010000fL) # define OPENSSL_1_1_1_API (OPENSSL_VERSION_NUMBER >= 0x10101000L) +# define OPENSSL_3_0_0_API (OPENSSL_VERSION_NUMBER >= 0x30000000L) # define LIBRESSL_IN_USE 0 # define LIBRESSL_LEGACY_API 0 # define LIBRESSL_2_7_API 0