diff --git a/lib/internal/crypto/keys.js b/lib/internal/crypto/keys.js index 327a6352d5c9..2bc63a2419fa 100644 --- a/lib/internal/crypto/keys.js +++ b/lib/internal/crypto/keys.js @@ -16,9 +16,11 @@ const { createNativeKeyObjectClass, // eslint-disable-next-line no-restricted-syntax -- intended here getKeyObjectSlots: nativeGetKeyObjectSlots, + isKeyObject: isNativeKeyObject, createCryptoKeyClass, // eslint-disable-next-line no-restricted-syntax -- intended here getCryptoKeySlots: nativeGetCryptoKeySlots, + isCryptoKey: isNativeCryptoKey, kKeyTypeSecret, kKeyTypePublic, kKeyTypePrivate, @@ -106,6 +108,7 @@ for (const m of [[kKeyEncodingPKCS1, 'pkcs1'], [kKeyEncodingPKCS8, 'pkcs8'], // slot tuple in a private field so no forgeable own Symbols are exposed on // public KeyObject instances. let getKeyObjectSlots; // Populated by the createNativeKeyObjectClass callback. +let isKeyObject; const kKeyObjectSlotType = 0; const kKeyObjectSlotHandle = 1; @@ -216,6 +219,10 @@ const { } static { + isKeyObject = (key) => { + if (key == null || typeof key !== 'object') return false; + return #slots in key || isNativeKeyObject(key); + }; getKeyObjectSlots = (key) => { if (!key || typeof key !== 'object') throw new ERR_INVALID_THIS('KeyObject'); @@ -1018,18 +1025,6 @@ function getKeyObjectAsymmetricKeyDetails(key) { return cached; } -function isKeyObject(obj) { - if (obj == null || typeof obj !== 'object') - return false; - - try { - getKeyObjectSlots(obj); - return true; - } catch { - return false; - } -} - // CryptoKey is a plain JS class whose prototype's [[Prototype]] is // Object.prototype, as Web Crypto requires. Instance storage (type enum, // extractable, algorithm, usages mask, and the KeyObject handle) lives @@ -1055,6 +1050,7 @@ function isKeyObject(obj) { // requires repeat reads to return the same object so a consumer's // mutation is visible next time). let getSlots; // Populated by the createCryptoKeyClass callback below. +let isCryptoKey; const kSlotType = 0; const kSlotExtractable = 1; @@ -1156,6 +1152,10 @@ const { } static { + isCryptoKey = (key) => { + if (key == null || typeof key !== 'object') return false; + return #slots in key || isNativeCryptoKey(key); + }; getSlots = (key) => { if (!key || typeof key !== 'object') throw new ERR_INVALID_THIS('CryptoKey'); @@ -1287,18 +1287,6 @@ function getCryptoKeyHandle(key) { return getSlots(key)[kSlotHandle]; } -function isCryptoKey(obj) { - if (obj == null || typeof obj !== 'object') - return false; - - try { - getSlots(obj); - return true; - } catch { - return false; - } -} - function importGenericSecretKey( algorithm, format, diff --git a/src/crypto/crypto_keys.cc b/src/crypto/crypto_keys.cc index b4e3aa72292f..2d80caf76661 100644 --- a/src/crypto/crypto_keys.cc +++ b/src/crypto/crypto_keys.cc @@ -1695,12 +1695,15 @@ void NativeKeyObject::Initialize(Environment* env, Local target) { NativeKeyObject::CreateNativeKeyObjectClass); SetMethod( env->context(), target, "getKeyObjectSlots", NativeKeyObject::GetSlots); + SetMethodNoSideEffect( + env->context(), target, "isKeyObject", NativeKeyObject::IsKeyObject); } void NativeKeyObject::RegisterExternalReferences( ExternalReferenceRegistry* registry) { registry->Register(NativeKeyObject::CreateNativeKeyObjectClass); registry->Register(NativeKeyObject::GetSlots); + registry->Register(NativeKeyObject::IsKeyObject); registry->Register(NativeKeyObject::New); } @@ -1709,6 +1712,12 @@ bool NativeKeyObject::HasInstance(Environment* env, Local value) { return !t.IsEmpty() && t->HasInstance(value); } +void NativeKeyObject::IsKeyObject(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + CHECK_EQ(args.Length(), 1); + args.GetReturnValue().Set(HasInstance(env, args[0])); +} + void NativeKeyObject::New(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); CHECK_EQ(args.Length(), 1); @@ -1839,12 +1848,15 @@ void NativeCryptoKey::Initialize(Environment* env, Local target) { NativeCryptoKey::CreateCryptoKeyClass); SetMethod( env->context(), target, "getCryptoKeySlots", NativeCryptoKey::GetSlots); + SetMethodNoSideEffect( + env->context(), target, "isCryptoKey", NativeCryptoKey::IsCryptoKey); } void NativeCryptoKey::RegisterExternalReferences( ExternalReferenceRegistry* registry) { registry->Register(NativeCryptoKey::CreateCryptoKeyClass); registry->Register(NativeCryptoKey::GetSlots); + registry->Register(NativeCryptoKey::IsCryptoKey); registry->Register(NativeCryptoKey::New); } @@ -1861,6 +1873,12 @@ bool NativeCryptoKey::HasInstance(Environment* env, Local value) { return IsNativeCryptoKey(env, value); } +void NativeCryptoKey::IsCryptoKey(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + CHECK_EQ(args.Length(), 1); + args.GetReturnValue().Set(HasInstance(env, args[0])); +} + MaybeLocal NativeCryptoKey::Create(Environment* env, const KeyObjectData& data, Local algorithm, diff --git a/src/crypto/crypto_keys.h b/src/crypto/crypto_keys.h index b09756f719cf..8fffbec60467 100644 --- a/src/crypto/crypto_keys.h +++ b/src/crypto/crypto_keys.h @@ -209,6 +209,7 @@ class NativeKeyObject : public BaseObject { static void New(const v8::FunctionCallbackInfo& args); static void CreateNativeKeyObjectClass( const v8::FunctionCallbackInfo& args); + static void IsKeyObject(const v8::FunctionCallbackInfo& args); // True if `value` is a real NativeKeyObject instance. Uses the // FunctionTemplate stored on the Environment as a brand check. @@ -277,6 +278,7 @@ class NativeCryptoKey : public BaseObject { static void New(const v8::FunctionCallbackInfo& args); static void CreateCryptoKeyClass( const v8::FunctionCallbackInfo& args); + static void IsCryptoKey(const v8::FunctionCallbackInfo& args); static v8::MaybeLocal Create(Environment* env, const KeyObjectData& data, diff --git a/typings/internalBinding/crypto.d.ts b/typings/internalBinding/crypto.d.ts index f532cf6a0c75..58303fe3c0f3 100644 --- a/typings/internalBinding/crypto.d.ts +++ b/typings/internalBinding/crypto.d.ts @@ -940,6 +940,8 @@ export interface CryptoBinding { getExtraCACertificates(): string[]; getFipsCrypto(): 0 | 1; getHashes(): string[]; + isCryptoKey(key: unknown): boolean; + isKeyObject(key: unknown): boolean; getKeyObjectSlots(key: object): InternalCryptoBinding.KeyObjectSlots; getOpenSSLSecLevelCrypto(): number | undefined; getSSLCiphers(): string[];