This is an automated email from the ASF dual-hosted git repository.
alexey pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/kudu.git
The following commit(s) were added to refs/heads/master by this push:
new cd9e59ebd OpenSSL 3.x compatibility adaptation
cd9e59ebd is described below
commit cd9e59ebdf2bc644b2d76a76f64886397e884771
Author: Yan-Daojiang <[email protected]>
AuthorDate: Fri Aug 15 17:36:55 2025 +0800
OpenSSL 3.x compatibility adaptation
Adopt EVP/RAII, update TLS methods, and protect old paths.
Follow the OpenSSL 3.0 migration guide [1], replace deprecated APIs,
prefer EVP-level interfaces, and improve memory safety through RAII.
For OpenSSL versions below 3.0, retain source compatible paths and
set up clear version protection mechanisms.
Key changes:
* security/crypto.cc
For OpenSSL 3.0 and above: switch RSA key generation to
EVP_PKEY_CTX_new_from_name + EVP_PKEY_keygen_init +
EVP_PKEY_CTX_set_rsa_keygen_bits + EVP_PKEY_keygen.
Add explicit return code/null value checks where applicable and
use RAII to wrap OpenSSL pointers.
Replace EVP_MD_CTX_create with EVP_MD_CTX_new
(under appropriate version protection mechanisms).
* util/jwt-util.cc
For OpenSSL 3.0 and above: Build RSA/EC public keys from
JWKs using EVP_PKEY_fromdata and OSSL_PARAM_BLD/OSSL_PARAM;
serialize via generic EVP_PKEY writers
(PEM_write_bio_PUBKEY, i2d_PUBKEY_bio).
Preserve legacy RSA_set0_key / EC_KEY paths from older
OpenSSL versions and place them after the version protection mechanism.
Use RAII functions for ctx/param builder/params;
unify ToString with EvpPublicKeyTraits.
* util/openssl_util.h
Centralize SslTypeTraits for EVP_PKEY_CTX, OSSL_PARAM_BLD,
and OSSL_PARAM (OpenSSL 3.x) and place required headers after
the version protection mechanism.
* security/tls_context.cc, util/openssl_util.cc
Replace the deprecated SSLv23_method() with TLS_method()
when OpenSSL 1.1.0 and above are available,
maintaining compatibility with older versions.
This patch does not change provider/FIPS management semantics;
existing detection/enforcement logic remains unchanged.
The scope of the changes is limited to API modernization;
no behavioral changes will occur in non-3.x releases.
[1] https://docs.openssl.org/3.0/man7/migration_guide/
Change-Id: Ic587a85e6b9088ffd353f9119b75431f1ec60b5c
Reviewed-on: http://gerrit.cloudera.org:8080/23429
Reviewed-by: Alexey Serbin <[email protected]>
Tested-by: Alexey Serbin <[email protected]>
---
src/kudu/security/ca/cert_management.cc | 4 +-
src/kudu/security/ca/cert_management.h | 4 +
src/kudu/security/crypto.cc | 68 ++++++++++++--
src/kudu/security/crypto.h | 6 +-
src/kudu/security/tls_context.cc | 17 +++-
src/kudu/util/jwt-util.cc | 161 +++++++++++++++++++++++++++-----
src/kudu/util/openssl_util.cc | 12 +++
src/kudu/util/openssl_util.h | 23 ++++-
8 files changed, 263 insertions(+), 32 deletions(-)
diff --git a/src/kudu/security/ca/cert_management.cc
b/src/kudu/security/ca/cert_management.cc
index 2a9e8eff8..84235c4da 100644
--- a/src/kudu/security/ca/cert_management.cc
+++ b/src/kudu/security/ca/cert_management.cc
@@ -374,7 +374,9 @@ Status CertSigner::DigestSign(const EVP_MD* md, EVP_PKEY*
pkey, X509* x) {
Status CertSigner::GenerateSerial(c_unique_ptr<ASN1_INTEGER>* ret) {
SCOPED_OPENSSL_NO_PENDING_ERRORS;
auto btmp = ssl_make_unique(BN_new());
- OPENSSL_RET_NOT_OK(BN_pseudo_rand(btmp.get(), 64, 0, 0),
+ // BN_pseudo_rand() is deprecated in OpenSSL 3.x; BN_rand() is the
recommended replacement.
+ // top=0 sets the most significant bit (fixed bit length), bottom=0 allows
even values.
+ OPENSSL_RET_NOT_OK(BN_rand(btmp.get(), 64, 0, 0),
"error generating random number");
auto serial = ssl_make_unique(ASN1_INTEGER_new());
OPENSSL_RET_IF_NULL(BN_to_ASN1_INTEGER(btmp.get(), serial.get()),
diff --git a/src/kudu/security/ca/cert_management.h
b/src/kudu/security/ca/cert_management.h
index be7fa88a6..a74881133 100644
--- a/src/kudu/security/ca/cert_management.h
+++ b/src/kudu/security/ca/cert_management.h
@@ -19,7 +19,11 @@
#include <openssl/asn1.h>
#include <openssl/crypto.h>
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+#include <openssl/types.h>
+#else
#include <openssl/ssl.h>
+#endif
#include <openssl/x509.h>
#include <cstdint>
diff --git a/src/kudu/security/crypto.cc b/src/kudu/security/crypto.cc
index 820ba0b96..a26918f9f 100644
--- a/src/kudu/security/crypto.cc
+++ b/src/kudu/security/crypto.cc
@@ -55,13 +55,21 @@ int PemWritePrivateKey(BIO* bio, EVP_PKEY* key) {
}
int PemWritePublicKey(BIO* bio, EVP_PKEY* key) {
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ return PEM_write_bio_PUBKEY(bio, key);
+#else
auto rsa = ssl_make_unique(EVP_PKEY_get1_RSA(key));
return PEM_write_bio_RSA_PUBKEY(bio, rsa.get());
+#endif
}
int DerWritePublicKey(BIO* bio, EVP_PKEY* key) {
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ return i2d_PUBKEY_bio(bio, key);
+#else
auto rsa = ssl_make_unique(EVP_PKEY_get1_RSA(key));
return i2d_RSA_PUBKEY_bio(bio, rsa.get());
+#endif
}
} // anonymous namespace
@@ -87,9 +95,11 @@ struct RsaPublicKeyTraits : public SslTypeTraits<EVP_PKEY> {
static constexpr auto kWritePemFunc = &PemWritePublicKey;
static constexpr auto kWriteDerFunc = &DerWritePublicKey;
};
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
template<> struct SslTypeTraits<RSA> {
static constexpr auto kFreeFunc = &RSA_free;
};
+#endif
template<> struct SslTypeTraits<EVP_MD_CTX> {
#if OPENSSL_VERSION_NUMBER < 0x10100000L
static constexpr auto kFreeFunc = &EVP_MD_CTX_destroy;
@@ -181,7 +191,11 @@ Status PublicKey::VerifySignature(DigestType digest,
const std::string& signature) const {
SCOPED_OPENSSL_NO_PENDING_ERRORS;
const EVP_MD* md = GetMessageDigest(digest);
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
auto md_ctx = ssl_make_unique(EVP_MD_CTX_create());
+#else
+ auto md_ctx = ssl_make_unique(EVP_MD_CTX_new());
+#endif
OPENSSL_RET_NOT_OK(EVP_DigestVerifyInit(md_ctx.get(), nullptr, md, nullptr,
GetRawData()),
"error initializing verification digest");
@@ -213,12 +227,19 @@ Status PublicKey::VerifySignature(DigestType digest,
Status PublicKey::Equals(const PublicKey& other, bool* equals) const {
SCOPED_OPENSSL_NO_PENDING_ERRORS;
- int cmp = EVP_PKEY_cmp(data_.get(), other.data_.get());
+ int cmp;
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ cmp = EVP_PKEY_eq(data_.get(), other.data_.get());
+#else
+ cmp = EVP_PKEY_cmp(data_.get(), other.data_.get());
+#endif
switch (cmp) {
case -2:
return Status::NotSupported("failed to compare public keys");
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
case -1: // Key types are different; treat this as not equal
- case 0: // Keys are not equal
+#endif
+ case 0: // Keys are not equal or types differ
*equals = false;
return Status::OK();
case 1:
@@ -263,15 +284,23 @@ Status PrivateKey::FromFile(const std::string& fpath,
DataFormat format,
Status PrivateKey::GetPublicKey(PublicKey* public_key) const {
SCOPED_OPENSSL_NO_PENDING_ERRORS;
CHECK(public_key);
- auto rsa = ssl_make_unique(EVP_PKEY_get1_RSA(CHECK_NOTNULL(data_.get())));
+ auto pkey = CHECK_NOTNULL(data_.get());
+
+ auto tmp = ssl_make_unique(BIO_new(BIO_s_mem()));
+ OPENSSL_RET_IF_NULL(tmp, "could not create memory BIO");
+
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ OPENSSL_RET_NOT_OK(i2d_PUBKEY_bio(tmp.get(), pkey),
+ "error extracting public key");
+#else
+ auto rsa = ssl_make_unique(EVP_PKEY_get1_RSA(pkey));
if (PREDICT_FALSE(!rsa)) {
return Status::RuntimeError(GetOpenSSLErrors());
}
- auto tmp = ssl_make_unique(BIO_new(BIO_s_mem()));
- OPENSSL_RET_IF_NULL(tmp, "could not create memory BIO");
- // Export public key in DER format into the temporary buffer.
OPENSSL_RET_NOT_OK(i2d_RSA_PUBKEY_bio(tmp.get(), rsa.get()),
"error extracting public RSA key");
+#endif
+
// Read the public key into the result placeholder.
return public_key->FromBIO(tmp.get(), DataFormat::DER);
}
@@ -283,7 +312,11 @@ Status PrivateKey::MakeSignature(DigestType digest,
SCOPED_OPENSSL_NO_PENDING_ERRORS;
CHECK(signature);
const EVP_MD* md = GetMessageDigest(digest);
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
auto md_ctx = ssl_make_unique(EVP_MD_CTX_create());
+#else
+ auto md_ctx = ssl_make_unique(EVP_MD_CTX_new());
+#endif
OPENSSL_RET_NOT_OK(EVP_DigestSignInit(md_ctx.get(), nullptr, md, nullptr,
GetRawData()),
"error initializing signing digest");
@@ -304,6 +337,26 @@ Status GeneratePrivateKey(int num_bits, PrivateKey* ret) {
CHECK(ret);
InitializeOpenSSL();
SCOPED_OPENSSL_NO_PENDING_ERRORS;
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ auto ctx = ssl_make_unique(EVP_PKEY_CTX_new_from_name(nullptr, "RSA",
nullptr));
+ OPENSSL_RET_IF_NULL(ctx.get(), "error creating EVP_PKEY_CTX for RSA");
+ int rc = EVP_PKEY_keygen_init(ctx.get());
+ if (rc <= 0) {
+ return Status::RuntimeError("error initializing RSA keygen",
GetOpenSSLErrors());
+ }
+ rc = EVP_PKEY_CTX_set_rsa_keygen_bits(ctx.get(), num_bits);
+ if (rc <= 0) {
+ return Status::RuntimeError("error setting RSA key size",
GetOpenSSLErrors());
+ }
+ EVP_PKEY* gen = nullptr;
+ rc = EVP_PKEY_keygen(ctx.get(), &gen);
+ if (rc <= 0) {
+ return Status::RuntimeError("error generating RSA key",
GetOpenSSLErrors());
+ }
+ OPENSSL_RET_IF_NULL(gen, "error generating RSA key");
+ // Directly adopt the generated key for OpenSSL 3.0+
+ ret->AdoptRawData(gen);
+#else
auto key = ssl_make_unique(EVP_PKEY_new());
{
auto bn = ssl_make_unique(BN_new());
@@ -316,7 +369,8 @@ Status GeneratePrivateKey(int num_bits, PrivateKey* ret) {
EVP_PKEY_set1_RSA(key.get(), rsa.get()), "error assigning RSA key");
}
ret->AdoptRawData(key.release());
-
+#endif
+ // Common success return for both OpenSSL branches
return Status::OK();
}
diff --git a/src/kudu/security/crypto.h b/src/kudu/security/crypto.h
index 51fdc2ec6..fc54863a3 100644
--- a/src/kudu/security/crypto.h
+++ b/src/kudu/security/crypto.h
@@ -17,7 +17,11 @@
#pragma once
-#include <openssl/ssl.h>
+// No direct OpenSSL includes needed here; we rely on openssl_util.h for
+// forward declarations and public aggregation.
+
+// IWYU pragma: no_include <openssl/types.h>
+// IWYU pragma: no_include <openssl/ssl.h>
#include <cstddef>
#include <string>
diff --git a/src/kudu/security/tls_context.cc b/src/kudu/security/tls_context.cc
index 73b4a44ed..e1b7d7e4a 100644
--- a/src/kudu/security/tls_context.cc
+++ b/src/kudu/security/tls_context.cc
@@ -18,6 +18,10 @@
#include "kudu/security/tls_context.h"
#include <openssl/crypto.h>
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+#include <openssl/prov_ssl.h>
+#include <openssl/types.h>
+#endif
#ifndef OPENSSL_NO_ECDH
#include <openssl/ec.h> // IWYU pragma: keep
#endif
@@ -32,7 +36,6 @@
#include <memory>
#include <optional>
#include <ostream>
-#include <shared_mutex>
#include <string>
#include <type_traits>
#include <vector>
@@ -127,7 +130,12 @@ Status CheckMaxSupportedTlsVersion(int tls_version, const
char* tls_version_str)
// OpenSSL 1.1.1 and newer supports all of the TLS versions we care about, so
// the below check is only necessary in older versions of OpenSSL.
#if OPENSSL_VERSION_NUMBER < 0x10101000L
+ // Prefer TLS_method() where available (>=1.1.0). Fall back to SSLv23_method
otherwise.
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+ auto max_supported_tls_version = TLS_method()->version;
+#else
auto max_supported_tls_version = SSLv23_method()->version;
+#endif
DCHECK_GE(max_supported_tls_version, TLS1_VERSION);
if (max_supported_tls_version < tls_version) {
@@ -176,7 +184,14 @@ Status TlsContext::Init() {
// We explicitly disable SSLv2 and SSLv3 below so that only TLS methods
remain.
// See the discussion on
https://trac.torproject.org/projects/tor/ticket/11598 for more
// info.
+ // Use generic TLS_method() on OpenSSL >= 1.1.0. Older versions use
SSLv23_method()
+ // which, despite its name, enables all protocol versions; we'll disable
legacy
+ // protocols explicitly below via SSL_OP_* flags.
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+ ctx_ = ssl_make_unique(SSL_CTX_new(TLS_method()));
+#else
ctx_ = ssl_make_unique(SSL_CTX_new(SSLv23_method()));
+#endif
if (!ctx_) {
return Status::RuntimeError("failed to create TLS context",
GetOpenSSLErrors());
}
diff --git a/src/kudu/util/jwt-util.cc b/src/kudu/util/jwt-util.cc
index 50cc29404..f41d9a396 100644
--- a/src/kudu/util/jwt-util.cc
+++ b/src/kudu/util/jwt-util.cc
@@ -19,13 +19,22 @@
#include "kudu/util/jwt-util.h"
+// IWYU pragma: no_include <bits/struct_stat.h>
#include <openssl/bn.h>
#include <openssl/crypto.h>
-#include <openssl/ec.h>
+#include <openssl/evp.h> // IWYU pragma: keep
#include <openssl/obj_mac.h>
-#include <openssl/pem.h>
#include <openssl/ssl.h>
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+#include <openssl/core_names.h>
+#include <openssl/param_build.h>
+#include <openssl/types.h>
+#else
+#include <openssl/ec.h>
+#include <openssl/pem.h>
+#include <openssl/rsa.h> // IWYU pragma: keep
#include <openssl/x509.h>
+#endif
#include <sys/stat.h>
#include <cerrno>
@@ -113,22 +122,27 @@ int WriteDerFuncNotImplementedEC(BIO* /*ununsed*/,
EC_KEY* /*unused*/) {
LOG(DFATAL) << "this should never be called";
return -1;
}
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
template<> struct SslTypeTraits<EC_KEY> {
static constexpr auto kFreeFunc = &EC_KEY_free;
static constexpr auto kWritePemFunc = &PEM_write_bio_EC_PUBKEY;
static constexpr auto kWriteDerFunc = &WriteDerFuncNotImplementedEC;
};
+#endif
// Need this function because of template instantiation, but it's never used.
int WriteDerNotImplementedRSA(BIO* /*unused*/, RSA* /*unused*/) {
LOG(DFATAL) << "this should never be called";
return -1;
}
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
template<> struct SslTypeTraits<RSA> {
static constexpr auto kFreeFunc = &RSA_free;
static constexpr auto kWritePemFunc = &PEM_write_bio_RSA_PUBKEY;
static constexpr auto kWriteDerFunc = &WriteDerNotImplementedRSA;
};
+#endif
+
} // namespace security
@@ -438,24 +452,56 @@ Status RSAJWTPublicKeyBuilder::ConvertJwkToPem(
if (!WebSafeBase64Unescape(base64_e, &str_e)) {
return Status::InvalidArgument("malformed 'e' key component");
}
- auto mod = ssl_make_unique(BN_bin2bn(
- reinterpret_cast<const unsigned char*>(str_n.c_str()),
- static_cast<int>(str_n.size()),
- nullptr));
- auto exp = ssl_make_unique(BN_bin2bn(
- reinterpret_cast<const unsigned char*>(str_e.c_str()),
- static_cast<int>(str_e.size()),
- nullptr));
+ // Helper to convert raw bytes to BIGNUM with RAII.
+ auto bn_from_bytes = [](const string& s) {
+ return ssl_make_unique(BN_bin2bn(
+ reinterpret_cast<const unsigned char*>(s.c_str()),
+ static_cast<int>(s.size()),
+ nullptr));
+ };
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ auto mod = bn_from_bytes(str_n);
+ auto exp = bn_from_bytes(str_e);
+
+ auto ctx = ssl_make_unique(EVP_PKEY_CTX_new_from_name(nullptr, "RSA",
nullptr));
+ OPENSSL_RET_IF_NULL(ctx.get(), "failed to create EVP_PKEY_CTX for RSA");
+ int rc = EVP_PKEY_fromdata_init(ctx.get());
+ if (rc <= 0) {
+ return Status::RuntimeError("failed to init fromdata for RSA",
GetOpenSSLErrors());
+ }
+ auto bld = ssl_make_unique(OSSL_PARAM_BLD_new());
+ if (!bld) {
+ return Status::RuntimeError("failed to allocate OSSL_PARAM_BLD",
GetOpenSSLErrors());
+ }
+ OPENSSL_CHECK_OK(OSSL_PARAM_BLD_push_BN(bld.get(), OSSL_PKEY_PARAM_RSA_N,
mod.get()));
+ OPENSSL_CHECK_OK(OSSL_PARAM_BLD_push_BN(bld.get(), OSSL_PKEY_PARAM_RSA_E,
exp.get()));
+ auto params = ssl_make_unique(OSSL_PARAM_BLD_to_param(bld.get()));
+ if (!params) {
+ return Status::RuntimeError("failed to build OSSL_PARAM",
GetOpenSSLErrors());
+ }
+ EVP_PKEY* pkey = nullptr;
+ rc = EVP_PKEY_fromdata(ctx.get(), &pkey, EVP_PKEY_PUBLIC_KEY, params.get());
+ if (rc <= 0) {
+ return Status::RuntimeError("failed to construct RSA EVP_PKEY",
GetOpenSSLErrors());
+ }
+ OPENSSL_RET_IF_NULL(pkey, "failed to construct RSA EVP_PKEY");
+
+ auto pkey_up = ssl_make_unique(pkey);
+ return ToString<EVP_PKEY>(&pub_key, DataFormat::PEM, pkey_up.get());
+#else // OPENSSL_VERSION_NUMBER < 0x30000000L (OpenSSL < 3.0)
+ auto mod = bn_from_bytes(str_n);
+ auto exp = bn_from_bytes(str_e);
auto rsa = ssl_make_unique(RSA_new());
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
+#if OPENSSL_VERSION_NUMBER < 0x10100000L // OpenSSL < 1.1.0
rsa->n = mod.release();
rsa->e = exp.release();
-#else
+#else // OPENSSL_VERSION_NUMBER >= 0x10100000L (OpenSSL >= 1.1.0)
// RSA_set0_key is a new API introduced in OpenSSL version 1.1
OPENSSL_RET_NOT_OK(RSA_set0_key(
rsa.get(), mod.release(), exp.release(), nullptr), "failed to set RSA
key");
-#endif
+#endif // OPENSSL_VERSION_NUMBER < 0x10100000L
return ToString(&pub_key, DataFormat::PEM, rsa.get());
+#endif // OPENSSL_VERSION_NUMBER >= 0x30000000L
}
// Create a JWKPublicKey of EC (ES256, ES384 or ES512) from the JWK.
@@ -557,14 +603,86 @@ Status ECJWTPublicKeyBuilder::ConvertJwkToPem(int eccgrp,
if (!WebSafeBase64Unescape(base64_y, &ascii_y)) {
return Status::InvalidArgument("malformed 'y' key component");
}
- auto x = ssl_make_unique(BN_bin2bn(
- reinterpret_cast<const unsigned char*>(ascii_x.c_str()),
- static_cast<int>(ascii_x.size()),
- nullptr));
- auto y = ssl_make_unique(BN_bin2bn(
- reinterpret_cast<const unsigned char*>(ascii_y.c_str()),
- static_cast<int>(ascii_y.size()),
- nullptr));
+ // Helper to convert raw bytes to BIGNUM with RAII.
+ auto bn_from_bytes = [](const string& s) {
+ return ssl_make_unique(BN_bin2bn(
+ reinterpret_cast<const unsigned char*>(s.c_str()),
+ static_cast<int>(s.size()),
+ nullptr));
+ };
+ // Common BIGNUMs for both OpenSSL branches
+ auto x = bn_from_bytes(ascii_x);
+ auto y = bn_from_bytes(ascii_y);
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+
+ const char* group_name = nullptr;
+ int coord_len = 0; // bytes per coordinate for uncompressed point
+ switch (eccgrp) {
+ case NID_X9_62_prime256v1:
+ group_name = "prime256v1";
+ coord_len = 32;
+ break;
+ case NID_secp384r1:
+ group_name = "secp384r1";
+ coord_len = 48;
+ break;
+ case NID_secp521r1:
+ group_name = "secp521r1";
+ coord_len = 66; // ceil(521/8)
+ break;
+ default: return Status::NotSupported("unsupported EC group NID");
+ }
+
+ auto ctx = ssl_make_unique(EVP_PKEY_CTX_new_from_name(nullptr, "EC",
nullptr));
+ OPENSSL_RET_IF_NULL(ctx.get(), "failed to create EVP_PKEY_CTX for EC");
+ int rc = EVP_PKEY_fromdata_init(ctx.get());
+ if (rc <= 0) {
+ return Status::RuntimeError("failed to init fromdata for EC",
GetOpenSSLErrors());
+ }
+ auto bld = ssl_make_unique(OSSL_PARAM_BLD_new());
+ if (!bld) {
+ return Status::RuntimeError("failed to allocate OSSL_PARAM_BLD",
GetOpenSSLErrors());
+ }
+ OPENSSL_CHECK_OK(OSSL_PARAM_BLD_push_utf8_string(bld.get(),
OSSL_PKEY_PARAM_GROUP_NAME,
+
const_cast<char*>(group_name), 0));
+
+ std::vector<unsigned char> uncompressed;
+ uncompressed.resize(1 + 2 * coord_len);
+ uncompressed[0] = 0x04;
+ int xn = BN_bn2binpad(x.get(), uncompressed.data() + 1, coord_len);
+ int yn = BN_bn2binpad(y.get(), uncompressed.data() + 1 + coord_len,
coord_len);
+ if (xn != coord_len || yn != coord_len) {
+ // bld and ctx are freed by RAII
+ return Status::InvalidArgument("invalid EC public key coordinate length");
+ }
+ // OSSL_PKEY_PARAM_PUB_KEY is a well-known constant name provided by OpenSSL
3.0
+ // headers (openssl/core_names.h). It's a compile-time constant string
literal
+ // and should never be null.
+ static_assert(OSSL_PKEY_PARAM_PUB_KEY != nullptr,
+ "OSSL_PKEY_PARAM_PUB_KEY must be a non-null constant string in
OpenSSL 3.0");
+ // push_octet_string should not fail here because:
+ // - 'bld' was successfully allocated.
+ // - 'uncompressed' contains a valid uncompressed EC point (0x04 || X || Y)
+ // with coordinate lengths pre-validated via BN_bn2binpad against the
+ // expected 'coord_len'.
+ // - OSSL_PKEY_PARAM_PUB_KEY expects a public key in octet string form for
EC.
+ // If OpenSSL returns failure, the following macro will surface detailed
error info.
+ OPENSSL_CHECK_OK(OSSL_PARAM_BLD_push_octet_string(
+ bld.get(), OSSL_PKEY_PARAM_PUB_KEY, uncompressed.data(),
uncompressed.size()));
+ auto params = ssl_make_unique(OSSL_PARAM_BLD_to_param(bld.get()));
+ if (!params) {
+ return Status::RuntimeError("failed to build OSSL_PARAM",
GetOpenSSLErrors());
+ }
+ EVP_PKEY* pkey = nullptr;
+ rc = EVP_PKEY_fromdata(ctx.get(), &pkey, EVP_PKEY_PUBLIC_KEY, params.get());
+ if (rc <= 0) {
+ return Status::RuntimeError("failed to construct EC EVP_PKEY",
GetOpenSSLErrors());
+ }
+ OPENSSL_RET_IF_NULL(pkey, "failed to construct EC EVP_PKEY");
+
+ auto pkey_up = ssl_make_unique(pkey);
+ return ToString<EVP_PKEY>(&pub_key, DataFormat::PEM, pkey_up.get());
+#else // OPENSSL_VERSION_NUMBER < 0x30000000L (OpenSSL < 3.0)
auto ec_key = ssl_make_unique(EC_KEY_new_by_curve_name(eccgrp));
OPENSSL_RET_IF_NULL(ec_key, "failed to create EC key");
EC_KEY_set_asn1_flag(ec_key.get(), OPENSSL_EC_NAMED_CURVE);
@@ -572,6 +690,7 @@ Status ECJWTPublicKeyBuilder::ConvertJwkToPem(int eccgrp,
ec_key.get(), x.get(), y.get()), "failed to set public key");
return ToString(&pub_key, DataFormat::PEM, ec_key.get());
+#endif // OPENSSL_VERSION_NUMBER >= 0x30000000L
}
//
diff --git a/src/kudu/util/openssl_util.cc b/src/kudu/util/openssl_util.cc
index 3f7c45f35..3d01319aa 100644
--- a/src/kudu/util/openssl_util.cc
+++ b/src/kudu/util/openssl_util.cc
@@ -154,6 +154,7 @@ Status CheckOpenSSLInitialized() {
if (!CRYPTO_get_locking_callback()) {
return Status::RuntimeError("Locking callback not initialized");
}
+
auto ctx = ssl_make_unique(SSL_CTX_new(SSLv23_method()));
if (!ctx) {
ERR_clear_error();
@@ -242,6 +243,7 @@ void DoInitializeOpenSSL() {
// initializes OpenSSL, and we risk installing conflicting callbacks
// or crashing due to concurrent initialization attempts. In that case,
// log a warning.
+ // Probe whether OpenSSL has been initialized already by attempting to
create SSL_CTX.
auto ctx = ssl_make_unique(SSL_CTX_new(SSLv23_method()));
if (ctx) {
RAW_LOG(WARNING, "It appears that OpenSSL has been previously initialized
by "
@@ -424,7 +426,12 @@ string GetOpenSSLErrors() {
int line, flags;
const char *file, *data;
bool is_first = true;
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ const char* func = nullptr;
+ while ((l = ERR_get_error_all(&file, &line, &func, &data, &flags)) != 0) {
+#else
while ((l = ERR_get_error_line_data(&file, &line, &data, &flags)) != 0) {
+#endif
if (is_first) {
is_first = false;
} else {
@@ -434,6 +441,11 @@ string GetOpenSSLErrors() {
char buf[256];
ERR_error_string_n(l, buf, sizeof(buf));
serr << buf << ":" << file << ":" << line;
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ if (func && *func) {
+ serr << ":" << func;
+ }
+#endif
if (flags & ERR_TXT_STRING) {
serr << ":" << data;
}
diff --git a/src/kudu/util/openssl_util.h b/src/kudu/util/openssl_util.h
index fcedea2d8..eb8457030 100644
--- a/src/kudu/util/openssl_util.h
+++ b/src/kudu/util/openssl_util.h
@@ -22,6 +22,8 @@
#include <openssl/pem.h>
#include <openssl/ssl.h>
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+#include <openssl/param_build.h>
+#include <openssl/params.h>
#include <openssl/types.h>
#endif
#include <openssl/x509.h>
@@ -39,7 +41,7 @@
namespace kudu {
namespace security {
namespace internal {
-struct ScopedCheckNoPendingSSLErrors;
+struct ScopedCheckNoPendingSSLErrors; // IWYU pragma: keep
} // namespace internal
} // namespace security
} // namespace kudu
@@ -204,6 +206,14 @@ template<> struct SslTypeTraits<X509_REQ> {
};
template<> struct SslTypeTraits<EVP_PKEY> {
static constexpr auto kFreeFunc = &EVP_PKEY_free;
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ static constexpr auto kWritePemFunc = &PEM_write_bio_PUBKEY;
+ static constexpr auto kWriteDerFunc = &i2d_PUBKEY_bio;
+#endif
+};
+// EVP_PKEY_CTX deleter for RAII management via ssl_make_unique
+template<> struct SslTypeTraits<EVP_PKEY_CTX> {
+ static constexpr auto kFreeFunc = &EVP_PKEY_CTX_free;
};
template<> struct SslTypeTraits<SSL_CTX> {
static constexpr auto kFreeFunc = &SSL_CTX_free;
@@ -212,6 +222,17 @@ template<> struct SslTypeTraits<BIO> {
static constexpr auto kFreeFunc = &BIO_free;
};
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+// OSSL_PARAM_BLD deleter for RAII management
+template<> struct SslTypeTraits<OSSL_PARAM_BLD> {
+ static constexpr auto kFreeFunc = &OSSL_PARAM_BLD_free;
+};
+// OSSL_PARAM deleter for RAII management
+template<> struct SslTypeTraits<OSSL_PARAM> {
+ static constexpr auto kFreeFunc = &OSSL_PARAM_free;
+};
+#endif
+
template<typename SSL_TYPE, typename Traits = SslTypeTraits<SSL_TYPE>>
c_unique_ptr<SSL_TYPE> ssl_make_unique(SSL_TYPE* d) {
return {d, Traits::kFreeFunc};