| #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_ml_dsa.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 "string_bytes.h" |
| #include "threadpoolwork-inl.h" |
| #include "util-inl.h" |
| #include "v8.h" |
| |
| namespace node { |
| |
| using ncrypto::BIOPointer; |
| using ncrypto::ECKeyPointer; |
| using ncrypto::EVPKeyCtxPointer; |
| using ncrypto::EVPKeyPointer; |
| using ncrypto::MarkPopErrorOnReturn; |
| using ncrypto::PKCS8Pointer; |
| using v8::Array; |
| 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 (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) return {}; |
| BUF_MEM* bptr = bio; |
| 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 |
| ->Set(env->context(), env->jwk_kty_string(), env->jwk_oct_string()) |
| .IsJust() && |
| target->Set(env->context(), env->jwk_k_string(), raw).IsJust(); |
| } |
| |
| 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) { |
| switch (key.GetAsymmetricKey().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: |
| // Fall through |
| case EVP_PKEY_ED448: |
| // Fall through |
| case EVP_PKEY_X25519: |
| // Fall through |
| case EVP_PKEY_X448: |
| return ExportJWKEdKey(env, key, target); |
| #if OPENSSL_WITH_PQC |
| case EVP_PKEY_ML_DSA_44: |
| // Fall through |
| case EVP_PKEY_ML_DSA_65: |
| // Fall through |
| case EVP_PKEY_ML_DSA_87: |
| return ExportJwkMlDsaKey(env, key, target); |
| #endif |
| } |
| THROW_ERR_CRYPTO_JWK_UNSUPPORTED_KEY_TYPE(env); |
| return false; |
| } |
| |
| KeyObjectData ImportJWKAsymmetricKey(Environment* env, |
| Local<Object> jwk, |
| std::string_view kty, |
| const FunctionCallbackInfo<Value>& args, |
| unsigned int offset) { |
| if (kty == "RSA") { |
| return ImportJWKRsaKey(env, jwk, args, offset); |
| } else if (kty == "EC") { |
| return ImportJWKEcKey(env, jwk, args, offset); |
| } |
| |
| THROW_ERR_CRYPTO_INVALID_JWK( |
| env, "%s is not a supported JWK key type", kty.data()); |
| return {}; |
| } |
| |
| 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) { |
| int nid; |
| if (strcmp(name, "Ed25519") == 0) { |
| nid = EVP_PKEY_ED25519; |
| } else if (strcmp(name, "Ed448") == 0) { |
| nid = EVP_PKEY_ED448; |
| } else if (strcmp(name, "X25519") == 0) { |
| nid = EVP_PKEY_X25519; |
| } else if (strcmp(name, "X448") == 0) { |
| nid = EVP_PKEY_X448; |
| #if OPENSSL_WITH_PQC |
| } else if (strcmp(name, "ML-DSA-44") == 0) { |
| nid = EVP_PKEY_ML_DSA_44; |
| } else if (strcmp(name, "ML-DSA-65") == 0) { |
| nid = EVP_PKEY_ML_DSA_65; |
| } else if (strcmp(name, "ML-DSA-87") == 0) { |
| nid = EVP_PKEY_ML_DSA_87; |
| } else if (strcmp(name, "ML-KEM-512") == 0) { |
| nid = EVP_PKEY_ML_KEM_512; |
| } else if (strcmp(name, "ML-KEM-768") == 0) { |
| nid = EVP_PKEY_ML_KEM_768; |
| } else if (strcmp(name, "ML-KEM-1024") == 0) { |
| nid = EVP_PKEY_ML_KEM_1024; |
| #endif |
| } else { |
| nid = NID_undef; |
| } |
| return nid; |
| } |
| } // 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); |
| } |
| |
| 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); |
| } |
| |
| 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 { |
| 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); |
| } |
| |
| KeyObjectData KeyObjectData::GetPrivateKeyFromJs( |
| const v8::FunctionCallbackInfo<v8::Value>& args, |
| unsigned int* offset, |
| bool allow_key_object) { |
| if (args[*offset]->IsString() || IsAnyBufferSource(args[*offset])) { |
| Environment* env = Environment::GetCurrent(args); |
| auto key = ByteSource::FromStringOrBuffer(env, args[(*offset)++]); |
| |
| EVPKeyPointer::PrivateKeyEncodingConfig config; |
| if (!GetPrivateKeyEncodingFromJs(args, offset, kKeyContextInput) |
| .To(&config)) { |
| return {}; |
| } |
| |
| 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) += 4; |
| return key->Data().addRef(); |
| } |
| |
| KeyObjectData KeyObjectData::GetPublicOrPrivateKeyFromJs( |
| const FunctionCallbackInfo<Value>& args, unsigned int* offset) { |
| if (IsAnyBufferSource(args[*offset])) { |
| Environment* env = Environment::GetCurrent(args); |
| 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 {}; |
| } |
| |
| 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) += 4; |
| 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), |
| data_(std::make_shared<Data>(std::move(symmetric_key))) {} |
| |
| KeyObjectData::KeyObjectData(KeyType type, EVPKeyPointer&& pkey) |
| : key_type_(type), data_(std::make_shared<Data>(std::move(pkey))) {} |
| |
| void KeyObjectData::MemoryInfo(MemoryTracker* tracker) const { |
| if (!*this) return; |
| switch (GetKeyType()) { |
| case kKeyTypeSecret: { |
| if (data_->symmetric_key) { |
| tracker->TrackFieldWithSize("symmetric_key", |
| data_->symmetric_key.size()); |
| } |
| break; |
| } |
| case kKeyTypePrivate: |
| // Fall through |
| case kKeyTypePublic: { |
| if (data_->asymmetric_key) { |
| tracker->TrackFieldWithSize( |
| "key", |
| kSizeOf_EVP_PKEY + data_->asymmetric_key.rawPublicKeySize() + |
| data_->asymmetric_key.rawPrivateKeySize()); |
| } |
| break; |
| } |
| default: |
| UNREACHABLE(); |
| } |
| } |
| |
| 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, "getSymmetricKeySize", GetSymmetricKeySize); |
| SetProtoMethodNoSideEffect( |
| isolate, templ, "getAsymmetricKeyType", GetAsymmetricKeyType); |
| SetProtoMethodNoSideEffect( |
| isolate, templ, "checkEcKeyData", CheckEcKeyData); |
| SetProtoMethod(isolate, templ, "export", Export); |
| SetProtoMethod(isolate, templ, "exportJwk", ExportJWK); |
| SetProtoMethod(isolate, templ, "initECRaw", InitECRaw); |
| SetProtoMethod(isolate, templ, "initEDRaw", InitEDRaw); |
| #if OPENSSL_WITH_PQC |
| SetProtoMethod(isolate, templ, "initPqcRaw", InitPqcRaw); |
| SetProtoMethodNoSideEffect(isolate, templ, "rawPublicKey", RawPublicKey); |
| SetProtoMethodNoSideEffect(isolate, templ, "rawSeed", RawSeed); |
| #endif |
| SetProtoMethod(isolate, templ, "initJwk", InitJWK); |
| 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(GetSymmetricKeySize); |
| registry->Register(GetAsymmetricKeyType); |
| registry->Register(CheckEcKeyData); |
| registry->Register(Export); |
| registry->Register(ExportJWK); |
| registry->Register(InitECRaw); |
| registry->Register(InitEDRaw); |
| #if OPENSSL_WITH_PQC |
| registry->Register(InitPqcRaw); |
| registry->Register(RawPublicKey); |
| registry->Register(RawSeed); |
| #endif |
| registry->Register(InitJWK); |
| 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; |
| |
| CHECK(args[0]->IsInt32()); |
| KeyType type = static_cast<KeyType>(args[0].As<Uint32>()->Value()); |
| |
| unsigned int offset; |
| |
| switch (type) { |
| case kKeyTypeSecret: { |
| CHECK_EQ(args.Length(), 2); |
| ArrayBufferOrViewContents<char> buf(args[1]); |
| key->data_ = KeyObjectData::CreateSecret(buf.ToCopy()); |
| break; |
| } |
| case kKeyTypePublic: { |
| CHECK_EQ(args.Length(), 5); |
| |
| offset = 1; |
| auto data = KeyObjectData::GetPublicOrPrivateKeyFromJs(args, &offset); |
| if (!data) return; |
| key->data_ = data.addRefWithType(kKeyTypePublic); |
| break; |
| } |
| case kKeyTypePrivate: { |
| CHECK_EQ(args.Length(), 5); |
| offset = 1; |
| if (auto data = KeyObjectData::GetPrivateKeyFromJs(args, &offset, false)) { |
| key->data_ = std::move(data); |
| } |
| break; |
| } |
| default: |
| UNREACHABLE(); |
| } |
| } |
| |
| void KeyObjectHandle::InitJWK(const FunctionCallbackInfo<Value>& args) { |
| Environment* env = Environment::GetCurrent(args); |
| KeyObjectHandle* key; |
| ASSIGN_OR_RETURN_UNWRAP(&key, args.This()); |
| MarkPopErrorOnReturn mark_pop_error_on_return; |
| |
| // The argument must be a JavaScript object that we will inspect |
| // to get the JWK properties from. |
| CHECK(args[0]->IsObject()); |
| |
| // Step one, Secret key or not? |
| Local<Object> input = args[0].As<Object>(); |
| |
| Local<Value> kty; |
| if (!input->Get(env->context(), env->jwk_kty_string()).ToLocal(&kty) || |
| !kty->IsString()) { |
| return THROW_ERR_CRYPTO_INVALID_JWK(env); |
| } |
| |
| Utf8Value kty_string(env->isolate(), kty); |
| |
| if (kty_string == "oct") { |
| // Secret key |
| key->data_ = ImportJWKSecretKey(env, input); |
| if (!key->data_) { |
| // ImportJWKSecretKey is responsible for throwing an appropriate error |
| return; |
| } |
| } else { |
| key->data_ = ImportJWKAsymmetricKey(env, input, *kty_string, args, 1); |
| if (!key->data_) { |
| // ImportJWKAsymmetricKey is responsible for throwing an appropriate error |
| return; |
| } |
| } |
| |
| args.GetReturnValue().Set(key->data_.GetKeyType()); |
| } |
| |
| void KeyObjectHandle::InitECRaw(const FunctionCallbackInfo<Value>& args) { |
| Environment* env = Environment::GetCurrent(args); |
| KeyObjectHandle* key; |
| ASSIGN_OR_RETURN_UNWRAP(&key, args.This()); |
| |
| CHECK(args[0]->IsString()); |
| Utf8Value name(env->isolate(), args[0]); |
| |
| MarkPopErrorOnReturn mark_pop_error_on_return; |
| |
| int id = OBJ_txt2nid(*name); |
| auto eckey = ECKeyPointer::NewByCurveName(id); |
| if (!eckey) |
| return args.GetReturnValue().Set(false); |
| |
| const auto group = eckey.getGroup(); |
| auto pub = ECDH::BufferToPoint(env, group, args[1]); |
| |
| if (!pub || !eckey || !eckey.setPublicKey(pub)) { |
| return args.GetReturnValue().Set(false); |
| } |
| |
| auto pkey = EVPKeyPointer::New(); |
| if (!pkey.assign(eckey)) { |
| args.GetReturnValue().Set(false); |
| } |
| |
| eckey.release(); // Release ownership of the key |
| |
| key->data_ = KeyObjectData::CreateAsymmetric(kKeyTypePublic, std::move(pkey)); |
| |
| args.GetReturnValue().Set(true); |
| } |
| |
| void KeyObjectHandle::InitEDRaw(const FunctionCallbackInfo<Value>& args) { |
| KeyObjectHandle* key; |
| ASSIGN_OR_RETURN_UNWRAP(&key, args.This()); |
| |
| CHECK(args[0]->IsString()); |
| Utf8Value name(args.GetIsolate(), args[0]); |
| |
| ArrayBufferOrViewContents<unsigned char> key_data(args[1]); |
| KeyType type = FromV8Value<KeyType>(args[2]); |
| |
| MarkPopErrorOnReturn mark_pop_error_on_return; |
| |
| typedef EVPKeyPointer (*new_key_fn)( |
| int, const ncrypto::Buffer<const unsigned char>&); |
| new_key_fn fn = type == kKeyTypePrivate ? EVPKeyPointer::NewRawPrivate |
| : EVPKeyPointer::NewRawPublic; |
| |
| int id = GetNidFromName(*name); |
| |
| switch (id) { |
| case EVP_PKEY_X25519: |
| case EVP_PKEY_X448: |
| case EVP_PKEY_ED25519: |
| case EVP_PKEY_ED448: { |
| auto pkey = fn(id, |
| ncrypto::Buffer<const unsigned char>{ |
| .data = key_data.data(), |
| .len = key_data.size(), |
| }); |
| if (!pkey) { |
| return args.GetReturnValue().Set(false); |
| } |
| key->data_ = KeyObjectData::CreateAsymmetric(type, std::move(pkey)); |
| CHECK(key->data_); |
| break; |
| } |
| default: |
| UNREACHABLE(); |
| } |
| |
| args.GetReturnValue().Set(true); |
| } |
| |
| #if OPENSSL_WITH_PQC |
| void KeyObjectHandle::InitPqcRaw(const FunctionCallbackInfo<Value>& args) { |
| KeyObjectHandle* key; |
| ASSIGN_OR_RETURN_UNWRAP(&key, args.This()); |
| |
| CHECK(args[0]->IsString()); |
| Utf8Value name(args.GetIsolate(), args[0]); |
| |
| ArrayBufferOrViewContents<unsigned char> key_data(args[1]); |
| KeyType type = FromV8Value<KeyType>(args[2]); |
| |
| MarkPopErrorOnReturn mark_pop_error_on_return; |
| |
| typedef EVPKeyPointer (*new_key_fn)( |
| int, const ncrypto::Buffer<const unsigned char>&); |
| new_key_fn fn = type == kKeyTypePrivate ? EVPKeyPointer::NewRawSeed |
| : EVPKeyPointer::NewRawPublic; |
| |
| int id = GetNidFromName(*name); |
| |
| switch (id) { |
| case EVP_PKEY_ML_DSA_44: |
| case EVP_PKEY_ML_DSA_65: |
| case EVP_PKEY_ML_DSA_87: |
| case EVP_PKEY_ML_KEM_512: |
| case EVP_PKEY_ML_KEM_768: |
| case EVP_PKEY_ML_KEM_1024: { |
| auto pkey = fn(id, |
| ncrypto::Buffer<const unsigned char>{ |
| .data = key_data.data(), |
| .len = key_data.size(), |
| }); |
| if (!pkey) { |
| return args.GetReturnValue().Set(false); |
| } |
| key->data_ = KeyObjectData::CreateAsymmetric(type, std::move(pkey)); |
| CHECK(key->data_); |
| break; |
| } |
| default: |
| UNREACHABLE(); |
| } |
| |
| args.GetReturnValue().Set(true); |
| } |
| #endif |
| |
| 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 |
| case EVP_PKEY_ML_DSA_44: |
| return env()->crypto_ml_dsa_44_string(); |
| case EVP_PKEY_ML_DSA_65: |
| return env()->crypto_ml_dsa_65_string(); |
| case EVP_PKEY_ML_DSA_87: |
| return env()->crypto_ml_dsa_87_string(); |
| case EVP_PKEY_ML_KEM_512: |
| return env()->crypto_ml_kem_512_string(); |
| case EVP_PKEY_ML_KEM_768: |
| return env()->crypto_ml_kem_768_string(); |
| case EVP_PKEY_ML_KEM_1024: |
| return env()->crypto_ml_kem_1024_string(); |
| case EVP_PKEY_SLH_DSA_SHA2_128F: |
| return env()->crypto_slh_dsa_sha2_128f_string(); |
| case EVP_PKEY_SLH_DSA_SHA2_128S: |
| return env()->crypto_slh_dsa_sha2_128s_string(); |
| case EVP_PKEY_SLH_DSA_SHA2_192F: |
| return env()->crypto_slh_dsa_sha2_192f_string(); |
| case EVP_PKEY_SLH_DSA_SHA2_192S: |
| return env()->crypto_slh_dsa_sha2_192s_string(); |
| case EVP_PKEY_SLH_DSA_SHA2_256F: |
| return env()->crypto_slh_dsa_sha2_256f_string(); |
| case EVP_PKEY_SLH_DSA_SHA2_256S: |
| return env()->crypto_slh_dsa_sha2_256s_string(); |
| case EVP_PKEY_SLH_DSA_SHAKE_128F: |
| return env()->crypto_slh_dsa_shake_128f_string(); |
| case EVP_PKEY_SLH_DSA_SHAKE_128S: |
| return env()->crypto_slh_dsa_shake_128s_string(); |
| case EVP_PKEY_SLH_DSA_SHAKE_192F: |
| return env()->crypto_slh_dsa_shake_192f_string(); |
| case EVP_PKEY_SLH_DSA_SHAKE_192S: |
| return env()->crypto_slh_dsa_shake_192s_string(); |
| case EVP_PKEY_SLH_DSA_SHAKE_256F: |
| return env()->crypto_slh_dsa_shake_256f_string(); |
| case EVP_PKEY_SLH_DSA_SHAKE_256S: |
| return env()->crypto_slh_dsa_shake_256s_string(); |
| #endif |
| default: |
| return Undefined(env()->isolate()); |
| } |
| } |
| |
| 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); |
| } |
| |
| #if OPENSSL_WITH_PQC |
| 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()); |
| auto raw_data = data.GetAsymmetricKey().rawPublicKey(); |
| if (!raw_data) { |
| return THROW_ERR_CRYPTO_OPERATION_FAILED(env, |
| "Failed to get raw public key"); |
| } |
| |
| args.GetReturnValue().Set( |
| Buffer::Copy( |
| env, reinterpret_cast<const char*>(raw_data.get()), raw_data.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); |
| |
| Mutex::ScopedLock lock(data.mutex()); |
| auto raw_data = data.GetAsymmetricKey().rawSeed(); |
| if (!raw_data) { |
| return THROW_ERR_CRYPTO_OPERATION_FAILED(env, "Failed to get raw seed"); |
| } |
| |
| args.GetReturnValue().Set( |
| Buffer::Copy( |
| env, reinterpret_cast<const char*>(raw_data.get()), raw_data.size()) |
| .FromMaybe(Local<Value>())); |
| } |
| #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]); |
| } |
| } |
| |
| void NativeKeyObject::Initialize(Environment* env, Local<Object> target) { |
| SetMethod(env->context(), |
| target, |
| "createNativeKeyObjectClass", |
| NativeKeyObject::CreateNativeKeyObjectClass); |
| } |
| |
| void NativeKeyObject::RegisterExternalReferences( |
| ExternalReferenceRegistry* registry) { |
| registry->Register(NativeKeyObject::CreateNativeKeyObjectClass); |
| registry->Register(NativeKeyObject::New); |
| } |
| |
| void NativeKeyObject::New(const FunctionCallbackInfo<Value>& args) { |
| Environment* env = Environment::GetCurrent(args); |
| CHECK_EQ(args.Length(), 1); |
| CHECK(args[0]->IsObject()); |
| 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( |
| KeyObjectHandle::kInternalFieldCount); |
| |
| 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); |
| } |
| |
| 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 = FIXED_ONE_BYTE_STRING(env->isolate(), |
| "internal/crypto/keys"); |
| 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<KeyObjectHandle>(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_); |
| } |
| |
| WebCryptoKeyExportStatus PKEY_SPKI_Export(const KeyObjectData& key_data, |
| ByteSource* out) { |
| CHECK_EQ(key_data.GetKeyType(), kKeyTypePublic); |
| Mutex::ScopedLock lock(key_data.mutex()); |
| auto bio = key_data.GetAsymmetricKey().derPublicKey(); |
| if (!bio) return WebCryptoKeyExportStatus::FAILED; |
| *out = ByteSource::FromBIO(bio); |
| return WebCryptoKeyExportStatus::OK; |
| } |
| |
| WebCryptoKeyExportStatus PKEY_PKCS8_Export(const KeyObjectData& key_data, |
| ByteSource* out) { |
| CHECK_EQ(key_data.GetKeyType(), kKeyTypePrivate); |
| Mutex::ScopedLock lock(key_data.mutex()); |
| const auto& m_pkey = key_data.GetAsymmetricKey(); |
| |
| auto bio = BIOPointer::NewMem(); |
| CHECK(bio); |
| PKCS8Pointer p8inf(EVP_PKEY2PKCS8(m_pkey.get())); |
| if (!i2d_PKCS8_PRIV_KEY_INFO_bio(bio.get(), p8inf.get())) |
| return WebCryptoKeyExportStatus::FAILED; |
| |
| *out = ByteSource::FromBIO(bio); |
| return WebCryptoKeyExportStatus::OK; |
| } |
| |
| 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 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); |
| NODE_DEFINE_CONSTANT(target, EVP_PKEY_ML_KEM_512); |
| NODE_DEFINE_CONSTANT(target, EVP_PKEY_ML_KEM_768); |
| NODE_DEFINE_CONSTANT(target, EVP_PKEY_ML_KEM_1024); |
| 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 |
| 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, 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 |