This is an automated email from the ASF dual-hosted git repository.
JosiahWI pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficserver.git
The following commit(s) were added to refs/heads/master by this push:
new ec93038b7e Support OpenSSL 3.0 APIs for Diffie-Hellman (#13349)
ec93038b7e is described below
commit ec93038b7e98d7881f111cb5fc45e71529f73e0a
Author: JosiahWI <[email protected]>
AuthorDate: Tue Jul 28 19:50:34 2026 -0500
Support OpenSSL 3.0 APIs for Diffie-Hellman (#13349)
* Add unit tests for SSL Diffie-Hellman key configuration
* Support OpenSSL 3.0 APIs for Diffie-Hellman
* Move DH keygen to SSLKeyUtils.{h,cc}
---
src/iocore/net/CMakeLists.txt | 4 +
src/iocore/net/P_SSLUtils.h | 26 ++++
src/iocore/net/SSLKeyUtils.cc | 190 ++++++++++++++++++++++++++
src/iocore/net/SSLKeyUtils.h | 42 ++++++
src/iocore/net/SSLUtils.cc | 76 ++---------
src/iocore/net/unit_tests/test_SSLDHParams.cc | 186 +++++++++++++++++++++++++
6 files changed, 457 insertions(+), 67 deletions(-)
diff --git a/src/iocore/net/CMakeLists.txt b/src/iocore/net/CMakeLists.txt
index 901a830228..d1d5875ab4 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
@@ -147,6 +148,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/P_SSLUtils.h b/src/iocore/net/P_SSLUtils.h
index 8e2cbf5788..af5728e486 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 <openssl/decoder.h>
+#include <openssl/evp.h>
+#endif
#define OPENSSL_THREAD_DEFINES
#if __has_include(<openssl/opensslconf.h>)
#include <openssl/opensslconf.h>
@@ -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 @@ private:
using scoped_X509 = std::unique_ptr<X509, ssl::detail::X509Deleter>;
using scoped_BIO = std::unique_ptr<BIO, ssl::detail::BIODeleter>;
+#ifdef OPENSSL_IS_OPENSSL3
+using scoped_PKEY_CTX = std::unique_ptr<EVP_PKEY_CTX,
ssl::detail::PKEYCTXDeleter>;
+using scoped_Decoder_CTX = std::unique_ptr<OSSL_DECODER_CTX,
ssl::detail::DecoderCTXDeleter>;
+#endif
diff --git a/src/iocore/net/SSLKeyUtils.cc b/src/iocore/net/SSLKeyUtils.cc
new file mode 100644
index 0000000000..4bf14c5f47
--- /dev/null
+++ b/src/iocore/net/SSLKeyUtils.cc
@@ -0,0 +1,190 @@
+/** @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 <tscore/Diags.h>
+#ifdef OPENSSL_IS_OPENSSL3
+#include <tscore/ink_assert.h>
+#else
+#include <tscore/ink_config.h>
+#endif
+
+#ifdef OPENSSL_IS_OPENSSL3
+#include <openssl/evp.h>
+#include <openssl/decoder.h>
+#include <openssl/params.h>
+#include <openssl/ssl.h>
+#else
+#include <openssl/bio.h>
+#include <openssl/bn.h>
+#include <openssl/pem.h>
+#include <openssl/ssl.h>
+#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 (!bio) {
+ Error("failed to open parameters file");
+ return nullptr;
+ }
+ 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 0000000000..64bea23e9e
--- /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 <openssl/evp.h>
+#else
+#include <openssl/dh.h>
+#endif
+#include <openssl/ssl.h>
+
+#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 e09df82dfe..9c865bbbf8 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"
@@ -42,6 +43,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"
@@ -52,8 +54,10 @@
#include "swoc/Errata.h"
#include <openssl/asn1.h>
#include <openssl/bio.h>
-#include <openssl/bn.h>
#include <openssl/conf.h>
+#ifdef OPENSSL_IS_OPENSSL3
+#include <openssl/evp.h>
+#endif
#include <openssl/dh.h>
#include <openssl/ec.h>
#if HAVE_ENGINE_LOAD_DYNAMIC
@@ -383,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)
{
@@ -490,28 +441,19 @@
SSLMultiCertConfigLoader::_enable_early_data([[maybe_unused]] SSL_CTX *ctx)
static SSL_CTX *
ssl_context_enable_dhe(const char *dhparams_file, SSL_CTX *ctx)
{
- DH *server_dh;
+ dh_key_t *pkey{};
if (dhparams_file) {
- scoped_BIO bio(BIO_new_file(dhparams_file, "r"));
- server_dh = PEM_read_bio_DHparams(bio.get(), nullptr, nullptr, nullptr);
+ pkey = load_dhparams_file(dhparams_file);
} else {
- server_dh = DH_get_2048_256();
- }
-
- if (!server_dh) {
- Error("SSL dhparams source returned invalid parameters");
- return nullptr;
+ pkey = gen_dh_2048_256_pkey();
}
- 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;
}
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 0000000000..6a32a7458b
--- /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 <iocore/net/SSLMultiCertConfigLoader.h>
+#include "../P_SSLCertLookup.h"
+#include "../P_SSLConfig.h"
+#include "../P_SSLUtils.h"
+
+#include <tscore/ink_memory.h>
+#include <tscore/ink_platform.h>
+
+#include <catch2/catch_test_macros.hpp>
+
+#include <openssl/bio.h>
+#include <openssl/core_names.h>
+#include <openssl/evp.h>
+#include <openssl/pem.h>
+#include <openssl/ssl.h>
+
+#include <cstdio>
+#include <string>
+
+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(2048);
+ 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<ssize_t>(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 dh_2048_256 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()));
+}