/
githubmirror
/
node
Обзор
Документация
Войти
/
githubmirror
/
node
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/crypto/crypto_keys.cc
2 215 строк
75 KB
James M Snell
src: update repeated use strings to env
05 авг 2026, 08:36
Не верифицирован
05 авг 2026, 08:36
41afbd3
Код
Авторство
О чём код?
#include "crypto/crypto_keys.h" #include "async_wrap-inl.h" #include "base_object-inl.h" #include "crypto/crypto_common.h" #include "crypto/crypto_dh.h" #include "crypto/crypto_dsa.h" #include "crypto/crypto_ec.h" #include "crypto/crypto_pqc.h" #include "crypto/crypto_rsa.h" #include "crypto/crypto_util.h" #include "env-inl.h" #include "memory_tracker-inl.h" #include "node.h" #include "node_buffer.h" #include "permission/permission.h" #include "string_bytes.h" #include "threadpoolwork-inl.h" #include "util-inl.h" #include "v8.h" namespace node { using ncrypto::BignumPointer; using ncrypto::BIOPointer; using ncrypto::ECKeyPointer; using ncrypto::ECPointPointer; using ncrypto::EVPKeyCtxPointer; using ncrypto::EVPKeyPointer; using ncrypto::MarkPopErrorOnReturn; using v8::Array; using v8::Boolean; using v8::Context; using v8::Function; using v8::FunctionCallbackInfo; using v8::FunctionTemplate; using v8::Int32; using v8::Isolate; using v8::Just; using v8::Local; using v8::Maybe; using v8::MaybeLocal; using v8::NewStringType; using v8::Nothing; using v8::Number; using v8::Object; using v8::String; using v8::Uint32; using v8::Undefined; using v8::Value; namespace crypto { namespace { Maybe<EVPKeyPointer::AsymmetricKeyEncodingConfig> GetKeyFormatAndTypeFromJs( const FunctionCallbackInfo<Value>& args, unsigned int* offset, KeyEncodingContext context) { EVPKeyPointer::AsymmetricKeyEncodingConfig config; // During key pair generation, it is possible not to specify a key encoding, // which will lead to a key object being returned. if (args[*offset]->IsUndefined()) { CHECK_EQ(context, kKeyContextGenerate); CHECK(args[*offset + 1]->IsUndefined()); config.output_key_object = true; } else { config.output_key_object = false; CHECK(args[*offset]->IsInt32()); config.format = static_cast<EVPKeyPointer::PKFormatType>( args[*offset].As<Int32>()->Value()); if (config.format == EVPKeyPointer::PKFormatType::RAW_PUBLIC || config.format == EVPKeyPointer::PKFormatType::RAW_PRIVATE || config.format == EVPKeyPointer::PKFormatType::RAW_SEED) { // Raw formats use the type slot for ec_point_form (int) or null. if (args[*offset + 1]->IsInt32()) { config.ec_point_form = args[*offset + 1].As<Int32>()->Value(); } else { CHECK(args[*offset + 1]->IsNullOrUndefined()); } } else if (args[*offset + 1]->IsInt32()) { config.type = static_cast<EVPKeyPointer::PKEncodingType>( args[*offset + 1].As<Int32>()->Value()); } else { CHECK((context == kKeyContextInput && config.format == EVPKeyPointer::PKFormatType::PEM) || (context == kKeyContextGenerate && config.format == EVPKeyPointer::PKFormatType::JWK)); CHECK(args[*offset + 1]->IsNullOrUndefined()); config.type = EVPKeyPointer::PKEncodingType::PKCS1; } } *offset += 2; return Just(config); } MaybeLocal<Value> ToV8Value( Environment* env, const BIOPointer& bio, const EVPKeyPointer::AsymmetricKeyEncodingConfig& config) { if (!bio) { THROW_ERR_CRYPTO_OPERATION_FAILED(env, "Invalid BIO pointer"); return {}; } BUF_MEM* bptr = bio; if (!bptr) { THROW_ERR_CRYPTO_OPERATION_FAILED(env, "Unable to create BUF_MEM pointer"); return {}; } if (config.format == EVPKeyPointer::PKFormatType::PEM) { // PEM is an ASCII format, so we will return it as a string. return String::NewFromUtf8( env->isolate(), bptr->data, NewStringType::kNormal, bptr->length) .FromMaybe(Local<Value>()); } CHECK_EQ(config.format, EVPKeyPointer::PKFormatType::DER); // DER is binary, return it as a buffer. return Buffer::Copy(env, bptr->data, bptr->length).FromMaybe(Local<Value>()); } MaybeLocal<Value> WritePrivateKey( Environment* env, const EVPKeyPointer& pkey, const EVPKeyPointer::PrivateKeyEncodingConfig& config) { if (!pkey) return {}; auto res = pkey.writePrivateKey(config); if (res) return ToV8Value(env, std::move(res.value), config); ThrowCryptoError( env, res.openssl_error.value_or(0), "Failed to encode private key"); return MaybeLocal<Value>(); } MaybeLocal<Value> WritePublicKey( Environment* env, const EVPKeyPointer& pkey, const EVPKeyPointer::PublicKeyEncodingConfig& config) { if (!pkey) return {}; auto res = pkey.writePublicKey(config); if (res) return ToV8Value(env, res.value, config); ThrowCryptoError( env, res.openssl_error.value_or(0), "Failed to encode public key"); return MaybeLocal<Value>(); } bool ExportJWKSecretKey(Environment* env, const KeyObjectData& key, Local<Object> target) { CHECK_EQ(key.GetKeyType(), kKeyTypeSecret); Local<Value> raw; return StringBytes::Encode(env->isolate(), key.GetSymmetricKey(), key.GetSymmetricKeySize(), BASE64URL) .ToLocal(&raw) && target ->DefineOwnProperty( env->context(), env->jwk_kty_string(), env->jwk_oct_string()) .FromMaybe(false) && target->DefineOwnProperty(env->context(), env->jwk_k_string(), raw) .FromMaybe(false); } KeyObjectData ImportJWKSecretKey(Environment* env, Local<Object> jwk) { Local<Value> key; if (!jwk->Get(env->context(), env->jwk_k_string()).ToLocal(&key) || !key->IsString()) { THROW_ERR_CRYPTO_INVALID_JWK(env, "Invalid JWK secret key format"); return {}; } static_assert(String::kMaxLength <= INT_MAX); return KeyObjectData::CreateSecret( ByteSource::FromEncodedString(env, key.As<String>())); } bool ExportJWKAsymmetricKey(Environment* env, const KeyObjectData& key, Local<Object> target, bool handleRsaPss) { const int id = key.GetAsymmetricKey().id(); #if OPENSSL_WITH_PQC if (IsPqcKeyId(id)) return ExportJwkPqcKey(env, key, target); #endif switch (id) { case EVP_PKEY_RSA_PSS: { if (handleRsaPss) return ExportJWKRsaKey(env, key, target); break; } case EVP_PKEY_RSA: return ExportJWKRsaKey(env, key, target); case EVP_PKEY_EC: return ExportJWKEcKey(env, key, target); case EVP_PKEY_ED25519: case EVP_PKEY_ED448: case EVP_PKEY_X25519: case EVP_PKEY_X448: return ExportJWKEdKey(env, key, target); } THROW_ERR_CRYPTO_JWK_UNSUPPORTED_KEY_TYPE(env); return false; } bool GetSecretKeyDetail(Environment* env, const KeyObjectData& key, Local<Object> target) { // For the secret key detail, all we care about is the length, // converted to bits. return target ->Set(env->context(), env->length_string(), Number::New( env->isolate(), static_cast<double>(key.GetSymmetricKeySize() * CHAR_BIT))) .IsJust(); } bool GetAsymmetricKeyDetail(Environment* env, const KeyObjectData& key, Local<Object> target) { if (!key) { THROW_ERR_CRYPTO_OPERATION_FAILED(env); return false; } switch (key.GetAsymmetricKey().id()) { case EVP_PKEY_RSA: // Fall through case EVP_PKEY_RSA2: // Fall through case EVP_PKEY_RSA_PSS: return GetRsaKeyDetail(env, key, target); case EVP_PKEY_DSA: return GetDsaKeyDetail(env, key, target); case EVP_PKEY_EC: return GetEcKeyDetail(env, key, target); case EVP_PKEY_DH: return GetDhKeyDetail(env, key, target); } THROW_ERR_CRYPTO_INVALID_KEYTYPE(env); return false; } KeyObjectData TryParsePrivateKey( Environment* env, const EVPKeyPointer::PrivateKeyEncodingConfig& config, const ncrypto::Buffer<const unsigned char>& buffer) { auto res = EVPKeyPointer::TryParsePrivateKey(config, buffer); if (res) { return KeyObjectData::CreateAsymmetric(KeyType::kKeyTypePrivate, std::move(res.value)); } if (res.error.value() == EVPKeyPointer::PKParseError::NEED_PASSPHRASE) { THROW_ERR_MISSING_PASSPHRASE(env, "Passphrase required for encrypted key"); } else { ThrowCryptoError( env, res.openssl_error.value_or(0), "Failed to read private key"); } return {}; } bool ExportJWKInner(Environment* env, const KeyObjectData& key, Local<Value> result, bool handleRsaPss) { return key.GetKeyType() == kKeyTypeSecret ? ExportJWKSecretKey(env, key, result.As<Object>()) : ExportJWKAsymmetricKey( env, key, result.As<Object>(), handleRsaPss); } int GetNidFromName(const char* name) { static constexpr struct { const char* name; int nid; } kNameToNid[] = { {"Ed25519", EVP_PKEY_ED25519}, {"Ed448", EVP_PKEY_ED448}, {"X25519", EVP_PKEY_X25519}, {"X448", EVP_PKEY_X448}, }; for (const auto& entry : kNameToNid) { if (StringEqualNoCase(name, entry.name)) return entry.nid; } #if OPENSSL_WITH_PQC return GetPqcNidFromName(name); #else return NID_undef; #endif } bool IsUnavailablePqcKeyType(Environment* env, Local<String> key_type) { return key_type->StringEquals(env->crypto_ml_dsa_44_string()) || key_type->StringEquals(env->crypto_ml_dsa_65_string()) || key_type->StringEquals(env->crypto_ml_dsa_87_string()) || key_type->StringEquals(env->crypto_ml_kem_512_string()) || key_type->StringEquals(env->crypto_ml_kem_768_string()) || key_type->StringEquals(env->crypto_ml_kem_1024_string()) || key_type->StringEquals(env->crypto_slh_dsa_sha2_128f_string()) || key_type->StringEquals(env->crypto_slh_dsa_sha2_128s_string()) || key_type->StringEquals(env->crypto_slh_dsa_sha2_192f_string()) || key_type->StringEquals(env->crypto_slh_dsa_sha2_192s_string()) || key_type->StringEquals(env->crypto_slh_dsa_sha2_256f_string()) || key_type->StringEquals(env->crypto_slh_dsa_sha2_256s_string()) || key_type->StringEquals(env->crypto_slh_dsa_shake_128f_string()) || key_type->StringEquals(env->crypto_slh_dsa_shake_128s_string()) || key_type->StringEquals(env->crypto_slh_dsa_shake_192f_string()) || key_type->StringEquals(env->crypto_slh_dsa_shake_192s_string()) || key_type->StringEquals(env->crypto_slh_dsa_shake_256f_string()) || key_type->StringEquals(env->crypto_slh_dsa_shake_256s_string()); } bool IsUnsupportedRawKeyType(Environment* env, Local<String> key_type) { return key_type->StringEquals(env->crypto_rsa_string()) || key_type->StringEquals(env->crypto_rsa_pss_string()) || key_type->StringEquals(env->crypto_dsa_string()) || key_type->StringEquals(env->crypto_dh_string()); } void ValidateRawKeyImportFormat(Environment* env, Local<String> key_type, const char* key_type_name, int id, EVPKeyPointer::PKFormatType format) { auto validate_raw_format = [&](EVPKeyPointer::PKFormatType expected_private_format) { if (format == EVPKeyPointer::PKFormatType::RAW_PUBLIC || format == expected_private_format) { return; } THROW_ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS(env); }; if (key_type->StringEquals(env->crypto_ec_string())) { return validate_raw_format(EVPKeyPointer::PKFormatType::RAW_PRIVATE); } switch (id) { case EVP_PKEY_X25519: case EVP_PKEY_X448: case EVP_PKEY_ED25519: case EVP_PKEY_ED448: return validate_raw_format(EVPKeyPointer::PKFormatType::RAW_PRIVATE); default: break; } #if OPENSSL_WITH_PQC if (IsPqcSeedKeyId(id)) { return validate_raw_format(EVPKeyPointer::PKFormatType::RAW_SEED); } if (IsPqcRawPrivateKeyId(id)) { return validate_raw_format(EVPKeyPointer::PKFormatType::RAW_PRIVATE); } #endif if (IsUnavailablePqcKeyType(env, key_type)) { THROW_ERR_INVALID_ARG_VALUE(env, "Unsupported key type"); return; } if (IsUnsupportedRawKeyType(env, key_type)) { THROW_ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS(env); return; } THROW_ERR_INVALID_ARG_VALUE( env, "Invalid asymmetricKeyType: %s", key_type_name); } } // namespace bool KeyObjectData::ToEncodedPublicKey( Environment* env, const EVPKeyPointer::PublicKeyEncodingConfig& config, Local<Value>* out) { CHECK(key_type_ != KeyType::kKeyTypeSecret); if (config.output_key_object) { // Note that this has the downside of containing sensitive data of the // private key. return KeyObjectHandle::Create(env, addRefWithType(KeyType::kKeyTypePublic)) .ToLocal(out); } else if (config.format == EVPKeyPointer::PKFormatType::JWK) { *out = Object::New(env->isolate()); return ExportJWKInner( env, addRefWithType(KeyType::kKeyTypePublic), *out, false); } else if (config.format == EVPKeyPointer::PKFormatType::RAW_PUBLIC) { Mutex::ScopedLock lock(mutex()); const auto& pkey = GetAsymmetricKey(); if (pkey.id() == EVP_PKEY_EC) { ECKeyPointer ec_key(pkey); if (!ec_key) { THROW_ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS(env); return false; } // A provider-backed key need not expose its public point. if (ec_key.getPublicKey() == nullptr) { THROW_ERR_CRYPTO_OPERATION_FAILED(env, "Failed to export EC public key"); return false; } auto form = static_cast<point_conversion_form_t>(config.ec_point_form); const auto group = ec_key.getGroup(); const auto point = ec_key.getPublicKey(); return ECPointToBuffer(env, group, point, form).ToLocal(out); } const int id = pkey.id(); bool is_raw_supported = id == EVP_PKEY_ED25519 || id == EVP_PKEY_ED448 || id == EVP_PKEY_X25519 || id == EVP_PKEY_X448; #if OPENSSL_WITH_PQC is_raw_supported = is_raw_supported || IsPqcKeyId(id); #endif if (!is_raw_supported) { THROW_ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS(env); return false; } auto raw_data = pkey.rawPublicKey(); if (!raw_data) { THROW_ERR_CRYPTO_OPERATION_FAILED(env, "Failed to get raw public key"); return false; } return Buffer::Copy(env, raw_data.get<const char>(), raw_data.size()) .ToLocal(out); } return WritePublicKey(env, GetAsymmetricKey(), config).ToLocal(out); } bool KeyObjectData::ToEncodedPrivateKey( Environment* env, const EVPKeyPointer::PrivateKeyEncodingConfig& config, Local<Value>* out) { CHECK(key_type_ != KeyType::kKeyTypeSecret); if (config.output_key_object) { return KeyObjectHandle::Create(env, addRefWithType(KeyType::kKeyTypePrivate)) .ToLocal(out); } else if (config.format == EVPKeyPointer::PKFormatType::JWK) { *out = Object::New(env->isolate()); return ExportJWKInner( env, addRefWithType(KeyType::kKeyTypePrivate), *out, false); } else if (config.format == EVPKeyPointer::PKFormatType::RAW_PRIVATE) { Mutex::ScopedLock lock(mutex()); const auto& pkey = GetAsymmetricKey(); if (pkey.id() == EVP_PKEY_EC) { ECKeyPointer ec_key(pkey); if (!ec_key) { THROW_ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS(env); return false; } const BIGNUM* private_key = ec_key.getPrivateKey(); if (private_key == nullptr) { THROW_ERR_CRYPTO_OPERATION_FAILED(env, "Failed to export EC private key"); return false; } const auto group = ec_key.getGroup(); auto order = BignumPointer::New(); if (!order || !EC_GROUP_get_order(group, order.get(), nullptr)) { THROW_ERR_CRYPTO_OPERATION_FAILED(env, "Failed to export EC private key"); return false; } auto buf = BignumPointer::EncodePadded(private_key, order.byteLength()); if (!buf) { THROW_ERR_CRYPTO_OPERATION_FAILED(env, "Failed to export EC private key"); return false; } return Buffer::Copy(env, buf.get<const char>(), buf.size()).ToLocal(out); } const int id = pkey.id(); bool is_raw_supported = id == EVP_PKEY_ED25519 || id == EVP_PKEY_ED448 || id == EVP_PKEY_X25519 || id == EVP_PKEY_X448; #if OPENSSL_WITH_PQC is_raw_supported = is_raw_supported || IsPqcRawPrivateKeyId(id); #endif if (!is_raw_supported) { THROW_ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS(env); return false; } auto raw_data = pkey.rawPrivateKey(); if (!raw_data) { THROW_ERR_CRYPTO_OPERATION_FAILED(env, "Failed to get raw private key"); return false; } return Buffer::Copy(env, raw_data.get<const char>(), raw_data.size()) .ToLocal(out); } else if (config.format == EVPKeyPointer::PKFormatType::RAW_SEED) { #if OPENSSL_WITH_PQC Mutex::ScopedLock lock(mutex()); const auto& pkey = GetAsymmetricKey(); if (!IsPqcSeedKeyId(pkey.id())) { THROW_ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS(env); return false; } auto raw_data = pkey.rawSeed(); if (!raw_data) { THROW_ERR_CRYPTO_OPERATION_FAILED(env, "Failed to get raw seed"); return false; } return Buffer::Copy(env, raw_data.get<const char>(), raw_data.size()) .ToLocal(out); #else THROW_ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS(env); return false; #endif } return WritePrivateKey(env, GetAsymmetricKey(), config).ToLocal(out); } Maybe<EVPKeyPointer::PrivateKeyEncodingConfig> KeyObjectData::GetPrivateKeyEncodingFromJs( const FunctionCallbackInfo<Value>& args, unsigned int* offset, KeyEncodingContext context) { Environment* env = Environment::GetCurrent(args); EVPKeyPointer::PrivateKeyEncodingConfig config; if (!GetKeyFormatAndTypeFromJs(args, offset, context).To(&config)) { return Nothing<EVPKeyPointer::PrivateKeyEncodingConfig>(); } if (config.output_key_object) { if (context != kKeyContextInput) (*offset)++; } else if (config.format == EVPKeyPointer::PKFormatType::RAW_PRIVATE || config.format == EVPKeyPointer::PKFormatType::RAW_SEED) { // Raw formats don't support encryption. Still consume the arg offsets. if (context != kKeyContextInput) { CHECK(args[*offset]->IsNullOrUndefined()); (*offset)++; } CHECK(args[*offset]->IsNullOrUndefined()); } else { bool needs_passphrase = false; if (context != kKeyContextInput) { if (args[*offset]->IsString()) { Utf8Value cipher_name(env->isolate(), args[*offset]); config.cipher = ncrypto::getCipherByName(*cipher_name); if (config.cipher == nullptr) { THROW_ERR_CRYPTO_UNKNOWN_CIPHER(env); return Nothing<EVPKeyPointer::PrivateKeyEncodingConfig>(); } needs_passphrase = true; } else { CHECK(args[*offset]->IsNullOrUndefined()); config.cipher = nullptr; } (*offset)++; } if (IsAnyBufferSource(args[*offset])) { CHECK_IMPLIES(context != kKeyContextInput, config.cipher != nullptr); ArrayBufferOrViewContents<char> passphrase(args[*offset]); if (!passphrase.CheckSizeInt32()) [[unlikely]] { THROW_ERR_OUT_OF_RANGE(env, "passphrase is too big"); return Nothing<EVPKeyPointer::PrivateKeyEncodingConfig>(); } config.passphrase = passphrase.ToDataPointer(); } else { CHECK(args[*offset]->IsNullOrUndefined() && !needs_passphrase); } } (*offset)++; return Just<EVPKeyPointer::PrivateKeyEncodingConfig>(std::move(config)); } Maybe<EVPKeyPointer::PublicKeyEncodingConfig> KeyObjectData::GetPublicKeyEncodingFromJs( const FunctionCallbackInfo<Value>& args, unsigned int* offset, KeyEncodingContext context) { return GetKeyFormatAndTypeFromJs(args, offset, context); } // Shared helper for importing raw asymmetric keys. Called from // ImportRawKeyFromArgs. static KeyObjectData ImportRawKey(Environment* env, const unsigned char* key_data, size_t key_data_len, EVPKeyPointer::PKFormatType format, Local<String> key_type, const char* key_type_name, const char* named_curve, KeyType target_type) { auto throw_invalid = [&]() { if (!env->isolate()->HasPendingException()) { THROW_ERR_INVALID_ARG_VALUE(env, "Invalid key data"); } }; const int id = GetNidFromName(key_type_name); ValidateRawKeyImportFormat(env, key_type, key_type_name, id, format); if (env->isolate()->HasPendingException()) { return {}; } // EC keys if (key_type->StringEquals(env->crypto_ec_string())) { int curve_nid = ncrypto::Ec::GetCurveIdFromName(named_curve); if (curve_nid == NID_undef) { THROW_ERR_CRYPTO_INVALID_CURVE(env); return {}; } auto eckey = ECKeyPointer::NewByCurveName(curve_nid); if (!eckey) { throw_invalid(); return {}; } if (format == EVPKeyPointer::PKFormatType::RAW_PUBLIC) { const auto group = eckey.getGroup(); auto pub = ECPointPointer::New(group); if (!pub) { throw_invalid(); return {}; } ncrypto::Buffer<const unsigned char> buffer{ .data = key_data, .len = key_data_len, }; if (!pub.setFromBuffer(buffer, group) || !eckey.setPublicKey(pub)) { throw_invalid(); return {}; } } else { const auto group = eckey.getGroup(); auto order = BignumPointer::New(); CHECK(order); CHECK(EC_GROUP_get_order(group, order.get(), nullptr)); if (key_data_len != order.byteLength()) { throw_invalid(); return {}; } BignumPointer priv_bn(key_data, key_data_len); if (!priv_bn || !eckey.setPrivateKey(priv_bn)) { throw_invalid(); return {}; } auto pub_point = ECPointPointer::New(group); if (!pub_point || !pub_point.mul(group, priv_bn.get()) || !eckey.setPublicKey(pub_point)) { throw_invalid(); return {}; } } auto pkey = EVPKeyPointer::New(); if (!pkey.assign(eckey)) { throw_invalid(); return {}; } #if NCRYPTO_USE_LEGACY_KEY_TYPES eckey.release(); #endif return KeyObjectData::CreateAsymmetric(target_type, std::move(pkey)); } typedef EVPKeyPointer (*new_key_fn)( int, const ncrypto::Buffer<const unsigned char>&); new_key_fn fn = nullptr; switch (id) { case EVP_PKEY_X25519: case EVP_PKEY_X448: case EVP_PKEY_ED25519: case EVP_PKEY_ED448: fn = target_type == kKeyTypePrivate ? EVPKeyPointer::NewRawPrivate : EVPKeyPointer::NewRawPublic; break; default: #if OPENSSL_WITH_PQC if (IsPqcKeyId(id)) { if (target_type == kKeyTypePrivate) { fn = IsPqcSeedKeyId(id) ? EVPKeyPointer::NewRawSeed : EVPKeyPointer::NewRawPrivate; } else { fn = EVPKeyPointer::NewRawPublic; } } #endif break; } if (fn != nullptr) { auto pkey = fn(id, ncrypto::Buffer<const unsigned char>{ .data = key_data, .len = key_data_len, }); if (!pkey) { throw_invalid(); return {}; } return KeyObjectData::CreateAsymmetric(target_type, std::move(pkey)); } return {}; } // Shared helper for importing a JWK asymmetric key. Extracts kty from the // JWK object and dispatches to the appropriate importer. static KeyObjectData ImportJWKFromArgs(Environment* env, Local<Object> jwk) { Local<Value> kty; if (!jwk->Get(env->context(), env->jwk_kty_string()).ToLocal(&kty) || !kty->IsString()) { THROW_ERR_CRYPTO_INVALID_JWK(env); return {}; } Utf8Value kty_string(env->isolate(), kty); if (*kty_string == std::string_view("RSA")) { return ImportJWKRsaKey(env, jwk); } else if (*kty_string == std::string_view("EC")) { return ImportJWKEcKey(env, jwk); } else if (*kty_string == std::string_view("OKP")) { return ImportJWKEdKey(env, jwk); } else if (*kty_string == std::string_view("AKP")) { #if OPENSSL_WITH_PQC return ImportJWKPqcKey(env, jwk); #else THROW_ERR_INVALID_ARG_VALUE(env, "Unsupported key type"); return {}; #endif } THROW_ERR_CRYPTO_INVALID_JWK( env, "%s is not a supported JWK key type", *kty_string); return {}; } // Shared helper for importing raw asymmetric keys from positional args. // args layout: [... offset+0: buffer, offset+1: formatInt, // offset+2: asymmetricKeyType, offset+3: passphrase, // offset+4: namedCurve] static KeyObjectData ImportRawKeyFromArgs( const FunctionCallbackInfo<Value>& args, unsigned int offset) { Environment* env = Environment::GetCurrent(args); auto format = static_cast<EVPKeyPointer::PKFormatType>( args[offset + 1].As<Int32>()->Value()); KeyType type = (format == EVPKeyPointer::PKFormatType::RAW_PUBLIC) ? kKeyTypePublic : kKeyTypePrivate; ArrayBufferOrViewContents<unsigned char> key_data(args[offset]); if (!key_data.CheckSizeInt32()) [[unlikely]] { THROW_ERR_OUT_OF_RANGE(env, "keyData is too big"); return {}; } CHECK(args[offset + 2]->IsString()); Local<String> key_type = args[offset + 2].As<String>(); Utf8Value key_type_name(env->isolate(), key_type); DCHECK_IMPLIES(key_type->StringEquals(env->crypto_ec_string()), args[offset + 4]->IsString()); Utf8Value curve(env->isolate(), args[offset + 4]->IsString() ? args[offset + 4].As<String>() : String::Empty(env->isolate())); return ImportRawKey(env, key_data.data(), key_data.size(), format, key_type, *key_type_name, *curve, type); } KeyObjectData KeyObjectData::GetPrivateKeyFromJs( const v8::FunctionCallbackInfo<v8::Value>& args, unsigned int* offset, bool allow_key_object) { Environment* env = Environment::GetCurrent(args); // Store descriptor: data is a { uri, properties } object, format int is // STORE, and the passphrase slot carries an optional passphrase/PIN. if (args[*offset]->IsObject() && !IsAnyBufferSource(args[*offset]) && args[*offset + 1]->IsInt32() && static_cast<EVPKeyPointer::PKFormatType>( args[*offset + 1].As<Int32>()->Value()) == EVPKeyPointer::PKFormatType::STORE) { Local<Object> store = args[*offset].As<Object>(); Local<Value> uri_value; if (!store ->Get(env->context(), FIXED_ONE_BYTE_STRING(env->isolate(), "uri")) .ToLocal(&uri_value)) { return {}; } CHECK(uri_value->IsString()); Utf8Value uri(env->isolate(), uri_value); Local<Value> properties_value; if (!store ->Get(env->context(), FIXED_ONE_BYTE_STRING(env->isolate(), "properties")) .ToLocal(&properties_value)) { return {}; } std::string properties_storage; std::optional<std::string_view> properties; if (properties_value->IsString()) { Utf8Value properties_string(env->isolate(), properties_value); std::string_view properties_view = properties_string.ToStringView(); properties_storage.assign(properties_view.data(), properties_view.size()); properties = std::string_view(properties_storage); } else { CHECK(properties_value->IsNullOrUndefined()); } // OpenSSLStore is a global permission. URIs passed to STORE loaders can // contain credentials, so they must not be exposed through permission // errors or diagnostics. THROW_IF_INSUFFICIENT_PERMISSIONS( env, permission::PermissionScope::kOpenSSLStore, "", KeyObjectData()); std::optional<ArrayBufferOrViewContents<char>> passphrase_content; std::optional<ncrypto::Buffer<const char>> passphrase; if (IsAnyBufferSource(args[*offset + 3])) { passphrase_content.emplace(args[*offset + 3]); if (!passphrase_content->CheckSizeInt32()) [[unlikely]] { THROW_ERR_OUT_OF_RANGE(env, "passphrase is too big"); return {}; } passphrase = ncrypto::Buffer<const char>{ .data = passphrase_content->data(), .len = passphrase_content->size(), }; } else { CHECK(args[*offset + 3]->IsNullOrUndefined()); } *offset += 5; EVPKeyPointer::StorePrivateKeyConfig config{ .uri = uri.ToStringView(), .properties = properties, .passphrase = passphrase, }; auto res = EVPKeyPointer::TryLoadPrivateKeyFromStore(config); if (res) { return CreateAsymmetric(KeyType::kKeyTypePrivate, std::move(res.value)); } switch (res.error.value()) { case EVPKeyPointer::PKParseError::NEED_PASSPHRASE: ERR_clear_error(); THROW_ERR_MISSING_PASSPHRASE(env, "Passphrase required for encrypted key"); break; case EVPKeyPointer::PKParseError::NOT_RECOGNIZED: ERR_clear_error(); THROW_ERR_CRYPTO_OPERATION_FAILED( env, "No private key found through the OpenSSL STORE loader"); break; default: { static constexpr const char* msg = "Failed to load private key through an OpenSSL STORE loader"; // A loader may report a failure without leaving anything in the error // queue, in which case ThrowCryptoError() would produce a bare Error // carrying no code at all. if (res.openssl_error.value_or(0) == 0) { THROW_ERR_CRYPTO_OPERATION_FAILED(env, msg); } else { ThrowCryptoError(env, res.openssl_error.value(), msg); } break; } } return {}; } // Object formats: data is a JS Object (not buffer), format int determines // whether this is a JWK or an OpenSSL STORE loader descriptor. if (args[*offset]->IsObject() && !IsAnyBufferSource(args[*offset]) && args[*offset + 1]->IsInt32()) { auto format = static_cast<EVPKeyPointer::PKFormatType>( args[*offset + 1].As<Int32>()->Value()); if (format == EVPKeyPointer::PKFormatType::JWK) { auto data = ImportJWKFromArgs(env, args[*offset].As<Object>()); *offset += 5; return data; } } if (args[*offset]->IsString() || IsAnyBufferSource(args[*offset])) { // Raw format: buffer + raw format int. if (args[*offset + 1]->IsInt32()) { auto format = static_cast<EVPKeyPointer::PKFormatType>( args[*offset + 1].As<Int32>()->Value()); if (format == EVPKeyPointer::PKFormatType::RAW_PRIVATE || format == EVPKeyPointer::PKFormatType::RAW_SEED) { auto data = ImportRawKeyFromArgs(args, *offset); *offset += 5; return data; } } auto key = ByteSource::FromStringOrBuffer(env, args[(*offset)++]); EVPKeyPointer::PrivateKeyEncodingConfig config; if (!GetPrivateKeyEncodingFromJs(args, offset, kKeyContextInput) .To(&config)) { return {}; } // Skip the namedCurve argument (only used by raw format imports). (*offset)++; return TryParsePrivateKey( env, config, ncrypto::Buffer<const unsigned char>{ .data = reinterpret_cast<const unsigned char*>(key.data()), .len = key.size(), }); } CHECK(args[*offset]->IsObject() && allow_key_object); KeyObjectHandle* key; ASSIGN_OR_RETURN_UNWRAP(&key, args[*offset].As<Object>(), KeyObjectData()); CHECK_EQ(key->Data().GetKeyType(), kKeyTypePrivate); (*offset) += 5; return key->Data().addRef(); } KeyObjectData KeyObjectData::GetPublicOrPrivateKeyFromJs( const FunctionCallbackInfo<Value>& args, unsigned int* offset, bool allow_private_key_store) { Environment* env = Environment::GetCurrent(args); // JWK format: data is a JS Object (not buffer), format int is JWK. if (args[*offset]->IsObject() && !IsAnyBufferSource(args[*offset]) && args[*offset + 1]->IsInt32()) { auto format = static_cast<EVPKeyPointer::PKFormatType>( args[*offset + 1].As<Int32>()->Value()); if (format == EVPKeyPointer::PKFormatType::JWK) { auto data = ImportJWKFromArgs(env, args[*offset].As<Object>()); *offset += 5; return data; } if (format == EVPKeyPointer::PKFormatType::STORE) { if (allow_private_key_store) { return GetPrivateKeyFromJs(args, offset, false); } THROW_ERR_INVALID_ARG_VALUE( env, "URLs for OpenSSL STORE loaders are only accepted for private keys"); return {}; } } if (args[*offset]->IsString() || IsAnyBufferSource(args[*offset])) { // Raw format: buffer + raw format int. if (args[*offset + 1]->IsInt32()) { auto format = static_cast<EVPKeyPointer::PKFormatType>( args[*offset + 1].As<Int32>()->Value()); if (format == EVPKeyPointer::PKFormatType::RAW_PUBLIC || format == EVPKeyPointer::PKFormatType::RAW_PRIVATE || format == EVPKeyPointer::PKFormatType::RAW_SEED) { auto data = ImportRawKeyFromArgs(args, *offset); *offset += 5; return data; } } ArrayBufferOrViewContents<char> data(args[(*offset)++]); if (!data.CheckSizeInt32()) [[unlikely]] { THROW_ERR_OUT_OF_RANGE(env, "keyData is too big"); return {}; } EVPKeyPointer::PrivateKeyEncodingConfig config; if (!KeyObjectData::GetPrivateKeyEncodingFromJs( args, offset, kKeyContextInput) .To(&config)) { return {}; } // Skip the namedCurve argument (only used by raw format imports). (*offset)++; ncrypto::Buffer<const unsigned char> buffer = { .data = reinterpret_cast<const unsigned char*>(data.data()), .len = data.size(), }; if (config.format == EVPKeyPointer::PKFormatType::PEM) { // For PEM, we can easily determine whether it is a public or private key // by looking for the respective PEM tags. auto res = EVPKeyPointer::TryParsePublicKeyPEM(buffer); if (res) { return CreateAsymmetric(kKeyTypePublic, std::move(res.value)); } if (res.error.value() == EVPKeyPointer::PKParseError::NOT_RECOGNIZED) { return TryParsePrivateKey(env, config, buffer); } ThrowCryptoError( env, res.openssl_error.value_or(0), "Failed to read asymmetric key"); return {}; } // For DER, the type determines how to parse it. SPKI, PKCS#8 and SEC1 are // easy, but PKCS#1 can be a public key or a private key. static const auto is_public = [](const auto& config, const auto& buffer) -> bool { switch (config.type) { case EVPKeyPointer::PKEncodingType::PKCS1: return !EVPKeyPointer::IsRSAPrivateKey(buffer); case EVPKeyPointer::PKEncodingType::SPKI: return true; case EVPKeyPointer::PKEncodingType::PKCS8: return false; case EVPKeyPointer::PKEncodingType::SEC1: return false; default: UNREACHABLE("Invalid key encoding type"); } }; if (is_public(config, buffer)) { auto res = EVPKeyPointer::TryParsePublicKey(config, buffer); if (res) { return CreateAsymmetric(KeyType::kKeyTypePublic, std::move(res.value)); } ThrowCryptoError( env, res.openssl_error.value_or(0), "Failed to read asymmetric key"); return {}; } return TryParsePrivateKey(env, config, buffer); } CHECK(args[*offset]->IsObject()); KeyObjectHandle* key = BaseObject::Unwrap<KeyObjectHandle>(args[*offset].As<Object>()); CHECK_NOT_NULL(key); CHECK_NE(key->Data().GetKeyType(), kKeyTypeSecret); (*offset) += 5; return key->Data().addRef(); } KeyObjectData KeyObjectData::GetParsedKey(KeyType type, Environment* env, EVPKeyPointer&& pkey, ParseKeyResult ret, const char* default_msg) { MarkPopErrorOnReturn mark_pop_error_on_return; switch (ret) { case ParseKeyResult::kParseKeyOk: { return CreateAsymmetric(type, std::move(pkey)); } case ParseKeyResult::kParseKeyNeedPassphrase: { THROW_ERR_MISSING_PASSPHRASE(env, "Passphrase required for encrypted key"); return {}; } default: { ThrowCryptoError(env, mark_pop_error_on_return.peekError(), default_msg); return {}; } } } KeyObjectData::KeyObjectData(std::nullptr_t) : key_type_(KeyType::kKeyTypeSecret) {} KeyObjectData::KeyObjectData(ByteSource symmetric_key) : key_type_(KeyType::kKeyTypeSecret), mutex_(std::make_shared<Mutex>()), 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))) {} void KeyObjectData::Data::MemoryInfo(MemoryTracker* tracker) const { if (asymmetric_key) { tracker->TrackFieldWithSize("key", kSizeOf_EVP_PKEY + asymmetric_key.rawPublicKeySize() + asymmetric_key.rawPrivateKeySize()); } else { tracker->TraitTrackInline(symmetric_key, "symmetric_key"); } } 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)); } KeyObjectData KeyObjectData::CreateAsymmetric(KeyType key_type, EVPKeyPointer&& pkey) { CHECK(pkey); return KeyObjectData(key_type, std::move(pkey)); } KeyType KeyObjectData::GetKeyType() const { CHECK(data_); return key_type_; } const EVPKeyPointer& KeyObjectData::GetAsymmetricKey() const { CHECK_NE(key_type_, kKeyTypeSecret); CHECK(data_); return data_->asymmetric_key; } const char* KeyObjectData::GetSymmetricKey() const { CHECK_EQ(key_type_, kKeyTypeSecret); CHECK(data_); return data_->symmetric_key.data<char>(); } size_t KeyObjectData::GetSymmetricKeySize() const { CHECK_EQ(key_type_, kKeyTypeSecret); CHECK(data_); return data_->symmetric_key.size(); } bool KeyObjectHandle::HasInstance(Environment* env, Local<Value> value) { auto t = env->crypto_key_object_handle_constructor(); return !t.IsEmpty() && t->HasInstance(value); } Local<Function> KeyObjectHandle::Initialize(Environment* env) { auto templ = env->crypto_key_object_handle_constructor(); if (templ.IsEmpty()) { Isolate* isolate = env->isolate(); templ = NewFunctionTemplate(isolate, New); templ->InstanceTemplate()->SetInternalFieldCount( KeyObjectHandle::kInternalFieldCount); SetProtoMethod(isolate, templ, "init", Init); SetProtoMethodNoSideEffect(isolate, templ, "getKeyType", GetKeyType); SetProtoMethodNoSideEffect( isolate, templ, "getSymmetricKeySize", GetSymmetricKeySize); SetProtoMethodNoSideEffect( isolate, templ, "getAsymmetricKeyType", GetAsymmetricKeyType); SetProtoMethodNoSideEffect( isolate, templ, "checkEcKeyData", CheckEcKeyData); SetProtoMethod(isolate, templ, "export", Export); SetProtoMethod(isolate, templ, "exportJwk", ExportJWK); SetProtoMethodNoSideEffect(isolate, templ, "rawPublicKey", RawPublicKey); SetProtoMethodNoSideEffect(isolate, templ, "rawPrivateKey", RawPrivateKey); SetProtoMethodNoSideEffect(isolate, templ, "rawSeed", RawSeed); SetProtoMethodNoSideEffect( isolate, templ, "exportECPublicRaw", ExportECPublicRaw); SetProtoMethodNoSideEffect( isolate, templ, "exportECPrivateRaw", ExportECPrivateRaw); SetProtoMethod(isolate, templ, "keyDetail", GetKeyDetail); SetProtoMethod(isolate, templ, "equals", Equals); env->set_crypto_key_object_handle_constructor(templ); } return templ->GetFunction(env->context()).ToLocalChecked(); } void KeyObjectHandle::RegisterExternalReferences( ExternalReferenceRegistry* registry) { registry->Register(New); registry->Register(Init); registry->Register(GetKeyType); registry->Register(GetSymmetricKeySize); registry->Register(GetAsymmetricKeyType); registry->Register(CheckEcKeyData); registry->Register(Export); registry->Register(ExportJWK); registry->Register(RawPublicKey); registry->Register(RawPrivateKey); registry->Register(RawSeed); registry->Register(ExportECPublicRaw); registry->Register(ExportECPrivateRaw); registry->Register(GetKeyDetail); registry->Register(Equals); } MaybeLocal<Object> KeyObjectHandle::Create(Environment* env, const KeyObjectData& data) { Local<Object> obj; Local<Function> ctor = KeyObjectHandle::Initialize(env); CHECK(!env->crypto_key_object_handle_constructor().IsEmpty()); if (!ctor->NewInstance(env->context(), 0, nullptr).ToLocal(&obj)) { return {}; } KeyObjectHandle* key = Unwrap<KeyObjectHandle>(obj); CHECK_NOT_NULL(key); key->data_ = data.addRef(); return obj; } const KeyObjectData& KeyObjectHandle::Data() { return data_; } void KeyObjectHandle::New(const FunctionCallbackInfo<Value>& args) { CHECK(args.IsConstructCall()); Environment* env = Environment::GetCurrent(args); new KeyObjectHandle(env, args.This()); } KeyObjectHandle::KeyObjectHandle(Environment* env, Local<Object> wrap) : BaseObject(env, wrap) { MakeWeak(); } void KeyObjectHandle::Init(const FunctionCallbackInfo<Value>& args) { KeyObjectHandle* key; ASSIGN_OR_RETURN_UNWRAP(&key, args.This()); MarkPopErrorOnReturn mark_pop_error_on_return; Environment* env = Environment::GetCurrent(args); CHECK(args[0]->IsInt32()); KeyType type = static_cast<KeyType>(args[0].As<Uint32>()->Value()); unsigned int offset; switch (type) { case kKeyTypeSecret: { if (args.Length() == 5 && args[2]->IsInt32()) { auto format = static_cast<EVPKeyPointer::PKFormatType>( args[2].As<Int32>()->Value()); if (format == EVPKeyPointer::PKFormatType::JWK) { CHECK(args[1]->IsObject()); key->data_ = ImportJWKSecretKey(env, args[1].As<Object>()); break; } } CHECK_EQ(args.Length(), 2); ArrayBufferOrViewContents<char> buf(args[1]); key->data_ = KeyObjectData::CreateSecret(buf.ToCopy()); break; } case kKeyTypePublic: case kKeyTypePrivate: { CHECK_EQ(args.Length(), 6); // Check if this is a raw or JWK format import: // args: [keyType, buffer/object, formatInt, typeString/null, // passphrase/null, namedCurve/null] if (args[2]->IsInt32()) { auto format = static_cast<EVPKeyPointer::PKFormatType>( args[2].As<Int32>()->Value()); if (format == EVPKeyPointer::PKFormatType::RAW_PUBLIC || format == EVPKeyPointer::PKFormatType::RAW_PRIVATE || format == EVPKeyPointer::PKFormatType::RAW_SEED) { auto data = ImportRawKeyFromArgs(args, 1); if (!data) return; if (type == kKeyTypePublic && data.GetKeyType() == kKeyTypePrivate) { key->data_ = data.addRefWithType(kKeyTypePublic); } else { key->data_ = std::move(data); } break; } if (format == EVPKeyPointer::PKFormatType::JWK) { CHECK(args[1]->IsObject()); key->data_ = ImportJWKFromArgs(env, args[1].As<Object>()); if (!key->data_) return; if (type == kKeyTypePublic && key->data_.GetKeyType() == kKeyTypePrivate) { key->data_ = key->data_.addRefWithType(kKeyTypePublic); } else if (type == kKeyTypePrivate && key->data_.GetKeyType() == kKeyTypePublic) { THROW_ERR_CRYPTO_INVALID_JWK( env, "JWK does not contain private key material"); return; } args.GetReturnValue().Set(key->data_.GetKeyType()); break; } } offset = 1; if (type == kKeyTypePublic) { auto data = KeyObjectData::GetPublicOrPrivateKeyFromJs(args, &offset); if (!data) return; key->data_ = data.addRefWithType(kKeyTypePublic); } else { if (auto data = KeyObjectData::GetPrivateKeyFromJs(args, &offset, false)) { key->data_ = std::move(data); } } break; } default: UNREACHABLE(); } } void KeyObjectHandle::GetKeyType(const FunctionCallbackInfo<Value>& args) { KeyObjectHandle* key; ASSIGN_OR_RETURN_UNWRAP(&key, args.This()); args.GetReturnValue().Set( Uint32::NewFromUnsigned(args.GetIsolate(), key->Data().GetKeyType())); } void KeyObjectHandle::Equals(const FunctionCallbackInfo<Value>& args) { KeyObjectHandle* self_handle; KeyObjectHandle* arg_handle; ASSIGN_OR_RETURN_UNWRAP(&self_handle, args.This()); ASSIGN_OR_RETURN_UNWRAP(&arg_handle, args[0].As<Object>()); const auto& key = self_handle->Data(); const auto& key2 = arg_handle->Data(); KeyType key_type = key.GetKeyType(); CHECK_EQ(key_type, key2.GetKeyType()); bool ret; switch (key_type) { case kKeyTypeSecret: { size_t size = key.GetSymmetricKeySize(); if (size == key2.GetSymmetricKeySize()) { ret = CRYPTO_memcmp( key.GetSymmetricKey(), key2.GetSymmetricKey(), size) == 0; } else { ret = false; } break; } case kKeyTypePublic: // Fall through case kKeyTypePrivate: { EVP_PKEY* pkey = key.GetAsymmetricKey().get(); EVP_PKEY* pkey2 = key2.GetAsymmetricKey().get(); #if OPENSSL_VERSION_MAJOR >= 3 int ok = EVP_PKEY_eq(pkey, pkey2); #else int ok = EVP_PKEY_cmp(pkey, pkey2); #endif if (ok == -2) { Environment* env = Environment::GetCurrent(args); return THROW_ERR_CRYPTO_UNSUPPORTED_OPERATION(env); } ret = ok == 1; break; } default: UNREACHABLE("unsupported key type"); } args.GetReturnValue().Set(ret); } void KeyObjectHandle::GetKeyDetail(const FunctionCallbackInfo<Value>& args) { Environment* env = Environment::GetCurrent(args); KeyObjectHandle* key; ASSIGN_OR_RETURN_UNWRAP(&key, args.This()); CHECK(args[0]->IsObject()); const auto& data = key->Data(); if (data.GetKeyType() == kKeyTypeSecret) { if (GetSecretKeyDetail(env, data, args[0].As<Object>())) [[likely]] { args.GetReturnValue().Set(args[0]); } return; } if (GetAsymmetricKeyDetail(env, data, args[0].As<Object>())) [[likely]] { args.GetReturnValue().Set(args[0]); } } Local<Value> KeyObjectHandle::GetAsymmetricKeyType() const { switch (data_.GetAsymmetricKey().id()) { case EVP_PKEY_RSA: return env()->crypto_rsa_string(); case EVP_PKEY_RSA_PSS: return env()->crypto_rsa_pss_string(); case EVP_PKEY_DSA: return env()->crypto_dsa_string(); case EVP_PKEY_DH: return env()->crypto_dh_string(); case EVP_PKEY_EC: return env()->crypto_ec_string(); case EVP_PKEY_ED25519: return env()->crypto_ed25519_string(); case EVP_PKEY_ED448: return env()->crypto_ed448_string(); case EVP_PKEY_X25519: return env()->crypto_x25519_string(); case EVP_PKEY_X448: return env()->crypto_x448_string(); #if OPENSSL_WITH_PQC default: return GetPqcAsymmetricKeyType(env(), data_.GetAsymmetricKey().id()); #else default: return Undefined(env()->isolate()); #endif } } void KeyObjectHandle::GetAsymmetricKeyType( const FunctionCallbackInfo<Value>& args) { KeyObjectHandle* key; ASSIGN_OR_RETURN_UNWRAP(&key, args.This()); args.GetReturnValue().Set(key->GetAsymmetricKeyType()); } bool KeyObjectHandle::CheckEcKeyData() const { MarkPopErrorOnReturn mark_pop_error_on_return; const auto& key = data_.GetAsymmetricKey(); EVPKeyCtxPointer ctx = key.newCtx(); CHECK(ctx); CHECK_EQ(key.id(), EVP_PKEY_EC); return data_.GetKeyType() == kKeyTypePrivate ? ctx.privateCheck() : ctx.publicCheck(); } void KeyObjectHandle::CheckEcKeyData(const FunctionCallbackInfo<Value>& args) { KeyObjectHandle* key; ASSIGN_OR_RETURN_UNWRAP(&key, args.This()); args.GetReturnValue().Set(key->CheckEcKeyData()); } void KeyObjectHandle::GetSymmetricKeySize( const FunctionCallbackInfo<Value>& args) { KeyObjectHandle* key; ASSIGN_OR_RETURN_UNWRAP(&key, args.This()); args.GetReturnValue().Set( static_cast<uint32_t>(key->Data().GetSymmetricKeySize())); } void KeyObjectHandle::Export(const FunctionCallbackInfo<Value>& args) { KeyObjectHandle* key; ASSIGN_OR_RETURN_UNWRAP(&key, args.This()); KeyType type = key->Data().GetKeyType(); unsigned int offset = 0; Local<Value> result; if (type == kKeyTypeSecret) { if (key->ExportSecretKey().ToLocal(&result)) [[likely]] { args.GetReturnValue().Set(result); } return; } if (type == kKeyTypePublic) { EVPKeyPointer::PublicKeyEncodingConfig config; if (!KeyObjectData::GetPublicKeyEncodingFromJs( args, &offset, kKeyContextExport) .To(&config)) { return; } CHECK_EQ(offset, static_cast<unsigned int>(args.Length())); if (key->ExportPublicKey(config).ToLocal(&result)) [[likely]] { args.GetReturnValue().Set(result); } return; } CHECK_EQ(type, kKeyTypePrivate); EVPKeyPointer::PrivateKeyEncodingConfig config; if (!KeyObjectData::GetPrivateKeyEncodingFromJs( args, &offset, kKeyContextExport) .To(&config)) { return; } CHECK_EQ(offset, static_cast<unsigned int>(args.Length())); if (key->ExportPrivateKey(config).ToLocal(&result)) [[likely]] { args.GetReturnValue().Set(result); } } MaybeLocal<Value> KeyObjectHandle::ExportSecretKey() const { return Buffer::Copy( env(), data_.GetSymmetricKey(), data_.GetSymmetricKeySize()) .FromMaybe(Local<Value>()); } MaybeLocal<Value> KeyObjectHandle::ExportPublicKey( const EVPKeyPointer::PublicKeyEncodingConfig& config) const { return WritePublicKey(env(), data_.GetAsymmetricKey(), config); } MaybeLocal<Value> KeyObjectHandle::ExportPrivateKey( const EVPKeyPointer::PrivateKeyEncodingConfig& config) const { return WritePrivateKey(env(), data_.GetAsymmetricKey(), config); } void KeyObjectHandle::RawPublicKey( const v8::FunctionCallbackInfo<v8::Value>& args) { Environment* env = Environment::GetCurrent(args); KeyObjectHandle* key; ASSIGN_OR_RETURN_UNWRAP(&key, args.This()); const KeyObjectData& data = key->Data(); CHECK_NE(data.GetKeyType(), kKeyTypeSecret); Mutex::ScopedLock lock(data.mutex()); const auto& pkey = data.GetAsymmetricKey(); const int id = pkey.id(); bool is_raw_supported = id == EVP_PKEY_ED25519 || id == EVP_PKEY_ED448 || id == EVP_PKEY_X25519 || id == EVP_PKEY_X448; #if OPENSSL_WITH_PQC is_raw_supported = is_raw_supported || IsPqcKeyId(id); #endif if (!is_raw_supported) { return THROW_ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS(env); } auto raw_data = pkey.rawPublicKey(); if (!raw_data) { return THROW_ERR_CRYPTO_OPERATION_FAILED(env, "Failed to get raw public key"); } args.GetReturnValue().Set( Buffer::Copy(env, raw_data.get<const char>(), raw_data.size()) .FromMaybe(Local<Value>())); } void KeyObjectHandle::RawPrivateKey( const v8::FunctionCallbackInfo<v8::Value>& args) { Environment* env = Environment::GetCurrent(args); KeyObjectHandle* key; ASSIGN_OR_RETURN_UNWRAP(&key, args.This()); const KeyObjectData& data = key->Data(); CHECK_EQ(data.GetKeyType(), kKeyTypePrivate); Mutex::ScopedLock lock(data.mutex()); const auto& pkey = data.GetAsymmetricKey(); const int id = pkey.id(); bool is_raw_supported = id == EVP_PKEY_ED25519 || id == EVP_PKEY_ED448 || id == EVP_PKEY_X25519 || id == EVP_PKEY_X448; #if OPENSSL_WITH_PQC is_raw_supported = is_raw_supported || IsPqcRawPrivateKeyId(id); #endif if (!is_raw_supported) { return THROW_ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS(env); } auto raw_data = pkey.rawPrivateKey(); if (!raw_data) { return THROW_ERR_CRYPTO_OPERATION_FAILED(env, "Failed to get raw private key"); } args.GetReturnValue().Set( Buffer::Copy(env, raw_data.get<const char>(), raw_data.size()) .FromMaybe(Local<Value>())); } void KeyObjectHandle::ExportECPublicRaw( const v8::FunctionCallbackInfo<v8::Value>& args) { Environment* env = Environment::GetCurrent(args); KeyObjectHandle* key; ASSIGN_OR_RETURN_UNWRAP(&key, args.This()); const KeyObjectData& data = key->Data(); CHECK_NE(data.GetKeyType(), kKeyTypeSecret); Mutex::ScopedLock lock(data.mutex()); const auto& m_pkey = data.GetAsymmetricKey(); if (m_pkey.id() != EVP_PKEY_EC) { return THROW_ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS(env); } ECKeyPointer ec_key(m_pkey); if (!ec_key) return THROW_ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS(env); // A provider-backed key need not expose its public point. if (ec_key.getPublicKey() == nullptr) { return THROW_ERR_CRYPTO_OPERATION_FAILED(env, "Failed to export EC public key"); } CHECK(args[0]->IsInt32()); auto form = static_cast<point_conversion_form_t>(args[0].As<Int32>()->Value()); const auto group = ec_key.getGroup(); const auto point = ec_key.getPublicKey(); Local<Object> buf; if (!ECPointToBuffer(env, group, point, form).ToLocal(&buf)) return; args.GetReturnValue().Set(buf); } void KeyObjectHandle::ExportECPrivateRaw( const v8::FunctionCallbackInfo<v8::Value>& args) { Environment* env = Environment::GetCurrent(args); KeyObjectHandle* key; ASSIGN_OR_RETURN_UNWRAP(&key, args.This()); const KeyObjectData& data = key->Data(); CHECK_EQ(data.GetKeyType(), kKeyTypePrivate); Mutex::ScopedLock lock(data.mutex()); const auto& m_pkey = data.GetAsymmetricKey(); if (m_pkey.id() != EVP_PKEY_EC) { return THROW_ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS(env); } ECKeyPointer ec_key(m_pkey); if (!ec_key) return THROW_ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS(env); const BIGNUM* private_key = ec_key.getPrivateKey(); if (private_key == nullptr) { return THROW_ERR_CRYPTO_OPERATION_FAILED(env, "Failed to export EC private key"); } const auto group = ec_key.getGroup(); auto order = BignumPointer::New(); if (!order || !EC_GROUP_get_order(group, order.get(), nullptr)) { return THROW_ERR_CRYPTO_OPERATION_FAILED(env, "Failed to export EC private key"); } auto buf = BignumPointer::EncodePadded(private_key, order.byteLength()); if (!buf) { return THROW_ERR_CRYPTO_OPERATION_FAILED(env, "Failed to export EC private key"); } args.GetReturnValue().Set(Buffer::Copy(env, buf.get<const char>(), buf.size()) .FromMaybe(Local<Value>())); } void KeyObjectHandle::RawSeed(const v8::FunctionCallbackInfo<v8::Value>& args) { Environment* env = Environment::GetCurrent(args); KeyObjectHandle* key; ASSIGN_OR_RETURN_UNWRAP(&key, args.This()); const KeyObjectData& data = key->Data(); CHECK_EQ(data.GetKeyType(), kKeyTypePrivate); #if OPENSSL_WITH_PQC Mutex::ScopedLock lock(data.mutex()); const auto& pkey = data.GetAsymmetricKey(); if (!IsPqcSeedKeyId(pkey.id())) { return THROW_ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS(env); } auto raw_data = pkey.rawSeed(); if (!raw_data) { return THROW_ERR_CRYPTO_OPERATION_FAILED(env, "Failed to get raw seed"); } args.GetReturnValue().Set( Buffer::Copy(env, raw_data.get<const char>(), raw_data.size()) .FromMaybe(Local<Value>())); #else return THROW_ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS(env); #endif } void KeyObjectHandle::ExportJWK( const v8::FunctionCallbackInfo<v8::Value>& args) { Environment* env = Environment::GetCurrent(args); KeyObjectHandle* key; ASSIGN_OR_RETURN_UNWRAP(&key, args.This()); CHECK(args[0]->IsObject()); CHECK(args[1]->IsBoolean()); if (ExportJWKInner(env, key->Data(), args[0], args[1]->IsTrue())) { args.GetReturnValue().Set(args[0]); } else if (!env->isolate()->HasPendingException()) { THROW_ERR_CRYPTO_OPERATION_FAILED(env, "Failed to export JWK"); } } void NativeKeyObject::Initialize(Environment* env, Local<Object> target) { SetMethod(env->context(), target, "createNativeKeyObjectClass", NativeKeyObject::CreateNativeKeyObjectClass); SetMethod( env->context(), target, "getKeyObjectSlots", NativeKeyObject::GetSlots); } void NativeKeyObject::RegisterExternalReferences( ExternalReferenceRegistry* registry) { registry->Register(NativeKeyObject::CreateNativeKeyObjectClass); registry->Register(NativeKeyObject::GetSlots); registry->Register(NativeKeyObject::New); } bool NativeKeyObject::HasInstance(Environment* env, Local<Value> value) { auto t = env->crypto_key_object_constructor_template(); return !t.IsEmpty() && t->HasInstance(value); } void NativeKeyObject::New(const FunctionCallbackInfo<Value>& args) { Environment* env = Environment::GetCurrent(args); CHECK_EQ(args.Length(), 1); CHECK(KeyObjectHandle::HasInstance(env, args[0])); KeyObjectHandle* handle = Unwrap<KeyObjectHandle>(args[0].As<Object>()); CHECK_NOT_NULL(handle); new NativeKeyObject(env, args.This(), handle->Data()); } void NativeKeyObject::CreateNativeKeyObjectClass( const FunctionCallbackInfo<Value>& args) { Environment* env = Environment::GetCurrent(args); Isolate* isolate = env->isolate(); CHECK_EQ(args.Length(), 1); Local<Value> callback = args[0]; CHECK(callback->IsFunction()); Local<FunctionTemplate> t = NewFunctionTemplate(isolate, NativeKeyObject::New); t->InstanceTemplate()->SetInternalFieldCount( NativeKeyObject::kInternalFieldCount); CHECK(env->crypto_key_object_constructor_template().IsEmpty()); env->set_crypto_key_object_constructor_template(t); Local<Value> ctor; if (!t->GetFunction(env->context()).ToLocal(&ctor)) return; Local<Value> recv = Undefined(env->isolate()); Local<Value> ret_v; if (!callback.As<Function>()->Call( env->context(), recv, 1, &ctor).ToLocal(&ret_v)) { return; } Local<Array> ret = ret_v.As<Array>(); if (!ret->Get(env->context(), 1).ToLocal(&ctor)) return; env->set_crypto_key_object_secret_constructor(ctor.As<Function>()); if (!ret->Get(env->context(), 2).ToLocal(&ctor)) return; env->set_crypto_key_object_public_constructor(ctor.As<Function>()); if (!ret->Get(env->context(), 3).ToLocal(&ctor)) return; env->set_crypto_key_object_private_constructor(ctor.As<Function>()); args.GetReturnValue().Set(ret); } // Returns the key's native hidden slot tuple as a single Array: // [type enum, handle]. JS-side helpers call this once per key to prime // a per-instance cache; derived metadata is appended lazily from JS by // calling methods on the returned KeyObjectHandle. void NativeKeyObject::GetSlots(const FunctionCallbackInfo<Value>& args) { Environment* env = Environment::GetCurrent(args); CHECK_EQ(args.Length(), 1); if (!HasInstance(env, args[0])) { THROW_ERR_INVALID_THIS(env, "Value of \"this\" must be of type KeyObject"); return; } NativeKeyObject* native = Unwrap<NativeKeyObject>(args[0].As<Object>()); CHECK_NOT_NULL(native); Local<Object> handle; if (!KeyObjectHandle::Create(env, native->handle_data_).ToLocal(&handle)) { return; } Isolate* isolate = env->isolate(); Local<Value> slots[] = { Uint32::NewFromUnsigned(isolate, native->handle_data_.GetKeyType()), handle, }; args.GetReturnValue().Set(Array::New(isolate, slots, arraysize(slots))); } BaseObjectPtr<BaseObject> NativeKeyObject::KeyObjectTransferData::Deserialize( Environment* env, Local<Context> context, std::unique_ptr<worker::TransferData> self) { if (context != env->context()) { THROW_ERR_MESSAGE_TARGET_CONTEXT_UNAVAILABLE(env); return {}; } Local<Value> handle; if (!KeyObjectHandle::Create(env, data_).ToLocal(&handle)) return {}; Local<Function> key_ctor; Local<Value> arg = env->internal_crypto_keys_string(); if (env->builtin_module_require() ->Call(context, Null(env->isolate()), 1, &arg) .IsEmpty()) { return {}; } switch (data_.GetKeyType()) { case kKeyTypeSecret: key_ctor = env->crypto_key_object_secret_constructor(); break; case kKeyTypePublic: key_ctor = env->crypto_key_object_public_constructor(); break; case kKeyTypePrivate: key_ctor = env->crypto_key_object_private_constructor(); break; default: UNREACHABLE(); } Local<Value> key; if (!key_ctor->NewInstance(context, 1, &handle).ToLocal(&key)) return {}; return BaseObjectPtr<BaseObject>(Unwrap<NativeKeyObject>(key.As<Object>())); } BaseObject::TransferMode NativeKeyObject::GetTransferMode() const { return BaseObject::TransferMode::kCloneable; } std::unique_ptr<worker::TransferData> NativeKeyObject::CloneForMessaging() const { return std::make_unique<KeyObjectTransferData>(handle_data_); } void NativeCryptoKey::Initialize(Environment* env, Local<Object> target) { SetMethod(env->context(), target, "createCryptoKeyClass", NativeCryptoKey::CreateCryptoKeyClass); SetMethod( env->context(), target, "getCryptoKeySlots", NativeCryptoKey::GetSlots); } void NativeCryptoKey::RegisterExternalReferences( ExternalReferenceRegistry* registry) { registry->Register(NativeCryptoKey::CreateCryptoKeyClass); registry->Register(NativeCryptoKey::GetSlots); registry->Register(NativeCryptoKey::New); } namespace { // Verifies that `value` is a `NativeCryptoKey` by checking whether it // was constructed from the Environment's `NativeCryptoKey` template. bool IsNativeCryptoKey(Environment* env, Local<Value> value) { auto t = env->crypto_cryptokey_constructor_template(); return !t.IsEmpty() && t->HasInstance(value); } } // namespace bool NativeCryptoKey::HasInstance(Environment* env, Local<Value> value) { return IsNativeCryptoKey(env, value); } MaybeLocal<Value> NativeCryptoKey::Create(Environment* env, const KeyObjectData& data, Local<Value> algorithm, uint32_t usages_mask, bool extractable) { Local<Context> context = env->context(); Isolate* isolate = env->isolate(); CHECK(algorithm->IsObject()); Local<Object> handle; if (!KeyObjectHandle::Create(env, data).ToLocal(&handle)) return {}; if (env->crypto_internal_cryptokey_constructor().IsEmpty()) { Local<Value> arg = env->internal_crypto_keys_string(); if (env->builtin_module_require() ->Call(context, Null(isolate), 1, &arg) .IsEmpty()) { return {}; } } Local<Function> cryptokey_ctor = env->crypto_internal_cryptokey_constructor(); CHECK(!cryptokey_ctor.IsEmpty()); Local<Value> ctor_args[] = { handle, algorithm, Uint32::NewFromUnsigned(isolate, usages_mask), Boolean::New(isolate, extractable), }; return cryptokey_ctor->NewInstance(context, arraysize(ctor_args), ctor_args); } void NativeCryptoKey::New(const FunctionCallbackInfo<Value>& args) { Environment* env = Environment::GetCurrent(args); CHECK_EQ(args.Length(), 4); // args[0] is a KeyObjectHandle; we keep its KeyObjectData directly. // args[1] is the algorithm dictionary object. // args[2] is the usages mask. // args[3] is the extractable boolean. // // args[1] is undefined only when called from // CryptoKeyTransferData::Deserialize for a partially-initialized // CryptoKey: algorithm/usages mask/extractable get filled in afterwards // by FinalizeTransferRead before any JS can see the object. // // This constructor is not exposed to user JS - the public CryptoKey // class throws from its constructor and InternalCryptoKey is kept // in a module-closure. CHECK(KeyObjectHandle::HasInstance(env, args[0])); KeyObjectHandle* handle = Unwrap<KeyObjectHandle>(args[0].As<Object>()); CHECK_NOT_NULL(handle); auto* native = new NativeCryptoKey(env, args.This(), handle->Data()); if (!args[1]->IsUndefined()) { CHECK(args[1]->IsObject()); CHECK(args[2]->IsUint32()); CHECK(args[3]->IsBoolean()); args.This()->SetInternalField(kAlgorithmField, args[1]); native->usages_mask_ = args[2].As<Uint32>()->Value(); native->extractable_ = args[3]->IsTrue(); } } void NativeCryptoKey::CreateCryptoKeyClass( const FunctionCallbackInfo<Value>& args) { Environment* env = Environment::GetCurrent(args); Isolate* isolate = env->isolate(); CHECK_EQ(args.Length(), 1); Local<Value> callback = args[0]; CHECK(callback->IsFunction()); Local<FunctionTemplate> t = NewFunctionTemplate(isolate, NativeCryptoKey::New); t->InstanceTemplate()->SetInternalFieldCount( NativeCryptoKey::kInternalFieldCount); CHECK(env->crypto_cryptokey_constructor_template().IsEmpty()); env->set_crypto_cryptokey_constructor_template(t); Local<Value> ctor; if (!t->GetFunction(env->context()).ToLocal(&ctor)) return; Local<Value> recv = Undefined(env->isolate()); Local<Value> ret_v; if (!callback.As<Function>() ->Call(env->context(), recv, 1, &ctor) .ToLocal(&ret_v)) { return; } Local<Array> ret = ret_v.As<Array>(); Local<Value> internal_ctor_v; if (!ret->Get(env->context(), 1).ToLocal(&internal_ctor_v)) return; CHECK(env->crypto_internal_cryptokey_constructor().IsEmpty()); env->set_crypto_internal_cryptokey_constructor( internal_ctor_v.As<Function>()); args.GetReturnValue().Set(ret); } // Returns all of the key's internal slot values as a single Array: // [type enum, extractable, algorithm, usages mask, handle]. JS-side helpers // call this once per key to prime a per-instance cache, so subsequent // reads don't need to cross into C++ at all. void NativeCryptoKey::GetSlots(const FunctionCallbackInfo<Value>& args) { Environment* env = Environment::GetCurrent(args); CHECK_EQ(args.Length(), 1); if (!HasInstance(env, args[0])) { THROW_ERR_INVALID_THIS(env, "Value of \"this\" must be of type CryptoKey"); return; } Local<Object> obj = args[0].As<Object>(); NativeCryptoKey* native = Unwrap<NativeCryptoKey>(obj); CHECK_NOT_NULL(native); Local<Object> handle; if (!KeyObjectHandle::Create(env, native->handle_data_).ToLocal(&handle)) { return; } Local<Value> algorithm = obj->GetInternalField(kAlgorithmField).As<Value>(); CHECK(algorithm->IsObject()); Isolate* isolate = env->isolate(); Local<Value> slots[] = { Uint32::NewFromUnsigned(isolate, native->handle_data_.GetKeyType()), v8::Boolean::New(isolate, native->extractable_), algorithm, Uint32::NewFromUnsigned(isolate, native->usages_mask_), handle, }; args.GetReturnValue().Set(Array::New(isolate, slots, arraysize(slots))); } BaseObject::TransferMode NativeCryptoKey::GetTransferMode() const { return BaseObject::TransferMode::kCloneable; } std::unique_ptr<worker::TransferData> NativeCryptoKey::CloneForMessaging() const { Isolate* isolate = env()->isolate(); Local<Object> obj = object(); Local<Value> algorithm_v = obj->GetInternalField(kAlgorithmField).As<Value>(); CHECK(algorithm_v->IsObject()); v8::Global<Object> algorithm_copy(isolate, algorithm_v.As<Object>()); return std::make_unique<CryptoKeyTransferData>( handle_data_, std::move(algorithm_copy), usages_mask_, extractable_); } Maybe<void> NativeCryptoKey::FinalizeTransferRead( Local<Context> context, v8::ValueDeserializer* deserializer) { Local<Value> bundle_v; if (!deserializer->ReadValue(context).ToLocal(&bundle_v)) { return Nothing<void>(); } CHECK(bundle_v->IsObject()); Local<Object> bundle = bundle_v.As<Object>(); Local<Object> obj = object(); // The partially-initialized object produced by // CryptoKeyTransferData::Deserialize should not have algorithm set yet. CHECK(obj->GetInternalField(kAlgorithmField).As<Value>()->IsUndefined()); Local<Value> algorithm_v; if (!bundle->Get(context, env()->algorithm_string()).ToLocal(&algorithm_v)) { return Nothing<void>(); } CHECK(algorithm_v->IsObject()); obj->SetInternalField(kAlgorithmField, algorithm_v); Local<Value> usages_v; if (!bundle->Get(context, env()->usages_string()).ToLocal(&usages_v)) { return Nothing<void>(); } CHECK(usages_v->IsUint32()); usages_mask_ = usages_v.As<Uint32>()->Value(); Local<Value> extractable_v; if (!bundle->Get(context, env()->extractable_string()) .ToLocal(&extractable_v)) { return Nothing<void>(); } CHECK(extractable_v->IsBoolean()); extractable_ = extractable_v->IsTrue(); return v8::JustVoid(); } Maybe<bool> NativeCryptoKey::CryptoKeyTransferData::FinalizeTransferWrite( Local<Context> context, v8::ValueSerializer* serializer) { Isolate* isolate = Isolate::GetCurrent(); Environment* env = Environment::GetCurrent(isolate); CHECK(!algorithm_.IsEmpty()); Local<Object> bundle = Object::New(isolate); Local<Value> algorithm_v = PersistentToLocal::Strong(algorithm_); if (bundle->Set(context, env->algorithm_string(), algorithm_v).IsNothing() || bundle ->Set(context, env->usages_string(), Uint32::NewFromUnsigned(isolate, usages_mask_)) .IsNothing() || bundle ->Set(context, env->extractable_string(), v8::Boolean::New(isolate, extractable_)) .IsNothing()) { return Nothing<bool>(); } auto ret = serializer->WriteValue(context, bundle); algorithm_.Reset(); return ret; } BaseObjectPtr<BaseObject> NativeCryptoKey::CryptoKeyTransferData::Deserialize( Environment* env, Local<Context> context, std::unique_ptr<worker::TransferData> self) { if (context != env->context()) { THROW_ERR_MESSAGE_TARGET_CONTEXT_UNAVAILABLE(env); return {}; } // Reconstruct the KeyObjectHandle for the transferred KeyObjectData. Local<Object> handle; if (!KeyObjectHandle::Create(env, data_).ToLocal(&handle)) return {}; // Make sure internal/crypto/keys has been loaded so that the // CryptoKey constructor is registered with the Environment. Isolate* isolate = env->isolate(); Local<Value> arg = env->internal_crypto_keys_string(); if (env->builtin_module_require() ->Call(context, Null(isolate), 1, &arg) .IsEmpty()) { return {}; } // Construct a partially-initialized InternalCryptoKey; algorithm, // usages mask and extractable are filled in via FinalizeTransferRead. Local<Function> cryptokey_ctor = env->crypto_internal_cryptokey_constructor(); CHECK(!cryptokey_ctor.IsEmpty()); Local<Value> ctor_args[] = { handle, Undefined(isolate), Undefined(isolate), Undefined(isolate), }; Local<Value> cryptokey; if (!cryptokey_ctor->NewInstance(context, 4, ctor_args).ToLocal(&cryptokey)) { return {}; } return BaseObjectPtr<BaseObject>( Unwrap<NativeCryptoKey>(cryptokey.As<Object>())); } void NativeCryptoKey::MemoryInfo(MemoryTracker* tracker) const { tracker->TrackField("handle_data", handle_data_); } void NativeCryptoKey::CryptoKeyTransferData::MemoryInfo( MemoryTracker* tracker) const { tracker->TrackField("data", data_); tracker->TrackField("algorithm", algorithm_); } namespace Keys { void Initialize(Environment* env, Local<Object> target) { target->Set(env->context(), FIXED_ONE_BYTE_STRING(env->isolate(), "KeyObjectHandle"), KeyObjectHandle::Initialize(env)).Check(); constexpr int kKeyEncodingPKCS1 = static_cast<int>(EVPKeyPointer::PKEncodingType::PKCS1); constexpr int kKeyEncodingPKCS8 = static_cast<int>(EVPKeyPointer::PKEncodingType::PKCS8); constexpr int kKeyEncodingSPKI = static_cast<int>(EVPKeyPointer::PKEncodingType::SPKI); constexpr int kKeyEncodingSEC1 = static_cast<int>(EVPKeyPointer::PKEncodingType::SEC1); constexpr int kKeyFormatDER = static_cast<int>(EVPKeyPointer::PKFormatType::DER); constexpr int kKeyFormatPEM = static_cast<int>(EVPKeyPointer::PKFormatType::PEM); constexpr int kKeyFormatJWK = static_cast<int>(EVPKeyPointer::PKFormatType::JWK); constexpr int kKeyFormatRawPublic = static_cast<int>(EVPKeyPointer::PKFormatType::RAW_PUBLIC); constexpr int kKeyFormatRawPrivate = static_cast<int>(EVPKeyPointer::PKFormatType::RAW_PRIVATE); constexpr int kKeyFormatRawSeed = static_cast<int>(EVPKeyPointer::PKFormatType::RAW_SEED); constexpr int kKeyFormatStore = static_cast<int>(EVPKeyPointer::PKFormatType::STORE); constexpr auto kSigEncDER = DSASigEnc::DER; constexpr auto kSigEncP1363 = DSASigEnc::P1363; NODE_DEFINE_CONSTANT(target, kWebCryptoKeyFormatRaw); NODE_DEFINE_CONSTANT(target, kWebCryptoKeyFormatPKCS8); NODE_DEFINE_CONSTANT(target, kWebCryptoKeyFormatSPKI); NODE_DEFINE_CONSTANT(target, kWebCryptoKeyFormatJWK); NODE_DEFINE_CONSTANT(target, EVP_PKEY_ED25519); NODE_DEFINE_CONSTANT(target, EVP_PKEY_ED448); #if OPENSSL_WITH_PQC NODE_DEFINE_CONSTANT(target, EVP_PKEY_ML_DSA_44); NODE_DEFINE_CONSTANT(target, EVP_PKEY_ML_DSA_65); NODE_DEFINE_CONSTANT(target, EVP_PKEY_ML_DSA_87); #if OPENSSL_WITH_PQC_ML_KEM_512 NODE_DEFINE_CONSTANT(target, EVP_PKEY_ML_KEM_512); #endif NODE_DEFINE_CONSTANT(target, EVP_PKEY_ML_KEM_768); NODE_DEFINE_CONSTANT(target, EVP_PKEY_ML_KEM_1024); #if OPENSSL_WITH_PQC_SLH_DSA NODE_DEFINE_CONSTANT(target, EVP_PKEY_SLH_DSA_SHA2_128F); NODE_DEFINE_CONSTANT(target, EVP_PKEY_SLH_DSA_SHA2_128S); NODE_DEFINE_CONSTANT(target, EVP_PKEY_SLH_DSA_SHA2_192F); NODE_DEFINE_CONSTANT(target, EVP_PKEY_SLH_DSA_SHA2_192S); NODE_DEFINE_CONSTANT(target, EVP_PKEY_SLH_DSA_SHA2_256F); NODE_DEFINE_CONSTANT(target, EVP_PKEY_SLH_DSA_SHA2_256S); NODE_DEFINE_CONSTANT(target, EVP_PKEY_SLH_DSA_SHAKE_128F); NODE_DEFINE_CONSTANT(target, EVP_PKEY_SLH_DSA_SHAKE_128S); NODE_DEFINE_CONSTANT(target, EVP_PKEY_SLH_DSA_SHAKE_192F); NODE_DEFINE_CONSTANT(target, EVP_PKEY_SLH_DSA_SHAKE_192S); NODE_DEFINE_CONSTANT(target, EVP_PKEY_SLH_DSA_SHAKE_256F); NODE_DEFINE_CONSTANT(target, EVP_PKEY_SLH_DSA_SHAKE_256S); #endif #endif NODE_DEFINE_CONSTANT(target, EVP_PKEY_X25519); NODE_DEFINE_CONSTANT(target, EVP_PKEY_X448); NODE_DEFINE_CONSTANT(target, kKeyEncodingPKCS1); NODE_DEFINE_CONSTANT(target, kKeyEncodingPKCS8); NODE_DEFINE_CONSTANT(target, kKeyEncodingSPKI); NODE_DEFINE_CONSTANT(target, kKeyEncodingSEC1); NODE_DEFINE_CONSTANT(target, kKeyFormatDER); NODE_DEFINE_CONSTANT(target, kKeyFormatPEM); NODE_DEFINE_CONSTANT(target, kKeyFormatJWK); NODE_DEFINE_CONSTANT(target, kKeyFormatRawPublic); NODE_DEFINE_CONSTANT(target, kKeyFormatRawPrivate); NODE_DEFINE_CONSTANT(target, kKeyFormatRawSeed); NODE_DEFINE_CONSTANT(target, kKeyFormatStore); NODE_DEFINE_CONSTANT(target, kKeyTypeSecret); NODE_DEFINE_CONSTANT(target, kKeyTypePublic); NODE_DEFINE_CONSTANT(target, kKeyTypePrivate); NODE_DEFINE_CONSTANT(target, kSigEncDER); NODE_DEFINE_CONSTANT(target, kSigEncP1363); } void RegisterExternalReferences(ExternalReferenceRegistry* registry) { KeyObjectHandle::RegisterExternalReferences(registry); } } // namespace Keys } // namespace crypto } // namespace node