summaryrefslogtreecommitdiff
path: root/test/parallel/test-crypto-key-objects-messageport.js
blob: d23fde0d00d79b375f5f8e5c4931eecf400f7126 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
'use strict';
const common = require('../common');
if (!common.hasCrypto)
  common.skip('missing crypto');

const assert = require('assert');
const {
  generateKeySync,
  generateKeyPairSync,
  KeyObject,
} = require('crypto');
const { subtle } = globalThis.crypto;
const { createContext } = require('vm');
const {
  MessageChannel,
  Worker,
  moveMessagePortToContext,
  parentPort
} = require('worker_threads');

function keyToString(key) {
  let ret;
  if (key instanceof CryptoKey) {
    key = KeyObject.from(key);
  }
  if (key.type === 'secret') {
    ret = key.export().toString('hex');
  } else {
    ret = key.export({ type: 'pkcs1', format: 'pem' });
  }
  return ret;
}

// Worker threads simply reply with their representation of the received key.
if (process.env.HAS_STARTED_WORKER) {
  return parentPort.once('message', ({ key }) => {
    parentPort.postMessage(keyToString(key));
  });
}

// Don't use isMainThread to allow running this test inside a worker.
process.env.HAS_STARTED_WORKER = 1;

(async () => {
  // The main thread generates keys and passes them to worker threads.
  const secretKey = generateKeySync('aes', { length: 128 });
  const { publicKey, privateKey } = generateKeyPairSync('rsa', {
    modulusLength: 1024
  });
  const cryptoKey = await subtle.generateKey(
    { name: 'AES-CBC', length: 128 }, false, ['encrypt']);

  // Get immutable representations of all keys.
  const keys = [secretKey, publicKey, privateKey, cryptoKey]
             .map((key) => [key, keyToString(key)]);

  for (const [key, repr] of keys) {
    {
    // Test 1: No context change.
      const { port1, port2 } = new MessageChannel();

      port1.postMessage({ key });
      assert.strictEqual(keyToString(key), repr);

      port2.once('message', common.mustCall(({ key }) => {
        assert.strictEqual(keyToString(key), repr);
      }));
    }

    {
    // Test 2: Across threads.
      const worker = new Worker(__filename);
      worker.once('message', common.mustCall((receivedRepresentation) => {
        assert.strictEqual(receivedRepresentation, repr);
      }));
      worker.on('disconnect', () => console.log('disconnect'));
      worker.postMessage({ key });
    }

    {
    // Test 3: Across contexts (should not work).
      const { port1, port2 } = new MessageChannel();
      const context = createContext();
      const port2moved = moveMessagePortToContext(port2, context);
      assert(!(port2moved instanceof Object));

      // TODO(addaleax): Switch this to a 'messageerror' event once MessagePort
      // implements EventTarget fully and in a cross-context manner.
      port2moved.onmessageerror = common.mustCall((event) => {
        assert.strictEqual(event.data.code,
                           'ERR_MESSAGE_TARGET_CONTEXT_UNAVAILABLE');
      });

      port2moved.start();
      port1.postMessage({ key });
      port1.close();
    }
  }
})().then(common.mustCall());