Skip to content

Commit

Permalink
crypto: fix webcrypto derive key lengths
Browse files Browse the repository at this point in the history
PR-URL: #42542
Reviewed-By: Rich Trott <rtrott@gmail.com>
Reviewed-By: Tobias Nießen <tniessen@tnie.de>
  • Loading branch information
panva authored and juanarbol committed Apr 4, 2022
1 parent f3c3479 commit 4e7b746
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 2 deletions.
37 changes: 36 additions & 1 deletion lib/internal/crypto/webcrypto.js
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,41 @@ async function deriveBits(algorithm, baseKey, length) {
throw lazyDOMException('Unrecognized name.');
}

function getKeyLength({ name, length, hash }) {
switch (name) {
case 'AES-CTR':
case 'AES-CBC':
case 'AES-GCM':
case 'AES-KW':
if (length !== 128 && length !== 192 && length !== 256)
throw lazyDOMException('Invalid key length', 'OperationError');

return length;
case 'HMAC':
if (length === undefined) {
switch (hash?.name) {
case 'SHA-1':
return 160;
case 'SHA-256':
return 256;
case 'SHA-384':
return 384;
case 'SHA-512':
return 512;
}
}

if (typeof length === 'number' && length !== 0) {
return length;
}

throw lazyDOMException('Invalid key length', 'OperationError');
case 'HKDF':
case 'PBKDF2':
return null;
}
}

async function deriveKey(
algorithm,
baseKey,
Expand All @@ -176,7 +211,7 @@ async function deriveKey(
validateBoolean(extractable, 'extractable');
validateArray(keyUsages, 'keyUsages');

const { length } = derivedKeyAlgorithm;
const length = getKeyLength(derivedKeyAlgorithm);
let bits;
switch (algorithm.name) {
case 'ECDH':
Expand Down
34 changes: 33 additions & 1 deletion test/parallel/test-webcrypto-derivekey.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ if (!common.hasCrypto)
common.skip('missing crypto');

const assert = require('assert');
const { subtle } = require('crypto').webcrypto;
const { webcrypto: { subtle }, KeyObject } = require('crypto');

const { internalBinding } = require('internal/test/binding');

Expand Down Expand Up @@ -152,3 +152,35 @@ if (typeof internalBinding('crypto').ScryptJob === 'function') {

tests.then(common.mustCall());
}

// Test default key lengths
{
const vectors = [
['PBKDF2', 'deriveKey', 528],
['HKDF', 'deriveKey', 528],
[{ name: 'HMAC', hash: 'SHA-1' }, 'sign', 160],
[{ name: 'HMAC', hash: 'SHA-256' }, 'sign', 256],
[{ name: 'HMAC', hash: 'SHA-384' }, 'sign', 384],
[{ name: 'HMAC', hash: 'SHA-512' }, 'sign', 512],
];

(async () => {
const keyPair = await subtle.generateKey({ name: 'ECDH', namedCurve: 'P-521' }, false, ['deriveKey']);
for (const [derivedKeyAlgorithm, usage, expected] of vectors) {
const derived = await subtle.deriveKey(
{ name: 'ECDH', public: keyPair.publicKey },
keyPair.privateKey,
derivedKeyAlgorithm,
false,
[usage]);

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());
}

0 comments on commit 4e7b746

Please sign in to comment.