mirror of
https://github.com/zebrajr/node.git
synced 2025-12-06 00:20:08 +01:00
PR-URL: https://github.com/nodejs/node/pull/59567 Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: Tobias Nießen <tniessen@tnie.de>
266 lines
8.5 KiB
JavaScript
266 lines
8.5 KiB
JavaScript
// Flags: --expose-internals --no-warnings
|
|
'use strict';
|
|
|
|
const common = require('../common');
|
|
|
|
if (!common.hasCrypto)
|
|
common.skip('missing crypto');
|
|
|
|
const assert = require('assert');
|
|
const { subtle } = globalThis.crypto;
|
|
const { KeyObject } = require('crypto');
|
|
|
|
// This is only a partial test. The WebCrypto Web Platform Tests
|
|
// will provide much greater coverage.
|
|
|
|
// Test ECDH key derivation
|
|
{
|
|
async function test(namedCurve) {
|
|
const [alice, bob] = await Promise.all([
|
|
subtle.generateKey({ name: 'ECDH', namedCurve }, true, ['deriveKey']),
|
|
subtle.generateKey({ name: 'ECDH', namedCurve }, true, ['deriveKey']),
|
|
]);
|
|
|
|
const [secret1, secret2] = await Promise.all([
|
|
subtle.deriveKey({
|
|
name: 'ECDH', namedCurve, public: alice.publicKey
|
|
}, bob.privateKey, {
|
|
name: 'AES-CBC',
|
|
length: 256
|
|
}, true, ['encrypt']),
|
|
subtle.deriveKey({
|
|
name: 'ECDH', namedCurve, public: bob.publicKey
|
|
}, alice.privateKey, {
|
|
name: 'AES-CBC',
|
|
length: 256
|
|
}, true, ['encrypt']),
|
|
]);
|
|
|
|
const [raw1, raw2] = await Promise.all([
|
|
subtle.exportKey('raw', secret1),
|
|
subtle.exportKey('raw', secret2),
|
|
]);
|
|
|
|
assert.deepStrictEqual(raw1, raw2);
|
|
}
|
|
|
|
test('P-521').then(common.mustCall());
|
|
}
|
|
|
|
// Test HKDF key derivation
|
|
{
|
|
async function test(pass, info, salt, hash, expected) {
|
|
const ec = new TextEncoder();
|
|
const key = await subtle.importKey(
|
|
'raw',
|
|
ec.encode(pass),
|
|
{ name: 'HKDF', hash },
|
|
false, ['deriveKey']);
|
|
|
|
const secret = await subtle.deriveKey({
|
|
name: 'HKDF',
|
|
hash,
|
|
salt: ec.encode(salt),
|
|
info: ec.encode(info)
|
|
}, key, {
|
|
name: 'AES-CTR',
|
|
length: 256
|
|
}, true, ['encrypt']);
|
|
|
|
const raw = await subtle.exportKey('raw', secret);
|
|
|
|
assert.strictEqual(Buffer.from(raw).toString('hex'), expected);
|
|
}
|
|
|
|
const kTests = [
|
|
['hello', 'there', 'my friend', 'SHA-1',
|
|
'365ca5d3f42d050c74302e420c83975327950f1913a151eecd00526bf52614a0'],
|
|
['hello', 'there', 'my friend', 'SHA-256',
|
|
'14d93b0ccd99d4f2cbd9fbfe9c830b5b8a43e3e45e32941ef21bdeb0fa87b6b6'],
|
|
['hello', 'there', 'my friend', 'SHA-384',
|
|
'e36cf2cf943d8f3a88adb80f478745c336ac811b1a86d03a7d10eb0b6b52295c'],
|
|
['hello', 'there', 'my friend', 'SHA-512',
|
|
'1e42d43fcacba361716f65853bd5f3c479f679612f0180eab3c51ed6c9d2b47d'],
|
|
];
|
|
|
|
if (!process.features.openssl_is_boringssl) {
|
|
kTests.push(
|
|
['hello', 'there', 'my friend', 'SHA3-256',
|
|
'2a49a3b6fb219117af9e251c6c65f16600cbca13bd0be6e70d96b0b9fa4cf3fd'],
|
|
['hello', 'there', 'my friend', 'SHA3-384',
|
|
'0437bb59b95f2db2c7684c0b439028cb0fdd6f0f5d03b9f489066a87ae147221'],
|
|
['hello', 'there', 'my friend', 'SHA3-512',
|
|
'3bbc469d38214371921e52c6f147e96cb7eb370421a81f53dea8b4851dfb8bce'],
|
|
);
|
|
} else {
|
|
common.printSkipMessage('Skipping unsupported SHA-3 test cases');
|
|
}
|
|
|
|
const tests = Promise.all(kTests.map((args) => test(...args)));
|
|
|
|
tests.then(common.mustCall());
|
|
}
|
|
|
|
// Test PBKDF2 key derivation
|
|
{
|
|
async function test(pass, salt, iterations, hash, expected) {
|
|
const ec = new TextEncoder();
|
|
const key = await subtle.importKey(
|
|
'raw',
|
|
ec.encode(pass),
|
|
{ name: 'PBKDF2', hash },
|
|
false, ['deriveKey']);
|
|
const secret = await subtle.deriveKey({
|
|
name: 'PBKDF2',
|
|
hash,
|
|
salt: ec.encode(salt),
|
|
iterations,
|
|
}, key, {
|
|
name: 'AES-CTR',
|
|
length: 256
|
|
}, true, ['encrypt']);
|
|
|
|
const raw = await subtle.exportKey('raw', secret);
|
|
|
|
assert.strictEqual(Buffer.from(raw).toString('hex'), expected);
|
|
}
|
|
|
|
const kTests = [
|
|
['hello', 'there', 5, 'SHA-1',
|
|
'f8f65a5fd92c9b74916083a7e9b0001c46bc89e2a14c48014cf1e0e1dbabf635'],
|
|
['hello', 'there', 5, 'SHA-256',
|
|
'2e575eae24267db32106c7dba01615e5417557e8c5cf33ba15a311cb0c2907ee'],
|
|
['hello', 'there', 5, 'SHA-384',
|
|
'201509b012c9cd2fbe7ea938f0c509b36ecb140f38bf9130e96923f55f46756d'],
|
|
['hello', 'there', 5, 'SHA-512',
|
|
'2e8d981741f98193e0af9c79870af0e985089341221edad9a130d297eae1984b'],
|
|
['hello', 'there', 5, 'SHA3-256',
|
|
'0aed29b61b3ca3978aea34a9793276574ea997b69e8d03727438199f90571649'],
|
|
['hello', 'there', 5, 'SHA3-384',
|
|
'7aa4a274aa19b4623c5d3091c4b06355de85ff6f25e53a83e3126cbb86ae68df'],
|
|
['hello', 'there', 5, 'SHA3-512',
|
|
'4d909c47a81c625f866d1f9406248e6bc3c7ea89225fbccf1f08820254c9ef56'],
|
|
];
|
|
|
|
const tests = Promise.all(kTests.map((args) => test(...args)));
|
|
|
|
tests.then(common.mustCall());
|
|
}
|
|
|
|
// Test default key lengths
|
|
{
|
|
const vectors = [
|
|
['PBKDF2', 'deriveKey', 528],
|
|
['HKDF', 'deriveKey', 528],
|
|
[{ name: 'HMAC', hash: 'SHA-1' }, 'sign', 512],
|
|
[{ name: 'HMAC', hash: 'SHA-256' }, 'sign', 512],
|
|
// Not long enough secret generated by ECDH
|
|
[{ name: 'HMAC', hash: 'SHA-384' }, 'sign', 1024],
|
|
[{ name: 'HMAC', hash: 'SHA-512' }, 'sign', 1024],
|
|
[{ name: 'HMAC', hash: 'SHA3-256', length: 256 }, 'sign', 256],
|
|
[{ name: 'HMAC', hash: 'SHA3-384', length: 384 }, 'sign', 384],
|
|
[{ name: 'HMAC', hash: 'SHA3-512', length: 512 }, 'sign', 512],
|
|
// This interaction is not defined for now.
|
|
// https://github.com/WICG/webcrypto-modern-algos/issues/23
|
|
// [{ name: 'HMAC', hash: 'SHA3-256' }, 'sign', 256],
|
|
// [{ name: 'HMAC', hash: 'SHA3-384' }, 'sign', 384],
|
|
// [{ name: 'HMAC', hash: 'SHA3-512' }, 'sign', 512],
|
|
];
|
|
|
|
(async () => {
|
|
const keyPair = await subtle.generateKey({ name: 'ECDH', namedCurve: 'P-521' }, false, ['deriveKey']);
|
|
for (const [derivedKeyAlgorithm, usage, expected] of vectors) {
|
|
const [result] = await Promise.allSettled([subtle.deriveKey(
|
|
{ name: 'ECDH', public: keyPair.publicKey },
|
|
keyPair.privateKey,
|
|
derivedKeyAlgorithm,
|
|
false,
|
|
[usage])]);
|
|
|
|
if (expected > 528) {
|
|
assert.strictEqual(result.status, 'rejected');
|
|
assert.match(result.reason.message, /derived bit length is too small/);
|
|
} else {
|
|
assert.strictEqual(result.status, 'fulfilled');
|
|
const derived = result.value;
|
|
if (derived.algorithm.name === 'HMAC') {
|
|
assert.strictEqual(derived.algorithm.length, expected);
|
|
} else {
|
|
// KDFs cannot be exportable and do not indicate their length
|
|
const secretKey = KeyObject.from(derived);
|
|
assert.strictEqual(secretKey.symmetricKeySize, expected / 8);
|
|
}
|
|
}
|
|
}
|
|
})().then(common.mustCall());
|
|
}
|
|
|
|
{
|
|
const vectors = [
|
|
[{ name: 'HMAC', hash: 'SHA-1' }, 'sign', 512],
|
|
[{ name: 'HMAC', hash: 'SHA-256' }, 'sign', 512],
|
|
[{ name: 'HMAC', hash: 'SHA-384' }, 'sign', 1024],
|
|
[{ name: 'HMAC', hash: 'SHA-512' }, 'sign', 1024],
|
|
[{ name: 'HMAC', hash: 'SHA3-256', length: 256 }, 'sign', 256],
|
|
[{ name: 'HMAC', hash: 'SHA3-384', length: 384 }, 'sign', 384],
|
|
[{ name: 'HMAC', hash: 'SHA3-512', length: 512 }, 'sign', 512],
|
|
// This interaction is not defined for now.
|
|
// https://github.com/WICG/webcrypto-modern-algos/issues/23
|
|
// [{ name: 'HMAC', hash: 'SHA3-256' }, 'sign', 256],
|
|
// [{ name: 'HMAC', hash: 'SHA3-384' }, 'sign', 384],
|
|
// [{ name: 'HMAC', hash: 'SHA3-512' }, 'sign', 512],
|
|
];
|
|
|
|
(async () => {
|
|
for (const [derivedKeyAlgorithm, usage, expected] of vectors) {
|
|
const derived = await subtle.deriveKey(
|
|
{ name: 'PBKDF2', salt: new Uint8Array([]), hash: 'SHA-256', iterations: 20 },
|
|
await subtle.importKey('raw', new Uint8Array([]), { name: 'PBKDF2' }, false, ['deriveKey']),
|
|
derivedKeyAlgorithm,
|
|
false,
|
|
[usage]);
|
|
|
|
assert.strictEqual(derived.algorithm.length, expected);
|
|
}
|
|
})().then(common.mustCall());
|
|
}
|
|
|
|
// Test X25519 and X448 key derivation
|
|
{
|
|
async function test(name) {
|
|
const [alice, bob] = await Promise.all([
|
|
subtle.generateKey({ name }, true, ['deriveKey']),
|
|
subtle.generateKey({ name }, true, ['deriveKey']),
|
|
]);
|
|
|
|
const [secret1, secret2] = await Promise.all([
|
|
subtle.deriveKey({
|
|
name, public: alice.publicKey
|
|
}, bob.privateKey, {
|
|
name: 'AES-CBC',
|
|
length: 256
|
|
}, true, ['encrypt']),
|
|
subtle.deriveKey({
|
|
name, public: bob.publicKey
|
|
}, alice.privateKey, {
|
|
name: 'AES-CBC',
|
|
length: 256
|
|
}, true, ['encrypt']),
|
|
]);
|
|
|
|
const [raw1, raw2] = await Promise.all([
|
|
subtle.exportKey('raw', secret1),
|
|
subtle.exportKey('raw', secret2),
|
|
]);
|
|
|
|
assert.deepStrictEqual(raw1, raw2);
|
|
}
|
|
|
|
test('X25519').then(common.mustCall());
|
|
if (!process.features.openssl_is_boringssl) {
|
|
test('X448').then(common.mustCall());
|
|
} else {
|
|
common.printSkipMessage('Skipping unsupported X448 test case');
|
|
}
|
|
}
|