From bd778b416291e6b61635055c738d361fceb42938 Mon Sep 17 00:00:00 2001 From: Filip Skokan Date: Tue, 5 May 2026 11:33:38 +0200 Subject: [PATCH 1/2] src: add BoringSSL EVP enumeration fallback BoringSSL declares EVP_CIPHER_do_all_sorted and EVP_MD_do_all_sorted, but stock no-decrepit builds do not provide those symbols. Add a Node build flag that keeps ncrypto and its dependents on a local BoringSSL fallback list when libdecrepit is absent. Keep embedders that provide the EVP enumeration symbols on the normal OpenSSL-compatible path, matching Electron's patched BoringSSL build. Signed-off-by: Filip Skokan --- deps/ncrypto/ncrypto.cc | 34 +++++++++++++++++++ deps/ncrypto/ncrypto.gyp | 7 ++++ deps/ncrypto/ncrypto.h | 18 ++++++++++ src/crypto/crypto_hash.cc | 29 +++++++++++++++- .../test-crypto-boringssl-evp-list.js | 31 +++++++++++++++++ test/parallel/test-crypto-key-objects-raw.js | 2 +- .../test-crypto-pqc-key-objects-ml-kem.js | 4 +++ test/wpt/status/WebCryptoAPI.cjs | 32 +++++++++++++++++ 8 files changed, 155 insertions(+), 2 deletions(-) create mode 100644 test/parallel/test-crypto-boringssl-evp-list.js diff --git a/deps/ncrypto/ncrypto.cc b/deps/ncrypto/ncrypto.cc index b7a0c96ee2ea60..ae7a343fe49767 100644 --- a/deps/ncrypto/ncrypto.cc +++ b/deps/ncrypto/ncrypto.cc @@ -7,6 +7,11 @@ #include #include #include +#if NCRYPTO_USE_BORINGSSL_EVP_DO_ALL_FALLBACK +#include +#include +#include +#endif #include #include #include @@ -67,6 +72,28 @@ using NetscapeSPKIPointer = DeleteFnPtr; static constexpr int kX509NameFlagsRFC2253WithinUtf8JSON = XN_FLAG_RFC2253 & ~ASN1_STRFLGS_ESC_MSB & ~ASN1_STRFLGS_ESC_CTRL; + +#if NCRYPTO_USE_BORINGSSL_EVP_DO_ALL_FALLBACK +struct BoringSSLCipher { + const EVP_CIPHER* (*get)(); + const char* name; +}; + +constexpr BoringSSLCipher kBoringSSLCiphers[] = { + {EVP_aes_128_cbc, "aes-128-cbc"}, {EVP_aes_128_ctr, "aes-128-ctr"}, + {EVP_aes_128_ecb, "aes-128-ecb"}, {EVP_aes_128_gcm, "aes-128-gcm"}, + {EVP_aes_128_ofb, "aes-128-ofb"}, {EVP_aes_192_cbc, "aes-192-cbc"}, + {EVP_aes_192_ctr, "aes-192-ctr"}, {EVP_aes_192_ecb, "aes-192-ecb"}, + {EVP_aes_192_gcm, "aes-192-gcm"}, {EVP_aes_192_ofb, "aes-192-ofb"}, + {EVP_aes_256_cbc, "aes-256-cbc"}, {EVP_aes_256_ctr, "aes-256-ctr"}, + {EVP_aes_256_ecb, "aes-256-ecb"}, {EVP_aes_256_gcm, "aes-256-gcm"}, + {EVP_aes_256_ofb, "aes-256-ofb"}, {EVP_des_cbc, "des-cbc"}, + {EVP_des_ecb, "des-ecb"}, {EVP_des_ede, "des-ede"}, + {EVP_des_ede3_cbc, "des-ede3-cbc"}, {EVP_des_ede_cbc, "des-ede-cbc"}, + {EVP_rc2_cbc, "rc2-cbc"}, {EVP_rc4, "rc4"}, +}; + +#endif } // namespace // ============================================================================ @@ -4209,6 +4236,12 @@ void Cipher::ForEach(Cipher::CipherNameCallback callback) { CipherCallbackContext context; context.cb = std::move(callback); +#if NCRYPTO_USE_BORINGSSL_EVP_DO_ALL_FALLBACK + for (const auto& cipher : kBoringSSLCiphers) { + static_cast(cipher.get); + context.cb(cipher.name); + } +#else EVP_CIPHER_do_all_sorted( #if OPENSSL_VERSION_MAJOR >= 3 array_push_back, #endif &context); +#endif } // ============================================================================ diff --git a/deps/ncrypto/ncrypto.gyp b/deps/ncrypto/ncrypto.gyp index cf9b7c6cdb6d2c..1747f3ea0149b9 100644 --- a/deps/ncrypto/ncrypto.gyp +++ b/deps/ncrypto/ncrypto.gyp @@ -1,5 +1,6 @@ { 'variables': { + 'ncrypto_bssl_libdecrepit_missing%': 1, 'ncrypto_sources': [ 'engine.cc', 'ncrypto.cc', @@ -11,8 +12,14 @@ 'target_name': 'ncrypto', 'type': 'static_library', 'include_dirs': ['.'], + 'defines': [ + 'NCRYPTO_BSSL_LIBDECREPIT_MISSING=<(ncrypto_bssl_libdecrepit_missing)', + ], 'direct_dependent_settings': { 'include_dirs': ['.'], + 'defines': [ + 'NCRYPTO_BSSL_LIBDECREPIT_MISSING=<(ncrypto_bssl_libdecrepit_missing)', + ], }, 'sources': [ '<@(ncrypto_sources)' ], 'conditions': [ diff --git a/deps/ncrypto/ncrypto.h b/deps/ncrypto/ncrypto.h index 1f116169f57a27..d3b0762f3313bb 100644 --- a/deps/ncrypto/ncrypto.h +++ b/deps/ncrypto/ncrypto.h @@ -22,6 +22,24 @@ #ifndef OPENSSL_NO_ENGINE #include #endif // !OPENSSL_NO_ENGINE + +#ifndef OPENSSL_VERSION_PREREQ +#define OPENSSL_VERSION_PREREQ(maj, min) \ + (OPENSSL_VERSION_NUMBER >= (((maj) << 28) | ((min) << 20))) +#endif + +// BoringSSL declares the EVP_*_do_all* APIs, but their implementation may +// live in libdecrepit. This matches standalone ncrypto's build flag. +#ifndef NCRYPTO_BSSL_LIBDECREPIT_MISSING +#define NCRYPTO_BSSL_LIBDECREPIT_MISSING 0 +#endif + +#if defined(OPENSSL_IS_BORINGSSL) && NCRYPTO_BSSL_LIBDECREPIT_MISSING +#define NCRYPTO_USE_BORINGSSL_EVP_DO_ALL_FALLBACK 1 +#else +#define NCRYPTO_USE_BORINGSSL_EVP_DO_ALL_FALLBACK 0 +#endif + // The FIPS-related functions are only available // when the OpenSSL itself was compiled with FIPS support. #if defined(OPENSSL_FIPS) && OPENSSL_VERSION_MAJOR < 3 diff --git a/src/crypto/crypto_hash.cc b/src/crypto/crypto_hash.cc index 9b76b900049484..c42926bb4ce61f 100644 --- a/src/crypto/crypto_hash.cc +++ b/src/crypto/crypto_hash.cc @@ -7,6 +7,10 @@ #include "threadpoolwork-inl.h" #include "v8.h" +#if NCRYPTO_USE_BORINGSSL_EVP_DO_ALL_FALLBACK +#include +#endif + #include namespace node { @@ -41,6 +45,24 @@ void Hash::MemoryInfo(MemoryTracker* tracker) const { tracker->TrackFieldWithSize("md", digest_ ? md_len_ : 0); } +#if NCRYPTO_USE_BORINGSSL_EVP_DO_ALL_FALLBACK +struct BoringSSLDigest { + const EVP_MD* (*get)(); + const char* name; +}; + +constexpr BoringSSLDigest kBoringSSLDigests[] = { + {EVP_md4, "md4"}, + {EVP_md5, "md5"}, + {EVP_sha1, "sha1"}, + {EVP_sha224, "sha224"}, + {EVP_sha256, "sha256"}, + {EVP_sha384, "sha384"}, + {EVP_sha512, "sha512"}, + {EVP_sha512_256, "sha512-256"}, +}; +#endif + #if OPENSSL_VERSION_MAJOR >= 3 void PushAliases(const char* name, void* data) { static_cast*>(data)->push_back(name); @@ -122,7 +144,12 @@ void SaveSupportedHashAlgorithms(const EVP_MD* md, const std::vector& GetSupportedHashAlgorithms(Environment* env) { if (env->supported_hash_algorithms.empty()) { MarkPopErrorOnReturn mark_pop_error_on_return; -#if OPENSSL_VERSION_MAJOR >= 3 +#if NCRYPTO_USE_BORINGSSL_EVP_DO_ALL_FALLBACK + for (const auto& digest : kBoringSSLDigests) { + static_cast(digest.get); + env->supported_hash_algorithms.emplace_back(digest.name); + } +#elif OPENSSL_VERSION_MAJOR >= 3 // Since we'll fetch the EVP_MD*, cache them along the way to speed up // later lookups instead of throwing them away immediately. EVP_MD_do_all_sorted(SaveSupportedHashAlgorithmsAndCacheMD, env); diff --git a/test/parallel/test-crypto-boringssl-evp-list.js b/test/parallel/test-crypto-boringssl-evp-list.js new file mode 100644 index 00000000000000..3f142c24f28a7c --- /dev/null +++ b/test/parallel/test-crypto-boringssl-evp-list.js @@ -0,0 +1,31 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +if (!process.features.openssl_is_boringssl) + common.skip('BoringSSL-only test'); + +const assert = require('assert'); +const { getCiphers, getHashes } = require('crypto'); + +const ciphers = getCiphers(); +[ + 'aes-128-cbc', + 'aes-256-gcm', + 'des-ede', + 'des-ede-cbc', + 'des-ede3-cbc', + 'rc2-cbc', + 'rc4', +].forEach((cipher) => assert(ciphers.includes(cipher), cipher)); + +const hashes = getHashes(); +[ + 'md4', + 'md5', + 'sha1', + 'sha256', + 'sha512-256', +].forEach((hash) => assert(hashes.includes(hash), hash)); diff --git a/test/parallel/test-crypto-key-objects-raw.js b/test/parallel/test-crypto-key-objects-raw.js index 311659ef004ea2..583cd4a1712a83 100644 --- a/test/parallel/test-crypto-key-objects-raw.js +++ b/test/parallel/test-crypto-key-objects-raw.js @@ -76,7 +76,7 @@ const { hasOpenSSL } = require('../common/crypto'); common.printSkipMessage('Skipping unsupported ed448/x448 test cases'); } - if (hasOpenSSL(3, 5) || process.features.openssl_is_boringssl) { + if (hasOpenSSL(3, 5)) { rawPublicKeys.push( ['ml-dsa-44', 'ml_dsa_44_public.pem'], ['ml-kem-768', 'ml_kem_768_public.pem'], diff --git a/test/parallel/test-crypto-pqc-key-objects-ml-kem.js b/test/parallel/test-crypto-pqc-key-objects-ml-kem.js index 0c344ed100c2da..19ed840544320d 100644 --- a/test/parallel/test-crypto-pqc-key-objects-ml-kem.js +++ b/test/parallel/test-crypto-pqc-key-objects-ml-kem.js @@ -4,6 +4,10 @@ const common = require('../common'); if (!common.hasCrypto) common.skip('missing crypto'); +if (process.features.openssl_is_boringssl) { + common.skip('Skipping unsupported ML-KEM key tests'); +} + const { hasOpenSSL } = require('../common/crypto'); const assert = require('assert'); diff --git a/test/wpt/status/WebCryptoAPI.cjs b/test/wpt/status/WebCryptoAPI.cjs index 253877f1a970e0..93ec24557e2701 100644 --- a/test/wpt/status/WebCryptoAPI.cjs +++ b/test/wpt/status/WebCryptoAPI.cjs @@ -61,6 +61,38 @@ if (!hasOpenSSL(3, 5)) { ['supports-modern.tentative.https.any.js', /ml-(?:kem|dsa)/i]); } +if (process.features.openssl_is_boringssl) { + skip( + 'derive_bits_keys/cfrg_curves_bits_curve448.tentative.https.any.js', + 'derive_bits_keys/cfrg_curves_keys_curve448.tentative.https.any.js', + 'digest/cshake.tentative.https.any.js', + 'digest/sha3.tentative.https.any.js', + 'encrypt_decrypt/chacha20_poly1305.tentative.https.any.js', + 'generateKey/failures_AES-KW.https.any.js', + 'generateKey/failures_Ed448.tentative.https.any.js', + 'generateKey/failures_X448.tentative.https.any.js', + 'generateKey/failures_chacha20_poly1305.tentative.https.any.js', + 'generateKey/successes_AES-KW.https.any.js', + 'generateKey/successes_Ed448.tentative.https.any.js', + 'generateKey/successes_X448.tentative.https.any.js', + 'generateKey/successes_chacha20_poly1305.tentative.https.any.js', + 'import_export/ChaCha20-Poly1305_importKey.tentative.https.any.js', + 'import_export/okp_importKey_Ed448.tentative.https.any.js', + 'import_export/okp_importKey_failures_Ed448.tentative.https.any.js', + 'import_export/okp_importKey_failures_X448.tentative.https.any.js', + 'import_export/okp_importKey_X448.tentative.https.any.js', + 'sign_verify/eddsa_curve448.tentative.https.any.js'); + + skipSubtests( + ['derive_bits_keys/hkdf.https.any.js', /AES-KW/], + ['derive_bits_keys/pbkdf2.https.any.js', /AES-KW/], + ['import_export/raw_format_aliases.tentative.https.any.js', /AES-KW/], + ['import_export/symmetric_importKey.https.any.js', /AES-KW/], + ['supports.tentative.https.any.js', /AES-KW/], + ['supports-modern.tentative.https.any.js', /ChaCha20-Poly1305/], + ['supports-modern.tentative.https.any.js', /^supports returns true for algorithm objects with valid parameters$/]); +} + function assertNoOverlap(fileSkips, subtestSkips) { const subtestSkipFiles = new Set(Object.keys(subtestSkips)); const overlap = Object.keys(fileSkips).filter((file) => subtestSkipFiles.has(file)); From d6d120c6619c3680dcdacd224c152a7e51086e3e Mon Sep 17 00:00:00 2001 From: Filip Skokan Date: Tue, 5 May 2026 16:16:48 +0200 Subject: [PATCH 2/2] tools: add boringssl to tools/nix/openssl-matrix.nix Signed-off-by: Filip Skokan --- tools/dep_updaters/update-nixpkgs-pin.sh | 12 ++++++------ tools/nix/openssl-matrix.nix | 1 + 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/tools/dep_updaters/update-nixpkgs-pin.sh b/tools/dep_updaters/update-nixpkgs-pin.sh index 97bcd878181c7b..eb5fde1526ab0c 100755 --- a/tools/dep_updaters/update-nixpkgs-pin.sh +++ b/tools/dep_updaters/update-nixpkgs-pin.sh @@ -29,16 +29,16 @@ mv "$TMP_FILE" "$NIXPKGS_PIN_FILE" nix-instantiate -I "nixpkgs=$NIXPKGS_PIN_FILE" --eval --strict --json -E " let pkgs = import {}; + opensslAttrs = builtins.filter + (n: builtins.match \"openssl_[0-9]+(_[0-9]+)?\" n != null) + (builtins.attrNames pkgs); + extraMatrixAttrs = [ \"boringssl\" ]; attrs = builtins.filter (n: let t = builtins.tryEval pkgs.\${n}; in t.success && (builtins.tryEval t.value.version).success ) - ( - builtins.filter - (n: builtins.match \"openssl_[0-9]+(_[0-9]+)?\" n != null) - (builtins.attrNames pkgs) - ); + (opensslAttrs ++ extraMatrixAttrs); in { inherit attrs; @@ -54,7 +54,7 @@ nix-instantiate -I "nixpkgs=$NIXPKGS_PIN_FILE" --eval --strict --json -E " { inherit (pkgs) - \(.attrs | join("\n ")) + \(.attrs | sort | join("\n ")) ; }"' > "$OPENSSL_MATRIX_FILE" diff --git a/tools/nix/openssl-matrix.nix b/tools/nix/openssl-matrix.nix index 3f9476acd7f7e0..36978c5d4efcb0 100644 --- a/tools/nix/openssl-matrix.nix +++ b/tools/nix/openssl-matrix.nix @@ -6,6 +6,7 @@ { inherit (pkgs) + boringssl openssl_1_1 openssl_3 openssl_3_5