Skip to content

Commit e107f3c

Browse files
panvanodejs-github-bot
authored andcommitted
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 <panva.ip@gmail.com> PR-URL: #63206 Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
1 parent a349018 commit e107f3c

8 files changed

Lines changed: 155 additions & 2 deletions

File tree

deps/ncrypto/ncrypto.cc

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@
77
#include <openssl/pkcs12.h>
88
#include <openssl/rand.h>
99
#include <openssl/x509v3.h>
10+
#if NCRYPTO_USE_BORINGSSL_EVP_DO_ALL_FALLBACK
11+
#include <openssl/bytestring.h>
12+
#include <openssl/cipher.h>
13+
#include <openssl/pem.h>
14+
#endif
1015
#include <algorithm>
1116
#include <array>
1217
#include <cstring>
@@ -67,6 +72,28 @@ using NetscapeSPKIPointer = DeleteFnPtr<NETSCAPE_SPKI, NETSCAPE_SPKI_free>;
6772

6873
static constexpr int kX509NameFlagsRFC2253WithinUtf8JSON =
6974
XN_FLAG_RFC2253 & ~ASN1_STRFLGS_ESC_MSB & ~ASN1_STRFLGS_ESC_CTRL;
75+
76+
#if NCRYPTO_USE_BORINGSSL_EVP_DO_ALL_FALLBACK
77+
struct BoringSSLCipher {
78+
const EVP_CIPHER* (*get)();
79+
const char* name;
80+
};
81+
82+
constexpr BoringSSLCipher kBoringSSLCiphers[] = {
83+
{EVP_aes_128_cbc, "aes-128-cbc"}, {EVP_aes_128_ctr, "aes-128-ctr"},
84+
{EVP_aes_128_ecb, "aes-128-ecb"}, {EVP_aes_128_gcm, "aes-128-gcm"},
85+
{EVP_aes_128_ofb, "aes-128-ofb"}, {EVP_aes_192_cbc, "aes-192-cbc"},
86+
{EVP_aes_192_ctr, "aes-192-ctr"}, {EVP_aes_192_ecb, "aes-192-ecb"},
87+
{EVP_aes_192_gcm, "aes-192-gcm"}, {EVP_aes_192_ofb, "aes-192-ofb"},
88+
{EVP_aes_256_cbc, "aes-256-cbc"}, {EVP_aes_256_ctr, "aes-256-ctr"},
89+
{EVP_aes_256_ecb, "aes-256-ecb"}, {EVP_aes_256_gcm, "aes-256-gcm"},
90+
{EVP_aes_256_ofb, "aes-256-ofb"}, {EVP_des_cbc, "des-cbc"},
91+
{EVP_des_ecb, "des-ecb"}, {EVP_des_ede, "des-ede"},
92+
{EVP_des_ede3_cbc, "des-ede3-cbc"}, {EVP_des_ede_cbc, "des-ede-cbc"},
93+
{EVP_rc2_cbc, "rc2-cbc"}, {EVP_rc4, "rc4"},
94+
};
95+
96+
#endif
7097
} // namespace
7198

7299
// ============================================================================
@@ -4209,6 +4236,12 @@ void Cipher::ForEach(Cipher::CipherNameCallback callback) {
42094236
CipherCallbackContext context;
42104237
context.cb = std::move(callback);
42114238

4239+
#if NCRYPTO_USE_BORINGSSL_EVP_DO_ALL_FALLBACK
4240+
for (const auto& cipher : kBoringSSLCiphers) {
4241+
static_cast<void>(cipher.get);
4242+
context.cb(cipher.name);
4243+
}
4244+
#else
42124245
EVP_CIPHER_do_all_sorted(
42134246
#if OPENSSL_VERSION_MAJOR >= 3
42144247
array_push_back<EVP_CIPHER,
@@ -4220,6 +4253,7 @@ void Cipher::ForEach(Cipher::CipherNameCallback callback) {
42204253
array_push_back<EVP_CIPHER>,
42214254
#endif
42224255
&context);
4256+
#endif
42234257
}
42244258

42254259
// ============================================================================

deps/ncrypto/ncrypto.gyp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{
22
'variables': {
3+
'ncrypto_bssl_libdecrepit_missing%': 1,
34
'ncrypto_sources': [
45
'engine.cc',
56
'ncrypto.cc',
@@ -11,8 +12,14 @@
1112
'target_name': 'ncrypto',
1213
'type': 'static_library',
1314
'include_dirs': ['.'],
15+
'defines': [
16+
'NCRYPTO_BSSL_LIBDECREPIT_MISSING=<(ncrypto_bssl_libdecrepit_missing)',
17+
],
1418
'direct_dependent_settings': {
1519
'include_dirs': ['.'],
20+
'defines': [
21+
'NCRYPTO_BSSL_LIBDECREPIT_MISSING=<(ncrypto_bssl_libdecrepit_missing)',
22+
],
1623
},
1724
'sources': [ '<@(ncrypto_sources)' ],
1825
'conditions': [

deps/ncrypto/ncrypto.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,24 @@
2222
#ifndef OPENSSL_NO_ENGINE
2323
#include <openssl/engine.h>
2424
#endif // !OPENSSL_NO_ENGINE
25+
26+
#ifndef OPENSSL_VERSION_PREREQ
27+
#define OPENSSL_VERSION_PREREQ(maj, min) \
28+
(OPENSSL_VERSION_NUMBER >= (((maj) << 28) | ((min) << 20)))
29+
#endif
30+
31+
// BoringSSL declares the EVP_*_do_all* APIs, but their implementation may
32+
// live in libdecrepit. This matches standalone ncrypto's build flag.
33+
#ifndef NCRYPTO_BSSL_LIBDECREPIT_MISSING
34+
#define NCRYPTO_BSSL_LIBDECREPIT_MISSING 0
35+
#endif
36+
37+
#if defined(OPENSSL_IS_BORINGSSL) && NCRYPTO_BSSL_LIBDECREPIT_MISSING
38+
#define NCRYPTO_USE_BORINGSSL_EVP_DO_ALL_FALLBACK 1
39+
#else
40+
#define NCRYPTO_USE_BORINGSSL_EVP_DO_ALL_FALLBACK 0
41+
#endif
42+
2543
// The FIPS-related functions are only available
2644
// when the OpenSSL itself was compiled with FIPS support.
2745
#if defined(OPENSSL_FIPS) && OPENSSL_VERSION_MAJOR < 3

src/crypto/crypto_hash.cc

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
#include "threadpoolwork-inl.h"
88
#include "v8.h"
99

10+
#if NCRYPTO_USE_BORINGSSL_EVP_DO_ALL_FALLBACK
11+
#include <openssl/digest.h>
12+
#endif
13+
1014
#include <cstdio>
1115

1216
namespace node {
@@ -41,6 +45,24 @@ void Hash::MemoryInfo(MemoryTracker* tracker) const {
4145
tracker->TrackFieldWithSize("md", digest_ ? md_len_ : 0);
4246
}
4347

48+
#if NCRYPTO_USE_BORINGSSL_EVP_DO_ALL_FALLBACK
49+
struct BoringSSLDigest {
50+
const EVP_MD* (*get)();
51+
const char* name;
52+
};
53+
54+
constexpr BoringSSLDigest kBoringSSLDigests[] = {
55+
{EVP_md4, "md4"},
56+
{EVP_md5, "md5"},
57+
{EVP_sha1, "sha1"},
58+
{EVP_sha224, "sha224"},
59+
{EVP_sha256, "sha256"},
60+
{EVP_sha384, "sha384"},
61+
{EVP_sha512, "sha512"},
62+
{EVP_sha512_256, "sha512-256"},
63+
};
64+
#endif
65+
4466
#if OPENSSL_VERSION_MAJOR >= 3
4567
void PushAliases(const char* name, void* data) {
4668
static_cast<std::vector<std::string>*>(data)->push_back(name);
@@ -122,7 +144,12 @@ void SaveSupportedHashAlgorithms(const EVP_MD* md,
122144
const std::vector<std::string>& GetSupportedHashAlgorithms(Environment* env) {
123145
if (env->supported_hash_algorithms.empty()) {
124146
MarkPopErrorOnReturn mark_pop_error_on_return;
125-
#if OPENSSL_VERSION_MAJOR >= 3
147+
#if NCRYPTO_USE_BORINGSSL_EVP_DO_ALL_FALLBACK
148+
for (const auto& digest : kBoringSSLDigests) {
149+
static_cast<void>(digest.get);
150+
env->supported_hash_algorithms.emplace_back(digest.name);
151+
}
152+
#elif OPENSSL_VERSION_MAJOR >= 3
126153
// Since we'll fetch the EVP_MD*, cache them along the way to speed up
127154
// later lookups instead of throwing them away immediately.
128155
EVP_MD_do_all_sorted(SaveSupportedHashAlgorithmsAndCacheMD, env);
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
if (!common.hasCrypto)
5+
common.skip('missing crypto');
6+
7+
if (!process.features.openssl_is_boringssl)
8+
common.skip('BoringSSL-only test');
9+
10+
const assert = require('assert');
11+
const { getCiphers, getHashes } = require('crypto');
12+
13+
const ciphers = getCiphers();
14+
[
15+
'aes-128-cbc',
16+
'aes-256-gcm',
17+
'des-ede',
18+
'des-ede-cbc',
19+
'des-ede3-cbc',
20+
'rc2-cbc',
21+
'rc4',
22+
].forEach((cipher) => assert(ciphers.includes(cipher), cipher));
23+
24+
const hashes = getHashes();
25+
[
26+
'md4',
27+
'md5',
28+
'sha1',
29+
'sha256',
30+
'sha512-256',
31+
].forEach((hash) => assert(hashes.includes(hash), hash));

test/parallel/test-crypto-key-objects-raw.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ const { hasOpenSSL } = require('../common/crypto');
7676
common.printSkipMessage('Skipping unsupported ed448/x448 test cases');
7777
}
7878

79-
if (hasOpenSSL(3, 5) || process.features.openssl_is_boringssl) {
79+
if (hasOpenSSL(3, 5)) {
8080
rawPublicKeys.push(
8181
['ml-dsa-44', 'ml_dsa_44_public.pem'],
8282
['ml-kem-768', 'ml_kem_768_public.pem'],

test/parallel/test-crypto-pqc-key-objects-ml-kem.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ const common = require('../common');
44
if (!common.hasCrypto)
55
common.skip('missing crypto');
66

7+
if (process.features.openssl_is_boringssl) {
8+
common.skip('Skipping unsupported ML-KEM key tests');
9+
}
10+
711
const { hasOpenSSL } = require('../common/crypto');
812

913
const assert = require('assert');

test/wpt/status/WebCryptoAPI.cjs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,38 @@ if (!hasOpenSSL(3, 5)) {
6161
['supports-modern.tentative.https.any.js', /ml-(?:kem|dsa)/i]);
6262
}
6363

64+
if (process.features.openssl_is_boringssl) {
65+
skip(
66+
'derive_bits_keys/cfrg_curves_bits_curve448.tentative.https.any.js',
67+
'derive_bits_keys/cfrg_curves_keys_curve448.tentative.https.any.js',
68+
'digest/cshake.tentative.https.any.js',
69+
'digest/sha3.tentative.https.any.js',
70+
'encrypt_decrypt/chacha20_poly1305.tentative.https.any.js',
71+
'generateKey/failures_AES-KW.https.any.js',
72+
'generateKey/failures_Ed448.tentative.https.any.js',
73+
'generateKey/failures_X448.tentative.https.any.js',
74+
'generateKey/failures_chacha20_poly1305.tentative.https.any.js',
75+
'generateKey/successes_AES-KW.https.any.js',
76+
'generateKey/successes_Ed448.tentative.https.any.js',
77+
'generateKey/successes_X448.tentative.https.any.js',
78+
'generateKey/successes_chacha20_poly1305.tentative.https.any.js',
79+
'import_export/ChaCha20-Poly1305_importKey.tentative.https.any.js',
80+
'import_export/okp_importKey_Ed448.tentative.https.any.js',
81+
'import_export/okp_importKey_failures_Ed448.tentative.https.any.js',
82+
'import_export/okp_importKey_failures_X448.tentative.https.any.js',
83+
'import_export/okp_importKey_X448.tentative.https.any.js',
84+
'sign_verify/eddsa_curve448.tentative.https.any.js');
85+
86+
skipSubtests(
87+
['derive_bits_keys/hkdf.https.any.js', /AES-KW/],
88+
['derive_bits_keys/pbkdf2.https.any.js', /AES-KW/],
89+
['import_export/raw_format_aliases.tentative.https.any.js', /AES-KW/],
90+
['import_export/symmetric_importKey.https.any.js', /AES-KW/],
91+
['supports.tentative.https.any.js', /AES-KW/],
92+
['supports-modern.tentative.https.any.js', /ChaCha20-Poly1305/],
93+
['supports-modern.tentative.https.any.js', /^supports returns true for algorithm objects with valid parameters$/]);
94+
}
95+
6496
function assertNoOverlap(fileSkips, subtestSkips) {
6597
const subtestSkipFiles = new Set(Object.keys(subtestSkips));
6698
const overlap = Object.keys(fileSkips).filter((file) => subtestSkipFiles.has(file));

0 commit comments

Comments
 (0)