Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit b828560

Browse filesBrowse files
tniessenaddaleax
authored andcommitted
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 <anna@addaleax.net> PR-URL: #33360 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent 50b1cde commit b828560
Copy full SHA for b828560

File tree

Expand file treeCollapse file tree

7 files changed

+252
-52
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

7 files changed

+252
-52
lines changed
Open diff view settings
Collapse file

‎doc/api/crypto.md‎

Copy file name to clipboardExpand all lines: doc/api/crypto.md
+9Lines changed: 9 additions & 0 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -1215,6 +1215,10 @@ This can be called many times with new data as it is streamed.
12151215
<!-- YAML
12161216
added: v11.6.0
12171217
changes:
1218+
- version: REPLACEME
1219+
pr-url: https://github.com/nodejs/node/pull/33360
1220+
description: Instances of this class can now be passed to worker threads
1221+
using `postMessage`.
12181222
- version: v11.13.0
12191223
pr-url: https://github.com/nodejs/node/pull/26438
12201224
description: This class is now exported.
@@ -1230,6 +1234,10 @@ keyword.
12301234
Most applications should consider using the new `KeyObject` API instead of
12311235
passing keys as strings or `Buffer`s due to improved security features.
12321236

1237+
`KeyObject` instances can be passed to other threads via [`postMessage()`][].
1238+
The receiver obtains a cloned `KeyObject`, and the `KeyObject` does not need to
1239+
be listed in the `transferList` argument.
1240+
12331241
### `keyObject.asymmetricKeyType`
12341242
<!-- YAML
12351243
added: v11.6.0
@@ -3560,6 +3568,7 @@ See the [list of SSL OP Flags][] for details.
35603568
[`hmac.digest()`]: #crypto_hmac_digest_encoding
35613569
[`hmac.update()`]: #crypto_hmac_update_data_inputencoding
35623570
[`keyObject.export()`]: #crypto_keyobject_export_options
3571+
[`postMessage()`]: worker_threads.html#worker_threads_port_postmessage_value_transferlist
35633572
[`sign.sign()`]: #crypto_sign_sign_privatekey_outputencoding
35643573
[`sign.update()`]: #crypto_sign_update_data_inputencoding
35653574
[`stream.Writable` options]: stream.html#stream_new_stream_writable_options
Collapse file

‎doc/api/worker_threads.md‎

Copy file name to clipboardExpand all lines: doc/api/worker_threads.md
+6-2Lines changed: 6 additions & 2 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,9 @@ are part of the channel.
328328
<!-- YAML
329329
added: v10.5.0
330330
changes:
331+
- version: REPLACEME
332+
pr-url: https://github.com/nodejs/node/pull/33360
333+
description: Added `KeyObject` to the list of cloneable types.
331334
- version: REPLACEME
332335
pr-url: https://github.com/nodejs/node/pull/33772
333336
description: Added `FileHandle` to the list of transferable types.
@@ -348,8 +351,8 @@ In particular, the significant differences to `JSON` are:
348351
* `value` may contain typed arrays, both using `ArrayBuffer`s
349352
and `SharedArrayBuffer`s.
350353
* `value` may contain [`WebAssembly.Module`][] instances.
351-
* `value` may not contain native (C++-backed) objects other than `MessagePort`s
352-
and [`FileHandle`][]s.
354+
* `value` may not contain native (C++-backed) objects other than `MessagePort`s,
355+
[`FileHandle`][]s, and [`KeyObject`][]s.
353356

354357
```js
355358
const { MessageChannel } = require('worker_threads');
@@ -846,6 +849,7 @@ active handle in the event system. If the worker is already `unref()`ed calling
846849
[`EventEmitter`]: events.html
847850
[`EventTarget`]: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget
848851
[`FileHandle`]: fs.html#fs_class_filehandle
852+
[`KeyObject`]: crypto.html#crypto_class_keyobject
849853
[`MessagePort`]: #worker_threads_class_messageport
850854
[`SharedArrayBuffer`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer
851855
[`Uint8Array`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array
Collapse file

‎lib/internal/crypto/keys.js‎

Copy file name to clipboardExpand all lines: lib/internal/crypto/keys.js
+8-5Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,13 @@ for (const m of [[kKeyEncodingPKCS1, 'pkcs1'], [kKeyEncodingPKCS8, 'pkcs8'],
4343
[kKeyEncodingSPKI, 'spki'], [kKeyEncodingSEC1, 'sec1']])
4444
encodingNames[m[0]] = m[1];
4545

46+
function checkKeyTypeAndHandle(type, handle) {
47+
if (type !== 'secret' && type !== 'public' && type !== 'private')
48+
throw new ERR_INVALID_ARG_VALUE('type', type);
49+
if (typeof handle !== 'object' || !(handle instanceof KeyObjectHandle))
50+
throw new ERR_INVALID_ARG_TYPE('handle', 'object', handle);
51+
}
52+
4653
// Creating the KeyObject class is a little complicated due to inheritance
4754
// and that fact that KeyObjects should be transferrable between threads,
4855
// which requires the KeyObject base class to be implemented in C++.
@@ -57,11 +64,7 @@ const [
5764
// Publicly visible KeyObject class.
5865
class KeyObject extends NativeKeyObject {
5966
constructor(type, handle) {
60-
super();
61-
if (type !== 'secret' && type !== 'public' && type !== 'private')
62-
throw new ERR_INVALID_ARG_VALUE('type', type);
63-
if (typeof handle !== 'object')
64-
throw new ERR_INVALID_ARG_TYPE('handle', 'object', handle);
67+
super(checkKeyTypeAndHandle(type, handle) || handle);
6568

6669
this[kKeyType] = type;
6770

Collapse file

‎src/env.h‎

Copy file name to clipboardExpand all lines: src/env.h
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,9 @@ constexpr size_t kFsStatsBufferLength =
452452
V(buffer_prototype_object, v8::Object) \
453453
V(crypto_key_object_constructor, v8::Function) \
454454
V(crypto_key_object_handle_constructor, v8::Function) \
455+
V(crypto_key_object_private_constructor, v8::Function) \
456+
V(crypto_key_object_public_constructor, v8::Function) \
457+
V(crypto_key_object_secret_constructor, v8::Function) \
455458
V(domexception_function, v8::Function) \
456459
V(enhance_fatal_stack_after_inspector, v8::Function) \
457460
V(enhance_fatal_stack_before_inspector, v8::Function) \
Collapse file

‎src/node_crypto.cc‎

Copy file name to clipboardExpand all lines: src/node_crypto.cc
+83-34Lines changed: 83 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -3206,27 +3206,24 @@ EVP_PKEY* ManagedEVPPKey::get() const {
32063206
return pkey_.get();
32073207
}
32083208

3209-
KeyObjectData* KeyObjectData::CreateSecret(v8::Local<v8::ArrayBufferView> abv) {
3209+
std::shared_ptr<KeyObjectData> KeyObjectData::CreateSecret(
3210+
Local<ArrayBufferView> abv) {
32103211
size_t key_len = abv->ByteLength();
32113212
char* mem = MallocOpenSSL<char>(key_len);
32123213
abv->CopyContents(mem, key_len);
3213-
KeyObjectData* data = new KeyObjectData();
3214-
data->key_type_ = kKeyTypeSecret;
3215-
data->symmetric_key_ = std::unique_ptr<char, std::function<void(char*)>>(mem,
3214+
return std::shared_ptr<KeyObjectData>(new KeyObjectData(
3215+
std::unique_ptr<char, std::function<void(char*)>>(mem,
32163216
[key_len](char* p) {
32173217
OPENSSL_clear_free(p, key_len);
3218-
});
3219-
data->symmetric_key_len_ = key_len;
3220-
return data;
3218+
}),
3219+
key_len));
32213220
}
32223221

3223-
KeyObjectData* KeyObjectData::CreateAsymmetric(KeyType key_type,
3224-
const ManagedEVPPKey& pkey) {
3222+
std::shared_ptr<KeyObjectData> KeyObjectData::CreateAsymmetric(
3223+
KeyType key_type,
3224+
const ManagedEVPPKey& pkey) {
32253225
CHECK(pkey);
3226-
KeyObjectData* data = new KeyObjectData();
3227-
data->key_type_ = key_type;
3228-
data->asymmetric_key_ = pkey;
3229-
return data;
3226+
return std::shared_ptr<KeyObjectData>(new KeyObjectData(key_type, pkey));
32303227
}
32313228

32323229
KeyType KeyObjectData::GetKeyType() const {
@@ -3270,26 +3267,24 @@ Local<Function> KeyObjectHandle::Initialize(Environment* env,
32703267
return function;
32713268
}
32723269

3273-
MaybeLocal<Object> KeyObjectHandle::Create(Environment* env,
3274-
KeyType key_type,
3275-
const ManagedEVPPKey& pkey) {
3276-
CHECK_NE(key_type, kKeyTypeSecret);
3277-
Local<Value> type = Integer::New(env->isolate(), key_type);
3270+
MaybeLocal<Object> KeyObjectHandle::Create(
3271+
Environment* env,
3272+
std::shared_ptr<KeyObjectData> data) {
32783273
Local<Object> obj;
32793274
if (!env->crypto_key_object_handle_constructor()
3280-
->NewInstance(env->context(), 1, &type)
3275+
->NewInstance(env->context(), 0, nullptr)
32813276
.ToLocal(&obj)) {
32823277
return MaybeLocal<Object>();
32833278
}
32843279

32853280
KeyObjectHandle* key = Unwrap<KeyObjectHandle>(obj);
32863281
CHECK_NOT_NULL(key);
3287-
key->data_.reset(KeyObjectData::CreateAsymmetric(key_type, pkey));
3282+
key->data_ = data;
32883283
return obj;
32893284
}
32903285

3291-
const KeyObjectData* KeyObjectHandle::Data() {
3292-
return data_.get();
3286+
const std::shared_ptr<KeyObjectData>& KeyObjectHandle::Data() {
3287+
return data_;
32933288
}
32943289

32953290
void KeyObjectHandle::New(const FunctionCallbackInfo<Value>& args) {
@@ -3319,8 +3314,7 @@ void KeyObjectHandle::Init(const FunctionCallbackInfo<Value>& args) {
33193314
case kKeyTypeSecret:
33203315
CHECK_EQ(args.Length(), 2);
33213316
CHECK(args[1]->IsArrayBufferView());
3322-
key->data_.reset(
3323-
KeyObjectData::CreateSecret(args[1].As<ArrayBufferView>()));
3317+
key->data_ = KeyObjectData::CreateSecret(args[1].As<ArrayBufferView>());
33243318
break;
33253319
case kKeyTypePublic:
33263320
CHECK_EQ(args.Length(), 4);
@@ -3329,7 +3323,7 @@ void KeyObjectHandle::Init(const FunctionCallbackInfo<Value>& args) {
33293323
pkey = GetPublicOrPrivateKeyFromJs(args, &offset);
33303324
if (!pkey)
33313325
return;
3332-
key->data_.reset(KeyObjectData::CreateAsymmetric(type, pkey));
3326+
key->data_ = KeyObjectData::CreateAsymmetric(type, pkey);
33333327
break;
33343328
case kKeyTypePrivate:
33353329
CHECK_EQ(args.Length(), 5);
@@ -3338,7 +3332,7 @@ void KeyObjectHandle::Init(const FunctionCallbackInfo<Value>& args) {
33383332
pkey = GetPrivateKeyFromJs(args, &offset, false);
33393333
if (!pkey)
33403334
return;
3341-
key->data_.reset(KeyObjectData::CreateAsymmetric(type, pkey));
3335+
key->data_ = KeyObjectData::CreateAsymmetric(type, pkey);
33423336
break;
33433337
default:
33443338
CHECK(false);
@@ -3434,7 +3428,50 @@ MaybeLocal<Value> KeyObjectHandle::ExportPrivateKey(
34343428
}
34353429

34363430
void NativeKeyObject::New(const FunctionCallbackInfo<Value>& args) {
3437-
CHECK_EQ(args.Length(), 0);
3431+
Environment* env = Environment::GetCurrent(args);
3432+
CHECK_EQ(args.Length(), 1);
3433+
CHECK(args[0]->IsObject());
3434+
KeyObjectHandle* handle = Unwrap<KeyObjectHandle>(args[0].As<Object>());
3435+
new NativeKeyObject(env, args.This(), handle->Data());
3436+
}
3437+
3438+
BaseObjectPtr<BaseObject> NativeKeyObject::KeyObjectTransferData::Deserialize(
3439+
Environment* env,
3440+
Local<Context> context,
3441+
std::unique_ptr<worker::TransferData> self) {
3442+
if (context != env->context()) {
3443+
THROW_ERR_MESSAGE_TARGET_CONTEXT_UNAVAILABLE(env);
3444+
return {};
3445+
}
3446+
3447+
Local<Value> handle = KeyObjectHandle::Create(env, data_).ToLocalChecked();
3448+
Local<Function> key_ctor;
3449+
switch (data_->GetKeyType()) {
3450+
case kKeyTypeSecret:
3451+
key_ctor = env->crypto_key_object_secret_constructor();
3452+
break;
3453+
case kKeyTypePublic:
3454+
key_ctor = env->crypto_key_object_public_constructor();
3455+
break;
3456+
case kKeyTypePrivate:
3457+
key_ctor = env->crypto_key_object_private_constructor();
3458+
break;
3459+
default:
3460+
CHECK(false);
3461+
}
3462+
3463+
Local<Value> key =
3464+
key_ctor->NewInstance(context, 1, &handle).ToLocalChecked();
3465+
return BaseObjectPtr<BaseObject>(Unwrap<KeyObjectHandle>(key.As<Object>()));
3466+
}
3467+
3468+
BaseObject::TransferMode NativeKeyObject::GetTransferMode() const {
3469+
return BaseObject::TransferMode::kCloneable;
3470+
}
3471+
3472+
std::unique_ptr<worker::TransferData> NativeKeyObject::CloneForMessaging()
3473+
const {
3474+
return std::make_unique<KeyObjectTransferData>(handle_data_);
34383475
}
34393476

34403477
static void CreateNativeKeyObjectClass(
@@ -3448,13 +3485,23 @@ static void CreateNativeKeyObjectClass(
34483485
Local<FunctionTemplate> t = env->NewFunctionTemplate(NativeKeyObject::New);
34493486
t->InstanceTemplate()->SetInternalFieldCount(
34503487
KeyObjectHandle::kInternalFieldCount);
3488+
t->Inherit(BaseObject::GetConstructorTemplate(env));
34513489

34523490
Local<Value> ctor = t->GetFunction(env->context()).ToLocalChecked();
34533491

34543492
Local<Value> recv = Undefined(env->isolate());
3455-
Local<Value> ret =
3456-
callback.As<Function>()->Call(env->context(), recv, 1, &ctor)
3457-
.ToLocalChecked();
3493+
Local<Value> ret_v;
3494+
if (!callback.As<Function>()->Call(
3495+
env->context(), recv, 1, &ctor).ToLocal(&ret_v)) {
3496+
return;
3497+
}
3498+
Local<Array> ret = ret_v.As<Array>();
3499+
if (!ret->Get(env->context(), 1).ToLocal(&ctor)) return;
3500+
env->set_crypto_key_object_secret_constructor(ctor.As<Function>());
3501+
if (!ret->Get(env->context(), 2).ToLocal(&ctor)) return;
3502+
env->set_crypto_key_object_public_constructor(ctor.As<Function>());
3503+
if (!ret->Get(env->context(), 3).ToLocal(&ctor)) return;
3504+
env->set_crypto_key_object_private_constructor(ctor.As<Function>());
34583505
args.GetReturnValue().Set(ret);
34593506
}
34603507

@@ -6318,8 +6365,9 @@ class GenerateKeyPairJob : public CryptoJob {
63186365
if (public_key_encoding_.output_key_object_) {
63196366
// Note that this has the downside of containing sensitive data of the
63206367
// private key.
6321-
if (!KeyObjectHandle::Create(env(), kKeyTypePublic, pkey_)
6322-
.ToLocal(pubkey))
6368+
std::shared_ptr<KeyObjectData> data =
6369+
KeyObjectData::CreateAsymmetric(kKeyTypePublic, pkey_);
6370+
if (!KeyObjectHandle::Create(env(), data).ToLocal(pubkey))
63236371
return false;
63246372
} else {
63256373
if (!WritePublicKey(env(), pkey_.get(), public_key_encoding_)
@@ -6329,8 +6377,9 @@ class GenerateKeyPairJob : public CryptoJob {
63296377

63306378
// Now do the same for the private key.
63316379
if (private_key_encoding_.output_key_object_) {
6332-
if (!KeyObjectHandle::Create(env(), kKeyTypePrivate, pkey_)
6333-
.ToLocal(privkey))
6380+
std::shared_ptr<KeyObjectData> data =
6381+
KeyObjectData::CreateAsymmetric(kKeyTypePrivate, pkey_);
6382+
if (!KeyObjectHandle::Create(env(), data).ToLocal(privkey))
63346383
return false;
63356384
} else {
63366385
if (!WritePrivateKey(env(), pkey_.get(), private_key_encoding_)

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.