diff --git a/lib/internal/crypto/keys.js b/lib/internal/crypto/keys.js index 09e8f54bfa85d6..99cff1891b0b01 100644 --- a/lib/internal/crypto/keys.js +++ b/lib/internal/crypto/keys.js @@ -7,6 +7,7 @@ const { const { KeyObjectHandle, + createNativeKeyObjectClass, kKeyTypeSecret, kKeyTypePublic, kKeyTypePrivate, @@ -42,80 +43,96 @@ for (const m of [[kKeyEncodingPKCS1, 'pkcs1'], [kKeyEncodingPKCS8, 'pkcs8'], [kKeyEncodingSPKI, 'spki'], [kKeyEncodingSEC1, 'sec1']]) encodingNames[m[0]] = m[1]; -class KeyObject { - constructor(type, handle) { - if (type !== 'secret' && type !== 'public' && type !== 'private') - throw new ERR_INVALID_ARG_VALUE('type', type); - if (typeof handle !== 'object') - throw new ERR_INVALID_ARG_TYPE('handle', 'object', handle); - - this[kKeyType] = type; - - ObjectDefineProperty(this, kHandle, { - value: handle, - enumerable: false, - configurable: false, - writable: false - }); - } +// Creating the KeyObject class is a little complicated due to inheritance +// and that fact that KeyObjects should be transferrable between threads, +// which requires the KeyObject base class to be implemented in C++. +// The creation requires a callback to make sure that the NativeKeyObject +// base class cannot exist without the other KeyObject implementations. +const [ + KeyObject, + SecretKeyObject, + PublicKeyObject, + PrivateKeyObject +] = createNativeKeyObjectClass((NativeKeyObject) => { + // Publicly visible KeyObject class. + class KeyObject extends NativeKeyObject { + constructor(type, handle) { + super(); + if (type !== 'secret' && type !== 'public' && type !== 'private') + throw new ERR_INVALID_ARG_VALUE('type', type); + if (typeof handle !== 'object') + throw new ERR_INVALID_ARG_TYPE('handle', 'object', handle); + + this[kKeyType] = type; + + ObjectDefineProperty(this, kHandle, { + value: handle, + enumerable: false, + configurable: false, + writable: false + }); + } - get type() { - return this[kKeyType]; + get type() { + return this[kKeyType]; + } } -} -class SecretKeyObject extends KeyObject { - constructor(handle) { - super('secret', handle); - } + class SecretKeyObject extends KeyObject { + constructor(handle) { + super('secret', handle); + } - get symmetricKeySize() { - return this[kHandle].getSymmetricKeySize(); - } + get symmetricKeySize() { + return this[kHandle].getSymmetricKeySize(); + } - export() { - return this[kHandle].export(); + export() { + return this[kHandle].export(); + } } -} -const kAsymmetricKeyType = Symbol('kAsymmetricKeyType'); + const kAsymmetricKeyType = Symbol('kAsymmetricKeyType'); -class AsymmetricKeyObject extends KeyObject { - get asymmetricKeyType() { - return this[kAsymmetricKeyType] || - (this[kAsymmetricKeyType] = this[kHandle].getAsymmetricKeyType()); + class AsymmetricKeyObject extends KeyObject { + get asymmetricKeyType() { + return this[kAsymmetricKeyType] || + (this[kAsymmetricKeyType] = this[kHandle].getAsymmetricKeyType()); + } } -} -class PublicKeyObject extends AsymmetricKeyObject { - constructor(handle) { - super('public', handle); - } + class PublicKeyObject extends AsymmetricKeyObject { + constructor(handle) { + super('public', handle); + } - export(encoding) { - const { - format, - type - } = parsePublicKeyEncoding(encoding, this.asymmetricKeyType); - return this[kHandle].export(format, type); + export(encoding) { + const { + format, + type + } = parsePublicKeyEncoding(encoding, this.asymmetricKeyType); + return this[kHandle].export(format, type); + } } -} -class PrivateKeyObject extends AsymmetricKeyObject { - constructor(handle) { - super('private', handle); - } + class PrivateKeyObject extends AsymmetricKeyObject { + constructor(handle) { + super('private', handle); + } - export(encoding) { - const { - format, - type, - cipher, - passphrase - } = parsePrivateKeyEncoding(encoding, this.asymmetricKeyType); - return this[kHandle].export(format, type, cipher, passphrase); + export(encoding) { + const { + format, + type, + cipher, + passphrase + } = parsePrivateKeyEncoding(encoding, this.asymmetricKeyType); + return this[kHandle].export(format, type, cipher, passphrase); + } } -} + + return [KeyObject, SecretKeyObject, PublicKeyObject, PrivateKeyObject]; +}); function parseKeyFormat(formatStr, defaultFormat, optionName) { if (formatStr === undefined && defaultFormat !== undefined) diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 906b6b7fad733c..40cbbdf1bc6c04 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -3430,6 +3430,31 @@ MaybeLocal KeyObjectHandle::ExportPrivateKey( return WritePrivateKey(env(), asymmetric_key_.get(), config); } +void NativeKeyObject::New(const FunctionCallbackInfo& args) { + CHECK_EQ(args.Length(), 0); +} + +static void CreateNativeKeyObjectClass( + const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + + CHECK_EQ(args.Length(), 1); + Local callback = args[0]; + CHECK(callback->IsFunction()); + + Local t = env->NewFunctionTemplate(NativeKeyObject::New); + t->InstanceTemplate()->SetInternalFieldCount( + KeyObjectHandle::kInternalFieldCount); + + Local ctor = t->GetFunction(env->context()).ToLocalChecked(); + + Local recv = Undefined(env->isolate()); + Local ret = + callback.As()->Call(env->context(), recv, 1, &ctor) + .ToLocalChecked(); + args.GetReturnValue().Set(ret); +} + CipherBase::CipherBase(Environment* env, Local wrap, CipherKind kind) @@ -6901,6 +6926,8 @@ void Initialize(Local target, SecureContext::Initialize(env, target); env->set_crypto_key_object_handle_constructor( KeyObjectHandle::Initialize(env, target)); + env->SetMethod(target, "createNativeKeyObjectClass", + CreateNativeKeyObjectClass); CipherBase::Initialize(env, target); DiffieHellman::Initialize(env, target); ECDH::Initialize(env, target); diff --git a/src/node_crypto.h b/src/node_crypto.h index c6e4824896718b..f3a9e83b9f27ca 100644 --- a/src/node_crypto.h +++ b/src/node_crypto.h @@ -462,6 +462,15 @@ class KeyObjectHandle : public BaseObject { ManagedEVPPKey asymmetric_key_; }; +class NativeKeyObject : public BaseObject { + public: + static void New(const v8::FunctionCallbackInfo& args); + + SET_NO_MEMORY_INFO() + SET_MEMORY_INFO_NAME(NativeKeyObject) + SET_SELF_SIZE(NativeKeyObject) +}; + class CipherBase : public BaseObject { public: static void Initialize(Environment* env, v8::Local target);