From 4e7b7465bcbc9f5b96ad5d5ca4c2a4a3fc7b2d8a Mon Sep 17 00:00:00 2001 From: Filip Skokan Date: Sat, 2 Apr 2022 16:54:35 +0200 Subject: [PATCH] crypto: fix webcrypto derive key lengths MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/42542 Reviewed-By: Rich Trott Reviewed-By: Tobias Nießen --- lib/internal/crypto/webcrypto.js | 37 ++++++++++++++++++++++- test/parallel/test-webcrypto-derivekey.js | 34 ++++++++++++++++++++- 2 files changed, 69 insertions(+), 2 deletions(-) diff --git a/lib/internal/crypto/webcrypto.js b/lib/internal/crypto/webcrypto.js index 63dd03bd00e0f0..a7916e6ac341f8 100644 --- a/lib/internal/crypto/webcrypto.js +++ b/lib/internal/crypto/webcrypto.js @@ -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, @@ -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': diff --git a/test/parallel/test-webcrypto-derivekey.js b/test/parallel/test-webcrypto-derivekey.js index ee48a61f4ac8f5..0c11d38af30dc6 100644 --- a/test/parallel/test-webcrypto-derivekey.js +++ b/test/parallel/test-webcrypto-derivekey.js @@ -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'); @@ -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()); +}