Skip to content

Commit 30c62de

Browse files
danbevdanielleadams
authored andcommitted
src,test: support dynamically linking OpenSSL 3.0
This commit enables node to dynamically link against OpenSSL 3.0. The motivation for opening this PR even though OpenSSL 3.0 has not been released yet is to allow a nightly CI job to be created. This will allow us stay on top of changes required for OpenSSL 3.0, and also to make sure that changes to node crypto do not cause issues when linking to OpenSSL 3.0. PR-URL: #37669 Refs: #29817 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Michael Dawson <midawson@redhat.com>
1 parent bd62771 commit 30c62de

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+513
-297
lines changed

node.gypi

+5-1
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,11 @@
361361
],
362362
}],
363363
],
364-
}]]
364+
}, {
365+
# Set 1.0.0 as the API compability level to avoid the
366+
# deprecation warnings when using OpenSSL 3.0.
367+
'defines': ['OPENSSL_API_COMPAT=0x10000000L'],
368+
}]]
365369

366370
}, {
367371
'defines': [ 'HAVE_OPENSSL=0' ]

src/crypto/crypto_cipher.cc

+10-1
Original file line numberDiff line numberDiff line change
@@ -342,8 +342,11 @@ void CipherBase::Init(const char* cipher_type,
342342
unsigned int auth_tag_len) {
343343
HandleScope scope(env()->isolate());
344344
MarkPopErrorOnReturn mark_pop_error_on_return;
345-
345+
#if OPENSSL_VERSION_MAJOR >= 3
346+
if (EVP_default_properties_is_fips_enabled(nullptr)) {
347+
#else
346348
if (FIPS_mode()) {
349+
#endif
347350
return THROW_ERR_CRYPTO_UNSUPPORTED_OPERATION(env(),
348351
"crypto.createCipher() is not supported in FIPS mode.");
349352
}
@@ -527,7 +530,13 @@ bool CipherBase::InitAuthenticated(
527530
}
528531

529532
// TODO(tniessen) Support CCM decryption in FIPS mode
533+
534+
#if OPENSSL_VERSION_MAJOR >= 3
535+
if (mode == EVP_CIPH_CCM_MODE && kind_ == kDecipher &&
536+
EVP_default_properties_is_fips_enabled(nullptr)) {
537+
#else
530538
if (mode == EVP_CIPH_CCM_MODE && kind_ == kDecipher && FIPS_mode()) {
539+
#endif
531540
THROW_ERR_CRYPTO_UNSUPPORTED_OPERATION(env(),
532541
"CCM encryption not supported in FIPS mode");
533542
return false;

src/crypto/crypto_dsa.cc

+1-1
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ Maybe<bool> GetDsaKeyDetail(
138138
int type = EVP_PKEY_id(m_pkey.get());
139139
CHECK(type == EVP_PKEY_DSA);
140140

141-
DSA* dsa = EVP_PKEY_get0_DSA(m_pkey.get());
141+
const DSA* dsa = EVP_PKEY_get0_DSA(m_pkey.get());
142142
CHECK_NOT_NULL(dsa);
143143

144144
DSA_get0_pqg(dsa, &p, &q, nullptr);

src/crypto/crypto_ec.cc

+24-16
Original file line numberDiff line numberDiff line change
@@ -463,19 +463,22 @@ bool ECDHBitsTraits::DeriveBits(
463463

464464
char* data = nullptr;
465465
size_t len = 0;
466+
ManagedEVPPKey m_privkey = params.private_->GetAsymmetricKey();
467+
ManagedEVPPKey m_pubkey = params.public_->GetAsymmetricKey();
466468

467469
switch (params.id_) {
468470
case EVP_PKEY_X25519:
469471
// Fall through
470472
case EVP_PKEY_X448: {
471-
EVPKeyCtxPointer ctx(
472-
EVP_PKEY_CTX_new(
473-
params.private_->GetAsymmetricKey().get(),
474-
nullptr));
473+
EVPKeyCtxPointer ctx = nullptr;
474+
{
475+
ctx.reset(EVP_PKEY_CTX_new(m_privkey.get(), nullptr));
476+
}
477+
Mutex::ScopedLock pub_lock(*m_pubkey.mutex());
475478
if (EVP_PKEY_derive_init(ctx.get()) <= 0 ||
476479
EVP_PKEY_derive_set_peer(
477480
ctx.get(),
478-
params.public_->GetAsymmetricKey().get()) <= 0 ||
481+
m_pubkey.get()) <= 0 ||
479482
EVP_PKEY_derive(ctx.get(), nullptr, &len) <= 0) {
480483
return false;
481484
}
@@ -492,10 +495,14 @@ bool ECDHBitsTraits::DeriveBits(
492495
break;
493496
}
494497
default: {
495-
const EC_KEY* private_key =
496-
EVP_PKEY_get0_EC_KEY(params.private_->GetAsymmetricKey().get());
497-
const EC_KEY* public_key =
498-
EVP_PKEY_get0_EC_KEY(params.public_->GetAsymmetricKey().get());
498+
const EC_KEY* private_key;
499+
{
500+
Mutex::ScopedLock priv_lock(*m_privkey.mutex());
501+
private_key = EVP_PKEY_get0_EC_KEY(m_privkey.get());
502+
}
503+
504+
Mutex::ScopedLock pub_lock(*m_pubkey.mutex());
505+
const EC_KEY* public_key = EVP_PKEY_get0_EC_KEY(m_pubkey.get());
499506

500507
const EC_GROUP* group = EC_KEY_get0_group(private_key);
501508
if (group == nullptr)
@@ -607,7 +614,7 @@ WebCryptoKeyExportStatus EC_Raw_Export(
607614
CHECK(m_pkey);
608615
Mutex::ScopedLock lock(*m_pkey.mutex());
609616

610-
EC_KEY* ec_key = EVP_PKEY_get0_EC_KEY(m_pkey.get());
617+
const EC_KEY* ec_key = EVP_PKEY_get0_EC_KEY(m_pkey.get());
611618

612619
unsigned char* data;
613620
size_t len = 0;
@@ -627,10 +634,10 @@ WebCryptoKeyExportStatus EC_Raw_Export(
627634
}
628635
CHECK_NOT_NULL(fn);
629636
// Get the size of the raw key data
630-
if (fn(key_data->GetAsymmetricKey().get(), nullptr, &len) == 0)
637+
if (fn(m_pkey.get(), nullptr, &len) == 0)
631638
return WebCryptoKeyExportStatus::INVALID_KEY_TYPE;
632639
data = MallocOpenSSL<unsigned char>(len);
633-
if (fn(key_data->GetAsymmetricKey().get(), data, &len) == 0)
640+
if (fn(m_pkey.get(), data, &len) == 0)
634641
return WebCryptoKeyExportStatus::INVALID_KEY_TYPE;
635642
} else {
636643
if (key_data->GetKeyType() != kKeyTypePublic)
@@ -696,7 +703,7 @@ Maybe<bool> ExportJWKEcKey(
696703
Mutex::ScopedLock lock(*m_pkey.mutex());
697704
CHECK_EQ(EVP_PKEY_id(m_pkey.get()), EVP_PKEY_EC);
698705

699-
EC_KEY* ec = EVP_PKEY_get0_EC_KEY(m_pkey.get());
706+
const EC_KEY* ec = EVP_PKEY_get0_EC_KEY(m_pkey.get());
700707
CHECK_NOT_NULL(ec);
701708

702709
const EC_POINT* pub = EC_KEY_get0_public_key(ec);
@@ -751,6 +758,7 @@ Maybe<bool> ExportJWKEdKey(
751758
std::shared_ptr<KeyObjectData> key,
752759
Local<Object> target) {
753760
ManagedEVPPKey pkey = key->GetAsymmetricKey();
761+
Mutex::ScopedLock lock(*pkey.mutex());
754762

755763
const char* curve = nullptr;
756764
switch (EVP_PKEY_id(pkey.get())) {
@@ -902,7 +910,7 @@ Maybe<bool> GetEcKeyDetail(
902910
Mutex::ScopedLock lock(*m_pkey.mutex());
903911
CHECK_EQ(EVP_PKEY_id(m_pkey.get()), EVP_PKEY_EC);
904912

905-
EC_KEY* ec = EVP_PKEY_get0_EC_KEY(m_pkey.get());
913+
const EC_KEY* ec = EVP_PKEY_get0_EC_KEY(m_pkey.get());
906914
CHECK_NOT_NULL(ec);
907915

908916
const EC_GROUP* group = EC_KEY_get0_group(ec);
@@ -919,8 +927,8 @@ Maybe<bool> GetEcKeyDetail(
919927
// implementation here is a adapted from Chromium's impl here:
920928
// https://github.com/chromium/chromium/blob/7af6cfd/components/webcrypto/algorithms/ecdsa.cc
921929

922-
size_t GroupOrderSize(ManagedEVPPKey key) {
923-
EC_KEY* ec = EVP_PKEY_get0_EC_KEY(key.get());
930+
size_t GroupOrderSize(const ManagedEVPPKey& key) {
931+
const EC_KEY* ec = EVP_PKEY_get0_EC_KEY(key.get());
924932
CHECK_NOT_NULL(ec);
925933
const EC_GROUP* group = EC_KEY_get0_group(ec);
926934
BignumPointer order(BN_new());

src/crypto/crypto_hkdf.cc

+3-3
Original file line numberDiff line numberDiff line change
@@ -110,15 +110,15 @@ bool HKDFTraits::DeriveBits(
110110
!EVP_PKEY_CTX_set_hkdf_md(ctx.get(), params.digest) ||
111111
!EVP_PKEY_CTX_set1_hkdf_salt(
112112
ctx.get(),
113-
params.salt.get(),
113+
reinterpret_cast<const unsigned char*>(params.salt.get()),
114114
params.salt.size()) ||
115115
!EVP_PKEY_CTX_set1_hkdf_key(
116116
ctx.get(),
117-
params.key->GetSymmetricKey(),
117+
reinterpret_cast<const unsigned char*>(params.key->GetSymmetricKey()),
118118
params.key->GetSymmetricKeySize()) ||
119119
!EVP_PKEY_CTX_add1_hkdf_info(
120120
ctx.get(),
121-
params.info.get(),
121+
reinterpret_cast<const unsigned char*>(params.info.get()),
122122
params.info.size())) {
123123
return false;
124124
}

src/crypto/crypto_keygen.h

+3
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,9 @@ struct KeyPairGenConfig final : public MemoryRetainer {
235235
AlgorithmParams params;
236236

237237
KeyPairGenConfig() = default;
238+
~KeyPairGenConfig() {
239+
Mutex::ScopedLock priv_lock(*key.mutex());
240+
}
238241

239242
explicit KeyPairGenConfig(KeyPairGenConfig&& other) noexcept
240243
: public_key_encoding(other.public_key_encoding),

src/crypto/crypto_keys.cc

+2
Original file line numberDiff line numberDiff line change
@@ -559,6 +559,8 @@ ManagedEVPPKey::ManagedEVPPKey(const ManagedEVPPKey& that) {
559559
}
560560

561561
ManagedEVPPKey& ManagedEVPPKey::operator=(const ManagedEVPPKey& that) {
562+
Mutex::ScopedLock lock(*that.mutex_);
563+
562564
pkey_.reset(that.get());
563565

564566
if (pkey_)

src/crypto/crypto_keys.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ struct PrivateKeyEncodingConfig : public AsymmetricKeyEncodingConfig {
7474
// use.
7575
class ManagedEVPPKey : public MemoryRetainer {
7676
public:
77-
ManagedEVPPKey() = default;
77+
ManagedEVPPKey() : mutex_(std::make_shared<Mutex>()) {}
7878
explicit ManagedEVPPKey(EVPKeyPointer&& pkey);
7979
ManagedEVPPKey(const ManagedEVPPKey& that);
8080
ManagedEVPPKey& operator=(const ManagedEVPPKey& that);

src/crypto/crypto_rsa.cc

+4-4
Original file line numberDiff line numberDiff line change
@@ -371,11 +371,11 @@ Maybe<bool> ExportJWKRsaKey(
371371

372372
// TODO(tniessen): Remove the "else" branch once we drop support for OpenSSL
373373
// versions older than 1.1.1e via FIPS / dynamic linking.
374-
RSA* rsa;
374+
const RSA* rsa;
375375
if (OpenSSL_version_num() >= 0x1010105fL) {
376376
rsa = EVP_PKEY_get0_RSA(m_pkey.get());
377377
} else {
378-
rsa = static_cast<RSA*>(EVP_PKEY_get0(m_pkey.get()));
378+
rsa = static_cast<const RSA*>(EVP_PKEY_get0(m_pkey.get()));
379379
}
380380
CHECK_NOT_NULL(rsa);
381381

@@ -520,11 +520,11 @@ Maybe<bool> GetRsaKeyDetail(
520520

521521
// TODO(tniessen): Remove the "else" branch once we drop support for OpenSSL
522522
// versions older than 1.1.1e via FIPS / dynamic linking.
523-
RSA* rsa;
523+
const RSA* rsa;
524524
if (OpenSSL_version_num() >= 0x1010105fL) {
525525
rsa = EVP_PKEY_get0_RSA(m_pkey.get());
526526
} else {
527-
rsa = static_cast<RSA*>(EVP_PKEY_get0(m_pkey.get()));
527+
rsa = static_cast<const RSA*>(EVP_PKEY_get0(m_pkey.get()));
528528
}
529529
CHECK_NOT_NULL(rsa);
530530

src/crypto/crypto_sig.cc

+10-8
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,13 @@ namespace crypto {
2828
namespace {
2929
bool ValidateDSAParameters(EVP_PKEY* key) {
3030
/* Validate DSA2 parameters from FIPS 186-4 */
31+
#if OPENSSL_VERSION_MAJOR >= 3
32+
if (EVP_default_properties_is_fips_enabled(nullptr) &&
33+
EVP_PKEY_DSA == EVP_PKEY_base_id(key)) {
34+
#else
3135
if (FIPS_mode() && EVP_PKEY_DSA == EVP_PKEY_base_id(key)) {
32-
DSA* dsa = EVP_PKEY_get0_DSA(key);
36+
#endif
37+
const DSA* dsa = EVP_PKEY_get0_DSA(key);
3338
const BIGNUM* p;
3439
DSA_get0_pqg(dsa, &p, nullptr, nullptr);
3540
size_t L = BN_num_bits(p);
@@ -103,11 +108,11 @@ unsigned int GetBytesOfRS(const ManagedEVPPKey& pkey) {
103108
int bits, base_id = EVP_PKEY_base_id(pkey.get());
104109

105110
if (base_id == EVP_PKEY_DSA) {
106-
DSA* dsa_key = EVP_PKEY_get0_DSA(pkey.get());
111+
const DSA* dsa_key = EVP_PKEY_get0_DSA(pkey.get());
107112
// Both r and s are computed mod q, so their width is limited by that of q.
108113
bits = BN_num_bits(DSA_get0_q(dsa_key));
109114
} else if (base_id == EVP_PKEY_EC) {
110-
EC_KEY* ec_key = EVP_PKEY_get0_EC_KEY(pkey.get());
115+
const EC_KEY* ec_key = EVP_PKEY_get0_EC_KEY(pkey.get());
111116
const EC_GROUP* ec_group = EC_KEY_get0_group(ec_key);
112117
bits = EC_GROUP_order_bits(ec_group);
113118
} else {
@@ -873,7 +878,7 @@ bool SignTraits::DeriveBits(
873878
case SignConfiguration::kSign: {
874879
size_t len;
875880
unsigned char* data = nullptr;
876-
if (IsOneShot(params.key->GetAsymmetricKey())) {
881+
if (IsOneShot(m_pkey)) {
877882
EVP_DigestSign(
878883
context.get(),
879884
nullptr,
@@ -905,10 +910,7 @@ bool SignTraits::DeriveBits(
905910
return false;
906911

907912
if (UseP1363Encoding(m_pkey, params.dsa_encoding)) {
908-
*out = ConvertSignatureToP1363(
909-
env,
910-
params.key->GetAsymmetricKey(),
911-
buf);
913+
*out = ConvertSignatureToP1363(env, m_pkey, buf);
912914
} else {
913915
buf.Resize(len);
914916
*out = std::move(buf);

src/crypto/crypto_util.cc

+18
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,12 @@ void InitCryptoOnce() {
137137
unsigned long err = 0; // NOLINT(runtime/int)
138138
if (per_process::cli_options->enable_fips_crypto ||
139139
per_process::cli_options->force_fips_crypto) {
140+
#if OPENSSL_VERSION_MAJOR >= 3
141+
if (0 == EVP_default_properties_is_fips_enabled(nullptr) &&
142+
!EVP_default_properties_enable_fips(nullptr, 1)) {
143+
#else
140144
if (0 == FIPS_mode() && !FIPS_mode_set(1)) {
145+
#endif
141146
err = ERR_get_error();
142147
}
143148
}
@@ -160,18 +165,31 @@ void InitCryptoOnce() {
160165
}
161166

162167
void GetFipsCrypto(const FunctionCallbackInfo<Value>& args) {
168+
#if OPENSSL_VERSION_MAJOR >= 3
169+
args.GetReturnValue().Set(EVP_default_properties_is_fips_enabled(nullptr) ?
170+
1 : 0);
171+
#else
163172
args.GetReturnValue().Set(FIPS_mode() ? 1 : 0);
173+
#endif
164174
}
165175

166176
void SetFipsCrypto(const FunctionCallbackInfo<Value>& args) {
167177
CHECK(!per_process::cli_options->force_fips_crypto);
168178
Environment* env = Environment::GetCurrent(args);
169179
bool enable = args[0]->BooleanValue(env->isolate());
170180

181+
#if OPENSSL_VERSION_MAJOR >= 3
182+
if (enable == EVP_default_properties_is_fips_enabled(nullptr))
183+
#else
171184
if (enable == FIPS_mode())
185+
#endif
172186
return; // No action needed.
173187

188+
#if OPENSSL_VERSION_MAJOR >= 3
189+
if (!EVP_default_properties_enable_fips(nullptr, enable)) {
190+
#else
174191
if (!FIPS_mode_set(enable)) {
192+
#endif
175193
unsigned long err = ERR_get_error(); // NOLINT(runtime/int)
176194
return ThrowCryptoError(env, err);
177195
}

src/node.cc

+4
Original file line numberDiff line numberDiff line change
@@ -1060,8 +1060,12 @@ InitializationResult InitializeOncePerProcess(int argc, char** argv) {
10601060
}
10611061
// In the case of FIPS builds we should make sure
10621062
// the random source is properly initialized first.
1063+
#if OPENSSL_VERSION_MAJOR >= 3
1064+
if (EVP_default_properties_is_fips_enabled(nullptr)) {
1065+
#else
10631066
if (FIPS_mode()) {
10641067
OPENSSL_init();
1068+
#endif
10651069
}
10661070
// V8 on Windows doesn't have a good source of entropy. Seed it from
10671071
// OpenSSL's pool.

test/common/index.js

+4
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ const noop = () => {};
5151
const hasCrypto = Boolean(process.versions.openssl) &&
5252
!process.env.NODE_SKIP_CRYPTO;
5353

54+
const hasOpenSSL3 = hasCrypto &&
55+
require('crypto').constants.OPENSSL_VERSION_NUMBER >= 805306368;
56+
5457
// Check for flags. Skip this for workers (both, the `cluster` module and
5558
// `worker_threads`) and child processes.
5659
// If the binary was built without-ssl then the crypto flags are
@@ -726,6 +729,7 @@ const common = {
726729
getTTYfd,
727730
hasIntl,
728731
hasCrypto,
732+
hasOpenSSL3,
729733
hasMultiLocalhost,
730734
invalidArgTypeHelper,
731735
isAIX,

test/parallel/test-crypto-binary-default.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -583,7 +583,8 @@ assert.throws(
583583
// Test Diffie-Hellman with two parties sharing a secret,
584584
// using various encodings as we go along
585585
{
586-
const dh1 = crypto.createDiffieHellman(common.hasFipsCrypto ? 1024 : 256);
586+
const size = common.hasFipsCrypto || common.hasOpenSSL3 ? 1024 : 256;
587+
const dh1 = crypto.createDiffieHellman(size);
587588
const p1 = dh1.getPrime('buffer');
588589
const dh2 = crypto.createDiffieHellman(p1, 'base64');
589590
const key1 = dh1.generateKeys();

test/parallel/test-crypto-cipheriv-decipheriv.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,9 @@ assert.throws(
193193
errMessage);
194194

195195
// But all other IV lengths should be accepted.
196-
for (let n = 1; n < 256; n += 1) {
196+
const minIvLength = common.hasOpenSSL3 ? 8 : 1;
197+
const maxIvLength = common.hasOpenSSL3 ? 64 : 256;
198+
for (let n = minIvLength; n < maxIvLength; n += 1) {
197199
if (common.hasFipsCrypto && n < 12) continue;
198200
crypto.createCipheriv('aes-128-gcm', Buffer.alloc(16), Buffer.alloc(n));
199201
}

test/parallel/test-crypto-classes.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ const TEST_CASES = {
2424
if (!common.hasFipsCrypto) {
2525
TEST_CASES.Cipher = ['aes192', 'secret'];
2626
TEST_CASES.Decipher = ['aes192', 'secret'];
27-
TEST_CASES.DiffieHellman = [256];
27+
TEST_CASES.DiffieHellman = [common.hasOpenSSL3 ? 1024 : 256];
2828
}
2929

3030
for (const [clazz, args] of Object.entries(TEST_CASES)) {

test/parallel/test-crypto-dh-leak.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ const crypto = require('crypto');
1212

1313
const before = process.memoryUsage.rss();
1414
{
15-
const dh = crypto.createDiffieHellman(common.hasFipsCrypto ? 1024 : 256);
15+
const size = common.hasFipsCrypto || common.hasOpenSSL3 ? 1024 : 256;
16+
const dh = crypto.createDiffieHellman(size);
1617
const publicKey = dh.generateKeys();
1718
const privateKey = dh.getPrivateKey();
1819
for (let i = 0; i < 5e4; i += 1) {

0 commit comments

Comments
 (0)