Skip to content

Commit ef00769

Browse files
committed
fixup! crypto: split OpenSSL 3, BoringSSL, and legacy backends
1 parent 50789f4 commit ef00769

10 files changed

Lines changed: 78 additions & 29 deletions

File tree

deps/ncrypto/engine.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
#include "ncrypto.h"
22

33
#if !defined(OPENSSL_NO_ENGINE) && \
4-
(NCRYPTO_ENGINE_COMPAT || NCRYPTO_USE_LEGACY_OPENSSL)
4+
((defined(NCRYPTO_ENGINE_COMPAT) && NCRYPTO_ENGINE_COMPAT) || \
5+
NCRYPTO_USE_LEGACY_OPENSSL)
56
#include <openssl/engine.h>
67
#endif
78

deps/ncrypto/ncrypto.cc

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1837,6 +1837,8 @@ bool GenerateDhPrivateKey(BignumPointer* out,
18371837
return true;
18381838
}
18391839

1840+
// Recompute DH public keys locally when a private key already exists. Provider
1841+
// keygen creates a fresh keypair, which is both slower and changes semantics.
18401842
bool GenerateDhPublicKey(BignumPointer* out,
18411843
const BIGNUM* p,
18421844
const BIGNUM* g,
@@ -1860,6 +1862,8 @@ std::optional<int> CheckDhParams(const BIGNUM* p,
18601862
const BIGNUM* g,
18611863
const BIGNUM* q,
18621864
const BIGNUM* j) {
1865+
// TODO(panva): In a semver-major, consider tightening OpenSSL 3 validation
1866+
// to report generator and q failures as strictly as legacy DH_check().
18631867
if (p == nullptr || g == nullptr) return std::nullopt;
18641868

18651869
const int p_bits = BN_num_bits(p);
@@ -2119,6 +2123,8 @@ DHPointer::CheckResult DHPointer::check() {
21192123
ClearErrorOnReturn clearErrorOnReturn;
21202124
if (!*this) return DHPointer::CheckResult::NONE;
21212125
#if NCRYPTO_USE_OPENSSL3_PROVIDER
2126+
// TODO(panva): In a semver-major, consider validating named DH groups
2127+
// through the provider instead of preserving the historical verifyError.
21222128
if (group_name_ != nullptr) return CheckResult::NONE;
21232129

21242130
DeleteFnPtr<BIGNUM, BN_free> p;
@@ -2564,12 +2570,8 @@ DataPointer DHPointer::stateless(const EVPKeyPointer& ourKey,
25642570
if (!ctx || EVP_PKEY_derive_init(ctx.get()) <= 0) {
25652571
return {};
25662572
}
2567-
#if NCRYPTO_USE_OPENSSL3_PROVIDER
2568-
if (ourKey.id() == EVP_PKEY_DH &&
2569-
EVP_PKEY_CTX_set_dh_pad(ctx.get(), 1) <= 0) {
2570-
return {};
2571-
}
2572-
#endif
2573+
// TODO(panva): In a semver-major, consider padding OpenSSL 3 DH derivation
2574+
// results here to match DiffieHellman::computeSecret().
25732575
if (EVP_PKEY_derive_set_peer(ctx.get(), theirKey.get()) <= 0 ||
25742576
EVP_PKEY_derive(ctx.get(), nullptr, &out_size) <= 0) {
25752577
return {};
@@ -5651,11 +5653,17 @@ bool ReadRsaPssParams(const EVP_PKEY* pkey, Rsa::PssParams* params) {
56515653
item, item_len, 0x02, &int_header, &int_len, &int_total)) {
56525654
return false;
56535655
}
5654-
int64_t salt_length = 0;
5656+
// TODO(panva): In a semver-major, reject malformed RSA-PSS parameters
5657+
// at key import instead of omitting asymmetricKeyDetails fields.
5658+
if (int_len == 0 || int_len > sizeof(uint64_t) ||
5659+
(item[int_header] & 0x80) != 0) {
5660+
return false;
5661+
}
5662+
uint64_t salt_length = 0;
56555663
for (size_t n = 0; n < int_len; n++) {
56565664
salt_length = (salt_length << 8) | item[int_header + n];
56575665
}
5658-
params->salt_length = salt_length;
5666+
params->salt_length = static_cast<int64_t>(salt_length);
56595667
break;
56605668
}
56615669
}

deps/ncrypto/ncrypto.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919
#include <optional>
2020
#include <string>
2121
#include <string_view>
22-
#if NCRYPTO_ENGINE_COMPAT && !defined(OPENSSL_NO_ENGINE)
22+
#if defined(NCRYPTO_ENGINE_COMPAT) && NCRYPTO_ENGINE_COMPAT && \
23+
!defined(OPENSSL_NO_ENGINE)
2324
#include <openssl/engine.h>
2425
#endif // NCRYPTO_ENGINE_COMPAT && !OPENSSL_NO_ENGINE
2526

deps/ncrypto/unofficial.gni

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,11 @@ template("ncrypto_gn_build") {
2626
source_set(target_name) {
2727
forward_variables_from(invoker, "*")
2828
public_configs = [ ":ncrypto_config" ]
29-
sources = gypi_values.ncrypto_sources
29+
defines = [
30+
"NCRYPTO_ENGINE_COMPAT=1",
31+
"OPENSSL_SUPPRESS_DEPRECATED",
32+
]
33+
sources = gypi_values.ncrypto_sources + gypi_values.ncrypto_engine_sources
3034
deps = [ "$node_openssl_path" ]
3135
}
3236
}

src/crypto/crypto_common.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,11 +232,14 @@ MaybeLocal<Object> GetEphemeralKey(Environment* env, const SSLPointer& ssl) {
232232
const char* curve_name;
233233
if (kid == EVP_PKEY_EC) {
234234
ECKeyPointer ec(key);
235+
if (!ec) break;
235236
int nid = EC_GROUP_get_curve_name(ec.getGroup());
237+
if (nid == NID_undef) break;
236238
curve_name = OBJ_nid2sn(nid);
237239
} else {
238240
curve_name = OBJ_nid2sn(kid);
239241
}
242+
if (curve_name == nullptr) break;
240243
values[0] = env->ecdh_string();
241244
values[1] = OneByteString(env->isolate(), curve_name);
242245
values[2] = Integer::New(env->isolate(), key.bits());

src/crypto/crypto_context.cc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2023,14 +2023,17 @@ void SecureContext::SetDHParam(const FunctionCallbackInfo<Value>& args) {
20232023
return;
20242024

20252025
#if NCRYPTO_USE_OPENSSL3_PROVIDER
2026-
dh.reset(PEM_read_bio_Parameters(bio.get(), nullptr));
2026+
EVPKeyPointer params(PEM_read_bio_Parameters(bio.get(), nullptr));
2027+
if (params && params.id() == EVP_PKEY_DH) dh.reset(params.release());
20272028
#else
20282029
dh.reset(PEM_read_bio_DHparams(bio.get(), nullptr, nullptr, nullptr));
20292030
#endif
20302031
}
20312032

20322033
// Invalid dhparam is silently discarded and DHE is no longer used.
20332034
// TODO(tniessen): don't silently discard invalid dhparam.
2035+
// TODO(panva): In a semver-major, reject non-DH parameter PEMs instead of
2036+
// silently treating them as absent.
20342037
if (!dh)
20352038
return;
20362039

src/crypto/crypto_ec.cc

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -471,7 +471,10 @@ bool ExportJWKEcKey(Environment* env,
471471
CHECK_EQ(m_pkey.id(), EVP_PKEY_EC);
472472

473473
ECKeyPointer ec(m_pkey);
474-
CHECK(ec);
474+
if (!ec) {
475+
THROW_ERR_CRYPTO_INVALID_JWK(env, "Invalid JWK EC key");
476+
return false;
477+
}
475478

476479
const auto pub = ec.getPublicKey();
477480
const auto group = ec.getGroup();
@@ -753,10 +756,11 @@ bool GetEcKeyDetail(Environment* env,
753756
CHECK_EQ(m_pkey.id(), EVP_PKEY_EC);
754757

755758
ECKeyPointer ec(m_pkey);
756-
CHECK(ec);
759+
if (!ec) return true;
757760

758761
const auto group = ec.getGroup();
759762
int nid = EC_GROUP_get_curve_name(group);
763+
if (nid == NID_undef) return true;
760764

761765
return target
762766
->Set(env->context(),
@@ -772,10 +776,11 @@ bool GetEcKeyDetail(Environment* env,
772776

773777
size_t GroupOrderSize(const EVPKeyPointer& key) {
774778
ECKeyPointer ec(key);
775-
CHECK(ec);
779+
if (!ec) return 0;
776780
auto order = BignumPointer::New();
777-
CHECK(order);
778-
CHECK(EC_GROUP_get_order(ec.getGroup(), order.get(), nullptr));
781+
if (!order || !EC_GROUP_get_order(ec.getGroup(), order.get(), nullptr)) {
782+
return 0;
783+
}
779784
return order.byteLength();
780785
}
781786
} // namespace crypto

src/crypto/crypto_keys.cc

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,10 @@ bool KeyObjectData::ToEncodedPublicKey(
386386
const auto& pkey = GetAsymmetricKey();
387387
if (pkey.id() == EVP_PKEY_EC) {
388388
ECKeyPointer ec_key(pkey);
389-
CHECK(ec_key);
389+
if (!ec_key) {
390+
THROW_ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS(env);
391+
return false;
392+
}
390393
auto form = static_cast<point_conversion_form_t>(config.ec_point_form);
391394
const auto group = ec_key.getGroup();
392395
const auto point = ec_key.getPublicKey();
@@ -432,13 +435,22 @@ bool KeyObjectData::ToEncodedPrivateKey(
432435
const auto& pkey = GetAsymmetricKey();
433436
if (pkey.id() == EVP_PKEY_EC) {
434437
ECKeyPointer ec_key(pkey);
435-
CHECK(ec_key);
438+
if (!ec_key) {
439+
THROW_ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS(env);
440+
return false;
441+
}
436442
const BIGNUM* private_key = ec_key.getPrivateKey();
437-
CHECK_NOT_NULL(private_key);
443+
if (private_key == nullptr) {
444+
THROW_ERR_CRYPTO_OPERATION_FAILED(env, "Failed to get EC private key");
445+
return false;
446+
}
438447
const auto group = ec_key.getGroup();
439448
auto order = BignumPointer::New();
440-
CHECK(order);
441-
CHECK(EC_GROUP_get_order(group, order.get(), nullptr));
449+
if (!order || !EC_GROUP_get_order(group, order.get(), nullptr)) {
450+
THROW_ERR_CRYPTO_OPERATION_FAILED(env,
451+
"Failed to export EC private key");
452+
return false;
453+
}
442454
auto buf = BignumPointer::EncodePadded(private_key, order.byteLength());
443455
if (!buf) {
444456
THROW_ERR_CRYPTO_OPERATION_FAILED(env,
@@ -1463,7 +1475,9 @@ void KeyObjectHandle::ExportECPublicRaw(
14631475
}
14641476

14651477
ECKeyPointer ec_key(m_pkey);
1466-
CHECK(ec_key);
1478+
if (!ec_key) {
1479+
return THROW_ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS(env);
1480+
}
14671481

14681482
CHECK(args[0]->IsInt32());
14691483
auto form =
@@ -1494,15 +1508,22 @@ void KeyObjectHandle::ExportECPrivateRaw(
14941508
}
14951509

14961510
ECKeyPointer ec_key(m_pkey);
1497-
CHECK(ec_key);
1511+
if (!ec_key) {
1512+
return THROW_ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS(env);
1513+
}
14981514

14991515
const BIGNUM* private_key = ec_key.getPrivateKey();
1500-
CHECK_NOT_NULL(private_key);
1516+
if (private_key == nullptr) {
1517+
return THROW_ERR_CRYPTO_OPERATION_FAILED(env,
1518+
"Failed to get EC private key");
1519+
}
15011520

15021521
const auto group = ec_key.getGroup();
15031522
auto order = BignumPointer::New();
1504-
CHECK(order);
1505-
CHECK(EC_GROUP_get_order(group, order.get(), nullptr));
1523+
if (!order || !EC_GROUP_get_order(group, order.get(), nullptr)) {
1524+
return THROW_ERR_CRYPTO_OPERATION_FAILED(env,
1525+
"Failed to export EC private key");
1526+
}
15061527

15071528
auto buf = BignumPointer::EncodePadded(private_key, order.byteLength());
15081529
if (!buf) {

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ if (!common.hasCrypto)
55

66
const assert = require('assert');
77
const crypto = require('crypto');
8-
const { hasOpenSSL3 } = require('../common/crypto');
8+
const { hasOpenSSL } = require('../common/crypto');
99

1010
// Second OAKLEY group, see
1111
// https://github.com/nodejs/node-v0.x-archive/issues/2338 and
@@ -22,7 +22,7 @@ const bad_dh = process.features.openssl_is_boringssl ?
2222
crypto.createDiffieHellman('02', 'hex');
2323
assert.notStrictEqual(bad_dh.verifyError, 0);
2424

25-
if (hasOpenSSL3) {
25+
if (hasOpenSSL(3)) {
2626
const smallSafePrime = crypto.createDiffieHellman(
2727
Buffer.from([23]), Buffer.from([2]));
2828
assert.notStrictEqual(smallSafePrime.verifyError, 0);

test/parallel/test-tls-connect-secure-context.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ connect({
2323
return cleanup();
2424
}));
2525

26+
// Invalid dhparam input is silently discarded for compatibility.
27+
tls.createSecureContext({ dhparam: fixtures.readKey('ec-key.pem') });
28+
2629
connect({
2730
client: {
2831
servername: 'agent1',

0 commit comments

Comments
 (0)