From 67d36ca015c8e9d53c5fc622da6df137f879604f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Nie=C3=9Fen?= Date: Sun, 10 May 2020 15:37:09 +0000 Subject: [PATCH 1/6] src: rename internal key handles to KeyObjectHandle --- lib/internal/crypto/keys.js | 2 +- src/env.h | 1 + src/node_crypto.cc | 89 ++++++++++++++++++++----------------- src/node_crypto.h | 12 ++--- 4 files changed, 56 insertions(+), 48 deletions(-) diff --git a/lib/internal/crypto/keys.js b/lib/internal/crypto/keys.js index bfb30dda3fd752..09e8f54bfa85d6 100644 --- a/lib/internal/crypto/keys.js +++ b/lib/internal/crypto/keys.js @@ -6,7 +6,7 @@ const { } = primordials; const { - KeyObject: KeyObjectHandle, + KeyObjectHandle, kKeyTypeSecret, kKeyTypePublic, kKeyTypePrivate, diff --git a/src/env.h b/src/env.h index ba3306a43347b8..35d1cb2724de88 100644 --- a/src/env.h +++ b/src/env.h @@ -486,6 +486,7 @@ constexpr size_t kFsStatsBufferLength = V(async_hooks_promise_resolve_function, v8::Function) \ V(buffer_prototype_object, v8::Object) \ V(crypto_key_object_constructor, v8::Function) \ + V(crypto_key_object_handle_constructor, v8::Function) \ V(domexception_function, v8::Function) \ V(enhance_fatal_stack_after_inspector, v8::Function) \ V(enhance_fatal_stack_before_inspector, v8::Function) \ diff --git a/src/node_crypto.cc b/src/node_crypto.cc index ca394f2e24e4bb..6967f1719d2ef1 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -2925,9 +2925,9 @@ ByteSource ByteSource::NullTerminatedCopy(Environment* env, : FromString(env, value.As(), true); } -ByteSource ByteSource::FromSymmetricKeyObject(Local handle) { +ByteSource ByteSource::FromSymmetricKeyObjectHandle(Local handle) { CHECK(handle->IsObject()); - KeyObject* key = Unwrap(handle.As()); + KeyObjectHandle* key = Unwrap(handle.As()); CHECK_NOT_NULL(key); return Foreign(key->GetSymmetricKey(), key->GetSymmetricKeySize()); } @@ -3073,7 +3073,7 @@ static ManagedEVPPKey GetPrivateKeyFromJs( "Failed to read private key"); } else { CHECK(args[*offset]->IsObject() && allow_key_object); - KeyObject* key; + KeyObjectHandle* key; ASSIGN_OR_RETURN_UNWRAP(&key, args[*offset].As(), ManagedEVPPKey()); CHECK_EQ(key->GetKeyType(), kKeyTypePrivate); (*offset) += 4; @@ -3133,7 +3133,7 @@ static ManagedEVPPKey GetPublicOrPrivateKeyFromJs( "Failed to read asymmetric key"); } else { CHECK(args[*offset]->IsObject()); - KeyObject* key = Unwrap(args[*offset].As()); + KeyObjectHandle* key = Unwrap(args[*offset].As()); CHECK_NOT_NULL(key); CHECK_NE(key->GetKeyType(), kKeyTypeSecret); (*offset) += 4; @@ -3243,10 +3243,11 @@ EVP_PKEY* ManagedEVPPKey::get() const { return pkey_.get(); } -Local KeyObject::Initialize(Environment* env, Local target) { +Local KeyObjectHandle::Initialize(Environment* env, + Local target) { Local t = env->NewFunctionTemplate(New); t->InstanceTemplate()->SetInternalFieldCount( - KeyObject::kInternalFieldCount); + KeyObjectHandle::kInternalFieldCount); t->Inherit(BaseObject::GetConstructorTemplate(env)); env->SetProtoMethod(t, "init", Init); @@ -3258,25 +3259,25 @@ Local KeyObject::Initialize(Environment* env, Local target) { auto function = t->GetFunction(env->context()).ToLocalChecked(); target->Set(env->context(), - FIXED_ONE_BYTE_STRING(env->isolate(), "KeyObject"), + FIXED_ONE_BYTE_STRING(env->isolate(), "KeyObjectHandle"), function).Check(); return function; } -MaybeLocal KeyObject::Create(Environment* env, - KeyType key_type, - const ManagedEVPPKey& pkey) { +MaybeLocal KeyObjectHandle::Create(Environment* env, + KeyType key_type, + const ManagedEVPPKey& pkey) { CHECK_NE(key_type, kKeyTypeSecret); Local type = Integer::New(env->isolate(), key_type); Local obj; - if (!env->crypto_key_object_constructor() + if (!env->crypto_key_object_handle_constructor() ->NewInstance(env->context(), 1, &type) .ToLocal(&obj)) { return MaybeLocal(); } - KeyObject* key = Unwrap(obj); + KeyObjectHandle* key = Unwrap(obj); CHECK_NOT_NULL(key); if (key_type == kKeyTypePublic) key->InitPublic(pkey); @@ -3285,44 +3286,44 @@ MaybeLocal KeyObject::Create(Environment* env, return obj; } -ManagedEVPPKey KeyObject::GetAsymmetricKey() const { +ManagedEVPPKey KeyObjectHandle::GetAsymmetricKey() const { CHECK_NE(key_type_, kKeyTypeSecret); return this->asymmetric_key_; } -const char* KeyObject::GetSymmetricKey() const { +const char* KeyObjectHandle::GetSymmetricKey() const { CHECK_EQ(key_type_, kKeyTypeSecret); return this->symmetric_key_.get(); } -size_t KeyObject::GetSymmetricKeySize() const { +size_t KeyObjectHandle::GetSymmetricKeySize() const { CHECK_EQ(key_type_, kKeyTypeSecret); return this->symmetric_key_len_; } -void KeyObject::New(const FunctionCallbackInfo& args) { +void KeyObjectHandle::New(const FunctionCallbackInfo& args) { CHECK(args.IsConstructCall()); CHECK(args[0]->IsInt32()); KeyType key_type = static_cast(args[0].As()->Value()); Environment* env = Environment::GetCurrent(args); - new KeyObject(env, args.This(), key_type); + new KeyObjectHandle(env, args.This(), key_type); } -KeyType KeyObject::GetKeyType() const { +KeyType KeyObjectHandle::GetKeyType() const { return this->key_type_; } -KeyObject::KeyObject(Environment* env, - Local wrap, - KeyType key_type) +KeyObjectHandle::KeyObjectHandle(Environment* env, + Local wrap, + KeyType key_type) : BaseObject(env, wrap), key_type_(key_type), symmetric_key_(nullptr, nullptr) { MakeWeak(); } -void KeyObject::Init(const FunctionCallbackInfo& args) { - KeyObject* key; +void KeyObjectHandle::Init(const FunctionCallbackInfo& args) { + KeyObjectHandle* key; ASSIGN_OR_RETURN_UNWRAP(&key, args.Holder()); MarkPopErrorOnReturn mark_pop_error_on_return; @@ -3358,7 +3359,7 @@ void KeyObject::Init(const FunctionCallbackInfo& args) { } } -void KeyObject::InitSecret(Local abv) { +void KeyObjectHandle::InitSecret(Local abv) { CHECK_EQ(this->key_type_, kKeyTypeSecret); size_t key_len = abv->ByteLength(); @@ -3371,19 +3372,19 @@ void KeyObject::InitSecret(Local abv) { this->symmetric_key_len_ = key_len; } -void KeyObject::InitPublic(const ManagedEVPPKey& pkey) { +void KeyObjectHandle::InitPublic(const ManagedEVPPKey& pkey) { CHECK_EQ(this->key_type_, kKeyTypePublic); CHECK(pkey); this->asymmetric_key_ = pkey; } -void KeyObject::InitPrivate(const ManagedEVPPKey& pkey) { +void KeyObjectHandle::InitPrivate(const ManagedEVPPKey& pkey) { CHECK_EQ(this->key_type_, kKeyTypePrivate); CHECK(pkey); this->asymmetric_key_ = pkey; } -Local KeyObject::GetAsymmetricKeyType() const { +Local KeyObjectHandle::GetAsymmetricKeyType() const { CHECK_NE(this->key_type_, kKeyTypeSecret); switch (EVP_PKEY_id(this->asymmetric_key_.get())) { case EVP_PKEY_RSA: @@ -3409,21 +3410,23 @@ Local KeyObject::GetAsymmetricKeyType() const { } } -void KeyObject::GetAsymmetricKeyType(const FunctionCallbackInfo& args) { - KeyObject* key; +void KeyObjectHandle::GetAsymmetricKeyType( + const FunctionCallbackInfo& args) { + KeyObjectHandle* key; ASSIGN_OR_RETURN_UNWRAP(&key, args.Holder()); args.GetReturnValue().Set(key->GetAsymmetricKeyType()); } -void KeyObject::GetSymmetricKeySize(const FunctionCallbackInfo& args) { - KeyObject* key; +void KeyObjectHandle::GetSymmetricKeySize( + const FunctionCallbackInfo& args) { + KeyObjectHandle* key; ASSIGN_OR_RETURN_UNWRAP(&key, args.Holder()); args.GetReturnValue().Set(static_cast(key->GetSymmetricKeySize())); } -void KeyObject::Export(const FunctionCallbackInfo& args) { - KeyObject* key; +void KeyObjectHandle::Export(const FunctionCallbackInfo& args) { + KeyObjectHandle* key; ASSIGN_OR_RETURN_UNWRAP(&key, args.Holder()); MaybeLocal result; @@ -3450,17 +3453,17 @@ void KeyObject::Export(const FunctionCallbackInfo& args) { args.GetReturnValue().Set(result.ToLocalChecked()); } -Local KeyObject::ExportSecretKey() const { +Local KeyObjectHandle::ExportSecretKey() const { return Buffer::Copy(env(), symmetric_key_.get(), symmetric_key_len_) .ToLocalChecked(); } -MaybeLocal KeyObject::ExportPublicKey( +MaybeLocal KeyObjectHandle::ExportPublicKey( const PublicKeyEncodingConfig& config) const { return WritePublicKey(env(), asymmetric_key_.get(), config); } -MaybeLocal KeyObject::ExportPrivateKey( +MaybeLocal KeyObjectHandle::ExportPrivateKey( const PrivateKeyEncodingConfig& config) const { return WritePrivateKey(env(), asymmetric_key_.get(), config); } @@ -3663,7 +3666,7 @@ static ByteSource GetSecretKeyBytes(Environment* env, Local value) { // in JS to avoid creating an unprotected copy on the heap. return value->IsString() || Buffer::HasInstance(value) ? ByteSource::FromStringOrBuffer(env, value) : - ByteSource::FromSymmetricKeyObject(value); + ByteSource::FromSymmetricKeyObjectHandle(value); } void CipherBase::InitIv(const FunctionCallbackInfo& args) { @@ -6277,7 +6280,8 @@ class GenerateKeyPairJob : public CryptoJob { if (public_key_encoding_.output_key_object_) { // Note that this has the downside of containing sensitive data of the // private key. - if (!KeyObject::Create(env(), kKeyTypePublic, pkey_).ToLocal(pubkey)) + if (!KeyObjectHandle::Create(env(), kKeyTypePublic, pkey_) + .ToLocal(pubkey)) return false; } else { if (!WritePublicKey(env(), pkey_.get(), public_key_encoding_) @@ -6287,7 +6291,7 @@ class GenerateKeyPairJob : public CryptoJob { // Now do the same for the private key. if (private_key_encoding_.output_key_object_) { - if (!KeyObject::Create(env(), kKeyTypePrivate, pkey_) + if (!KeyObjectHandle::Create(env(), kKeyTypePrivate, pkey_) .ToLocal(privkey)) return false; } else { @@ -6731,10 +6735,10 @@ void StatelessDiffieHellman(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); CHECK(args[0]->IsObject() && args[1]->IsObject()); - KeyObject* our_key_object; + KeyObjectHandle* our_key_object; ASSIGN_OR_RETURN_UNWRAP(&our_key_object, args[0].As()); CHECK_EQ(our_key_object->GetKeyType(), kKeyTypePrivate); - KeyObject* their_key_object; + KeyObjectHandle* their_key_object; ASSIGN_OR_RETURN_UNWRAP(&their_key_object, args[1].As()); CHECK_NE(their_key_object->GetKeyType(), kKeyTypeSecret); @@ -6865,7 +6869,8 @@ void Initialize(Local target, Environment* env = Environment::GetCurrent(context); SecureContext::Initialize(env, target); - env->set_crypto_key_object_constructor(KeyObject::Initialize(env, target)); + env->set_crypto_key_object_handle_constructor( + KeyObjectHandle::Initialize(env, target)); 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 1eea1239f1956d..b3f3c2133c0ba4 100644 --- a/src/node_crypto.h +++ b/src/node_crypto.h @@ -343,7 +343,7 @@ class ByteSource { static ByteSource NullTerminatedCopy(Environment* env, v8::Local value); - static ByteSource FromSymmetricKeyObject(v8::Local handle); + static ByteSource FromSymmetricKeyObjectHandle(v8::Local handle); ByteSource(const ByteSource&) = delete; ByteSource& operator=(const ByteSource&) = delete; @@ -408,7 +408,7 @@ class ManagedEVPPKey { EVPKeyPointer pkey_; }; -class KeyObject : public BaseObject { +class KeyObjectHandle : public BaseObject { public: static v8::Local Initialize(Environment* env, v8::Local target); @@ -419,8 +419,8 @@ class KeyObject : public BaseObject { // TODO(tniessen): track the memory used by OpenSSL types SET_NO_MEMORY_INFO() - SET_MEMORY_INFO_NAME(KeyObject) - SET_SELF_SIZE(KeyObject) + SET_MEMORY_INFO_NAME(KeyObjectHandle) + SET_SELF_SIZE(KeyObjectHandle) KeyType GetKeyType() const; @@ -452,7 +452,9 @@ class KeyObject : public BaseObject { v8::MaybeLocal ExportPrivateKey( const PrivateKeyEncodingConfig& config) const; - KeyObject(Environment* env, v8::Local wrap, KeyType key_type); + KeyObjectHandle(Environment* env, + v8::Local wrap, + KeyType key_type); private: const KeyType key_type_; From be4f6868d047cb0a2e1e1ddeecd7914fee8e234d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Nie=C3=9Fen?= Date: Sun, 10 May 2020 18:09:18 +0000 Subject: [PATCH 2/6] src: add NativeKeyObject base class +---------------------+ | BaseObject | +---------------------+ | | | +---------------------+ | NativeKeyObject | +---------------------+ | | | +---------------------+ | KeyObject | +---------------------+ / \ / \ / \ / \ +---------------------+ +---------------------+ | SecretKeyObject | | AsymmetricKeyObject | +---------------------+ +---------------------+ / \ / \ / \ / \ +---------------------+ +---------------------+ | PublicKeyObject | | PrivateKeyObject | +---------------------+ +---------------------+ --- lib/internal/crypto/keys.js | 135 ++++++++++++++++++++---------------- src/node_crypto.cc | 27 ++++++++ src/node_crypto.h | 9 +++ 3 files changed, 112 insertions(+), 59 deletions(-) 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 6967f1719d2ef1..42cfe138c32d9a 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -3468,6 +3468,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) @@ -6871,6 +6896,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 b3f3c2133c0ba4..f95ea64e973007 100644 --- a/src/node_crypto.h +++ b/src/node_crypto.h @@ -463,6 +463,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); From 023856d907a6155a43d5ca4870cf2159faae66a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Nie=C3=9Fen?= Date: Mon, 11 May 2020 13:05:38 +0000 Subject: [PATCH 3/6] src: store key data in separate class This separates key handles from the actual key data: +-----------------+ | NativeKeyObject | +-----------------+ ^ extends | +-----------------+ +-----------------+ +---------------+ | KeyObject (JS) | -> | KeyObjectHandle | -> | KeyObjectData | +-----------------+ +-----------------+ +---------------+ --- lib/internal/crypto/keys.js | 12 +-- src/node_crypto.cc | 169 ++++++++++++++++++------------------ src/node_crypto.h | 40 +++++---- 3 files changed, 116 insertions(+), 105 deletions(-) diff --git a/lib/internal/crypto/keys.js b/lib/internal/crypto/keys.js index 99cff1891b0b01..61d3859553230d 100644 --- a/lib/internal/crypto/keys.js +++ b/lib/internal/crypto/keys.js @@ -331,23 +331,23 @@ function createSecretKey(key) { key = prepareSecretKey(key, true); if (key.byteLength === 0) throw new ERR_OUT_OF_RANGE('key.byteLength', '> 0', key.byteLength); - const handle = new KeyObjectHandle(kKeyTypeSecret); - handle.init(key); + const handle = new KeyObjectHandle(); + handle.init(kKeyTypeSecret, key); return new SecretKeyObject(handle); } function createPublicKey(key) { const { format, type, data } = prepareAsymmetricKey(key, kCreatePublic); - const handle = new KeyObjectHandle(kKeyTypePublic); - handle.init(data, format, type); + const handle = new KeyObjectHandle(); + handle.init(kKeyTypePublic, data, format, type); return new PublicKeyObject(handle); } function createPrivateKey(key) { const { format, type, data, passphrase } = prepareAsymmetricKey(key, kCreatePrivate); - const handle = new KeyObjectHandle(kKeyTypePrivate); - handle.init(data, format, type, passphrase); + const handle = new KeyObjectHandle(); + handle.init(kKeyTypePrivate, data, format, type, passphrase); return new PrivateKeyObject(handle); } diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 42cfe138c32d9a..57c4764e39d0af 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -2929,7 +2929,8 @@ ByteSource ByteSource::FromSymmetricKeyObjectHandle(Local handle) { CHECK(handle->IsObject()); KeyObjectHandle* key = Unwrap(handle.As()); CHECK_NOT_NULL(key); - return Foreign(key->GetSymmetricKey(), key->GetSymmetricKeySize()); + return Foreign(key->Data()->GetSymmetricKey(), + key->Data()->GetSymmetricKeySize()); } ByteSource::ByteSource(const char* data, char* allocated_data, size_t size) @@ -3075,9 +3076,9 @@ static ManagedEVPPKey GetPrivateKeyFromJs( CHECK(args[*offset]->IsObject() && allow_key_object); KeyObjectHandle* key; ASSIGN_OR_RETURN_UNWRAP(&key, args[*offset].As(), ManagedEVPPKey()); - CHECK_EQ(key->GetKeyType(), kKeyTypePrivate); + CHECK_EQ(key->Data()->GetKeyType(), kKeyTypePrivate); (*offset) += 4; - return key->GetAsymmetricKey(); + return key->Data()->GetAsymmetricKey(); } } @@ -3135,9 +3136,9 @@ static ManagedEVPPKey GetPublicOrPrivateKeyFromJs( CHECK(args[*offset]->IsObject()); KeyObjectHandle* key = Unwrap(args[*offset].As()); CHECK_NOT_NULL(key); - CHECK_NE(key->GetKeyType(), kKeyTypeSecret); + CHECK_NE(key->Data()->GetKeyType(), kKeyTypeSecret); (*offset) += 4; - return key->GetAsymmetricKey(); + return key->Data()->GetAsymmetricKey(); } } @@ -3243,6 +3244,48 @@ EVP_PKEY* ManagedEVPPKey::get() const { return pkey_.get(); } +KeyObjectData* KeyObjectData::CreateSecret(v8::Local abv) { + size_t key_len = abv->ByteLength(); + char* mem = MallocOpenSSL(key_len); + abv->CopyContents(mem, key_len); + KeyObjectData* data = new KeyObjectData(); + data->key_type_ = kKeyTypeSecret; + data->symmetric_key_ = std::unique_ptr>(mem, + [key_len](char* p) { + OPENSSL_clear_free(p, key_len); + }); + data->symmetric_key_len_ = key_len; + return data; +} + +KeyObjectData* KeyObjectData::CreateAsymmetric(KeyType key_type, + const ManagedEVPPKey& pkey) { + CHECK(pkey); + KeyObjectData* data = new KeyObjectData(); + data->key_type_ = key_type; + data->asymmetric_key_ = pkey; + return data; +} + +KeyType KeyObjectData::GetKeyType() const { + return key_type_; +} + +ManagedEVPPKey KeyObjectData::GetAsymmetricKey() const { + CHECK_NE(key_type_, kKeyTypeSecret); + return asymmetric_key_; +} + +const char* KeyObjectData::GetSymmetricKey() const { + CHECK_EQ(key_type_, kKeyTypeSecret); + return symmetric_key_.get(); +} + +size_t KeyObjectData::GetSymmetricKeySize() const { + CHECK_EQ(key_type_, kKeyTypeSecret); + return symmetric_key_len_; +} + Local KeyObjectHandle::Initialize(Environment* env, Local target) { Local t = env->NewFunctionTemplate(New); @@ -3279,46 +3322,23 @@ MaybeLocal KeyObjectHandle::Create(Environment* env, KeyObjectHandle* key = Unwrap(obj); CHECK_NOT_NULL(key); - if (key_type == kKeyTypePublic) - key->InitPublic(pkey); - else - key->InitPrivate(pkey); + key->data_.reset(KeyObjectData::CreateAsymmetric(key_type, pkey)); return obj; } -ManagedEVPPKey KeyObjectHandle::GetAsymmetricKey() const { - CHECK_NE(key_type_, kKeyTypeSecret); - return this->asymmetric_key_; -} - -const char* KeyObjectHandle::GetSymmetricKey() const { - CHECK_EQ(key_type_, kKeyTypeSecret); - return this->symmetric_key_.get(); -} - -size_t KeyObjectHandle::GetSymmetricKeySize() const { - CHECK_EQ(key_type_, kKeyTypeSecret); - return this->symmetric_key_len_; +const KeyObjectData* KeyObjectHandle::Data() { + return data_.get(); } void KeyObjectHandle::New(const FunctionCallbackInfo& args) { CHECK(args.IsConstructCall()); - CHECK(args[0]->IsInt32()); - KeyType key_type = static_cast(args[0].As()->Value()); Environment* env = Environment::GetCurrent(args); - new KeyObjectHandle(env, args.This(), key_type); -} - -KeyType KeyObjectHandle::GetKeyType() const { - return this->key_type_; + new KeyObjectHandle(env, args.This()); } KeyObjectHandle::KeyObjectHandle(Environment* env, - Local wrap, - KeyType key_type) - : BaseObject(env, wrap), - key_type_(key_type), - symmetric_key_(nullptr, nullptr) { + Local wrap) + : BaseObject(env, wrap) { MakeWeak(); } @@ -3327,66 +3347,45 @@ void KeyObjectHandle::Init(const FunctionCallbackInfo& args) { ASSIGN_OR_RETURN_UNWRAP(&key, args.Holder()); MarkPopErrorOnReturn mark_pop_error_on_return; + CHECK(args[0]->IsInt32()); + KeyType type = static_cast(args[0].As()->Value()); + unsigned int offset; ManagedEVPPKey pkey; - switch (key->key_type_) { + switch (type) { case kKeyTypeSecret: - CHECK_EQ(args.Length(), 1); - CHECK(args[0]->IsArrayBufferView()); - key->InitSecret(args[0].As()); + CHECK_EQ(args.Length(), 2); + CHECK(args[1]->IsArrayBufferView()); + key->data_.reset( + KeyObjectData::CreateSecret(args[1].As())); break; case kKeyTypePublic: - CHECK_EQ(args.Length(), 3); + CHECK_EQ(args.Length(), 4); - offset = 0; + offset = 1; pkey = GetPublicOrPrivateKeyFromJs(args, &offset); if (!pkey) return; - key->InitPublic(pkey); + key->data_.reset(KeyObjectData::CreateAsymmetric(type, pkey)); break; case kKeyTypePrivate: - CHECK_EQ(args.Length(), 4); + CHECK_EQ(args.Length(), 5); - offset = 0; + offset = 1; pkey = GetPrivateKeyFromJs(args, &offset, false); if (!pkey) return; - key->InitPrivate(pkey); + key->data_.reset(KeyObjectData::CreateAsymmetric(type, pkey)); break; default: CHECK(false); } } -void KeyObjectHandle::InitSecret(Local abv) { - CHECK_EQ(this->key_type_, kKeyTypeSecret); - - size_t key_len = abv->ByteLength(); - char* mem = MallocOpenSSL(key_len); - abv->CopyContents(mem, key_len); - this->symmetric_key_ = std::unique_ptr>(mem, - [key_len](char* p) { - OPENSSL_clear_free(p, key_len); - }); - this->symmetric_key_len_ = key_len; -} - -void KeyObjectHandle::InitPublic(const ManagedEVPPKey& pkey) { - CHECK_EQ(this->key_type_, kKeyTypePublic); - CHECK(pkey); - this->asymmetric_key_ = pkey; -} - -void KeyObjectHandle::InitPrivate(const ManagedEVPPKey& pkey) { - CHECK_EQ(this->key_type_, kKeyTypePrivate); - CHECK(pkey); - this->asymmetric_key_ = pkey; -} - Local KeyObjectHandle::GetAsymmetricKeyType() const { - CHECK_NE(this->key_type_, kKeyTypeSecret); - switch (EVP_PKEY_id(this->asymmetric_key_.get())) { + const ManagedEVPPKey& key = data_->GetAsymmetricKey(); + switch (EVP_PKEY_id(key.get())) { case EVP_PKEY_RSA: return env()->crypto_rsa_string(); case EVP_PKEY_RSA_PSS: @@ -3422,24 +3421,27 @@ void KeyObjectHandle::GetSymmetricKeySize( const FunctionCallbackInfo& args) { KeyObjectHandle* key; ASSIGN_OR_RETURN_UNWRAP(&key, args.Holder()); - args.GetReturnValue().Set(static_cast(key->GetSymmetricKeySize())); + args.GetReturnValue().Set( + static_cast(key->Data()->GetSymmetricKeySize())); } void KeyObjectHandle::Export(const FunctionCallbackInfo& args) { KeyObjectHandle* key; ASSIGN_OR_RETURN_UNWRAP(&key, args.Holder()); + KeyType type = key->Data()->GetKeyType(); + MaybeLocal result; - if (key->key_type_ == kKeyTypeSecret) { + if (type == kKeyTypeSecret) { result = key->ExportSecretKey(); - } else if (key->key_type_ == kKeyTypePublic) { + } else if (type == kKeyTypePublic) { unsigned int offset = 0; PublicKeyEncodingConfig config = GetPublicKeyEncodingFromJs(args, &offset, kKeyContextExport); CHECK_EQ(offset, static_cast(args.Length())); result = key->ExportPublicKey(config); } else { - CHECK_EQ(key->key_type_, kKeyTypePrivate); + CHECK_EQ(type, kKeyTypePrivate); unsigned int offset = 0; NonCopyableMaybe config = GetPrivateKeyEncodingFromJs(args, &offset, kKeyContextExport); @@ -3454,18 +3456,19 @@ void KeyObjectHandle::Export(const FunctionCallbackInfo& args) { } Local KeyObjectHandle::ExportSecretKey() const { - return Buffer::Copy(env(), symmetric_key_.get(), symmetric_key_len_) - .ToLocalChecked(); + const char* buf = data_->GetSymmetricKey(); + unsigned int len = data_->GetSymmetricKeySize(); + return Buffer::Copy(env(), buf, len).ToLocalChecked(); } MaybeLocal KeyObjectHandle::ExportPublicKey( const PublicKeyEncodingConfig& config) const { - return WritePublicKey(env(), asymmetric_key_.get(), config); + return WritePublicKey(env(), data_->GetAsymmetricKey().get(), config); } MaybeLocal KeyObjectHandle::ExportPrivateKey( const PrivateKeyEncodingConfig& config) const { - return WritePrivateKey(env(), asymmetric_key_.get(), config); + return WritePrivateKey(env(), data_->GetAsymmetricKey().get(), config); } void NativeKeyObject::New(const FunctionCallbackInfo& args) { @@ -6762,13 +6765,13 @@ void StatelessDiffieHellman(const FunctionCallbackInfo& args) { CHECK(args[0]->IsObject() && args[1]->IsObject()); KeyObjectHandle* our_key_object; ASSIGN_OR_RETURN_UNWRAP(&our_key_object, args[0].As()); - CHECK_EQ(our_key_object->GetKeyType(), kKeyTypePrivate); + CHECK_EQ(our_key_object->Data()->GetKeyType(), kKeyTypePrivate); KeyObjectHandle* their_key_object; ASSIGN_OR_RETURN_UNWRAP(&their_key_object, args[1].As()); - CHECK_NE(their_key_object->GetKeyType(), kKeyTypeSecret); + CHECK_NE(their_key_object->Data()->GetKeyType(), kKeyTypeSecret); - ManagedEVPPKey our_key = our_key_object->GetAsymmetricKey(); - ManagedEVPPKey their_key = their_key_object->GetAsymmetricKey(); + ManagedEVPPKey our_key = our_key_object->Data()->GetAsymmetricKey(); + ManagedEVPPKey their_key = their_key_object->Data()->GetAsymmetricKey(); AllocatedBuffer out = StatelessDiffieHellman(env, our_key, their_key); if (out.size() == 0) diff --git a/src/node_crypto.h b/src/node_crypto.h index f95ea64e973007..9aaa188baa99ec 100644 --- a/src/node_crypto.h +++ b/src/node_crypto.h @@ -408,6 +408,27 @@ class ManagedEVPPKey { EVPKeyPointer pkey_; }; +class KeyObjectData { + public: + static KeyObjectData* CreateSecret(v8::Local abv); + static KeyObjectData* CreateAsymmetric(KeyType type, + const ManagedEVPPKey& pkey); + + KeyType GetKeyType() const; + + // These functions allow unprotected access to the raw key material and should + // only be used to implement cryptograohic operations requiring the key. + ManagedEVPPKey GetAsymmetricKey() const; + const char* GetSymmetricKey() const; + size_t GetSymmetricKeySize() const; + + private: + KeyType key_type_; + std::unique_ptr> symmetric_key_; + unsigned int symmetric_key_len_; + ManagedEVPPKey asymmetric_key_; +}; + class KeyObjectHandle : public BaseObject { public: static v8::Local Initialize(Environment* env, @@ -422,21 +443,12 @@ class KeyObjectHandle : public BaseObject { SET_MEMORY_INFO_NAME(KeyObjectHandle) SET_SELF_SIZE(KeyObjectHandle) - KeyType GetKeyType() const; - - // These functions allow unprotected access to the raw key material and should - // only be used to implement cryptograohic operations requiring the key. - ManagedEVPPKey GetAsymmetricKey() const; - const char* GetSymmetricKey() const; - size_t GetSymmetricKeySize() const; + const KeyObjectData* Data(); protected: static void New(const v8::FunctionCallbackInfo& args); static void Init(const v8::FunctionCallbackInfo& args); - void InitSecret(v8::Local abv); - void InitPublic(const ManagedEVPPKey& pkey); - void InitPrivate(const ManagedEVPPKey& pkey); static void GetAsymmetricKeyType( const v8::FunctionCallbackInfo& args); @@ -453,14 +465,10 @@ class KeyObjectHandle : public BaseObject { const PrivateKeyEncodingConfig& config) const; KeyObjectHandle(Environment* env, - v8::Local wrap, - KeyType key_type); + v8::Local wrap); private: - const KeyType key_type_; - std::unique_ptr> symmetric_key_; - unsigned int symmetric_key_len_; - ManagedEVPPKey asymmetric_key_; + std::unique_ptr data_; }; class NativeKeyObject : public BaseObject { From 43a87c7262b38ed99a43bbf5015298fd92e9eca0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Nie=C3=9Fen?= Date: Mon, 11 May 2020 17:55:37 +0000 Subject: [PATCH 4/6] crypto: allow KeyObjects in postMessage This change allows sharing KeyObjects between threads via postMessage. The receiver acquires a new KeyObject and a new KeyObjectHandle, but refers to the same KeyObjectData: +-------------------+ | NativeKeyObject 1 | ------------------------------------------+ +-------------------+ | ^ | extends | | | +-------------------+ +-------------------+ | | KeyObject 1 (JS) | -> | KeyObjectHandle 1 | --------------+ | +-------------------+ +-------------------+ | | | | | | | | | | | | +-------------------+ | | | NativeKeyObject 2 | ------------------------------------+ | | +-------------------+ | | | ^ | | | extends | | | | | | | +-------------------+ +-------------------+ | | | | KeyObject 2 (JS) | -> | KeyObjectHandle 2 | --------+ | | | +-------------------+ +-------------------+ | | | | | | | | | | | | | | | | | | | | | | | | +-------------------+ | | | | | NativeKeyObject 3 | ------------------------------+ | | | | +-------------------+ | | | | | ^ | | | | | extends | | | | | | v v v v v +-------------------+ +-------------------+ +---------------+ | KeyObject 3 (JS) | -> | KeyObjectHandle 3 | -> | KeyObjectData | +-------------------+ +-------------------+ +---------------+ Co-authored-by: Anna Henningsen --- doc/api/crypto.md | 9 ++ doc/api/worker_threads.md | 8 +- lib/internal/crypto/keys.js | 13 +- src/env.h | 3 + src/node_crypto.cc | 118 +++++++++++++----- src/node_crypto.h | 67 ++++++++-- .../test-crypto-key-objects-messageport.js | 85 +++++++++++++ 7 files changed, 251 insertions(+), 52 deletions(-) create mode 100644 test/parallel/test-crypto-key-objects-messageport.js diff --git a/doc/api/crypto.md b/doc/api/crypto.md index 2e1f2981ee7232..6439608cfd80ff 100644 --- a/doc/api/crypto.md +++ b/doc/api/crypto.md @@ -1214,6 +1214,10 @@ This can be called many times with new data as it is streamed.