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};

Reply via email to