Skip to content

Commit b828560

Browse files
tniessenaddaleax
authored andcommittedSep 28, 2020
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

File tree

7 files changed

+252
-52
lines changed

7 files changed

+252
-52
lines changed
 

‎doc/api/crypto.md

+9
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

‎doc/api/worker_threads.md

+6-2
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

‎lib/internal/crypto/keys.js

+8-5
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

‎src/env.h

+3
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) \

‎src/node_crypto.cc

+83-34
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_)

‎src/node_crypto.h

+56-11
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include "env.h"
3131
#include "base_object.h"
3232
#include "util.h"
33+
#include "node_messaging.h"
3334

3435
#include "v8.h"
3536

@@ -407,11 +408,13 @@ class ManagedEVPPKey {
407408
EVPKeyPointer pkey_;
408409
};
409410

411+
// Objects of this class can safely be shared among threads.
410412
class KeyObjectData {
411413
public:
412-
static KeyObjectData* CreateSecret(v8::Local<v8::ArrayBufferView> abv);
413-
static KeyObjectData* CreateAsymmetric(KeyType type,
414-
const ManagedEVPPKey& pkey);
414+
static std::shared_ptr<KeyObjectData> CreateSecret(
415+
v8::Local<v8::ArrayBufferView> abv);
416+
static std::shared_ptr<KeyObjectData> CreateAsymmetric(
417+
KeyType type, const ManagedEVPPKey& pkey);
415418

416419
KeyType GetKeyType() const;
417420

@@ -422,10 +425,23 @@ class KeyObjectData {
422425
size_t GetSymmetricKeySize() const;
423426

424427
private:
425-
KeyType key_type_;
426-
std::unique_ptr<char, std::function<void(char*)>> symmetric_key_;
427-
unsigned int symmetric_key_len_;
428-
ManagedEVPPKey asymmetric_key_;
428+
KeyObjectData(std::unique_ptr<char, std::function<void(char*)>> symmetric_key,
429+
unsigned int symmetric_key_len)
430+
: key_type_(KeyType::kKeyTypeSecret),
431+
symmetric_key_(std::move(symmetric_key)),
432+
symmetric_key_len_(symmetric_key_len),
433+
asymmetric_key_() {}
434+
435+
KeyObjectData(KeyType type, const ManagedEVPPKey& pkey)
436+
: key_type_(type),
437+
symmetric_key_(),
438+
symmetric_key_len_(0),
439+
asymmetric_key_{pkey} {}
440+
441+
const KeyType key_type_;
442+
const std::unique_ptr<char, std::function<void(char*)>> symmetric_key_;
443+
const unsigned int symmetric_key_len_;
444+
const ManagedEVPPKey asymmetric_key_;
429445
};
430446

431447
class KeyObjectHandle : public BaseObject {
@@ -434,15 +450,14 @@ class KeyObjectHandle : public BaseObject {
434450
v8::Local<v8::Object> target);
435451

436452
static v8::MaybeLocal<v8::Object> Create(Environment* env,
437-
KeyType type,
438-
const ManagedEVPPKey& pkey);
453+
std::shared_ptr<KeyObjectData> data);
439454

440455
// TODO(tniessen): track the memory used by OpenSSL types
441456
SET_NO_MEMORY_INFO()
442457
SET_MEMORY_INFO_NAME(KeyObjectHandle)
443458
SET_SELF_SIZE(KeyObjectHandle)
444459

445-
const KeyObjectData* Data();
460+
const std::shared_ptr<KeyObjectData>& Data();
446461

447462
protected:
448463
static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
@@ -467,7 +482,7 @@ class KeyObjectHandle : public BaseObject {
467482
v8::Local<v8::Object> wrap);
468483

469484
private:
470-
std::unique_ptr<KeyObjectData> data_;
485+
std::shared_ptr<KeyObjectData> data_;
471486
};
472487

473488
class NativeKeyObject : public BaseObject {
@@ -477,6 +492,36 @@ class NativeKeyObject : public BaseObject {
477492
SET_NO_MEMORY_INFO()
478493
SET_MEMORY_INFO_NAME(NativeKeyObject)
479494
SET_SELF_SIZE(NativeKeyObject)
495+
496+
class KeyObjectTransferData : public worker::TransferData {
497+
public:
498+
explicit KeyObjectTransferData(const std::shared_ptr<KeyObjectData>& data)
499+
: data_(data) {}
500+
501+
BaseObjectPtr<BaseObject> Deserialize(
502+
Environment* env,
503+
v8::Local<v8::Context> context,
504+
std::unique_ptr<worker::TransferData> self) override;
505+
506+
SET_MEMORY_INFO_NAME(KeyObjectTransferData)
507+
SET_SELF_SIZE(KeyObjectTransferData)
508+
SET_NO_MEMORY_INFO()
509+
510+
private:
511+
std::shared_ptr<KeyObjectData> data_;
512+
};
513+
514+
BaseObject::TransferMode GetTransferMode() const override;
515+
std::unique_ptr<worker::TransferData> CloneForMessaging() const override;
516+
517+
private:
518+
NativeKeyObject(Environment* env,
519+
v8::Local<v8::Object> wrap,
520+
const std::shared_ptr<KeyObjectData>& handle_data)
521+
: BaseObject(env, wrap),
522+
handle_data_(handle_data) {}
523+
524+
std::shared_ptr<KeyObjectData> handle_data_;
480525
};
481526

482527
class CipherBase : public BaseObject {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
'use strict';
2+
const common = require('../common');
3+
if (!common.hasCrypto)
4+
common.skip('missing crypto');
5+
6+
const assert = require('assert');
7+
const { createSecretKey, generateKeyPairSync, randomBytes } = require('crypto');
8+
const { createContext } = require('vm');
9+
const {
10+
MessageChannel,
11+
Worker,
12+
moveMessagePortToContext,
13+
parentPort
14+
} = require('worker_threads');
15+
16+
function keyToString(key) {
17+
let ret;
18+
if (key.type === 'secret') {
19+
ret = key.export().toString('hex');
20+
} else {
21+
ret = key.export({ type: 'pkcs1', format: 'pem' });
22+
}
23+
return ret;
24+
}
25+
26+
// Worker threads simply reply with their representation of the received key.
27+
if (process.env.HAS_STARTED_WORKER) {
28+
return parentPort.once('message', ({ key }) => {
29+
parentPort.postMessage(keyToString(key));
30+
});
31+
}
32+
33+
// Don't use isMainThread to allow running this test inside a worker.
34+
process.env.HAS_STARTED_WORKER = 1;
35+
36+
// The main thread generates keys and passes them to worker threads.
37+
const secretKey = createSecretKey(randomBytes(32));
38+
const { publicKey, privateKey } = generateKeyPairSync('rsa', {
39+
modulusLength: 1024
40+
});
41+
42+
// Get immutable representations of all keys.
43+
const keys = [secretKey, publicKey, privateKey]
44+
.map((key) => [key, keyToString(key)]);
45+
46+
for (const [key, repr] of keys) {
47+
{
48+
// Test 1: No context change.
49+
const { port1, port2 } = new MessageChannel();
50+
51+
port1.postMessage({ key });
52+
assert.strictEqual(keyToString(key), repr);
53+
54+
port2.once('message', common.mustCall(({ key }) => {
55+
assert.strictEqual(keyToString(key), repr);
56+
}));
57+
}
58+
59+
{
60+
// Test 2: Across threads.
61+
const worker = new Worker(__filename);
62+
worker.once('message', common.mustCall((receivedRepresentation) => {
63+
assert.strictEqual(receivedRepresentation, repr);
64+
}));
65+
worker.on('disconnect', () => console.log('disconnect'));
66+
worker.postMessage({ key });
67+
}
68+
69+
{
70+
// Test 3: Across contexts (should not work).
71+
const { port1, port2 } = new MessageChannel();
72+
const context = createContext();
73+
const port2moved = moveMessagePortToContext(port2, context);
74+
assert(!(port2moved instanceof Object));
75+
76+
// TODO(addaleax): Switch this to a 'messageerror' event once MessagePort
77+
// implements EventTarget fully and in a cross-context manner.
78+
port2moved.emit = common.mustCall((name, err) => {
79+
assert.strictEqual(name, 'messageerror');
80+
assert.strictEqual(err.code, 'ERR_MESSAGE_TARGET_CONTEXT_UNAVAILABLE');
81+
});
82+
83+
port2moved.start();
84+
port1.postMessage({ key });
85+
port1.close();
86+
}
87+
}

0 commit comments

Comments
 (0)
Please sign in to comment.