Commit 71689e8d392 for nodejs
commit 71689e8d392bfc6c2e69024b978b1c04d7976ca1
Author: Filip Skokan <panva.ip@gmail.com>
Date: Tue Sep 22 22:18:27 2026 +0200
crypto: copy parameters without array species
Allocate byte copies directly so typed-array species cannot replace
normalized parameters or bit-truncated key material.
Signed-off-by: Filip Skokan <panva.ip@gmail.com>
Assisted-by: Codex
PR-URL: https://github.com/nodejs/node/pull/66237
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Aviv Keller <me@aviv.sh>
diff --git a/lib/internal/crypto/util.js b/lib/internal/crypto/util.js
index b045696bd11..34e09b8b6ed 100644
--- a/lib/internal/crypto/util.js
+++ b/lib/internal/crypto/util.js
@@ -12,6 +12,7 @@ const {
DataViewPrototypeGetByteLength,
DataViewPrototypeGetByteOffset,
MathFloor,
+ MathMin,
Number,
ObjectDefineProperty,
ObjectEntries,
@@ -31,7 +32,6 @@ const {
TypedArrayPrototypeGetByteLength,
TypedArrayPrototypeGetByteOffset,
TypedArrayPrototypeGetLength,
- TypedArrayPrototypeSlice,
Uint8Array,
} = primordials;
@@ -757,14 +757,11 @@ function truncateToBitLength(length, bytes) {
new Uint8Array(
getDataViewOrTypedArrayBuffer(bytes),
getDataViewOrTypedArrayByteOffset(bytes),
- getDataViewOrTypedArrayByteLength(bytes),
+ MathMin(lengthBytes, getDataViewOrTypedArrayByteLength(bytes)),
) :
- new Uint8Array(bytes, 0, ArrayBufferPrototypeGetByteLength(bytes));
- const result = TypedArrayPrototypeSlice(
- byteView,
- 0,
- lengthBytes,
- );
+ new Uint8Array(bytes, 0,
+ MathMin(lengthBytes, ArrayBufferPrototypeGetByteLength(bytes)));
+ const result = new Uint8Array(byteView);
const remainder = length % 8;
if (remainder !== 0)
@@ -840,7 +837,7 @@ function normalizeAlgorithm(algorithm, op) {
const idlValue = normalizedAlgorithm[member];
// 3.
if (idlType === 'BufferSource' && idlValue) {
- normalizedAlgorithm[member] = TypedArrayPrototypeSlice(
+ normalizedAlgorithm[member] = new Uint8Array(
getBufferSourceBytes(idlValue),
);
} else if (idlType === 'HashAlgorithmIdentifier') {
diff --git a/test/parallel/test-webcrypto-parameter-species.js b/test/parallel/test-webcrypto-parameter-species.js
new file mode 100644
index 00000000000..2a02eb8a281
--- /dev/null
+++ b/test/parallel/test-webcrypto-parameter-species.js
@@ -0,0 +1,36 @@
+'use strict';
+
+const common = require('../common');
+if (!common.hasCrypto)
+ common.skip('missing crypto');
+
+const assert = require('assert');
+const { subtle } = globalThis.crypto;
+
+function withSpecies(callback) {
+ Object.defineProperty(Uint8Array, Symbol.species, {
+ configurable: true,
+ value: Float64Array,
+ });
+ try {
+ return callback();
+ } finally {
+ delete Uint8Array[Symbol.species];
+ }
+}
+
+(async () => {
+ const key = await subtle.importKey('raw', new Uint8Array(16),
+ 'AES-GCM', false, ['encrypt']);
+ const algorithm = { name: 'AES-GCM', iv: new Uint8Array(12).fill(1) };
+ const data = new Uint8Array(16);
+ const expected = await subtle.encrypt(algorithm, key, data);
+ assert.deepStrictEqual(await withSpecies(() => subtle.encrypt(algorithm, key, data)), expected);
+
+ const hmac = { name: 'HMAC', hash: 'SHA-256', length: 100 };
+ const secret = new Uint8Array(13).fill(1);
+ const normal = await subtle.importKey('raw', secret, hmac, true, ['sign']);
+ const tampered = await withSpecies(() => subtle.importKey('raw', secret, hmac, true, ['sign']));
+ assert.deepStrictEqual(await subtle.exportKey('raw', tampered), await subtle.exportKey('raw', normal));
+ assert.deepStrictEqual(await subtle.sign('HMAC', tampered, data), await subtle.sign('HMAC', normal, data));
+})().then(common.mustCall());