From 1e7eb59e2520402a513ce1ea80e5f4c87050a507 Mon Sep 17 00:00:00 2001 From: Josiah VanderZee Date: Mon, 29 Jun 2026 13:08:50 -0500 Subject: [PATCH 1/3] Add unit tests for SSL Diffie-Hellman key configuration --- src/iocore/net/CMakeLists.txt | 3 + src/iocore/net/unit_tests/test_SSLDHParams.cc | 186 ++++++++++++++++++ 2 files changed, 189 insertions(+) create mode 100644 src/iocore/net/unit_tests/test_SSLDHParams.cc diff --git a/src/iocore/net/CMakeLists.txt b/src/iocore/net/CMakeLists.txt index ac3f12d7cc8..6ca310c44d3 100644 --- a/src/iocore/net/CMakeLists.txt +++ b/src/iocore/net/CMakeLists.txt @@ -144,6 +144,9 @@ if(BUILD_TESTING) unit_tests/unit_test_main.cc unit_tests/benchmark_TLSCertCompression.cc ) + if(SSLLIB_IS_OPENSSL3) + target_sources(test_net PRIVATE unit_tests/test_SSLDHParams.cc) + endif() # Use link groups to solve circular dependency set(LINK_GROUP_LIBS ts::logging diff --git a/src/iocore/net/unit_tests/test_SSLDHParams.cc b/src/iocore/net/unit_tests/test_SSLDHParams.cc new file mode 100644 index 00000000000..4b9a65ac556 --- /dev/null +++ b/src/iocore/net/unit_tests/test_SSLDHParams.cc @@ -0,0 +1,186 @@ +/** @file + + Catch based unit tests for the DH-parameter handling behavior of + SSLMultiCertConfigLoader::init_server_ssl_ctx, which is the inknet + public boundary that transitively invokes ssl_context_enable_dhe + and (when a file is configured) load_dhparams_file. + + @section license License + + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#include +#include "../P_SSLCertLookup.h" +#include "../P_SSLConfig.h" +#include "../P_SSLUtils.h" + +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include + +namespace +{ + +std::string +make_valid_dh_pem() +{ + EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_from_name(nullptr, "DH", nullptr); + REQUIRE(pctx != nullptr); + REQUIRE(EVP_PKEY_paramgen_init(pctx) > 0); + char prime_group[]{"dh_2048_256"}; + OSSL_PARAM const params[2] = { + OSSL_PARAM_construct_utf8_string("group", prime_group, 0), + OSSL_PARAM_construct_end(), + }; + REQUIRE(EVP_PKEY_CTX_set_params(pctx, params) > 0); + EVP_PKEY *pkey = nullptr; + REQUIRE(EVP_PKEY_generate(pctx, &pkey) > 0); + + BIO *bio = BIO_new(BIO_s_mem()); + REQUIRE(PEM_write_bio_Parameters(bio, pkey) == 1); + BUF_MEM *bm = nullptr; + BIO_get_mem_ptr(bio, &bm); + std::string out{bm->data, bm->length}; + BIO_free(bio); + EVP_PKEY_free(pkey); + EVP_PKEY_CTX_free(pctx); + return out; +} + +std::string +make_rsa_pem() +{ + EVP_PKEY *pkey = EVP_RSA_gen(1024); + REQUIRE(pkey != nullptr); + BIO *bio = BIO_new(BIO_s_mem()); + REQUIRE(PEM_write_bio_PrivateKey(bio, pkey, nullptr, nullptr, 0, nullptr, nullptr) == 1); + BUF_MEM *bm = nullptr; + BIO_get_mem_ptr(bio, &bm); + std::string out{bm->data, bm->length}; + BIO_free(bio); + EVP_PKEY_free(pkey); + return out; +} + +class TempFile +{ +public: + explicit TempFile(std::string const &contents) + { + char tmpl[] = "/tmp/ats_dhparams_XXXXXX"; + int fd = mkstemp(tmpl); + REQUIRE(fd != -1); + this->path = tmpl; + if (!contents.empty()) { + REQUIRE(write(fd, contents.data(), contents.size()) == static_cast(contents.size())); + } + close(fd); + } + TempFile(TempFile const &) = delete; + TempFile(TempFile &&) = delete; + TempFile &operator=(TempFile const &) = delete; + TempFile &operator=(TempFile &&) = delete; + ~TempFile() { unlink(this->path.c_str()); } + + char const * + get_path() const + { + return this->path.c_str(); + } + +private: + std::string path; +}; + +// Drives ssl_context_enable_dhe via init_server_ssl_ctx, holding every +// non-DHE input fixed and varying only dhparamsFile. An empty CertLoadData +// selects the "default generated ctx" branch which still traverses +// ssl_context_enable_dhe but skips cert/key loading entirely, so a non-empty +// returned vector with a non-null SSL_CTX is observable iff DHE configuration +// succeeded. +bool +init_with_dhparams(char const *dhparams_file) +{ + SSLConfigParams params; + params.dhparamsFile = dhparams_file ? ats_strdup(dhparams_file) : nullptr; + + SSLMultiCertConfigLoader loader{¶ms}; + SSLMultiCertConfigLoader::CertLoadData data; + auto contexts = loader.init_server_ssl_ctx(data, nullptr); + + bool ok = !contexts.empty() && contexts.front().ctx != nullptr; + for (auto const &lc : contexts) { + SSL_CTX_free(lc.ctx); + } + return ok; +} + +} // namespace + +TEST_CASE("ssl_context_enable_dhe: nullptr dhparams file falls back to built-in DH parameters") +{ + CHECK(init_with_dhparams(nullptr)); +} + +TEST_CASE("ssl_context_enable_dhe: valid ffdhe2048 DH PEM file is accepted") +{ + TempFile dh{make_valid_dh_pem()}; + CHECK(init_with_dhparams(dh.get_path())); +} + +TEST_CASE("ssl_context_enable_dhe: nonexistent dhparams path is rejected") +{ + CHECK_FALSE(init_with_dhparams("/tmp/ats_dhparams_does_not_exist_zzz_xyz")); +} + +TEST_CASE("ssl_context_enable_dhe: empty dhparams file is rejected") +{ + TempFile empty{""}; + CHECK_FALSE(init_with_dhparams(empty.get_path())); +} + +TEST_CASE("ssl_context_enable_dhe: non-PEM garbage in dhparams file is rejected") +{ + TempFile garbage{"this is definitely not a PEM-encoded DH parameter block\n"}; + CHECK_FALSE(init_with_dhparams(garbage.get_path())); +} + +TEST_CASE("ssl_context_enable_dhe: PEM of wrong key type (RSA) is rejected by DH-only decoder") +{ + TempFile rsa{make_rsa_pem()}; + CHECK_FALSE(init_with_dhparams(rsa.get_path())); +} + +TEST_CASE("ssl_context_enable_dhe: truncated DH PEM (missing END marker) is rejected") +{ + std::string pem = make_valid_dh_pem(); + auto end = pem.find("-----END"); + REQUIRE(end != std::string::npos); + TempFile truncated{pem.substr(0, end)}; + CHECK_FALSE(init_with_dhparams(truncated.get_path())); +} From f5e94264e9d34a4d8d0f5bdbf9005f20e642325d Mon Sep 17 00:00:00 2001 From: Josiah VanderZee Date: Mon, 29 Jun 2026 13:11:57 -0500 Subject: [PATCH 2/3] Support OpenSSL 3.0 APIs for Diffie-Hellman --- src/iocore/net/P_SSLUtils.h | 26 ++++++++++++ src/iocore/net/SSLUtils.cc | 84 +++++++++++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+) diff --git a/src/iocore/net/P_SSLUtils.h b/src/iocore/net/P_SSLUtils.h index 8e2cbf57884..af5728e4869 100644 --- a/src/iocore/net/P_SSLUtils.h +++ b/src/iocore/net/P_SSLUtils.h @@ -25,6 +25,10 @@ #include "iocore/net/SSLTypes.h" #include "tscore/Diags.h" +#ifdef OPENSSL_IS_OPENSSL3 +#include +#include +#endif #define OPENSSL_THREAD_DEFINES #if __has_include() #include @@ -110,6 +114,24 @@ namespace detail } }; +#ifdef OPENSSL_IS_OPENSSL3 + struct PKEYCTXDeleter { + void + operator()(EVP_PKEY_CTX *pctx) + { + EVP_PKEY_CTX_free(pctx); + } + }; + + struct DecoderCTXDeleter { + void + operator()(OSSL_DECODER_CTX *dctx) + { + OSSL_DECODER_CTX_free(dctx); + } + }; +#endif + } // namespace detail } // namespace ssl @@ -134,3 +156,7 @@ struct ats_wildcard_matcher { using scoped_X509 = std::unique_ptr; using scoped_BIO = std::unique_ptr; +#ifdef OPENSSL_IS_OPENSSL3 +using scoped_PKEY_CTX = std::unique_ptr; +using scoped_Decoder_CTX = std::unique_ptr; +#endif diff --git a/src/iocore/net/SSLUtils.cc b/src/iocore/net/SSLUtils.cc index 7c7637affa1..ddf282a0dbd 100644 --- a/src/iocore/net/SSLUtils.cc +++ b/src/iocore/net/SSLUtils.cc @@ -42,6 +42,7 @@ #include "tscore/ink_config.h" #include "tscore/SimpleTokenizer.h" #include "tscore/Layout.h" +#include "tscore/ink_assert.h" #include "tscore/ink_cap.h" #include "tscore/ink_mutex.h" #include "tscore/Filenames.h" @@ -54,6 +55,11 @@ #include #include #include +#ifdef OPENSSL_IS_OPENSSL3 +#include +#include +#include +#endif #include #include #if HAVE_ENGINE_LOAD_DYNAMIC @@ -487,6 +493,83 @@ SSLMultiCertConfigLoader::_enable_early_data([[maybe_unused]] SSL_CTX *ctx) return true; } +#if OPENSSL_IS_OPENSSL3 +static EVP_PKEY * +load_dhparams_file(char const *dhparams_file) +{ + EVP_PKEY *pkey{}; + scoped_Decoder_CTX dctx{OSSL_DECODER_CTX_new_for_pkey(&pkey, "PEM", NULL, "DH", OSSL_KEYMGMT_SELECT_ALL_PARAMETERS, NULL, NULL)}; + if (!dctx) { + Error("failed to create OpenSSL decoder context - could not enable DH"); + return nullptr; + } + + ink_assert(OSSL_DECODER_CTX_get_num_decoders(dctx.get()) > 0); + scoped_BIO bio{BIO_new_file(dhparams_file, "r")}; + if (!OSSL_DECODER_from_bio(dctx.get(), bio.get())) { + Error("SSL dhparams source returned invalid parameters"); + return nullptr; + } + + return pkey; +} + +static EVP_PKEY * +gen_dh_2048_256_pkey() +{ + scoped_PKEY_CTX pctx{EVP_PKEY_CTX_new_from_name(NULL, "DH", NULL)}; + if (!pctx) { + Error("failed to create OpenSSL pkey context - could not enable DH"); + return nullptr; + } + + if (EVP_PKEY_keygen_init(pctx.get()) <= 0) { + Error("failed to initialize OpenSSL keygen - could not enable DH"); + return nullptr; + } + + char prime_group[]{"dh_2048_256"}; + OSSL_PARAM const params[]{OSSL_PARAM_utf8_string("group", prime_group, 0), OSSL_PARAM_END}; + + if (!EVP_PKEY_CTX_set_params(pctx.get(), params)) { + Error("SSL dhparams source returned invalid parameters"); + return nullptr; + } + + EVP_PKEY *pkey{}; + EVP_PKEY_generate(pctx.get(), &pkey); + + return pkey; +} + +static SSL_CTX * +ssl_context_enable_dhe(const char *dhparams_file, SSL_CTX *ctx) +{ + EVP_PKEY *pkey{}; + + if (dhparams_file) { + pkey = load_dhparams_file(dhparams_file); + if (!pkey) { + return nullptr; + } + } else { + pkey = gen_dh_2048_256_pkey(); + if (!pkey) { + Error("SSL dhparams source returned invalid parameters"); + return nullptr; + } + } + + // If set0_tmp_dh_pkey succeeds, pkey ownership tranfers to ctx. + if (!SSL_CTX_set_options(ctx, SSL_OP_SINGLE_DH_USE) || !SSL_CTX_set0_tmp_dh_pkey(ctx, pkey)) { + EVP_PKEY_free(pkey); + Error("failed to configure SSL DH"); + return nullptr; + } + + return ctx; +} +#else static SSL_CTX * ssl_context_enable_dhe(const char *dhparams_file, SSL_CTX *ctx) { @@ -514,6 +597,7 @@ ssl_context_enable_dhe(const char *dhparams_file, SSL_CTX *ctx) return ctx; } +#endif #if TS_HAS_TLS_SESSION_TICKET static bool From 53c609059ad921b9e1c1a99ff7ea00fb9db3f2de Mon Sep 17 00:00:00 2001 From: Josiah VanderZee Date: Mon, 29 Jun 2026 14:07:42 -0500 Subject: [PATCH 3/3] Move DH keygen to SSLKeyUtils.{h,cc} --- src/iocore/net/CMakeLists.txt | 1 + src/iocore/net/SSLKeyUtils.cc | 186 ++++++++++++++++++++++++++++++++++ src/iocore/net/SSLKeyUtils.h | 42 ++++++++ src/iocore/net/SSLUtils.cc | 148 +-------------------------- 4 files changed, 232 insertions(+), 145 deletions(-) create mode 100644 src/iocore/net/SSLKeyUtils.cc create mode 100644 src/iocore/net/SSLKeyUtils.h diff --git a/src/iocore/net/CMakeLists.txt b/src/iocore/net/CMakeLists.txt index 6ca310c44d3..29618c96764 100644 --- a/src/iocore/net/CMakeLists.txt +++ b/src/iocore/net/CMakeLists.txt @@ -52,6 +52,7 @@ add_library( SSLSessionCache.cc SSLSessionTicket.cc SSLUtils.cc + SSLKeyUtils.cc OCSPStapling.cc TLSBasicSupport.cc TLSEventSupport.cc diff --git a/src/iocore/net/SSLKeyUtils.cc b/src/iocore/net/SSLKeyUtils.cc new file mode 100644 index 00000000000..fdcecc04ce0 --- /dev/null +++ b/src/iocore/net/SSLKeyUtils.cc @@ -0,0 +1,186 @@ +/** @file + + @section license License + + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#include "SSLKeyUtils.h" +#include "P_SSLUtils.h" + +#include +#ifdef OPENSSL_IS_OPENSSL3 +#include +#else +#include +#endif + +#ifdef OPENSSL_IS_OPENSSL3 +#include +#include +#include +#include +#else +#include +#include +#include +#include +#endif + +#ifdef OPENSSL_IS_OPENSSL3 + +EVP_PKEY * +gen_dh_2048_256_pkey() +{ + scoped_PKEY_CTX pctx{EVP_PKEY_CTX_new_from_name(NULL, "DH", NULL)}; + if (!pctx) { + Error("failed to create OpenSSL pkey context"); + return nullptr; + } + + if (EVP_PKEY_keygen_init(pctx.get()) <= 0) { + Error("failed to initialize OpenSSL keygen"); + return nullptr; + } + + char prime_group[]{"dh_2048_256"}; + OSSL_PARAM const params[]{OSSL_PARAM_utf8_string("group", prime_group, 0), OSSL_PARAM_END}; + + if (!EVP_PKEY_CTX_set_params(pctx.get(), params)) { + Error("SSL dhparams source returned invalid parameters"); + return nullptr; + } + + EVP_PKEY *pkey{}; + EVP_PKEY_generate(pctx.get(), &pkey); + + return pkey; +} + +EVP_PKEY * +load_dhparams_file(char const *dhparams_file) +{ + EVP_PKEY *pkey{}; + scoped_Decoder_CTX dctx{OSSL_DECODER_CTX_new_for_pkey(&pkey, "PEM", NULL, "DH", OSSL_KEYMGMT_SELECT_ALL_PARAMETERS, NULL, NULL)}; + if (!dctx) { + Error("failed to create OpenSSL decoder context"); + return nullptr; + } + + ink_assert(OSSL_DECODER_CTX_get_num_decoders(dctx.get()) > 0); + scoped_BIO bio{BIO_new_file(dhparams_file, "r")}; + if (!OSSL_DECODER_from_bio(dctx.get(), bio.get())) { + Error("SSL dhparams source returned invalid parameters"); + return nullptr; + } + + return pkey; +} + +bool +set_ctx_dh(SSL_CTX *ctx, dh_key_t *pkey) +{ + bool result{SSL_CTX_set_options(ctx, SSL_OP_SINGLE_DH_USE) && SSL_CTX_set0_tmp_dh_pkey(ctx, pkey)}; + if (!result) { + EVP_PKEY_free(pkey); + } + return result; +} + +#else + +DH * +load_dhparams_file(char const *dhparams_file) +{ + scoped_BIO bio(BIO_new_file(dhparams_file, "r")); + DH *dh{PEM_read_bio_DHparams(bio.get(), nullptr, nullptr, nullptr)}; + if (!dh) { + Error("SSL dhparams source returned invalid parameters"); + return nullptr; + } + + return dh; +} + +#if TS_USE_GET_DH_2048_256 +DH * +gen_dh_2048_256_pkey() +{ + return DH_get_2048_256(); +} +#else +DH * +gen_dh_2048_256_pkey() +{ + /* Build 2048-bit MODP Group with 256-bit Prime Order Subgroup from RFC 5114 */ + static const unsigned char dh2048_p[] = { + 0x87, 0xA8, 0xE6, 0x1D, 0xB4, 0xB6, 0x66, 0x3C, 0xFF, 0xBB, 0xD1, 0x9C, 0x65, 0x19, 0x59, 0x99, 0x8C, 0xEE, 0xF6, 0x08, + 0x66, 0x0D, 0xD0, 0xF2, 0x5D, 0x2C, 0xEE, 0xD4, 0x43, 0x5E, 0x3B, 0x00, 0xE0, 0x0D, 0xF8, 0xF1, 0xD6, 0x19, 0x57, 0xD4, + 0xFA, 0xF7, 0xDF, 0x45, 0x61, 0xB2, 0xAA, 0x30, 0x16, 0xC3, 0xD9, 0x11, 0x34, 0x09, 0x6F, 0xAA, 0x3B, 0xF4, 0x29, 0x6D, + 0x83, 0x0E, 0x9A, 0x7C, 0x20, 0x9E, 0x0C, 0x64, 0x97, 0x51, 0x7A, 0xBD, 0x5A, 0x8A, 0x9D, 0x30, 0x6B, 0xCF, 0x67, 0xED, + 0x91, 0xF9, 0xE6, 0x72, 0x5B, 0x47, 0x58, 0xC0, 0x22, 0xE0, 0xB1, 0xEF, 0x42, 0x75, 0xBF, 0x7B, 0x6C, 0x5B, 0xFC, 0x11, + 0xD4, 0x5F, 0x90, 0x88, 0xB9, 0x41, 0xF5, 0x4E, 0xB1, 0xE5, 0x9B, 0xB8, 0xBC, 0x39, 0xA0, 0xBF, 0x12, 0x30, 0x7F, 0x5C, + 0x4F, 0xDB, 0x70, 0xC5, 0x81, 0xB2, 0x3F, 0x76, 0xB6, 0x3A, 0xCA, 0xE1, 0xCA, 0xA6, 0xB7, 0x90, 0x2D, 0x52, 0x52, 0x67, + 0x35, 0x48, 0x8A, 0x0E, 0xF1, 0x3C, 0x6D, 0x9A, 0x51, 0xBF, 0xA4, 0xAB, 0x3A, 0xD8, 0x34, 0x77, 0x96, 0x52, 0x4D, 0x8E, + 0xF6, 0xA1, 0x67, 0xB5, 0xA4, 0x18, 0x25, 0xD9, 0x67, 0xE1, 0x44, 0xE5, 0x14, 0x05, 0x64, 0x25, 0x1C, 0xCA, 0xCB, 0x83, + 0xE6, 0xB4, 0x86, 0xF6, 0xB3, 0xCA, 0x3F, 0x79, 0x71, 0x50, 0x60, 0x26, 0xC0, 0xB8, 0x57, 0xF6, 0x89, 0x96, 0x28, 0x56, + 0xDE, 0xD4, 0x01, 0x0A, 0xBD, 0x0B, 0xE6, 0x21, 0xC3, 0xA3, 0x96, 0x0A, 0x54, 0xE7, 0x10, 0xC3, 0x75, 0xF2, 0x63, 0x75, + 0xD7, 0x01, 0x41, 0x03, 0xA4, 0xB5, 0x43, 0x30, 0xC1, 0x98, 0xAF, 0x12, 0x61, 0x16, 0xD2, 0x27, 0x6E, 0x11, 0x71, 0x5F, + 0x69, 0x38, 0x77, 0xFA, 0xD7, 0xEF, 0x09, 0xCA, 0xDB, 0x09, 0x4A, 0xE9, 0x1E, 0x1A, 0x15, 0x97}; + static const unsigned char dh2048_g[] = { + 0x3F, 0xB3, 0x2C, 0x9B, 0x73, 0x13, 0x4D, 0x0B, 0x2E, 0x77, 0x50, 0x66, 0x60, 0xED, 0xBD, 0x48, 0x4C, 0xA7, 0xB1, 0x8F, + 0x21, 0xEF, 0x20, 0x54, 0x07, 0xF4, 0x79, 0x3A, 0x1A, 0x0B, 0xA1, 0x25, 0x10, 0xDB, 0xC1, 0x50, 0x77, 0xBE, 0x46, 0x3F, + 0xFF, 0x4F, 0xED, 0x4A, 0xAC, 0x0B, 0xB5, 0x55, 0xBE, 0x3A, 0x6C, 0x1B, 0x0C, 0x6B, 0x47, 0xB1, 0xBC, 0x37, 0x73, 0xBF, + 0x7E, 0x8C, 0x6F, 0x62, 0x90, 0x12, 0x28, 0xF8, 0xC2, 0x8C, 0xBB, 0x18, 0xA5, 0x5A, 0xE3, 0x13, 0x41, 0x00, 0x0A, 0x65, + 0x01, 0x96, 0xF9, 0x31, 0xC7, 0x7A, 0x57, 0xF2, 0xDD, 0xF4, 0x63, 0xE5, 0xE9, 0xEC, 0x14, 0x4B, 0x77, 0x7D, 0xE6, 0x2A, + 0xAA, 0xB8, 0xA8, 0x62, 0x8A, 0xC3, 0x76, 0xD2, 0x82, 0xD6, 0xED, 0x38, 0x64, 0xE6, 0x79, 0x82, 0x42, 0x8E, 0xBC, 0x83, + 0x1D, 0x14, 0x34, 0x8F, 0x6F, 0x2F, 0x91, 0x93, 0xB5, 0x04, 0x5A, 0xF2, 0x76, 0x71, 0x64, 0xE1, 0xDF, 0xC9, 0x67, 0xC1, + 0xFB, 0x3F, 0x2E, 0x55, 0xA4, 0xBD, 0x1B, 0xFF, 0xE8, 0x3B, 0x9C, 0x80, 0xD0, 0x52, 0xB9, 0x85, 0xD1, 0x82, 0xEA, 0x0A, + 0xDB, 0x2A, 0x3B, 0x73, 0x13, 0xD3, 0xFE, 0x14, 0xC8, 0x48, 0x4B, 0x1E, 0x05, 0x25, 0x88, 0xB9, 0xB7, 0xD2, 0xBB, 0xD2, + 0xDF, 0x01, 0x61, 0x99, 0xEC, 0xD0, 0x6E, 0x15, 0x57, 0xCD, 0x09, 0x15, 0xB3, 0x35, 0x3B, 0xBB, 0x64, 0xE0, 0xEC, 0x37, + 0x7F, 0xD0, 0x28, 0x37, 0x0D, 0xF9, 0x2B, 0x52, 0xC7, 0x89, 0x14, 0x28, 0xCD, 0xC6, 0x7E, 0xB6, 0x18, 0x4B, 0x52, 0x3D, + 0x1D, 0xB2, 0x46, 0xC3, 0x2F, 0x63, 0x07, 0x84, 0x90, 0xF0, 0x0E, 0xF8, 0xD6, 0x47, 0xD1, 0x48, 0xD4, 0x79, 0x54, 0x51, + 0x5E, 0x23, 0x27, 0xCF, 0xEF, 0x98, 0xC5, 0x82, 0x66, 0x4B, 0x4C, 0x0F, 0x6C, 0xC4, 0x16, 0x59}; + DH *dh; + BIGNUM *p; + BIGNUM *g; + + if ((dh = DH_new()) == nullptr) { + return nullptr; + } + p = BN_bin2bn(dh2048_p, sizeof(dh2048_p), nullptr); + g = BN_bin2bn(dh2048_g, sizeof(dh2048_g), nullptr); + if (p == nullptr || g == nullptr) { + DH_free(dh); + BN_free(p); + BN_free(g); + return nullptr; + } + DH_set0_pqg(dh, p, nullptr, g); + return (dh); +} +#endif // TS_USE_GET_DH_2048_256 + +bool +set_ctx_dh(SSL_CTX *ctx, dh_key_t *pkey) +{ + bool result{SSL_CTX_set_options(ctx, SSL_OP_SINGLE_DH_USE) && SSL_CTX_set_tmp_dh(ctx, pkey)}; + DH_free(pkey); + return result; +} + +#endif // OPENSSL_IS_OPENSSL3 diff --git a/src/iocore/net/SSLKeyUtils.h b/src/iocore/net/SSLKeyUtils.h new file mode 100644 index 00000000000..64bea23e9ea --- /dev/null +++ b/src/iocore/net/SSLKeyUtils.h @@ -0,0 +1,42 @@ +/** @file + + @section license License + + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#pragma once + +#if OPENSSL_IS_OPENSSL3 +#include +#else +#include +#endif +#include + +#ifdef OPENSSL_IS_OPENSSL3 +using dh_key_t = EVP_PKEY; +#else +using dh_key_t = DH; +#endif + +// Both gen_dh_2048_256_pkey and load_dhparams_file return owning pointers. +dh_key_t *gen_dh_2048_256_pkey(); +dh_key_t *load_dhparams_file(char const *dhparams_file); + +// Takes ownership of pkey. +bool set_ctx_dh(SSL_CTX *ctx, dh_key_t *pkey); diff --git a/src/iocore/net/SSLUtils.cc b/src/iocore/net/SSLUtils.cc index ddf282a0dbd..9b7bb5e4d98 100644 --- a/src/iocore/net/SSLUtils.cc +++ b/src/iocore/net/SSLUtils.cc @@ -26,6 +26,7 @@ #include "P_SSLConfig.h" #include "P_SSLNetVConnection.h" #include "P_TLSKeyLogger.h" +#include "SSLKeyUtils.h" #include "SSLStats.h" #include "SSLSessionCache.h" #include "SSLSessionTicket.h" @@ -53,12 +54,9 @@ #include "swoc/Errata.h" #include #include -#include #include #ifdef OPENSSL_IS_OPENSSL3 -#include #include -#include #endif #include #include @@ -389,59 +387,6 @@ ssl_alpn_select_callback(SSL *ssl, const unsigned char **out, unsigned char *out return SSL_TLSEXT_ERR_NOACK; } -#if TS_USE_GET_DH_2048_256 == 0 -/* Build 2048-bit MODP Group with 256-bit Prime Order Subgroup from RFC 5114 */ -static DH * -DH_get_2048_256() -{ - static const unsigned char dh2048_p[] = { - 0x87, 0xA8, 0xE6, 0x1D, 0xB4, 0xB6, 0x66, 0x3C, 0xFF, 0xBB, 0xD1, 0x9C, 0x65, 0x19, 0x59, 0x99, 0x8C, 0xEE, 0xF6, 0x08, - 0x66, 0x0D, 0xD0, 0xF2, 0x5D, 0x2C, 0xEE, 0xD4, 0x43, 0x5E, 0x3B, 0x00, 0xE0, 0x0D, 0xF8, 0xF1, 0xD6, 0x19, 0x57, 0xD4, - 0xFA, 0xF7, 0xDF, 0x45, 0x61, 0xB2, 0xAA, 0x30, 0x16, 0xC3, 0xD9, 0x11, 0x34, 0x09, 0x6F, 0xAA, 0x3B, 0xF4, 0x29, 0x6D, - 0x83, 0x0E, 0x9A, 0x7C, 0x20, 0x9E, 0x0C, 0x64, 0x97, 0x51, 0x7A, 0xBD, 0x5A, 0x8A, 0x9D, 0x30, 0x6B, 0xCF, 0x67, 0xED, - 0x91, 0xF9, 0xE6, 0x72, 0x5B, 0x47, 0x58, 0xC0, 0x22, 0xE0, 0xB1, 0xEF, 0x42, 0x75, 0xBF, 0x7B, 0x6C, 0x5B, 0xFC, 0x11, - 0xD4, 0x5F, 0x90, 0x88, 0xB9, 0x41, 0xF5, 0x4E, 0xB1, 0xE5, 0x9B, 0xB8, 0xBC, 0x39, 0xA0, 0xBF, 0x12, 0x30, 0x7F, 0x5C, - 0x4F, 0xDB, 0x70, 0xC5, 0x81, 0xB2, 0x3F, 0x76, 0xB6, 0x3A, 0xCA, 0xE1, 0xCA, 0xA6, 0xB7, 0x90, 0x2D, 0x52, 0x52, 0x67, - 0x35, 0x48, 0x8A, 0x0E, 0xF1, 0x3C, 0x6D, 0x9A, 0x51, 0xBF, 0xA4, 0xAB, 0x3A, 0xD8, 0x34, 0x77, 0x96, 0x52, 0x4D, 0x8E, - 0xF6, 0xA1, 0x67, 0xB5, 0xA4, 0x18, 0x25, 0xD9, 0x67, 0xE1, 0x44, 0xE5, 0x14, 0x05, 0x64, 0x25, 0x1C, 0xCA, 0xCB, 0x83, - 0xE6, 0xB4, 0x86, 0xF6, 0xB3, 0xCA, 0x3F, 0x79, 0x71, 0x50, 0x60, 0x26, 0xC0, 0xB8, 0x57, 0xF6, 0x89, 0x96, 0x28, 0x56, - 0xDE, 0xD4, 0x01, 0x0A, 0xBD, 0x0B, 0xE6, 0x21, 0xC3, 0xA3, 0x96, 0x0A, 0x54, 0xE7, 0x10, 0xC3, 0x75, 0xF2, 0x63, 0x75, - 0xD7, 0x01, 0x41, 0x03, 0xA4, 0xB5, 0x43, 0x30, 0xC1, 0x98, 0xAF, 0x12, 0x61, 0x16, 0xD2, 0x27, 0x6E, 0x11, 0x71, 0x5F, - 0x69, 0x38, 0x77, 0xFA, 0xD7, 0xEF, 0x09, 0xCA, 0xDB, 0x09, 0x4A, 0xE9, 0x1E, 0x1A, 0x15, 0x97}; - static const unsigned char dh2048_g[] = { - 0x3F, 0xB3, 0x2C, 0x9B, 0x73, 0x13, 0x4D, 0x0B, 0x2E, 0x77, 0x50, 0x66, 0x60, 0xED, 0xBD, 0x48, 0x4C, 0xA7, 0xB1, 0x8F, - 0x21, 0xEF, 0x20, 0x54, 0x07, 0xF4, 0x79, 0x3A, 0x1A, 0x0B, 0xA1, 0x25, 0x10, 0xDB, 0xC1, 0x50, 0x77, 0xBE, 0x46, 0x3F, - 0xFF, 0x4F, 0xED, 0x4A, 0xAC, 0x0B, 0xB5, 0x55, 0xBE, 0x3A, 0x6C, 0x1B, 0x0C, 0x6B, 0x47, 0xB1, 0xBC, 0x37, 0x73, 0xBF, - 0x7E, 0x8C, 0x6F, 0x62, 0x90, 0x12, 0x28, 0xF8, 0xC2, 0x8C, 0xBB, 0x18, 0xA5, 0x5A, 0xE3, 0x13, 0x41, 0x00, 0x0A, 0x65, - 0x01, 0x96, 0xF9, 0x31, 0xC7, 0x7A, 0x57, 0xF2, 0xDD, 0xF4, 0x63, 0xE5, 0xE9, 0xEC, 0x14, 0x4B, 0x77, 0x7D, 0xE6, 0x2A, - 0xAA, 0xB8, 0xA8, 0x62, 0x8A, 0xC3, 0x76, 0xD2, 0x82, 0xD6, 0xED, 0x38, 0x64, 0xE6, 0x79, 0x82, 0x42, 0x8E, 0xBC, 0x83, - 0x1D, 0x14, 0x34, 0x8F, 0x6F, 0x2F, 0x91, 0x93, 0xB5, 0x04, 0x5A, 0xF2, 0x76, 0x71, 0x64, 0xE1, 0xDF, 0xC9, 0x67, 0xC1, - 0xFB, 0x3F, 0x2E, 0x55, 0xA4, 0xBD, 0x1B, 0xFF, 0xE8, 0x3B, 0x9C, 0x80, 0xD0, 0x52, 0xB9, 0x85, 0xD1, 0x82, 0xEA, 0x0A, - 0xDB, 0x2A, 0x3B, 0x73, 0x13, 0xD3, 0xFE, 0x14, 0xC8, 0x48, 0x4B, 0x1E, 0x05, 0x25, 0x88, 0xB9, 0xB7, 0xD2, 0xBB, 0xD2, - 0xDF, 0x01, 0x61, 0x99, 0xEC, 0xD0, 0x6E, 0x15, 0x57, 0xCD, 0x09, 0x15, 0xB3, 0x35, 0x3B, 0xBB, 0x64, 0xE0, 0xEC, 0x37, - 0x7F, 0xD0, 0x28, 0x37, 0x0D, 0xF9, 0x2B, 0x52, 0xC7, 0x89, 0x14, 0x28, 0xCD, 0xC6, 0x7E, 0xB6, 0x18, 0x4B, 0x52, 0x3D, - 0x1D, 0xB2, 0x46, 0xC3, 0x2F, 0x63, 0x07, 0x84, 0x90, 0xF0, 0x0E, 0xF8, 0xD6, 0x47, 0xD1, 0x48, 0xD4, 0x79, 0x54, 0x51, - 0x5E, 0x23, 0x27, 0xCF, 0xEF, 0x98, 0xC5, 0x82, 0x66, 0x4B, 0x4C, 0x0F, 0x6C, 0xC4, 0x16, 0x59}; - DH *dh; - BIGNUM *p; - BIGNUM *g; - - if ((dh = DH_new()) == nullptr) { - return nullptr; - } - p = BN_bin2bn(dh2048_p, sizeof(dh2048_p), nullptr); - g = BN_bin2bn(dh2048_g, sizeof(dh2048_g), nullptr); - if (p == nullptr || g == nullptr) { - DH_free(dh); - BN_free(p); - BN_free(g); - return nullptr; - } - DH_set0_pqg(dh, p, nullptr, g); - return (dh); -} -#endif - bool SSLMultiCertConfigLoader::_enable_cert_compression(SSL_CTX *ctx) { @@ -493,111 +438,24 @@ SSLMultiCertConfigLoader::_enable_early_data([[maybe_unused]] SSL_CTX *ctx) return true; } -#if OPENSSL_IS_OPENSSL3 -static EVP_PKEY * -load_dhparams_file(char const *dhparams_file) -{ - EVP_PKEY *pkey{}; - scoped_Decoder_CTX dctx{OSSL_DECODER_CTX_new_for_pkey(&pkey, "PEM", NULL, "DH", OSSL_KEYMGMT_SELECT_ALL_PARAMETERS, NULL, NULL)}; - if (!dctx) { - Error("failed to create OpenSSL decoder context - could not enable DH"); - return nullptr; - } - - ink_assert(OSSL_DECODER_CTX_get_num_decoders(dctx.get()) > 0); - scoped_BIO bio{BIO_new_file(dhparams_file, "r")}; - if (!OSSL_DECODER_from_bio(dctx.get(), bio.get())) { - Error("SSL dhparams source returned invalid parameters"); - return nullptr; - } - - return pkey; -} - -static EVP_PKEY * -gen_dh_2048_256_pkey() -{ - scoped_PKEY_CTX pctx{EVP_PKEY_CTX_new_from_name(NULL, "DH", NULL)}; - if (!pctx) { - Error("failed to create OpenSSL pkey context - could not enable DH"); - return nullptr; - } - - if (EVP_PKEY_keygen_init(pctx.get()) <= 0) { - Error("failed to initialize OpenSSL keygen - could not enable DH"); - return nullptr; - } - - char prime_group[]{"dh_2048_256"}; - OSSL_PARAM const params[]{OSSL_PARAM_utf8_string("group", prime_group, 0), OSSL_PARAM_END}; - - if (!EVP_PKEY_CTX_set_params(pctx.get(), params)) { - Error("SSL dhparams source returned invalid parameters"); - return nullptr; - } - - EVP_PKEY *pkey{}; - EVP_PKEY_generate(pctx.get(), &pkey); - - return pkey; -} - static SSL_CTX * ssl_context_enable_dhe(const char *dhparams_file, SSL_CTX *ctx) { - EVP_PKEY *pkey{}; + dh_key_t *pkey{}; if (dhparams_file) { pkey = load_dhparams_file(dhparams_file); - if (!pkey) { - return nullptr; - } } else { pkey = gen_dh_2048_256_pkey(); - if (!pkey) { - Error("SSL dhparams source returned invalid parameters"); - return nullptr; - } - } - - // If set0_tmp_dh_pkey succeeds, pkey ownership tranfers to ctx. - if (!SSL_CTX_set_options(ctx, SSL_OP_SINGLE_DH_USE) || !SSL_CTX_set0_tmp_dh_pkey(ctx, pkey)) { - EVP_PKEY_free(pkey); - Error("failed to configure SSL DH"); - return nullptr; - } - - return ctx; -} -#else -static SSL_CTX * -ssl_context_enable_dhe(const char *dhparams_file, SSL_CTX *ctx) -{ - DH *server_dh; - - if (dhparams_file) { - scoped_BIO bio(BIO_new_file(dhparams_file, "r")); - server_dh = PEM_read_bio_DHparams(bio.get(), nullptr, nullptr, nullptr); - } else { - server_dh = DH_get_2048_256(); - } - - if (!server_dh) { - Error("SSL dhparams source returned invalid parameters"); - return nullptr; } - if (!SSL_CTX_set_options(ctx, SSL_OP_SINGLE_DH_USE) || !SSL_CTX_set_tmp_dh(ctx, server_dh)) { - DH_free(server_dh); + if (!pkey || !set_ctx_dh(ctx, pkey)) { Error("failed to configure SSL DH"); return nullptr; } - DH_free(server_dh); - return ctx; } -#endif #if TS_HAS_TLS_SESSION_TICKET static bool