Commit 8be36fb4941 for nodejs

commit 8be36fb49413e8bbc0d4202eeb52b93c62ec4887
Author: Filip Skokan <panva.ip@gmail.com>
Date:   Sun Jul 26 14:18:38 2026 +0200

    crypto: remove legacy OpenSSL code paths

    Remove the OpenSSL 1.x branches and stale version-specific assumptions
    from src/. BoringSSL does not define OPENSSL_VERSION_MAJOR, so the
    remaining version guards were excluding it as well; use
    OPENSSL_IS_BORINGSSL checks instead, including the shared PKCS12 parser
    and TLS PFX error handling.

    Gate BoringSSL-only alternatives explicitly instead of leaving them
    in #else branches, including the MAC/KMAC and OCB fallbacks. Fix the
    BoringSSL guard on QUIC's unsupported-backend diagnostic.

    Make the OpenSSL legacy-provider command-line help version-neutral.

    Signed-off-by: Filip Skokan <panva.ip@gmail.com>
    Assisted-by: Codex
    PR-URL: https://github.com/nodejs/node/pull/64777
    Fixes: https://github.com/nodejs/node/issues/56733
    Refs: https://github.com/nodejs/node/pull/64211
    Refs: https://github.com/nodejs/node/pull/63966
    Refs: https://github.com/nodejs/node/pull/53329
    Refs: https://github.com/nodejs/node/pull/48950
    Refs: https://github.com/nodejs/node/issues/45465
    Refs: https://github.com/nodejs/node/issues/42827
    Refs: https://github.com/nodejs/node/issues/47076
    Refs: https://github.com/nodejs/node/pull/62862
    Refs: https://github.com/nodejs/node/pull/38512
    Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
    Reviewed-By: James M Snell <jasnell@gmail.com>
    Reviewed-By: Richard Lau <richard.lau@ibm.com>
    Reviewed-By: Gürgün Dayıoğlu <hey@gurgun.day>

diff --git a/src/crypto/README.md b/src/crypto/README.md
index 1655fc5ea0f..63f14030697 100644
--- a/src/crypto/README.md
+++ b/src/crypto/README.md
@@ -93,8 +93,8 @@ use their methods to keep that adaptation inside ncrypto.
 Examples of these being used are pervasive through the `src/crypto` code.

 `HMACCtxPointer` is a dedicated HMAC state wrapper rather than a plain
-`DeleteFnPtr` alias. On OpenSSL 3 and later it owns the provider-backed
-`EVP_MAC`/`EVP_MAC_CTX` state. On OpenSSL 1.1.1 and BoringSSL it owns the
+`DeleteFnPtr` alias. On OpenSSL it owns the provider-backed
+`EVP_MAC`/`EVP_MAC_CTX` state. On BoringSSL it owns the
 legacy `HMAC_CTX` state. HMAC call sites should use `HMACCtxPointer::New()`,
 `init()`, `update()`, and `digest()`/`digestInto()` so the backend selection
 stays contained in ncrypto.
diff --git a/src/crypto/crypto_aes.cc b/src/crypto/crypto_aes.cc
index ea869a8eef1..070c05582ee 100644
--- a/src/crypto/crypto_aes.cc
+++ b/src/crypto/crypto_aes.cc
@@ -150,11 +150,8 @@ WebCryptoCipherStatus AES_Cipher(Environment* env,
   auto buf = DataPointer::Alloc(buf_len);
   auto ptr = static_cast<unsigned char*>(buf.get());

-  // In some outdated version of OpenSSL (e.g.
-  // ubi81_sharedlibs_openssl111fips_x64) may be used in sharedlib mode, the
-  // logic will be failed when input size is zero. The newer OpenSSL has fixed
-  // it up. But we still have to regard zero as special in Node.js code to
-  // prevent old OpenSSL failure.
+  // Some shared OpenSSL builds fail when the input size is zero. Keep handling
+  // zero-length input in Node.js to avoid relying on backend-specific behavior.
   //
   // Refs:
   // https://github.com/openssl/openssl/commit/420cb707b880e4fb649094241371701013eeb15f
diff --git a/src/crypto/crypto_aes.h b/src/crypto/crypto_aes.h
index 6b18b1e29e2..bea409da94a 100644
--- a/src/crypto/crypto_aes.h
+++ b/src/crypto/crypto_aes.h
@@ -43,7 +43,7 @@ constexpr unsigned kNoAuthTagLength = static_cast<unsigned>(-1);
   V(OCB_128, AES_Cipher, ncrypto::Cipher::AES_128_OCB())                       \
   V(OCB_192, AES_Cipher, ncrypto::Cipher::AES_192_OCB())                       \
   V(OCB_256, AES_Cipher, ncrypto::Cipher::AES_256_OCB())
-#else
+#elif defined(OPENSSL_IS_BORINGSSL)
 #define VARIANTS_OCB(V)
 #endif

diff --git a/src/crypto/crypto_cipher.cc b/src/crypto/crypto_cipher.cc
index 348d96e6043..f28ecdae965 100644
--- a/src/crypto/crypto_cipher.cc
+++ b/src/crypto/crypto_cipher.cc
@@ -830,8 +830,8 @@ bool CipherBase::Final(std::unique_ptr<BackingStore>* out) {
       static_cast<size_t>(ctx_.getBlockSize()),
       BackingStoreInitializationMode::kUninitialized);

-#if !OPENSSL_VERSION_PREREQ(3, 0)
-  // OpenSSL v1.x doesn't verify the presence of the auth tag so do
+#ifdef OPENSSL_IS_BORINGSSL
+  // BoringSSL doesn't verify the presence of the auth tag so do
   // it ourselves, see https://github.com/nodejs/node/issues/45874.
   if (kind_ == kDecipher && ctx_.isChaCha20Poly1305() &&
       auth_tag_state_ != kAuthTagSetByUser) {
diff --git a/src/crypto/crypto_context.cc b/src/crypto/crypto_context.cc
index 5163cc1c445..4956d7f5908 100644
--- a/src/crypto/crypto_context.cc
+++ b/src/crypto/crypto_context.cc
@@ -1857,7 +1857,7 @@ void SecureContext::Init(const FunctionCallbackInfo<Value>& args) {
   // SSLv3 is disabled because it's susceptible to downgrade attacks (POODLE.)
   SSL_CTX_set_options(sc->ctx_.get(), SSL_OP_NO_SSLv2);
   SSL_CTX_set_options(sc->ctx_.get(), SSL_OP_NO_SSLv3);
-#if OPENSSL_VERSION_MAJOR >= 3
+#ifndef OPENSSL_IS_BORINGSSL
   SSL_CTX_set_options(sc->ctx_.get(), SSL_OP_ALLOW_CLIENT_RENEGOTIATION);
 #endif

@@ -1876,9 +1876,9 @@ void SecureContext::Init(const FunctionCallbackInfo<Value>& args) {
   CHECK(SSL_CTX_set_min_proto_version(sc->ctx_.get(), min_version));
   CHECK(SSL_CTX_set_max_proto_version(sc->ctx_.get(), max_version));

-  // OpenSSL 1.1.0 changed the ticket key size, but the OpenSSL 1.0.x size was
-  // exposed in the public API. To retain compatibility, install a callback
-  // which restores the old algorithm.
+  // The ticket key size changed after the original size was exposed in the
+  // public API. To retain compatibility, install a callback which restores
+  // the old algorithm.
   if (!ncrypto::CSPRNG(sc->ticket_key_name_, sizeof(sc->ticket_key_name_)) ||
       !ncrypto::CSPRNG(sc->ticket_key_hmac_, sizeof(sc->ticket_key_hmac_)) ||
       !ncrypto::CSPRNG(sc->ticket_key_aes_, sizeof(sc->ticket_key_aes_))) {
@@ -1888,7 +1888,7 @@ void SecureContext::Init(const FunctionCallbackInfo<Value>& args) {
 #if NCRYPTO_USE_OPENSSL_PROVIDER
   SSL_CTX_set_tlsext_ticket_key_evp_cb(sc->ctx_.get(),
                                        TicketCompatibilityCallback);
-#else
+#elif NCRYPTO_USE_BORINGSSL
   SSL_CTX_set_tlsext_ticket_key_cb(sc->ctx_.get(), TicketCompatibilityCallback);
 #endif
 }
@@ -2200,7 +2200,7 @@ void SecureContext::SetDHParam(const FunctionCallbackInfo<Value>& args) {
 #if NCRYPTO_USE_OPENSSL_PROVIDER
     EVPKeyPointer params(PEM_read_bio_Parameters(bio.get(), nullptr));
     if (params && params.isA(KeyAlgorithm::DH)) dh.reset(params.release());
-#else
+#elif NCRYPTO_USE_BORINGSSL
     dh.reset(PEM_read_bio_DHparams(bio.get(), nullptr, nullptr, nullptr));
 #endif
   }
@@ -2224,7 +2224,7 @@ void SecureContext::SetDHParam(const FunctionCallbackInfo<Value>& args) {
 #if NCRYPTO_USE_OPENSSL_PROVIDER
   EVPKeyPointer dh_pkey(dh.release());
   if (!SSL_CTX_set0_tmp_dh_pkey(sc->ctx_.get(), dh_pkey.get())) {
-#else
+#elif NCRYPTO_USE_BORINGSSL
   if (!SSL_CTX_set_tmp_dh(sc->ctx_.get(), dh.get())) {
 #endif
     return THROW_ERR_CRYPTO_OPERATION_FAILED(
@@ -2438,12 +2438,12 @@ void SecureContext::Close(const FunctionCallbackInfo<Value>& args) {

 namespace {
 // The historical error shape for the TLS `pfx` option: the OpenSSL reason
-// string, except for OpenSSL 3's bare "unsupported" error, which on its own
+// string, except for OpenSSL's bare "unsupported" error, which on its own
 // says nothing useful.
 // TODO(@jasnell): Should this use ThrowCryptoError?
 // NOLINTNEXTLINE(runtime/int) -- matches ERR_get_error()
 void ThrowPFXError(Environment* env, unsigned long err) {
-#if OPENSSL_VERSION_MAJOR >= 3
+#ifndef OPENSSL_IS_BORINGSSL
   if (ERR_GET_REASON(err) == ERR_R_UNSUPPORTED) {
     return THROW_ERR_CRYPTO_UNSUPPORTED_OPERATION(
         env, "Unsupported PKCS12 PFX data");
@@ -2622,7 +2622,7 @@ void SecureContext::EnableTicketKeyCallback(

 #if NCRYPTO_USE_OPENSSL_PROVIDER
   SSL_CTX_set_tlsext_ticket_key_evp_cb(wrap->ctx_.get(), TicketKeyCallback);
-#else
+#elif NCRYPTO_USE_BORINGSSL
   SSL_CTX_set_tlsext_ticket_key_cb(wrap->ctx_.get(), TicketKeyCallback);
 #endif
 }
@@ -2641,7 +2641,7 @@ bool InitTicketHmac(EVP_MAC_CTX* hctx,
   };
   return EVP_MAC_init(hctx, key, key_len, params) == 1;
 }
-#else
+#elif NCRYPTO_USE_BORINGSSL
 bool InitTicketHmac(HMAC_CTX* hctx, const unsigned char* key, size_t key_len) {
   return HMAC_Init_ex(hctx, key, key_len, Digest::SHA256, nullptr) == 1;
 }
@@ -2654,7 +2654,7 @@ int SecureContext::TicketKeyCallback(SSL* ssl,
                                      EVP_CIPHER_CTX* ectx,
 #if NCRYPTO_USE_OPENSSL_PROVIDER
                                      EVP_MAC_CTX* hctx,
-#else
+#elif NCRYPTO_USE_BORINGSSL
                                      HMAC_CTX* hctx,
 #endif
                                      int enc) {
@@ -2751,7 +2751,7 @@ int SecureContext::TicketCompatibilityCallback(SSL* ssl,
                                                EVP_CIPHER_CTX* ectx,
 #if NCRYPTO_USE_OPENSSL_PROVIDER
                                                EVP_MAC_CTX* hctx,
-#else
+#elif NCRYPTO_USE_BORINGSSL
                                                HMAC_CTX* hctx,
 #endif
                                                int enc) {
diff --git a/src/crypto/crypto_context.h b/src/crypto/crypto_context.h
index 1a4eba212b6..3a564f83827 100644
--- a/src/crypto/crypto_context.h
+++ b/src/crypto/crypto_context.h
@@ -112,7 +112,7 @@ class SecureContext final : public BaseObject {
   static const int kTicketKeyIVIndex = 4;

  protected:
-  // OpenSSL structures are opaque. This is sizeof(SSL_CTX) for OpenSSL 1.1.1b:
+  // OpenSSL structures are opaque. Estimate SSL_CTX memory usage:
   static const int64_t kExternalSize = 1024;

   static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
@@ -175,7 +175,7 @@ class SecureContext final : public BaseObject {
                                EVP_CIPHER_CTX* ectx,
 #if NCRYPTO_USE_OPENSSL_PROVIDER
                                EVP_MAC_CTX* hctx,
-#else
+#elif NCRYPTO_USE_BORINGSSL
                                HMAC_CTX* hctx,
 #endif
                                int enc);
@@ -186,7 +186,7 @@ class SecureContext final : public BaseObject {
                                          EVP_CIPHER_CTX* ectx,
 #if NCRYPTO_USE_OPENSSL_PROVIDER
                                          EVP_MAC_CTX* hctx,
-#else
+#elif NCRYPTO_USE_BORINGSSL
                                          HMAC_CTX* hctx,
 #endif
                                          int enc);
diff --git a/src/crypto/crypto_dh.cc b/src/crypto/crypto_dh.cc
index 8bfde65327f..a65cc2e12e5 100644
--- a/src/crypto/crypto_dh.cc
+++ b/src/crypto/crypto_dh.cc
@@ -81,20 +81,14 @@ MaybeLocal<Value> DataPointerToBuffer(Environment* env, DataPointer&& data) {
 void PutDhError(int reason) {
 #ifdef OPENSSL_IS_BORINGSSL
   OPENSSL_PUT_ERROR(DH, reason);
-#elif NCRYPTO_USE_OPENSSL3_PROVIDER
-  ERR_raise(ERR_LIB_DH, reason);
 #else
-  ERR_put_error(ERR_LIB_DH, 0, reason, __FILE__, __LINE__);
+  ERR_raise(ERR_LIB_DH, reason);
 #endif
 }

-#if defined(OPENSSL_IS_BORINGSSL) || !NCRYPTO_USE_OPENSSL3_PROVIDER
-void PutBnError(int reason) {
 #ifdef OPENSSL_IS_BORINGSSL
+void PutBnError(int reason) {
   OPENSSL_PUT_ERROR(BN, reason);
-#else
-  ERR_put_error(ERR_LIB_BN, 0, reason, __FILE__, __LINE__);
-#endif
 }
 #endif

@@ -123,12 +117,8 @@ void New(const FunctionCallbackInfo<Value>& args) {
     int32_t bits = args[0].As<Int32>()->Value();
     if (bits < 2) {
 #ifndef OPENSSL_IS_BORINGSSL
-#if OPENSSL_VERSION_MAJOR >= 3
       PutDhError(DH_R_MODULUS_TOO_SMALL);
-#else
-      PutBnError(BN_R_BITS_TOO_SMALL);
-#endif  // OPENSSL_VERSION_MAJOR >= 3
-#else   // OPENSSL_IS_BORINGSSL
+#elif defined(OPENSSL_IS_BORINGSSL)
       PutBnError(BN_R_BITS_TOO_SMALL);
 #endif  // OPENSSL_IS_BORINGSSL
       return ThrowCryptoError(env, ERR_get_error(), "Invalid prime length");
diff --git a/src/crypto/crypto_hash.cc b/src/crypto/crypto_hash.cc
index 81977941889..11274d98568 100644
--- a/src/crypto/crypto_hash.cc
+++ b/src/crypto/crypto_hash.cc
@@ -226,7 +226,7 @@ void SaveSupportedProviderHashAlgorithms(EVP_MD* md, void* arg) {
   EVP_MD_names_do_all(md, SaveSupportedProviderHashName, &context);
 }

-#else
+#elif NCRYPTO_USE_BORINGSSL
 void SaveSupportedHashAlgorithms(const EVP_MD* md,
                                  const char* from,
                                  const char* to,
@@ -253,7 +253,7 @@ const std::vector<std::string>& GetSupportedHashAlgorithms(Environment* env) {
       // later lookups instead of throwing them away immediately.
       EVP_MD_do_all_sorted(SaveSupportedHashAlgorithmsAndCacheMD, env);
       EVP_MD_do_all_provided(nullptr, SaveSupportedProviderHashAlgorithms, env);
-#else
+#elif NCRYPTO_USE_BORINGSSL
       EVP_MD_do_all_sorted(SaveSupportedHashAlgorithms, env);
 #endif
 #if NCRYPTO_USE_BORINGSSL
@@ -351,7 +351,7 @@ const EVP_MD* GetDigestImplementation(
     return digest_owner->get();
   }
   return nullptr;
-#else
+#elif NCRYPTO_USE_BORINGSSL
   Utf8Value utf8(env->isolate(), algorithm);
   return ncrypto::getDigestByName(*utf8);
 #endif
@@ -360,7 +360,7 @@ const EVP_MD* GetDigestImplementation(
 void MarkInvalidXofLength() {
 #if NCRYPTO_USE_OPENSSL_PROVIDER
   ERR_raise(ERR_LIB_EVP, EVP_R_NOT_XOF_OR_INVALID_LENGTH);
-#else
+#elif NCRYPTO_USE_BORINGSSL
   EVPerr(EVP_F_EVP_DIGESTFINALXOF, EVP_R_NOT_XOF_OR_INVALID_LENGTH);
 #endif
 }
@@ -375,7 +375,7 @@ void MarkInvalidXofLength() {
 bool IsShakeDigest(const EVP_MD* md) {
 #if NCRYPTO_USE_OPENSSL_PROVIDER
   return EVP_MD_is_a(md, "SHAKE128") || EVP_MD_is_a(md, "SHAKE256");
-#else
+#elif NCRYPTO_USE_BORINGSSL
   const char* name = OBJ_nid2sn(EVP_MD_type(md));
   return name != nullptr &&
          (strcmp(name, "SHAKE128") == 0 || strcmp(name, "SHAKE256") == 0);
@@ -546,7 +546,7 @@ void Hash::OneShotDigest(const FunctionCallbackInfo<Value>& args) {
       }
       if (env->isolate()->HasPendingException()) return;
     }
-#else
+#elif NCRYPTO_USE_BORINGSSL
     Utf8Value utf8(env->isolate(), args[0]);
     return OneShotDigestWithMD(
         env, args, ncrypto::getDigestByName(*utf8), nullptr);
diff --git a/src/crypto/crypto_kem.h b/src/crypto/crypto_kem.h
index dc60001e2d1..bdc99499204 100644
--- a/src/crypto/crypto_kem.h
+++ b/src/crypto/crypto_kem.h
@@ -112,15 +112,15 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry);

 #else

-// Provide stub implementations when OpenSSL < 3.0
+// Provide stub implementations when KEM is unavailable.
 namespace node {
 namespace crypto {
 namespace KEM {
 inline void Initialize(Environment* env, v8::Local<v8::Object> target) {
-  // No-op when OpenSSL < 3.0
+  // No-op when KEM is unavailable.
 }
 inline void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
-  // No-op when OpenSSL < 3.0
+  // No-op when KEM is unavailable.
 }
 }  // namespace KEM
 }  // namespace crypto
diff --git a/src/crypto/crypto_keys.cc b/src/crypto/crypto_keys.cc
index 71464785c11..88381b901cd 100644
--- a/src/crypto/crypto_keys.cc
+++ b/src/crypto/crypto_keys.cc
@@ -1313,9 +1313,9 @@ void KeyObjectHandle::Equals(const FunctionCallbackInfo<Value>& args) {
     case kKeyTypePrivate: {
       EVP_PKEY* pkey = key.GetAsymmetricKey().get();
       EVP_PKEY* pkey2 = key2.GetAsymmetricKey().get();
-#if OPENSSL_VERSION_MAJOR >= 3
+#ifndef OPENSSL_IS_BORINGSSL
       int ok = EVP_PKEY_eq(pkey, pkey2);
-#else
+#elif defined(OPENSSL_IS_BORINGSSL)
       int ok = EVP_PKEY_cmp(pkey, pkey2);
 #endif
       if (ok == -2) {
diff --git a/src/crypto/crypto_kmac.h b/src/crypto/crypto_kmac.h
index 4f8fe27d2ff..7698a0dfaa1 100644
--- a/src/crypto/crypto_kmac.h
+++ b/src/crypto/crypto_kmac.h
@@ -64,7 +64,7 @@ void Initialize(Environment* env, v8::Local<v8::Object> target);
 void RegisterExternalReferences(ExternalReferenceRegistry* registry);
 }  // namespace Kmac

-#else
+#elif defined(OPENSSL_IS_BORINGSSL)
 // If there is no KMAC support, provide empty namespace functions.
 namespace Kmac {
 void Initialize(Environment* env, v8::Local<v8::Object> target) {}
diff --git a/src/crypto/crypto_mac.cc b/src/crypto/crypto_mac.cc
index 7f43e760ccf..ba34211275f 100644
--- a/src/crypto/crypto_mac.cc
+++ b/src/crypto/crypto_mac.cc
@@ -522,7 +522,7 @@ Mac::Mac(Environment* env,
       has_output_length_(has_output_length) {
   MakeWeak();
 }
-#else
+#elif defined(OPENSSL_IS_BORINGSSL)
 Mac::Mac(Environment* env, Local<Object> wrap) : BaseObject(env, wrap) {
   MakeWeak();
 }
@@ -531,7 +531,7 @@ Mac::Mac(Environment* env, Local<Object> wrap) : BaseObject(env, wrap) {
 void Mac::MemoryInfo(MemoryTracker* tracker) const {
 #if OPENSSL_WITH_EVP_MAC
   tracker->TrackFieldWithSize("context", context_ ? kSizeOf_EVP_MAC_CTX : 0);
-#else
+#elif defined(OPENSSL_IS_BORINGSSL)
   static_cast<void>(tracker);
 #endif
 }
@@ -570,7 +570,7 @@ void Mac::New(const FunctionCallbackInfo<Value>& args) {
           std::move(initialized.context),
           initialized.output_size,
           initialized.has_output_length);
-#else
+#elif defined(OPENSSL_IS_BORINGSSL)
   THROW_ERR_CRYPTO_MAC_NOT_SUPPORTED(Environment::GetCurrent(args),
                                      "MAC is not supported");
 #endif
@@ -585,7 +585,7 @@ void Mac::MacUpdate(const FunctionCallbackInfo<Value>& args) {
                  size_t length) {
                 args.GetReturnValue().Set(mac->MacUpdate(data, length));
               });
-#else
+#elif defined(OPENSSL_IS_BORINGSSL)
   THROW_ERR_CRYPTO_MAC_NOT_SUPPORTED(Environment::GetCurrent(args),
                                      "MAC is not supported");
 #endif
@@ -613,7 +613,7 @@ void Mac::MacFinal(const FunctionCallbackInfo<Value>& args) {
           .ToLocal(&result)) {
     args.GetReturnValue().Set(result);
   }
-#else
+#elif defined(OPENSSL_IS_BORINGSSL)
   THROW_ERR_CRYPTO_MAC_NOT_SUPPORTED(Environment::GetCurrent(args),
                                      "MAC is not supported");
 #endif
@@ -627,7 +627,7 @@ void Mac::GetMacs(const FunctionCallbackInfo<Value>& args) {
   if (ToV8Value(context, GetSupportedMacAlgorithms(env)).ToLocal(&result)) {
     args.GetReturnValue().Set(result);
   }
-#else
+#elif defined(OPENSSL_IS_BORINGSSL)
   args.GetReturnValue().Set(Array::New(args.GetIsolate(), 0));
 #endif
 }
diff --git a/src/crypto/crypto_mac.h b/src/crypto/crypto_mac.h
index 3223b148710..4da9dcef2dc 100644
--- a/src/crypto/crypto_mac.h
+++ b/src/crypto/crypto_mac.h
@@ -39,7 +39,7 @@ class Mac final : public BaseObject {
   ncrypto::EVPMacCtxPointer context_;
   size_t output_size_ = 0;
   bool has_output_length_ = false;
-#else
+#elif defined(OPENSSL_IS_BORINGSSL)
   Mac(Environment* env, v8::Local<v8::Object> wrap);
 #endif
 };
diff --git a/src/crypto/crypto_pkcs12.cc b/src/crypto/crypto_pkcs12.cc
index 8b0af44d9d7..b4e909b2629 100644
--- a/src/crypto/crypto_pkcs12.cc
+++ b/src/crypto/crypto_pkcs12.cc
@@ -60,8 +60,8 @@ PKCS12ParseResult ParsePKCS12Bundle(const BIOPointer& bio, const char* pass) {
       return PKCS12ParseResult(PKCS12ParseError::NOT_RECOGNIZED, err);
     }
 #endif
-#if OPENSSL_VERSION_MAJOR >= 3
-    // OpenSSL 3 reports algorithms that moved to the legacy provider as a
+#ifndef OPENSSL_IS_BORINGSSL
+    // OpenSSL reports algorithms that moved to the legacy provider as a
     // bare "unsupported" error.
     if (ERR_GET_REASON(err) == ERR_R_UNSUPPORTED) {
       return PKCS12ParseResult(PKCS12ParseError::UNSUPPORTED_ALGORITHM, err);
diff --git a/src/crypto/crypto_rsa.cc b/src/crypto/crypto_rsa.cc
index c95bd46635f..1ec3a487dfe 100644
--- a/src/crypto/crypto_rsa.cc
+++ b/src/crypto/crypto_rsa.cc
@@ -50,7 +50,7 @@ bool IsRsaPssDigestEncodable(const Digest& digest) {

   const ASN1_OBJECT* object = OBJ_nid2obj(nid);
   return object != nullptr && OBJ_length(object) > 0;
-#else
+#elif NCRYPTO_USE_BORINGSSL
   static_cast<void>(digest);
   return true;
 #endif
@@ -82,10 +82,8 @@ EVPKeyCtxPointer RsaKeyGenTraits::Setup(RsaKeyPairGenConfig* params) {
       return {};
     }

-    // TODO(tniessen): This appears to only be necessary in OpenSSL 3, while
-    // OpenSSL 1.1.1 behaves as recommended by RFC 8017 and defaults the MGF1
-    // hash algorithm to the RSA-PSS hashAlgorithm. Remove this code if the
-    // behavior of OpenSSL 3 changes.
+    // OpenSSL does not default the MGF1 hash algorithm to the RSA-PSS
+    // hashAlgorithm as recommended by RFC 8017, so set it explicitly.
     auto& mgf1_md = params->params.mgf1_md;
     if (!mgf1_md && params->params.md) {
       mgf1_md = params->params.md;
@@ -397,7 +395,7 @@ KeyObjectData ImportJWKRsaKey(Environment* env, Local<Object> jwk) {

 #if NCRYPTO_USE_OPENSSL_PROVIDER
   ncrypto::Rsa rsa_view;
-#else
+#elif NCRYPTO_USE_BORINGSSL
   RSAPointer rsa(RSA_new());
   if (!rsa) {
     THROW_ERR_CRYPTO_OPERATION_FAILED(env, "Unable to create RSA pointer");
@@ -511,7 +509,7 @@ KeyObjectData ImportJWKRsaKey(Environment* env, Local<Object> jwk) {

 #if NCRYPTO_USE_OPENSSL_PROVIDER
   auto pkey = EVPKeyPointer::NewRSA(rsa_view);
-#else
+#elif NCRYPTO_USE_BORINGSSL
   auto pkey = EVPKeyPointer::NewRSA(std::move(rsa));
 #endif
   if (!pkey) {
diff --git a/src/crypto/crypto_tls.cc b/src/crypto/crypto_tls.cc
index a73eb7220a4..5792de0515d 100644
--- a/src/crypto/crypto_tls.cc
+++ b/src/crypto/crypto_tls.cc
@@ -81,7 +81,7 @@ namespace {
 // that the user user Connection::VerifyError after the `secure`
 // callback has been made.
 int VerifyCallback(int preverify_ok, X509_STORE_CTX* ctx) {
-  // From https://www.openssl.org/docs/man1.1.1/man3/SSL_verify_cb:
+  // From https://www.openssl.org/docs/man3.0/man3/SSL_verify_cb:
   //
   //   If VerifyCallback returns 1, the verification process is continued. If
   //   VerifyCallback always returns 1, the TLS/SSL handshake will not be
@@ -544,9 +544,9 @@ void TLSWrap::InitSSL() {
   SSL_set_mode(ssl_.get(), SSL_MODE_RELEASE_BUFFERS);
 #endif  // SSL_MODE_RELEASE_BUFFERS

-  // This is default in 1.1.1, but set it anyway, Cycle() doesn't currently
-  // re-call ClearIn() if SSL_read() returns SSL_ERROR_WANT_READ, so data can be
-  // left sitting in the incoming enc_in_ and never get processed.
+  // Set SSL_MODE_AUTO_RETRY explicitly because Cycle() doesn't currently
+  // re-call ClearIn() if SSL_read() returns SSL_ERROR_WANT_READ, so data can
+  // be left sitting in the incoming enc_in_ and never get processed.
   // - https://wiki.openssl.org/index.php/TLS1.3#Non-application_data_records
   SSL_set_mode(ssl_.get(), SSL_MODE_AUTO_RETRY);

@@ -678,8 +678,8 @@ void TLSWrap::SSLInfoCallback(const SSL* ssl_, int where, int ret) {
     }
   }

-  // SSL_CB_HANDSHAKE_START and SSL_CB_HANDSHAKE_DONE are called
-  // sending HelloRequest in OpenSSL-1.1.1.
+  // SSL_CB_HANDSHAKE_START and SSL_CB_HANDSHAKE_DONE are called when sending
+  // HelloRequest.
   // We need to check whether this is in a renegotiation state or not.
   if (where & SSL_CB_HANDSHAKE_DONE && !SSL_renegotiate_pending(ssl)) {
     Debug(c, "SSLInfoCallback(SSL_CB_HANDSHAKE_DONE);");
@@ -953,7 +953,7 @@ void TLSWrap::ClearOut() {
           const char* ls = ERR_lib_error_string(ssl_err);
 #if NCRYPTO_USE_OPENSSL_PROVIDER
           const char* fs = nullptr;
-#else
+#elif NCRYPTO_USE_BORINGSSL
           const char* fs = ERR_func_error_string(ssl_err);
 #endif
           const char* rs = ERR_reason_error_string(ssl_err);
diff --git a/src/crypto/crypto_tls.h b/src/crypto/crypto_tls.h
index ab602401887..f3e0dfff9bc 100644
--- a/src/crypto/crypto_tls.h
+++ b/src/crypto/crypto_tls.h
@@ -155,7 +155,7 @@ class TLSWrap : public AsyncWrap,
   }

  private:
-  // OpenSSL structures are opaque. Estimate SSL memory size for OpenSSL 1.1.1b:
+  // OpenSSL structures are opaque. Estimate SSL memory usage:
   //   SSL: 6224
   //   SSL->SSL3_STATE: 1040
   //   ...some buffers: 42 * 1024
diff --git a/src/crypto/crypto_util.cc b/src/crypto/crypto_util.cc
index b980a578721..8dd7a14e205 100644
--- a/src/crypto/crypto_util.cc
+++ b/src/crypto/crypto_util.cc
@@ -19,7 +19,7 @@
 #include <mutex>
 #include "math.h"

-#if OPENSSL_VERSION_MAJOR >= 3
+#ifndef OPENSSL_IS_BORINGSSL
 #include "openssl/provider.h"
 #endif

@@ -451,7 +451,7 @@ std::optional<std::string> ProcessFipsOptions() {
   const bool force_fips = per_process::cli_options->force_fips_crypto;
   if (!enable_fips && !force_fips) return std::nullopt;

-#if OPENSSL_VERSION_MAJOR >= 3
+#ifndef OPENSSL_IS_BORINGSSL
   // Whether FIPS-approved implementations are reachable is decided by the
   // OpenSSL configuration, not by Node.js. Refuse to start rather than
   // restrict the default property query to a provider that is not there,
@@ -505,15 +505,6 @@ void InitCryptoOnce() {
   OPENSSL_INIT_SETTINGS* settings = OPENSSL_INIT_new();
   CHECK_NOT_NULL(settings);

-#if OPENSSL_VERSION_MAJOR < 3
-  // --openssl-config=...
-  if (!per_process::cli_options->openssl_config.empty()) {
-    const char* conf = per_process::cli_options->openssl_config.c_str();
-    OPENSSL_INIT_set_config_filename(settings, conf);
-  }
-#endif
-
-#if OPENSSL_VERSION_MAJOR >= 3
   // --openssl-legacy-provider
   if (per_process::cli_options->openssl_legacy_provider) {
     OSSL_PROVIDER* legacy_provider = OSSL_PROVIDER_load(nullptr, "legacy");
@@ -521,7 +512,6 @@ void InitCryptoOnce() {
       fprintf(stderr, "Unable to load legacy provider.\n");
     }
   }
-#endif

   OPENSSL_init_ssl(0, settings);
   InstallFipsIndicatorCallback();
@@ -901,7 +891,7 @@ Maybe<void> Decorate(Environment* env,
   const char* ls = ERR_lib_error_string(err);
 #if NCRYPTO_USE_OPENSSL_PROVIDER
   const char* fs = nullptr;
-#else
+#elif NCRYPTO_USE_BORINGSSL
   const char* fs = ERR_func_error_string(err);
 #endif
   const char* rs = ERR_reason_error_string(err);
diff --git a/src/node.cc b/src/node.cc
index f7a19747bed..0b5b305d688 100644
--- a/src/node.cc
+++ b/src/node.cc
@@ -51,7 +51,7 @@
 #if HAVE_OPENSSL
 #include "ncrypto.h"
 #include "node_crypto.h"
-#if OPENSSL_VERSION_MAJOR >= 3 && !defined(CONF_MFLAGS_IGNORE_MISSING_FILE)
+#if !defined(OPENSSL_IS_BORINGSSL) && !defined(CONF_MFLAGS_IGNORE_MISSING_FILE)
 // OpenSSL hides this deprecated macro under OPENSSL_NO_DEPRECATED, but the
 // non-deprecated OPENSSL_INIT settings API still accepts the flag value.
 #define CONF_MFLAGS_IGNORE_MISSING_FILE 0x10
@@ -1305,7 +1305,6 @@ InitializeOncePerProcessInternal(
   if (!(flags & ProcessInitializationFlags::kNoInitOpenSSL)) {
 #if HAVE_OPENSSL
 #ifndef OPENSSL_IS_BORINGSSL
-#if OPENSSL_VERSION_MAJOR >= 3
     auto GetOpenSSLErrorString = []() -> std::string {
       std::string ret;
       ERR_print_errors_cb(
@@ -1321,6 +1320,7 @@ InitializeOncePerProcessInternal(

     // In the case of FIPS builds we should make sure
     // the random source is properly initialized first.
+    //
     // Call OPENSSL_init_crypto to initialize OPENSSL_INIT_LOAD_CONFIG to
     // avoid the default behavior where errors raised during the parsing of the
     // OpenSSL configuration file are not propagated and cannot be detected.
@@ -1377,11 +1377,7 @@ InitializeOncePerProcessInternal(
                                    GetOpenSSLErrorString());
       return result;
     }
-#else  // OPENSSL_VERSION_MAJOR < 3
-    if (FIPS_mode()) {
-      OPENSSL_init();
-    }
-#endif
+
     if (auto fips_error = crypto::ProcessFipsOptions()) {
       result->exit_code_ = ExitCode::kGenericUserError;
       result->early_return_ = true;
diff --git a/src/node_constants.cc b/src/node_constants.cc
index e8815a80080..ee4eb04ba62 100644
--- a/src/node_constants.cc
+++ b/src/node_constants.cc
@@ -70,7 +70,7 @@
 #define RSA_PSS_SALTLEN_AUTO -2
 #endif

-#if !defined(OPENSSL_IS_BORINGSSL) && OPENSSL_VERSION_MAJOR >= 3
+#ifndef OPENSSL_IS_BORINGSSL
 // OpenSSL hides these deprecated DH check constants under
 // OPENSSL_NO_DEPRECATED, but the numeric verifyError values remain public API.
 #if !defined(DH_CHECK_P_NOT_PRIME)
@@ -87,7 +87,7 @@
 #endif
 #endif
 #ifndef OPENSSL_NO_ENGINE
-#if !defined(OPENSSL_IS_BORINGSSL) && OPENSSL_VERSION_MAJOR >= 3
+#ifndef OPENSSL_IS_BORINGSSL
 // Engine constants remain public API while engine implementation lives in the
 // dedicated compatibility target.
 #define ENGINE_METHOD_RSA (unsigned int)0x0001
diff --git a/src/node_constants.h b/src/node_constants.h
index 7e02db84321..0658c94ef4e 100644
--- a/src/node_constants.h
+++ b/src/node_constants.h
@@ -36,7 +36,7 @@
 #define DEFAULT_CIPHER_LIST_CORE NODE_OPENSSL_DEFAULT_CIPHER_LIST
 #else
 // TLSv1.3 suites start with TLS_, and are the OpenSSL defaults, see:
-//   https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_set_ciphersuites.html
+//   https://www.openssl.org/docs/man3.0/man3/SSL_CTX_set_ciphersuites.html
 #define DEFAULT_CIPHER_LIST_CORE \
                                  "TLS_AES_256_GCM_SHA384:"          \
                                  "TLS_CHACHA20_POLY1305_SHA256:"    \
diff --git a/src/node_crypto.cc b/src/node_crypto.cc
index 99cb556ecd9..9b096ac8437 100644
--- a/src/node_crypto.cc
+++ b/src/node_crypto.cc
@@ -77,7 +77,7 @@ namespace crypto {

 #if OPENSSL_WITH_EVP_MAC
 #define KMAC_NAMESPACE_LIST(V) V(Kmac)
-#else
+#elif defined(OPENSSL_IS_BORINGSSL)
 #define KMAC_NAMESPACE_LIST(V)
 #endif  // OPENSSL_WITH_EVP_MAC

diff --git a/src/node_metadata.cc b/src/node_metadata.cc
index 0f3a76105a1..44d9de8abd6 100644
--- a/src/node_metadata.cc
+++ b/src/node_metadata.cc
@@ -68,7 +68,7 @@ static constexpr size_t search(const char* s, char c, size_t n = 0) {

 static inline std::string GetOpenSSLVersion() {
   // sample openssl version string format
-  // for reference: "OpenSSL 1.1.0i 14 Aug 2018"
+  // for reference: "OpenSSL 3.5.7 9 Jun 2026"
   const char* version = OpenSSL_version(OPENSSL_VERSION);
   const size_t first_space = search(version, ' ');

diff --git a/src/node_options.cc b/src/node_options.cc
index bdc8171529b..4ba4801ad1d 100644
--- a/src/node_options.cc
+++ b/src/node_options.cc
@@ -1790,9 +1790,9 @@ PerProcessOptionsParser::PerProcessOptionsParser(
             kAllowedInEnvvar);
 #endif  // V8_ENABLE_SANDBOX
 #endif  // HAVE_OPENSSL
-#if OPENSSL_VERSION_MAJOR >= 3
+#if HAVE_OPENSSL && !defined(OPENSSL_IS_BORINGSSL)
   AddOption("--openssl-legacy-provider",
-            "enable OpenSSL 3.0 legacy provider",
+            "enable OpenSSL's legacy provider",
             BOOL_FIELD(openssl_legacy_provider),
             kAllowedInEnvvar);
   AddOption("--openssl-shared-config",
@@ -1800,7 +1800,7 @@ PerProcessOptionsParser::PerProcessOptionsParser(
             BOOL_FIELD(openssl_shared_config),
             kAllowedInEnvvar);

-#endif  // OPENSSL_VERSION_MAJOR
+#endif  // HAVE_OPENSSL && !OPENSSL_IS_BORINGSSL
   AddOption("--use-largepages",
             "This option is no longer supported and a no-op. It still accepts"
             " these values for compatibility: 'off' (default), 'on' (report a "
diff --git a/src/node_options.h b/src/node_options.h
index 6323f36dce1..f8aa5825386 100644
--- a/src/node_options.h
+++ b/src/node_options.h
@@ -445,7 +445,7 @@ class PerProcessOptions : public Options {
   DEFINE_BOOL_FIELD(force_fips_crypto) = false;
   std::string force_fips_crypto_policy = "provider";
 #endif  // HAVE_OPENSSL
-#if OPENSSL_VERSION_MAJOR >= 3
+#if HAVE_OPENSSL && !defined(OPENSSL_IS_BORINGSSL)
   DEFINE_BOOL_FIELD(openssl_legacy_provider) = false;
   DEFINE_BOOL_FIELD(openssl_shared_config) = false;
 #endif
diff --git a/src/quic/tlscontext.h b/src/quic/tlscontext.h
index 8209eda12db..ecadc1fa78d 100644
--- a/src/quic/tlscontext.h
+++ b/src/quic/tlscontext.h
@@ -21,7 +21,7 @@ namespace node::quic {
 class Session;
 class TLSContext;

-#if OPENSSL_IS_BORING
+#ifdef OPENSSL_IS_BORINGSSL
 static_assert(false,
               "This implementation of tlscontext relies on OpenSSL APIS "
               "that are not available in BoringSSL. An alternative impl "