Commit d20c97851eb for nodejs
commit d20c97851eb0300fd1e13930b4dcce233dabf1ec
Author: Filip Skokan <panva.ip@gmail.com>
Date: Fri Sep 18 11:43:57 2026 +0200
crypto: use provider EC group names
Pass group names through EC key generation and report provider names
in key details without requiring an OpenSSL NID. Preserve established
curve aliases and synchronous invalid-curve errors.
Filter built-in curves through EC parameter generation and refresh
getCurves() results when FIPS properties change.
Signed-off-by: Filip Skokan <panva.ip@gmail.com>
Assisted-by: Codex
PR-URL: https://github.com/nodejs/node/pull/66108
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
diff --git a/deps/ncrypto/ncrypto.cc b/deps/ncrypto/ncrypto.cc
index 44b56c1b3ef..ca1eb4653df 100644
--- a/deps/ncrypto/ncrypto.cc
+++ b/deps/ncrypto/ncrypto.cc
@@ -6408,10 +6408,15 @@ bool EVPKeyCtxPointer::setDsaParameters(uint32_t bits,
}
bool EVPKeyCtxPointer::setEcParameters(int curve, int encoding) {
- if (!ctx_) return false;
+ return setEcParameters(OBJ_nid2sn(curve), encoding);
+}
+
+bool EVPKeyCtxPointer::setEcParameters(const char* group_name, int encoding) {
+ if (!ctx_ || group_name == nullptr) return false;
+ const int curve = Ec::GetCurveIdFromName(group_name);
#if NCRYPTO_USE_OPENSSL3_PROVIDER
- const char* group_name = OBJ_nid2sn(curve);
- if (group_name == nullptr) return false;
+ // Keep the historical aliases while allowing names known only to providers.
+ if (curve != NID_undef) group_name = OBJ_nid2sn(curve);
const char* encoding_name = nullptr;
switch (encoding) {
@@ -6433,7 +6438,8 @@ bool EVPKeyCtxPointer::setEcParameters(int curve, int encoding) {
};
return EVP_PKEY_CTX_set_params(ctx_.get(), params) == 1;
#else
- return EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx_.get(), curve) == 1 &&
+ return curve != NID_undef &&
+ EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx_.get(), curve) == 1 &&
EVP_PKEY_CTX_set_ec_param_enc(ctx_.get(), encoding) == 1;
#endif
}
@@ -7617,6 +7623,57 @@ int Ec::GetCurveId(const EVPKeyPointer& key) {
#endif
}
+std::optional<std::string> Ec::GetCurveName(const EVPKeyPointer& key) {
+ if (!key) return std::nullopt;
+#if NCRYPTO_USE_OPENSSL3_PROVIDER
+ size_t length = 0;
+ if (EVP_PKEY_get_utf8_string_param(
+ key.get(), OSSL_PKEY_PARAM_GROUP_NAME, nullptr, 0, &length) != 1) {
+ return std::nullopt;
+ }
+ std::string name(length, '\0');
+ if (EVP_PKEY_get_utf8_string_param(key.get(),
+ OSSL_PKEY_PARAM_GROUP_NAME,
+ name.data(),
+ name.size() + 1,
+ &length) != 1) {
+ return std::nullopt;
+ }
+ name.resize(length);
+ // Preserve the public short names for the curves OpenSSL already knows.
+ const int nid = GetCurveIdFromName(name.c_str());
+ return nid == NID_undef ? name : std::string(OBJ_nid2sn(nid));
+#else
+ const int nid = GetCurveId(key);
+ if (nid == NID_undef) return std::nullopt;
+ return std::string(OBJ_nid2sn(nid));
+#endif
+}
+
+#if NCRYPTO_USE_OPENSSL3_PROVIDER
+namespace {
+bool IsAvailableEcGroup(const char* name) {
+ MarkPopErrorOnReturn mark;
+ auto ctx = EVPKeyCtxPointer::NewFromAlgorithm(KeyAlgorithm::EC);
+ return ctx.initForParamgen() &&
+ ctx.setEcParameters(name, OPENSSL_EC_NAMED_CURVE) && ctx.paramgen();
+}
+} // namespace
+#endif
+
+bool Ec::CheckCurveName(const char* name) {
+ if (name == nullptr) return false;
+ if (GetCurveIdFromName(name) != NID_undef) return true;
+#if NCRYPTO_USE_OPENSSL3_PROVIDER
+ // Keep invalid names a synchronous argument error. Generation contexts can
+ // defer rejecting a group until parameter generation. Use the same parameter
+ // generation path as key generation without requiring parameter import.
+ return IsAvailableEcGroup(name);
+#else
+ return false;
+#endif
+}
+
int Ec::GetCurveIdFromName(const char* name) {
int nid = EC_curve_nist2nid(name);
if (nid == NID_undef) {
@@ -7639,8 +7696,12 @@ bool Ec::GetCurves(Ec::GetCurveCallback callback) {
if (EC_get_builtin_curves(curves.data(), count) != count) {
return false;
}
- for (auto curve : curves) {
- if (!callback(OBJ_nid2sn(curve.nid))) return false;
+ for (const auto& curve : curves) {
+ const char* name = OBJ_nid2sn(curve.nid);
+#if NCRYPTO_USE_OPENSSL3_PROVIDER
+ if (!IsAvailableEcGroup(name)) continue;
+#endif
+ if (!callback(name)) return false;
}
return true;
}
diff --git a/deps/ncrypto/ncrypto.h b/deps/ncrypto/ncrypto.h
index 22948f6e85a..0ffd664a1ea 100644
--- a/deps/ncrypto/ncrypto.h
+++ b/deps/ncrypto/ncrypto.h
@@ -771,6 +771,8 @@ class Ec final {
static int GetCurveIdFromName(const char* name);
static int GetCurveId(const EVPKeyPointer& key);
+ static std::optional<std::string> GetCurveName(const EVPKeyPointer& key);
+ static bool CheckCurveName(const char* name);
static DataPointer TryExportPublic(const EVPKeyPointer& key,
point_conversion_form_t form);
static DataPointer ExportPrivate(const EVPKeyPointer& key);
@@ -1150,6 +1152,7 @@ class EVPKeyCtxPointer final {
bool setDhParameters(int prime_size, uint32_t generator);
bool setDsaParameters(uint32_t bits, std::optional<int> q_bits);
bool setEcParameters(int curve, int encoding);
+ bool setEcParameters(const char* group_name, int encoding);
bool setRsaOaepMd(const Digest& md);
bool setRsaMgf1Md(const Digest& md);
diff --git a/lib/internal/crypto/util.js b/lib/internal/crypto/util.js
index b38db7c6603..22b51f72074 100644
--- a/lib/internal/crypto/util.js
+++ b/lib/internal/crypto/util.js
@@ -80,7 +80,6 @@ const {
const { Buffer } = require('buffer');
const {
- cachedResult,
emitExperimentalWarning,
filterDuplicateStrings,
getDeprecationWarningEmitter,
@@ -193,7 +192,8 @@ const getMacs = cachedArrayByFipsGeneration(
_macCache = undefined;
});
-const getCurves = cachedResult(() => filterDuplicateStrings(_getCurves()));
+const getCurves = cachedArrayByFipsGeneration(
+ () => filterDuplicateStrings(_getCurves()));
const emitOpenSSLEngineDeprecation = getDeprecationWarningEmitter(
'DEP0183',
diff --git a/src/crypto/crypto_ec.cc b/src/crypto/crypto_ec.cc
index 80845fed281..1126d72ad5f 100644
--- a/src/crypto/crypto_ec.cc
+++ b/src/crypto/crypto_ec.cc
@@ -406,6 +406,10 @@ void ECDH::ConvertKey(const FunctionCallbackInfo<Value>& args) {
}
}
+void EcKeyPairParams::MemoryInfo(MemoryTracker* tracker) const {
+ tracker->TrackField("curve_name", curve_name);
+}
+
EVPKeyCtxPointer EcKeyGenTraits::Setup(EcKeyPairGenConfig* params) {
EVPKeyCtxPointer key_ctx;
if (params->params.algorithm != nullptr) {
@@ -413,7 +417,7 @@ EVPKeyCtxPointer EcKeyGenTraits::Setup(EcKeyPairGenConfig* params) {
} else {
auto param_ctx = EVPKeyCtxPointer::NewFromAlgorithm(KeyAlgorithm::EC);
if (!param_ctx.initForParamgen() ||
- !param_ctx.setEcParameters(params->params.curve_nid,
+ !param_ctx.setEcParameters(params->params.curve_name.c_str(),
params->params.param_encoding)) {
return {};
}
@@ -447,11 +451,11 @@ Maybe<void> EcKeyGenTraits::AdditionalConfig(
Utf8Value curve_name(env->isolate(), args[*offset]);
params->params.algorithm = ncrypto::Ec::GetNamedKeyAlgorithm(*curve_name);
if (params->params.algorithm == nullptr) {
- params->params.curve_nid = Ec::GetCurveIdFromName(*curve_name);
- if (params->params.curve_nid == NID_undef) {
+ if (!Ec::CheckCurveName(*curve_name)) {
THROW_ERR_CRYPTO_INVALID_CURVE(env);
return Nothing<void>();
}
+ params->params.curve_name = *curve_name;
}
// param encoding
@@ -624,13 +628,13 @@ bool GetEcKeyDetail(Environment* env,
const auto& m_pkey = key.GetAsymmetricKey();
DCHECK(m_pkey.isA(KeyAlgorithm::EC));
- int nid = Ec::GetCurveId(m_pkey);
- if (nid == NID_undef) return true;
+ const auto name = Ec::GetCurveName(m_pkey);
+ if (!name) return true;
return target
->Set(env->context(),
env->named_curve_string(),
- OneByteString(env->isolate(), OBJ_nid2sn(nid)))
+ OneByteString(env->isolate(), name.value()))
.IsJust();
}
diff --git a/src/crypto/crypto_ec.h b/src/crypto/crypto_ec.h
index 3f4e6d6f410..5b8fbf3aa59 100644
--- a/src/crypto/crypto_ec.h
+++ b/src/crypto/crypto_ec.h
@@ -13,6 +13,8 @@
#include "node_internals.h"
#include "v8.h"
+#include <string>
+
namespace node {
namespace crypto {
@@ -60,9 +62,9 @@ class ECDH final : public BaseObject {
struct EcKeyPairParams final : public MemoryRetainer {
const ncrypto::KeyAlgorithm* algorithm = nullptr;
- int curve_nid = NID_undef;
+ std::string curve_name;
int param_encoding;
- SET_NO_MEMORY_INFO()
+ void MemoryInfo(MemoryTracker* tracker) const override;
SET_MEMORY_INFO_NAME(EcKeyPairParams)
SET_SELF_SIZE(EcKeyPairParams)
};
diff --git a/test/cctest/test_node_crypto.cc b/test/cctest/test_node_crypto.cc
index 56911c69dec..7e6cf5f6315 100644
--- a/test/cctest/test_node_crypto.cc
+++ b/test/cctest/test_node_crypto.cc
@@ -7,8 +7,20 @@
#include "gtest/gtest.h"
#include "node_options.h"
#include "openssl/err.h"
+#include "util.h"
#include <climits>
+#include <memory>
+#include <string>
+#include <string_view>
+#include <vector>
+
+#if NCRYPTO_USE_OPENSSL3_PROVIDER
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/params.h>
+#include <openssl/provider.h>
+#endif
using ncrypto::Ec;
using ncrypto::EVPKeyCtxPointer;
@@ -530,6 +542,202 @@ TEST(NodeCrypto, NamedKeysAndEcCurves) {
Ec::GetCurveIdFromName("prime256v1"));
}
+TEST(NodeCrypto, EcGroupNames) {
+ ncrypto::ClearErrorOnReturn clear_errors;
+ EXPECT_FALSE(Ec::GetCurveName(EVPKeyPointer()));
+ EXPECT_FALSE(Ec::CheckCurveName(nullptr));
+ EXPECT_FALSE(Ec::CheckCurveName("node-test-unknown-curve"));
+ for (const char* name : {"P-256", "prime256v1"}) {
+ EXPECT_TRUE(Ec::CheckCurveName(name));
+ auto ctx = EVPKeyCtxPointer::NewFromAlgorithm(KeyAlgorithm::EC);
+ ASSERT_TRUE(ctx.initForParamgen());
+ ASSERT_TRUE(ctx.setEcParameters(name, OPENSSL_EC_NAMED_CURVE));
+ auto key = ctx.paramgen();
+ ASSERT_TRUE(key);
+ const auto group = Ec::GetCurveName(key);
+ ASSERT_TRUE(group);
+ EXPECT_EQ(group.value(), "prime256v1");
+ }
+}
+
+#if NCRYPTO_USE_OPENSSL3_PROVIDER
+namespace {
+// Deliberately longer than the fixed-size group buffer formerly used by
+// ncrypto.
+constexpr char kProviderEcGroup[] =
+ "node-test-provider-ec-group-without-an-object-identifier-"
+ "and-with-a-name-longer-than-eighty-bytes";
+
+int EcGroupTestSetParams(void* data, const OSSL_PARAM params[]) {
+ const OSSL_PARAM* group =
+ OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_GROUP_NAME);
+ if (group == nullptr) return 1;
+ const char* name = nullptr;
+ if (OSSL_PARAM_get_utf8_string_ptr(group, &name) != 1 || name == nullptr) {
+ return 0;
+ }
+ *static_cast<std::string*>(data) = name;
+ return 1;
+}
+
+void* EcGroupTestGenInit(void*, int, const OSSL_PARAM params[]) {
+ auto data = std::make_unique<std::string>();
+ if (params != nullptr && !EcGroupTestSetParams(data.get(), params)) {
+ return nullptr;
+ }
+ return data.release();
+}
+
+void* EcGroupTestGen(void* data, OSSL_CALLBACK*, void*) {
+ const auto& group = *static_cast<std::string*>(data);
+ // Defer rejecting unknown names until generation, as a provider may do.
+ if (group != kProviderEcGroup && group != "prime256v1") {
+ ERR_raise(ERR_LIB_USER, ERR_R_PASSED_INVALID_ARGUMENT);
+ return nullptr;
+ }
+ return new std::string(group);
+}
+
+void EcGroupTestFree(void* data) {
+ delete static_cast<std::string*>(data);
+}
+
+int EcGroupTestHas(const void* data, int selection) {
+ return (selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0 &&
+ !static_cast<const std::string*>(data)->empty();
+}
+
+int EcGroupTestGetParams(void* data, OSSL_PARAM params[]) {
+ OSSL_PARAM* group = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_GROUP_NAME);
+ return group == nullptr ||
+ OSSL_PARAM_set_utf8_string(group,
+ static_cast<std::string*>(data)->c_str());
+}
+
+const OSSL_PARAM* EcGroupTestParams(void*, void*) {
+ static const OSSL_PARAM params[] = {
+ OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, nullptr, 0),
+ OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_EC_ENCODING, nullptr, 0),
+ OSSL_PARAM_END,
+ };
+ return params;
+}
+
+const OSSL_PARAM* EcGroupTestGettableParams(void*) {
+ return EcGroupTestParams(nullptr, nullptr);
+}
+
+const OSSL_ALGORITHM* EcGroupTestQuery(void*, int operation, int* no_cache) {
+ *no_cache = 0;
+ static const OSSL_DISPATCH keymgmt[] = {
+ {OSSL_FUNC_KEYMGMT_GEN_INIT,
+ reinterpret_cast<void (*)(void)>(EcGroupTestGenInit)},
+ {OSSL_FUNC_KEYMGMT_GEN_SET_PARAMS,
+ reinterpret_cast<void (*)(void)>(EcGroupTestSetParams)},
+ {OSSL_FUNC_KEYMGMT_GEN_SETTABLE_PARAMS,
+ reinterpret_cast<void (*)(void)>(EcGroupTestParams)},
+ {OSSL_FUNC_KEYMGMT_GEN, reinterpret_cast<void (*)(void)>(EcGroupTestGen)},
+ {OSSL_FUNC_KEYMGMT_GEN_CLEANUP,
+ reinterpret_cast<void (*)(void)>(EcGroupTestFree)},
+ {OSSL_FUNC_KEYMGMT_FREE,
+ reinterpret_cast<void (*)(void)>(EcGroupTestFree)},
+ {OSSL_FUNC_KEYMGMT_HAS, reinterpret_cast<void (*)(void)>(EcGroupTestHas)},
+ {OSSL_FUNC_KEYMGMT_GET_PARAMS,
+ reinterpret_cast<void (*)(void)>(EcGroupTestGetParams)},
+ {OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS,
+ reinterpret_cast<void (*)(void)>(EcGroupTestGettableParams)},
+ {0, nullptr},
+ };
+ static const OSSL_ALGORITHM algorithms[] = {
+ {"EC", "provider=node-test-ec", keymgmt, "Test provider EC group names"},
+ {nullptr, nullptr, nullptr, nullptr},
+ };
+ return operation == OSSL_OP_KEYMGMT ? algorithms : nullptr;
+}
+
+int EcGroupTestProviderInit(const OSSL_CORE_HANDLE*,
+ const OSSL_DISPATCH*,
+ const OSSL_DISPATCH** out,
+ void**) {
+ static const OSSL_DISPATCH dispatch[] = {
+ {OSSL_FUNC_PROVIDER_QUERY_OPERATION,
+ reinterpret_cast<void (*)(void)>(EcGroupTestQuery)},
+ {0, nullptr},
+ };
+ *out = dispatch;
+ return 1;
+}
+
+void EcGroupTestUnloadProvider(OSSL_PROVIDER* provider) {
+ OSSL_PROVIDER_unload(provider);
+}
+} // namespace
+
+TEST(NodeCrypto, ProviderEcGroupName) {
+ ncrypto::ClearErrorOnReturn clear_errors;
+ ncrypto::DeleteFnPtr<OSSL_LIB_CTX, OSSL_LIB_CTX_free> libctx(
+ OSSL_LIB_CTX_new());
+ ASSERT_TRUE(libctx);
+ ASSERT_EQ(OSSL_PROVIDER_add_builtin(
+ libctx.get(), "node-test-ec", EcGroupTestProviderInit),
+ 1);
+ ncrypto::DeleteFnPtr<OSSL_PROVIDER, EcGroupTestUnloadProvider> provider(
+ OSSL_PROVIDER_load(libctx.get(), "node-test-ec"));
+ ASSERT_TRUE(provider);
+ {
+ OSSL_LIB_CTX* previous_libctx = OSSL_LIB_CTX_set0_default(libctx.get());
+ auto restore_libctx = node::OnScopeLeave(
+ [previous_libctx] { OSSL_LIB_CTX_set0_default(previous_libctx); });
+ // The provider supports generation, but intentionally has no import API.
+ EXPECT_TRUE(Ec::CheckCurveName(kProviderEcGroup));
+ EXPECT_FALSE(Ec::CheckCurveName("node-test-unknown-curve"));
+ EXPECT_TRUE(Ec::CheckCurveName("P-256"));
+
+ auto ctx = EVPKeyCtxPointer::NewFromAlgorithm(KeyAlgorithm::EC);
+ ASSERT_TRUE(ctx);
+ ASSERT_TRUE(ctx.initForParamgen());
+ ASSERT_TRUE(ctx.setEcParameters(kProviderEcGroup, OPENSSL_EC_NAMED_CURVE));
+ auto key = ctx.paramgen();
+ ASSERT_TRUE(key);
+ EXPECT_EQ(Ec::GetCurveId(key), NID_undef);
+ const auto name = Ec::GetCurveName(key);
+ ASSERT_TRUE(name);
+ EXPECT_EQ(name.value(), kProviderEcGroup);
+
+ // Only usable built-in curves are enumerated. The provider-only group
+ // remains usable by name without appearing in this list.
+ ERR_clear_error();
+ ERR_raise(ERR_LIB_USER, ERR_R_INTERNAL_ERROR);
+ const auto saved_error = ERR_peek_error();
+ std::vector<std::string> curves;
+ EXPECT_TRUE(Ec::GetCurves([&curves](const char* curve) {
+ curves.emplace_back(curve);
+ return true;
+ }));
+ EXPECT_EQ(curves, std::vector<std::string>{"prime256v1"});
+ EXPECT_EQ(ERR_peek_error(), saved_error);
+ EXPECT_EQ(ERR_peek_last_error(), saved_error);
+
+ unsigned int calls = 0;
+ EXPECT_FALSE(Ec::GetCurves([&calls](const char*) {
+ calls++;
+ ERR_raise(ERR_LIB_USER, ERR_R_PASSED_INVALID_ARGUMENT);
+ return false;
+ }));
+ EXPECT_EQ(calls, 1U);
+ EXPECT_EQ(ERR_get_error(), saved_error);
+ EXPECT_EQ(ERR_GET_REASON(ERR_get_error()), ERR_R_PASSED_INVALID_ARGUMENT);
+ EXPECT_EQ(ERR_get_error(), 0UL);
+
+ ASSERT_EQ(EVP_set_default_properties(nullptr, "provider=default"), 1);
+ EXPECT_TRUE(Ec::GetCurves([](const char*) {
+ ADD_FAILURE() << "No EC groups should match the default properties";
+ return true;
+ }));
+ }
+}
+#endif
+
TEST(NodeCrypto, NamedRawKey) {
const unsigned char seed[32] = {};
const ncrypto::Buffer<const unsigned char> input{seed, sizeof(seed)};
diff --git a/test/parallel/test-crypto-key-objects.js b/test/parallel/test-crypto-key-objects.js
index fbd2ae6ff1c..6dfdada9430 100644
--- a/test/parallel/test-crypto-key-objects.js
+++ b/test/parallel/test-crypto-key-objects.js
@@ -1147,9 +1147,10 @@ if (!isBoringSSL) {
{
// Exporting a JWK unsupported curve EC key
const supported = ['prime256v1', 'secp256k1', 'secp384r1', 'secp521r1'];
- // Find an unsupported curve regardless of whether a FIPS compliant crypto
- // provider is currently in use.
- const namedCurve = getCurves().find((curve) => !supported.includes(curve));
+ // FIPS-disallowed curves are omitted from getCurves(). Select one explicitly
+ // to keep checking their rejection; otherwise find a curve JWK cannot encode.
+ const namedCurve = fips3 ? 'secp256k1' :
+ getCurves().find((curve) => !supported.includes(curve));
assert(namedCurve);
if (fips3) {
assert.throws(() => generateKeyPairSync('ec', { namedCurve }), {
diff --git a/test/parallel/test-crypto-keygen-ec-group-names.js b/test/parallel/test-crypto-keygen-ec-group-names.js
new file mode 100644
index 00000000000..b7afa67f9c2
--- /dev/null
+++ b/test/parallel/test-crypto-keygen-ec-group-names.js
@@ -0,0 +1,38 @@
+'use strict';
+
+const common = require('../common');
+if (!common.hasCrypto)
+ common.skip('missing crypto');
+
+const assert = require('assert');
+const { generateKeyPair, generateKeyPairSync, sign, verify } = require('crypto');
+
+const message = Buffer.from('EC group names');
+for (const [alias, canonical] of [
+ ['P-256', 'prime256v1'],
+ ['P-384', 'secp384r1'],
+ ['P-521', 'secp521r1'],
+]) {
+ for (const namedCurve of [alias, canonical]) {
+ const check = common.mustCall((publicKey, privateKey) => {
+ assert.deepStrictEqual(publicKey.asymmetricKeyDetails,
+ { namedCurve: canonical });
+ assert.deepStrictEqual(privateKey.asymmetricKeyDetails,
+ { namedCurve: canonical });
+ assert(verify('sha256', message, publicKey,
+ sign('sha256', message, privateKey)));
+ }, 2);
+ const { publicKey, privateKey } = generateKeyPairSync('ec', { namedCurve });
+ check(publicKey, privateKey);
+ generateKeyPair('ec', { namedCurve }, common.mustSucceed(check));
+ }
+}
+
+for (const namedCurve of ['', 'node-test-unknown-curve']) {
+ assert.throws(() => generateKeyPairSync('ec', { namedCurve }), {
+ code: 'ERR_CRYPTO_INVALID_CURVE',
+ });
+ assert.throws(() => generateKeyPair('ec', { namedCurve }, common.mustNotCall()), {
+ code: 'ERR_CRYPTO_INVALID_CURVE',
+ });
+}