blob: 1d68e4b9911a0a5fbe32f8b39f4262833cd8b01a [file] [edit]
'use strict';
// KeyObject instances must survive structured cloning with their
// native backing data and hidden JS slot semantics preserved.
const common = require('../common');
if (!common.hasCrypto)
common.skip('missing crypto');
const assert = require('node:assert');
const { once } = require('node:events');
const {
createHmac,
createSecretKey,
generateKeyPairSync,
sign,
verify,
} = require('node:crypto');
const { MessageChannel, Worker } = require('node:worker_threads');
const { types: { isKeyObject } } = require('node:util');
function assertNoOwnKeys(key) {
assert.deepStrictEqual(Object.getOwnPropertySymbols(key), []);
assert.deepStrictEqual(Object.getOwnPropertyNames(key), []);
assert.deepStrictEqual(Reflect.ownKeys(key), []);
}
function assertSameKeyObject(original, clone) {
assert.notStrictEqual(original, clone);
assert.strictEqual(isKeyObject(clone), true);
assert.strictEqual(original.type, clone.type);
assert.strictEqual(original.equals(clone), true);
assert.deepStrictEqual(original, clone);
if (clone.type === 'secret') {
assert.strictEqual(original.symmetricKeySize, clone.symmetricKeySize);
} else {
assert.strictEqual(original.asymmetricKeyType, clone.asymmetricKeyType);
assert.deepStrictEqual(
original.asymmetricKeyDetails,
clone.asymmetricKeyDetails);
}
assertNoOwnKeys(original);
assertNoOwnKeys(clone);
}
async function roundTripViaMessageChannel(key) {
const { port1, port2 } = new MessageChannel();
port1.postMessage(key);
const [received] = await once(port2, 'message');
port1.close();
port2.close();
return received;
}
async function roundTripViaWorker(key) {
const worker = new Worker(`
'use strict';
const { parentPort } = require('node:worker_threads');
const { types: { isKeyObject } } = require('node:util');
parentPort.once('message', ({ key, expectedType }) => {
try {
if (!isKeyObject(key) || key.type !== expectedType) {
throw new Error('KeyObject slot mismatch in worker');
}
parentPort.postMessage({ key });
} catch (err) {
parentPort.postMessage({ error: err.stack || err.message });
}
});
`, { eval: true });
worker.postMessage({ key, expectedType: key.type });
const [msg] = await once(worker, 'message');
await worker.terminate();
assert.strictEqual(msg.error, undefined, msg.error);
return msg.key;
}
function hmacDigest(key) {
return createHmac('sha256', key).update('payload').digest('hex');
}
(async () => {
const secret = createSecretKey(Buffer.alloc(16));
const { publicKey, privateKey } = generateKeyPairSync('rsa', {
modulusLength: 1024,
});
for (const key of [secret, publicKey, privateKey]) {
const cloned = structuredClone(key);
assertSameKeyObject(key, cloned);
const viaPort = await roundTripViaMessageChannel(key);
assertSameKeyObject(key, viaPort);
const clonedAgain = structuredClone(viaPort);
assertSameKeyObject(key, clonedAgain);
const viaWorker = await roundTripViaWorker(key);
assertSameKeyObject(key, viaWorker);
}
const secretClones = [
secret,
structuredClone(secret),
await roundTripViaMessageChannel(secret),
await roundTripViaWorker(secret),
];
const digest = hmacDigest(secret);
for (const key of secretClones) {
assert.strictEqual(hmacDigest(key), digest);
}
const data = Buffer.from('payload');
const publicClones = [
publicKey,
structuredClone(publicKey),
await roundTripViaMessageChannel(publicKey),
await roundTripViaWorker(publicKey),
];
const privateClones = [
privateKey,
structuredClone(privateKey),
await roundTripViaMessageChannel(privateKey),
await roundTripViaWorker(privateKey),
];
const signature = sign('sha256', data, privateKey);
for (const key of publicClones) {
assert.strictEqual(verify('sha256', data, key, signature), true);
}
for (const key of privateClones) {
const clonedSignature = sign('sha256', data, key);
assert.strictEqual(verify('sha256', data, publicKey, clonedSignature), true);
}
})().then(common.mustCall());