Commit 9267ac84c07 for nodejs
commit 9267ac84c07430581324df3568c41f0c904668f2
Author: Filip Skokan <panva.ip@gmail.com>
Date: Wed Sep 30 14:05:16 2026 +0200
crypto: remove key export and metadata locks
Raw and JWK getters return independently owned bytes or BIGNUMs. RSA
metadata snapshots public parameters and PSS restrictions. EC fallback
paths reconstruct or duplicate the source key. EC PKCS8 export changes
encoding flags only on its own clone.
Early OpenSSL 3 key downgrading mutated shared provider fields. Current
OpenSSL publishes separate legacy and provider conversion caches under
internal read/write locks. Ordinary DER/PEM export, equality, signing,
and key derivation already access the same keys without these locks.
Remove export and metadata acquisitions, including their coverage of V8
allocation and encoding, and remove the now-unused shared mutex storage.
Retain shared immutable key ownership. The concurrent reuse test checks
that EC PKCS8 export leaves the original flags intact.
Refs: https://github.com/nodejs/node/pull/36825
Refs: https://docs.openssl.org/3.5/man7/openssl-threads/
Signed-off-by: Filip Skokan <panva.ip@gmail.com>
Assisted-by: Codex
PR-URL: https://github.com/nodejs/node/pull/66413
Reviewed-By: James M Snell <jasnell@gmail.com>
diff --git a/src/crypto/crypto_ec.cc b/src/crypto/crypto_ec.cc
index 1126d72ad5f..f51d311846c 100644
--- a/src/crypto/crypto_ec.cc
+++ b/src/crypto/crypto_ec.cc
@@ -479,7 +479,6 @@ Maybe<void> EcKeyGenTraits::AdditionalConfig(
bool ExportJWKEcKey(Environment* env,
const KeyObjectData& key,
Local<Object> target) {
- Mutex::ScopedLock lock(key.mutex());
const auto& m_pkey = key.GetAsymmetricKey();
DCHECK(m_pkey.isA(KeyAlgorithm::EC));
@@ -624,7 +623,6 @@ KeyObjectData ImportJWKEcKey(Environment* env, Local<Object> jwk) {
bool GetEcKeyDetail(Environment* env,
const KeyObjectData& key,
Local<Object> target) {
- Mutex::ScopedLock lock(key.mutex());
const auto& m_pkey = key.GetAsymmetricKey();
DCHECK(m_pkey.isA(KeyAlgorithm::EC));
diff --git a/src/crypto/crypto_keys.cc b/src/crypto/crypto_keys.cc
index da56f4a4e26..64bbd6da072 100644
--- a/src/crypto/crypto_keys.cc
+++ b/src/crypto/crypto_keys.cc
@@ -187,7 +187,6 @@ KeyObjectData ImportJWKSecretKey(Environment* env, Local<Object> jwk) {
static bool ExportJWKRawKey(Environment* env,
const KeyObjectData& key,
Local<Object> target) {
- Mutex::ScopedLock lock(key.mutex());
auto result =
key.GetAsymmetricKey().exportRawJwk(key.GetKeyType() == kKeyTypePrivate);
if (!result) {
@@ -416,7 +415,6 @@ bool KeyObjectData::ToEncodedPublicKey(
return ExportJWKInner(
env, addRefWithType(KeyType::kKeyTypePublic), *out, false);
} else if (config.format == EVPKeyPointer::PKFormatType::RAW_PUBLIC) {
- Mutex::ScopedLock lock(mutex());
const auto& pkey = GetAsymmetricKey();
const auto* algorithm = pkey.getAlgorithm();
if (algorithm == &KeyAlgorithm::EC) {
@@ -471,7 +469,6 @@ bool KeyObjectData::ToEncodedPrivateKey(
return ExportJWKInner(
env, addRefWithType(KeyType::kKeyTypePrivate), *out, false);
} else if (config.format == EVPKeyPointer::PKFormatType::RAW_PRIVATE) {
- Mutex::ScopedLock lock(mutex());
const auto& pkey = GetAsymmetricKey();
const auto* algorithm = pkey.getAlgorithm();
if (algorithm == &KeyAlgorithm::EC) {
@@ -497,7 +494,6 @@ bool KeyObjectData::ToEncodedPrivateKey(
return Buffer::Copy(env, raw_data.get<const char>(), raw_data.size())
.ToLocal(out);
} else if (config.format == EVPKeyPointer::PKFormatType::RAW_SEED) {
- Mutex::ScopedLock lock(mutex());
const auto& pkey = GetAsymmetricKey();
auto raw_data = pkey.rawSeed();
if (!raw_data) {
@@ -1056,9 +1052,7 @@ KeyObjectData::KeyObjectData(ByteSource symmetric_key)
data_(std::make_shared<Data>(std::move(symmetric_key))) {}
KeyObjectData::KeyObjectData(KeyType type, EVPKeyPointer&& pkey)
- : key_type_(type),
- mutex_(std::make_shared<Mutex>()),
- data_(std::make_shared<Data>(std::move(pkey))) {}
+ : key_type_(type), data_(std::make_shared<Data>(std::move(pkey))) {}
void KeyObjectData::Data::MemoryInfo(MemoryTracker* tracker) const {
if (asymmetric_key) {
@@ -1075,11 +1069,6 @@ void KeyObjectData::MemoryInfo(MemoryTracker* tracker) const {
tracker->TrackField("data", data_);
}
-Mutex& KeyObjectData::mutex() const {
- if (!mutex_) mutex_ = std::make_shared<Mutex>();
- return *mutex_.get();
-}
-
KeyObjectData KeyObjectData::CreateSecret(ByteSource key) {
return KeyObjectData(std::move(key));
}
@@ -1474,7 +1463,6 @@ void KeyObjectHandle::RawPublicKey(
const KeyObjectData& data = key->Data();
CHECK_NE(data.GetKeyType(), kKeyTypeSecret);
- Mutex::ScopedLock lock(data.mutex());
const auto& pkey = data.GetAsymmetricKey();
const bool is_raw_supported = pkey.supportsRawPublic();
@@ -1502,7 +1490,6 @@ void KeyObjectHandle::RawPrivateKey(
const KeyObjectData& data = key->Data();
CHECK_EQ(data.GetKeyType(), kKeyTypePrivate);
- Mutex::ScopedLock lock(data.mutex());
const auto& pkey = data.GetAsymmetricKey();
const bool is_raw_supported = pkey.supportsRawPrivate();
@@ -1530,7 +1517,6 @@ void KeyObjectHandle::ExportECPublicRaw(
const KeyObjectData& data = key->Data();
CHECK_NE(data.GetKeyType(), kKeyTypeSecret);
- Mutex::ScopedLock lock(data.mutex());
const auto& m_pkey = data.GetAsymmetricKey();
if (!m_pkey.isA(KeyAlgorithm::EC)) {
return THROW_ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS(env);
@@ -1569,7 +1555,6 @@ void KeyObjectHandle::ExportECPrivateRaw(
const KeyObjectData& data = key->Data();
CHECK_EQ(data.GetKeyType(), kKeyTypePrivate);
- Mutex::ScopedLock lock(data.mutex());
const auto& m_pkey = data.GetAsymmetricKey();
if (!m_pkey.isA(KeyAlgorithm::EC)) {
return THROW_ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS(env);
@@ -1592,7 +1577,6 @@ void KeyObjectHandle::ExportECPrivatePkcs8(
ASSIGN_OR_RETURN_UNWRAP(&key, args.This());
const KeyObjectData& data = key->Data();
CHECK_EQ(data.GetKeyType(), kKeyTypePrivate);
- Mutex::ScopedLock lock(data.mutex());
auto encoded = ncrypto::Ec::ExportPrivatePkcs8(data.GetAsymmetricKey());
if (!encoded) {
return THROW_ERR_CRYPTO_OPERATION_FAILED(env,
@@ -1611,7 +1595,6 @@ void KeyObjectHandle::RawSeed(const v8::FunctionCallbackInfo<v8::Value>& args) {
const KeyObjectData& data = key->Data();
CHECK_EQ(data.GetKeyType(), kKeyTypePrivate);
- Mutex::ScopedLock lock(data.mutex());
const auto& pkey = data.GetAsymmetricKey();
auto raw_data = pkey.rawSeed();
diff --git a/src/crypto/crypto_keys.h b/src/crypto/crypto_keys.h
index cb7209d0835..5a14651deaa 100644
--- a/src/crypto/crypto_keys.h
+++ b/src/crypto/crypto_keys.h
@@ -54,8 +54,8 @@ class KeyObjectData final : public MemoryRetainer {
KeyType GetKeyType() const;
- // These functions allow unprotected access to the raw key material and should
- // only be used to implement cryptographic operations requiring the key.
+ // The key material is immutable and can be used concurrently by operations
+ // with separate contexts.
const ncrypto::EVPKeyPointer& GetAsymmetricKey() const;
const char* GetSymmetricKey() const;
size_t GetSymmetricKeySize() const;
@@ -64,8 +64,6 @@ class KeyObjectData final : public MemoryRetainer {
SET_MEMORY_INFO_NAME(KeyObjectData)
SET_SELF_SIZE(KeyObjectData)
- Mutex& mutex() const;
-
static v8::Maybe<ncrypto::EVPKeyPointer::PublicKeyEncodingConfig>
GetPublicKeyEncodingFromJs(const v8::FunctionCallbackInfo<v8::Value>& args,
unsigned int* offset,
@@ -97,11 +95,11 @@ class KeyObjectData final : public MemoryRetainer {
v8::Local<v8::Value>* out);
inline KeyObjectData addRef() const {
- return KeyObjectData(key_type_, mutex_, data_);
+ return KeyObjectData(key_type_, data_);
}
inline KeyObjectData addRefWithType(KeyType type) const {
- return KeyObjectData(type, mutex_, data_);
+ return KeyObjectData(type, data_);
}
private:
@@ -115,7 +113,6 @@ class KeyObjectData final : public MemoryRetainer {
const char* default_msg);
KeyType key_type_;
- mutable std::shared_ptr<Mutex> mutex_;
struct Data final : public MemoryRetainer {
const ByteSource symmetric_key;
@@ -131,10 +128,8 @@ class KeyObjectData final : public MemoryRetainer {
};
std::shared_ptr<Data> data_;
- KeyObjectData(KeyType type,
- std::shared_ptr<Mutex> mutex,
- std::shared_ptr<Data> data)
- : key_type_(type), mutex_(std::move(mutex)), data_(std::move(data)) {}
+ KeyObjectData(KeyType type, std::shared_ptr<Data> data)
+ : key_type_(type), data_(std::move(data)) {}
};
class KeyObjectHandle : public BaseObject {
diff --git a/src/crypto/crypto_rsa.cc b/src/crypto/crypto_rsa.cc
index 0a4dbd80ab8..25d8808e6ea 100644
--- a/src/crypto/crypto_rsa.cc
+++ b/src/crypto/crypto_rsa.cc
@@ -298,7 +298,6 @@ WebCryptoCipherStatus RSACipherTraits::DoCipher(Environment* env,
bool ExportJWKRsaKey(Environment* env,
const KeyObjectData& key,
Local<Object> target) {
- Mutex::ScopedLock lock(key.mutex());
const auto& m_pkey = key.GetAsymmetricKey();
const ncrypto::Rsa rsa = m_pkey;
@@ -526,7 +525,6 @@ KeyObjectData ImportJWKRsaKey(Environment* env, Local<Object> jwk) {
bool GetRsaKeyDetail(Environment* env,
const KeyObjectData& key,
Local<Object> target) {
- Mutex::ScopedLock lock(key.mutex());
const auto& m_pkey = key.GetAsymmetricKey();
const auto rsa = ncrypto::Rsa::PublicOnly(m_pkey);