Commit e0748dfd9d7 for nodejs

commit e0748dfd9d7d54f4e93d21754744cbe116046c98
Author: Filip Skokan <panva.ip@gmail.com>
Date:   Wed Sep 23 11:11:50 2026 +0200

    benchmark: cover Web Crypto conversion costs

    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/benchmark/crypto/webcrypto-export.js b/benchmark/crypto/webcrypto-export.js
new file mode 100644
index 00000000000..4d96b54ec74
--- /dev/null
+++ b/benchmark/crypto/webcrypto-export.js
@@ -0,0 +1,47 @@
+'use strict';
+
+const common = require('../common.js');
+const fixtures = require('../../test/common/fixtures.js');
+const { createPrivateKey, createPublicKey, subtle } = require('node:crypto');
+
+const bench = common.createBenchmark(main, {
+  keyType: ['hmac', 'aes-gcm', 'ecdsa-private', 'rsa-private', 'rsa-public'],
+  n: [1e5],
+});
+
+async function createKey(keyType) {
+  switch (keyType) {
+    case 'hmac':
+      return subtle.importKey(
+        'raw', new Uint8Array(32), { name: 'HMAC', hash: 'SHA-256' },
+        true, ['sign', 'verify']);
+    case 'aes-gcm':
+      return subtle.importKey(
+        'raw', new Uint8Array(32), 'AES-GCM',
+        true, ['encrypt', 'decrypt', 'wrapKey', 'unwrapKey']);
+    case 'ecdsa-private':
+      return createPrivateKey(fixtures.readKey('ec_p256_private.pem'))
+        .toCryptoKey({ name: 'ECDSA', namedCurve: 'P-256' }, true, ['sign']);
+    case 'rsa-private':
+      return createPrivateKey(fixtures.readKey('rsa_private_2048.pem'))
+        .toCryptoKey({ name: 'RSA-PSS', hash: 'SHA-256' }, true, ['sign']);
+    case 'rsa-public':
+      return createPublicKey(fixtures.readKey('rsa_private_2048.pem'))
+        .toCryptoKey({ name: 'RSA-PSS', hash: 'SHA-256' }, true, ['verify']);
+    default:
+      throw new Error(`Unsupported key type: ${keyType}`);
+  }
+}
+
+async function main({ n, keyType }) {
+  const key = await createKey(keyType);
+  let result;
+
+  bench.start();
+  for (let i = 0; i < n; i++)
+    result = await subtle.exportKey('jwk', key);
+  bench.end(n);
+
+  if (result.kty === undefined)
+    throw new Error('Missing key type');
+}
diff --git a/benchmark/misc/webcrypto-util.js b/benchmark/misc/webcrypto-util.js
new file mode 100644
index 00000000000..581602ab347
--- /dev/null
+++ b/benchmark/misc/webcrypto-util.js
@@ -0,0 +1,117 @@
+'use strict';
+
+const common = require('../common.js');
+
+const inputs = [
+  'arraybuffer',
+  'uint8array',
+  'dataview',
+  'buffer',
+  'sharedview',
+  'empty-arraybuffer',
+  'empty-uint8array',
+  'empty-dataview',
+  'detached-arraybuffer',
+  'detached-uint8array',
+  'detached-dataview',
+];
+
+const bench = common.createBenchmark(main, {
+  op: [
+    ...inputs.flatMap((input) => [`byteLength:${input}`, `bytes:${input}`]),
+    'truncate:arraybuffer:100',
+    'truncate:arraybuffer:128',
+    'truncate:uint8array:100',
+    'truncate:uint8array:128',
+    'usages:0',
+    'usages:1',
+    'usages:2',
+    'usages:4',
+  ],
+  n: [1e6],
+}, { flags: ['--expose-internals'] });
+
+function createInput(input) {
+  const empty = input.startsWith('empty-');
+  const detached = input.startsWith('detached-');
+  const type = input.replace(/^(empty|detached)-/, '');
+  const size = empty ? 0 : 32;
+  const buffer = new ArrayBuffer(size + 16);
+  let value;
+  switch (type) {
+    case 'arraybuffer':
+      value = new ArrayBuffer(size);
+      break;
+    case 'uint8array':
+      value = new Uint8Array(buffer, 8, size);
+      break;
+    case 'dataview':
+      value = new DataView(buffer, 8, size);
+      break;
+    case 'buffer':
+      value = Buffer.from(buffer, 8, size);
+      break;
+    case 'sharedview':
+      value = new Uint8Array(new SharedArrayBuffer(size));
+      break;
+    default:
+      throw new Error(`Unsupported input: ${input}`);
+  }
+  if (detached) {
+    const backing = type === 'arraybuffer' ? value : buffer;
+    structuredClone(backing, { transfer: [backing] });
+  }
+  return value;
+}
+
+function main({ n, op }) {
+  const {
+    getBufferSourceByteLength,
+    getBufferSourceBytes,
+    getUsagesFromMask,
+    getUsagesMask,
+    truncateToBitLength,
+  } = require('internal/crypto/util');
+  const [operation, input, length] = op.split(':');
+  let run;
+  switch (operation) {
+    case 'byteLength': {
+      const value = createInput(input);
+      run = () => getBufferSourceByteLength(value);
+      break;
+    }
+    case 'bytes': {
+      const value = createInput(input);
+      run = () => getBufferSourceBytes(value);
+      break;
+    }
+    case 'truncate': {
+      const value = createInput(input);
+      const bits = Number(length);
+      run = () => truncateToBitLength(bits, value);
+      break;
+    }
+    case 'usages': {
+      const usages = {
+        0: [],
+        1: ['sign'],
+        2: ['sign', 'verify'],
+        4: ['encrypt', 'decrypt', 'wrapKey', 'unwrapKey'],
+      };
+      const mask = getUsagesMask(usages[input]);
+      run = () => getUsagesFromMask(mask);
+      break;
+    }
+    default:
+      throw new Error(`Unsupported operation: ${operation}`);
+  }
+
+  let result;
+  bench.start();
+  for (let i = 0; i < n; i++)
+    result = run();
+  bench.end(n);
+
+  if (result === undefined)
+    throw new Error('Missing benchmark result');
+}
diff --git a/benchmark/misc/webcrypto-webidl.js b/benchmark/misc/webcrypto-webidl.js
index 0f6275ed095..318ea798207 100644
--- a/benchmark/misc/webcrypto-webidl.js
+++ b/benchmark/misc/webcrypto-webidl.js
@@ -6,6 +6,13 @@ const bench = common.createBenchmark(main, {
   op: [
     'normalizeAlgorithm-string',
     'normalizeAlgorithm-dict',
+    'normalizeAlgorithm-validate-aes-gcm',
+    'normalizeAlgorithm-validate-aes-cbc',
+    'normalizeAlgorithm-validate-aes-ctr',
+    'normalizeAlgorithm-validate-aes-generate',
+    'normalizeAlgorithm-validate-hkdf',
+    'normalizeAlgorithm-validate-hmac',
+    'normalizeAlgorithm-validate-rsa',
     'webidl-dict',
     'webidl-algorithm-identifier-string',
     'webidl-algorithm-identifier-object',
@@ -17,7 +24,7 @@ const bench = common.createBenchmark(main, {
 }, { flags: ['--expose-internals'] });

 function main({ n, op }) {
-  const { normalizeAlgorithm } = require('internal/crypto/util');
+  const { normalizeAlgorithm, validateAlgorithm } = require('internal/crypto/util');

   switch (op) {
     case 'normalizeAlgorithm-string': {
@@ -37,6 +44,54 @@ function main({ n, op }) {
       bench.end(n);
       break;
     }
+    case 'normalizeAlgorithm-validate-aes-gcm':
+    case 'normalizeAlgorithm-validate-aes-cbc':
+    case 'normalizeAlgorithm-validate-aes-ctr':
+    case 'normalizeAlgorithm-validate-aes-generate':
+    case 'normalizeAlgorithm-validate-hkdf':
+    case 'normalizeAlgorithm-validate-hmac':
+    case 'normalizeAlgorithm-validate-rsa': {
+      const cases = {
+        'aes-gcm': [
+          { name: 'AES-GCM', iv: new Uint8Array(12), tagLength: 128 },
+          'encrypt',
+        ],
+        'aes-cbc': [
+          { name: 'AES-CBC', iv: new Uint8Array(16) },
+          'encrypt',
+        ],
+        'aes-ctr': [
+          { name: 'AES-CTR', counter: new Uint8Array(16), length: 64 },
+          'encrypt',
+        ],
+        'aes-generate': [{ name: 'AES-GCM', length: 256 }, 'generateKey'],
+        'hkdf': [
+          {
+            name: 'HKDF', hash: 'SHA-256',
+            salt: new Uint8Array(32), info: new Uint8Array(32),
+          },
+          'deriveBits',
+        ],
+        'hmac': [{ name: 'HMAC', hash: 'SHA-256', length: 256 }, 'importKey'],
+        'rsa': [
+          {
+            name: 'RSA-PSS', hash: 'SHA-256', modulusLength: 2048,
+            publicExponent: new Uint8Array([1, 0, 1]),
+          },
+          'generateKey',
+        ],
+      };
+      const name = op.slice('normalizeAlgorithm-validate-'.length);
+      const [input, operation] = cases[name];
+      bench.start();
+      for (let i = 0; i < n; i++) {
+        const normalized = normalizeAlgorithm(input, operation);
+        // Older revisions validate inside normalizeAlgorithm.
+        validateAlgorithm?.(normalized, operation);
+      }
+      bench.end(n);
+      break;
+    }
     case 'webidl-dict': {
       // WebIDL dictionary converter in isolation.
       const webidl = require('internal/crypto/webidl');
@@ -85,7 +140,7 @@ function main({ n, op }) {
       break;
     }
     case 'webidl-dict-ensure-sha': {
-      // Exercises ensureSHA on a hash member.
+      // Converts a dictionary containing a hash identifier.
       const webidl = require('internal/crypto/webidl');
       const input = { name: 'RSASSA-PKCS1-v1_5', hash: 'SHA-256' };
       const opts = { prefix: 'test', context: 'test' };