node/test/parallel/test-webcrypto-derivebits-ecdh.js
Filip Skokan 3ef38c4bd7
crypto: use WebIDL converters in WebCryptoAPI
WebCryptoAPI functions' arguments are now coersed and validated as per
their WebIDL definitions like in other Web Crypto API implementations.
This further improves interoperability with other implementations of
Web Crypto API.

PR-URL: https://github.com/nodejs/node/pull/46067
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
2023-01-17 08:57:58 +00:00

268 lines
7.4 KiB
JavaScript

'use strict';
const common = require('../common');
if (!common.hasCrypto)
common.skip('missing crypto');
const assert = require('assert');
const { subtle } = globalThis.crypto;
const kTests = [
{
namedCurve: 'P-521',
size: 66,
pkcs8: '3081ee020100301006072a8648ce3d020106052b810400230481d63081d302010' +
'1044201a67ed321915a64aa359b7d648ddc2618fa8e8d1867e8f71830b10d25ed' +
'2891faf12f3c7e75421a2ea264f9a915320d274fe1470742b984e96b98912081f' +
'acd478da18189038186000400209d483f28666881c6641f3a126f400f51e46511' +
'70fe678c75e85712e2868adc850824997bebf0bc82b43028a6d2ec1777ca45279' +
'f7206a3ea8b5cd2073f493e45000cb54c3a5acaa268c56710428878d98b8afbf6' +
'8a612153632846d807e92672698f1b9c611de7d38e34cd6c73889092c56e52d68' +
'0f1dfd092b87ac8ef9ff3c8fb48',
spki: '30819b301006072a8648ce3d020106052b81040023038186000400ee69f94715d7' +
'01e9e2011333d4f4f96cba7d91f88b112baf75cf09cc1f8aca97618da9389822d2' +
'9b6fe9996a61203ef752b771e8958fc4677bb3778565ab60d6ed00deab6761895b' +
'935e3ad325fb8549e56f13786aa73f88a2ecfe40933473d8aef240c4dfd7d506f2' +
'2cdd0e55558f3fbf05ebf7efef7a72d78f46469b8448f26e2712',
result: '009c2bce57be80adab3b07385b8e5990eb7d6fdebdb01bf35371a4f6075e9d28' +
'8ac12a6dfe03aa5743bc81709d49a822940219b64b768acd520fa1368ea0af8d' +
'475d',
},
{
namedCurve: 'P-384',
size: 48,
pkcs8: '3081b6020100301006072a8648ce3d020106052b8104002204819e30819b02010' +
'10430f871a5666589c14a5747263ef85b319cc023db6e35676c3d781eef8b055f' +
'cfbe86fa0d06d056b5195fb1323af8de25b3a16403620004f11965df7dd4594d0' +
'419c5086482a3b826b9797f9be0bd0d109c9e1e9989c1b9a92b8f269f98e17ad1' +
'84ba73c1f79762af45af8141602642da271a6bb0ffeb0cb4478fcf707e661aa6d' +
'6cdf51549c88c3f130be9e8201f6f6a09f4185aaf95c4',
spki: '3076301006072a8648ce3d020106052b810400220362000491822dc2af59c18f5b' +
'67f80df61a2603c2a8f0b3c0af822d63c279701a824560404401dde9a56ee52757' +
'ea8bc748d4c82b5337b48d7b65583a3d572438880036bac6730f42ca5278966bd5' +
'f21e86e21d30c5a6d0463ec513dd509ffcdcaf1ff5',
result: 'e0bd6bce0aef8ca48838a6e2fcc57e67b9c5e8860c5f0be9dabec53e454e18a0' +
'a174c48888a26488115b2dc9f1dfa52d',
},
];
async function prepareKeys() {
const keys = {};
await Promise.all(
kTests.map(async ({ namedCurve, size, pkcs8, spki, result }) => {
const [
privateKey,
publicKey,
] = await Promise.all([
subtle.importKey(
'pkcs8',
Buffer.from(pkcs8, 'hex'),
{
name: 'ECDH',
namedCurve
},
true,
['deriveKey', 'deriveBits']),
subtle.importKey(
'spki',
Buffer.from(spki, 'hex'),
{
name: 'ECDH',
namedCurve
},
true,
[]),
]);
keys[namedCurve] = {
privateKey,
publicKey,
size,
result,
};
}));
return keys;
}
(async function() {
const keys = await prepareKeys();
await Promise.all(
Object.keys(keys).map(async (namedCurve) => {
const { size, result, privateKey, publicKey } = keys[namedCurve];
{
// Good parameters
const bits = await subtle.deriveBits({
name: 'ECDH',
public: publicKey
}, privateKey, 8 * size);
assert(bits instanceof ArrayBuffer);
assert.strictEqual(Buffer.from(bits).toString('hex'), result);
}
{
// Case insensitivity
const bits = await subtle.deriveBits({
name: 'eCdH',
public: publicKey
}, privateKey, 8 * size);
assert.strictEqual(Buffer.from(bits).toString('hex'), result);
}
{
// Null length
const bits = await subtle.deriveBits({
name: 'ECDH',
public: publicKey
}, privateKey, null);
assert.strictEqual(Buffer.from(bits).toString('hex'), result);
}
{
// Short Result
const bits = await subtle.deriveBits({
name: 'ECDH',
public: publicKey
}, privateKey, 8 * size - 32);
assert.strictEqual(
Buffer.from(bits).toString('hex'),
result.slice(0, -8));
}
{
// Too long result
await assert.rejects(subtle.deriveBits({
name: 'ECDH',
public: publicKey
}, privateKey, 8 * size + 8), {
message: /derived bit length is too small/
});
}
{
// Non-multiple of 8
const bits = await subtle.deriveBits({
name: 'ECDH',
public: publicKey
}, privateKey, 8 * size - 11);
assert.strictEqual(
Buffer.from(bits).toString('hex'),
result.slice(0, -2));
}
}));
// Error tests
{
// Missing public property
await assert.rejects(
subtle.deriveBits(
{ name: 'ECDH' },
keys['P-384'].privateKey,
8 * keys['P-384'].size),
{ code: 'ERR_MISSING_OPTION' });
}
{
// The public property is not a CryptoKey
await assert.rejects(
subtle.deriveBits(
{
name: 'ECDH',
public: { message: 'Not a CryptoKey' }
},
keys['P-384'].privateKey,
8 * keys['P-384'].size),
{ code: 'ERR_INVALID_ARG_TYPE' });
}
{
// Mismatched named curves
await assert.rejects(
subtle.deriveBits(
{
name: 'ECDH',
public: keys['P-384'].publicKey
},
keys['P-521'].privateKey,
8 * keys['P-521'].size),
{ message: /Named curve mismatch/ });
}
{
// Incorrect public key algorithm
const { publicKey } = await subtle.generateKey(
{
name: 'ECDSA',
namedCurve: 'P-521'
}, false, ['sign', 'verify']);
await assert.rejects(subtle.deriveBits({
name: 'ECDH',
public: publicKey
}, keys['P-521'].privateKey, null), {
message: /Keys must be ECDH, X25519, or X448 keys/
});
}
{
// Private key does not have correct usages
const privateKey = await subtle.importKey(
'pkcs8',
Buffer.from(kTests[0].pkcs8, 'hex'),
{
name: 'ECDH',
namedCurve: 'P-521'
}, false, ['deriveKey']);
await assert.rejects(subtle.deriveBits({
name: 'ECDH',
public: keys['P-521'].publicKey,
}, privateKey, null), {
message: /baseKey does not have deriveBits usage/
});
}
{
// Base key is not a private key
await assert.rejects(subtle.deriveBits({
name: 'ECDH',
public: keys['P-521'].publicKey
}, keys['P-521'].publicKey, null), {
name: 'InvalidAccessError'
});
}
{
// Public is not a public key
await assert.rejects(subtle.deriveBits({
name: 'ECDH',
public: keys['P-521'].privateKey
}, keys['P-521'].privateKey, null), {
name: 'InvalidAccessError'
});
}
{
// Public is a secret key
const keyData = globalThis.crypto.getRandomValues(new Uint8Array(32));
const key = await subtle.importKey(
'raw',
keyData,
{ name: 'AES-CBC', length: 256 },
false, ['encrypt']);
await assert.rejects(subtle.deriveBits({
name: 'ECDH',
public: key
}, keys['P-521'].publicKey, null), {
name: 'InvalidAccessError'
});
}
})().then(common.mustCall());