From 8d20784f7adc1eee6cd58f1b797263fc19d07327 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Bertron?= Date: Tue, 4 Oct 2022 08:10:34 +0200 Subject: [PATCH] fix(ext/crypto): deriveBits for ECDH not taking length into account (#16128) Fixes #16047 --- cli/tests/unit/webcrypto_test.ts | 65 ++++++++++++++++++++++++++++++++ ext/crypto/00_crypto.js | 24 +++++++++--- 2 files changed, 83 insertions(+), 6 deletions(-) diff --git a/cli/tests/unit/webcrypto_test.ts b/cli/tests/unit/webcrypto_test.ts index 6695b157a3..c5f5dc6c2f 100644 --- a/cli/tests/unit/webcrypto_test.ts +++ b/cli/tests/unit/webcrypto_test.ts @@ -541,6 +541,71 @@ Deno.test(async function testHkdfDeriveBitsWithLargeKeySize() { ); }); +Deno.test(async function testEcdhDeriveBitsWithShorterLength() { + const keypair = await crypto.subtle.generateKey( + { + name: "ECDH", + namedCurve: "P-384", + }, + true, + ["deriveBits", "deriveKey"], + ); + const result = await crypto.subtle.deriveBits( + { + name: "ECDH", + public: keypair.publicKey, + }, + keypair.privateKey, + 256, + ); + assertEquals(result.byteLength * 8, 256); +}); + +Deno.test(async function testEcdhDeriveBitsWithLongerLength() { + const keypair = await crypto.subtle.generateKey( + { + name: "ECDH", + namedCurve: "P-384", + }, + true, + ["deriveBits", "deriveKey"], + ); + await assertRejects( + () => + crypto.subtle.deriveBits( + { + name: "ECDH", + public: keypair.publicKey, + }, + keypair.privateKey, + 512, + ), + DOMException, + "Invalid length", + ); +}); + +Deno.test(async function testEcdhDeriveBitsWithNullLength() { + const keypair = await crypto.subtle.generateKey( + { + name: "ECDH", + namedCurve: "P-384", + }, + true, + ["deriveBits", "deriveKey"], + ); + const result = await crypto.subtle.deriveBits( + { + name: "ECDH", + public: keypair.publicKey, + }, + keypair.privateKey, + // @ts-ignore: necessary until .d.ts file allows passing null (see https://github.com/microsoft/TypeScript-DOM-lib-generator/pull/1416) + null, + ); + assertEquals(result.byteLength * 8, 384); +}); + Deno.test(async function testDeriveKey() { // Test deriveKey const rawKey = await crypto.getRandomValues(new Uint8Array(16)); diff --git a/ext/crypto/00_crypto.js b/ext/crypto/00_crypto.js index 5e918cc7d4..5826132d2f 100644 --- a/ext/crypto/00_crypto.js +++ b/ext/crypto/00_crypto.js @@ -1078,7 +1078,7 @@ /** * @param {AlgorithmIdentifier} algorithm * @param {CryptoKey} baseKey - * @param {number} length + * @param {number | null} length * @returns {Promise} */ async deriveBits(algorithm, baseKey, length) { @@ -1093,10 +1093,12 @@ prefix, context: "Argument 2", }); - length = webidl.converters["unsigned long"](length, { - prefix, - context: "Argument 3", - }); + if (length !== null) { + length = webidl.converters["unsigned long"](length, { + prefix, + context: "Argument 3", + }); + } // 2. const normalizedAlgorithm = normalizeAlgorithm(algorithm, "deriveBits"); @@ -4391,7 +4393,17 @@ length, }); - return buf.buffer; + // 8. + if (length === null) { + return buf.buffer; + } + if ( + length === 0 || buf.buffer.byteLength * 8 < length || + length % 8 !== 0 + ) { + throw new DOMException("Invalid length", "OperationError"); + } + return buf.buffer.slice(0, length / 8); } else { throw new DOMException("Not implemented", "NotSupportedError"); }