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