From f930e3ad44976b756efabbe4dcb5e2df6ee1d4b6 Mon Sep 17 00:00:00 2001 From: Filip Skokan Date: Tue, 22 Sep 2026 22:25:22 +0200 Subject: [PATCH] crypto: copy derived bits without species Use the byte-copy helper for truncated ECDH results so ArrayBuffer species cannot replace or resize the returned key material. Signed-off-by: Filip Skokan Assisted-by: Codex PR-URL: https://github.com/nodejs/node/pull/66237 Reviewed-By: James M Snell Reviewed-By: Aviv Keller --- lib/internal/crypto/diffiehellman.js | 8 ++---- .../test-webcrypto-derived-bits-species.js | 26 +++++++++++++++++++ 2 files changed, 28 insertions(+), 6 deletions(-) create mode 100644 test/parallel/test-webcrypto-derived-bits-species.js diff --git a/lib/internal/crypto/diffiehellman.js b/lib/internal/crypto/diffiehellman.js index 63a9451b3cc..46659419c8d 100644 --- a/lib/internal/crypto/diffiehellman.js +++ b/lib/internal/crypto/diffiehellman.js @@ -2,7 +2,6 @@ const { ArrayBufferPrototypeGetByteLength, - ArrayBufferPrototypeSlice, FunctionPrototypeCall, ObjectDefineProperty, TypedArrayPrototypeGetBuffer, @@ -381,11 +380,8 @@ function ecdhDeriveBits(algorithm, baseKey, length) { if (byteLength < sliceLength) throw lazyDOMException('derived bit length is too small', 'OperationError'); - if (length % 8 === 0) { - if (byteLength === sliceLength) - return bits; - return ArrayBufferPrototypeSlice(bits, 0, sliceLength); - } + if (length % 8 === 0 && byteLength === sliceLength) + return bits; return TypedArrayPrototypeGetBuffer(truncateToBitLength(length, bits)); }); diff --git a/test/parallel/test-webcrypto-derived-bits-species.js b/test/parallel/test-webcrypto-derived-bits-species.js new file mode 100644 index 00000000000..491dc0df877 --- /dev/null +++ b/test/parallel/test-webcrypto-derived-bits-species.js @@ -0,0 +1,26 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const { subtle } = globalThis.crypto; + +(async () => { + const keys = await subtle.generateKey({ name: 'ECDH', namedCurve: 'P-256' }, false, ['deriveBits']); + const algorithm = { name: 'ECDH', public: keys.publicKey }; + const expected = await subtle.deriveBits(algorithm, keys.privateKey, 128); + const species = Object.getOwnPropertyDescriptor(ArrayBuffer, Symbol.species); + Object.defineProperty(ArrayBuffer, Symbol.species, { + configurable: true, + get: common.mustNotCall(), + }); + try { + const actual = await subtle.deriveBits(algorithm, keys.privateKey, 128); + assert.deepStrictEqual(actual, expected); + assert.strictEqual(Object.getPrototypeOf(actual), ArrayBuffer.prototype); + } finally { + Object.defineProperty(ArrayBuffer, Symbol.species, species); + } +})().then(common.mustCall());