Skip to content

Commit e0748df

Browse files
panvanodejs-github-bot
authored andcommitted
benchmark: cover Web Crypto conversion costs
Signed-off-by: Filip Skokan <panva.ip@gmail.com> Assisted-by: Codex PR-URL: #66237 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Aviv Keller <me@aviv.sh>
1 parent 2eb0433 commit e0748df

3 files changed

Lines changed: 221 additions & 2 deletions

File tree

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
'use strict';
2+
3+
const common = require('../common.js');
4+
const fixtures = require('../../test/common/fixtures.js');
5+
const { createPrivateKey, createPublicKey, subtle } = require('node:crypto');
6+
7+
const bench = common.createBenchmark(main, {
8+
keyType: ['hmac', 'aes-gcm', 'ecdsa-private', 'rsa-private', 'rsa-public'],
9+
n: [1e5],
10+
});
11+
12+
async function createKey(keyType) {
13+
switch (keyType) {
14+
case 'hmac':
15+
return subtle.importKey(
16+
'raw', new Uint8Array(32), { name: 'HMAC', hash: 'SHA-256' },
17+
true, ['sign', 'verify']);
18+
case 'aes-gcm':
19+
return subtle.importKey(
20+
'raw', new Uint8Array(32), 'AES-GCM',
21+
true, ['encrypt', 'decrypt', 'wrapKey', 'unwrapKey']);
22+
case 'ecdsa-private':
23+
return createPrivateKey(fixtures.readKey('ec_p256_private.pem'))
24+
.toCryptoKey({ name: 'ECDSA', namedCurve: 'P-256' }, true, ['sign']);
25+
case 'rsa-private':
26+
return createPrivateKey(fixtures.readKey('rsa_private_2048.pem'))
27+
.toCryptoKey({ name: 'RSA-PSS', hash: 'SHA-256' }, true, ['sign']);
28+
case 'rsa-public':
29+
return createPublicKey(fixtures.readKey('rsa_private_2048.pem'))
30+
.toCryptoKey({ name: 'RSA-PSS', hash: 'SHA-256' }, true, ['verify']);
31+
default:
32+
throw new Error(`Unsupported key type: ${keyType}`);
33+
}
34+
}
35+
36+
async function main({ n, keyType }) {
37+
const key = await createKey(keyType);
38+
let result;
39+
40+
bench.start();
41+
for (let i = 0; i < n; i++)
42+
result = await subtle.exportKey('jwk', key);
43+
bench.end(n);
44+
45+
if (result.kty === undefined)
46+
throw new Error('Missing key type');
47+
}

‎benchmark/misc/webcrypto-util.js‎

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
'use strict';
2+
3+
const common = require('../common.js');
4+
5+
const inputs = [
6+
'arraybuffer',
7+
'uint8array',
8+
'dataview',
9+
'buffer',
10+
'sharedview',
11+
'empty-arraybuffer',
12+
'empty-uint8array',
13+
'empty-dataview',
14+
'detached-arraybuffer',
15+
'detached-uint8array',
16+
'detached-dataview',
17+
];
18+
19+
const bench = common.createBenchmark(main, {
20+
op: [
21+
...inputs.flatMap((input) => [`byteLength:${input}`, `bytes:${input}`]),
22+
'truncate:arraybuffer:100',
23+
'truncate:arraybuffer:128',
24+
'truncate:uint8array:100',
25+
'truncate:uint8array:128',
26+
'usages:0',
27+
'usages:1',
28+
'usages:2',
29+
'usages:4',
30+
],
31+
n: [1e6],
32+
}, { flags: ['--expose-internals'] });
33+
34+
function createInput(input) {
35+
const empty = input.startsWith('empty-');
36+
const detached = input.startsWith('detached-');
37+
const type = input.replace(/^(empty|detached)-/, '');
38+
const size = empty ? 0 : 32;
39+
const buffer = new ArrayBuffer(size + 16);
40+
let value;
41+
switch (type) {
42+
case 'arraybuffer':
43+
value = new ArrayBuffer(size);
44+
break;
45+
case 'uint8array':
46+
value = new Uint8Array(buffer, 8, size);
47+
break;
48+
case 'dataview':
49+
value = new DataView(buffer, 8, size);
50+
break;
51+
case 'buffer':
52+
value = Buffer.from(buffer, 8, size);
53+
break;
54+
case 'sharedview':
55+
value = new Uint8Array(new SharedArrayBuffer(size));
56+
break;
57+
default:
58+
throw new Error(`Unsupported input: ${input}`);
59+
}
60+
if (detached) {
61+
const backing = type === 'arraybuffer' ? value : buffer;
62+
structuredClone(backing, { transfer: [backing] });
63+
}
64+
return value;
65+
}
66+
67+
function main({ n, op }) {
68+
const {
69+
getBufferSourceByteLength,
70+
getBufferSourceBytes,
71+
getUsagesFromMask,
72+
getUsagesMask,
73+
truncateToBitLength,
74+
} = require('internal/crypto/util');
75+
const [operation, input, length] = op.split(':');
76+
let run;
77+
switch (operation) {
78+
case 'byteLength': {
79+
const value = createInput(input);
80+
run = () => getBufferSourceByteLength(value);
81+
break;
82+
}
83+
case 'bytes': {
84+
const value = createInput(input);
85+
run = () => getBufferSourceBytes(value);
86+
break;
87+
}
88+
case 'truncate': {
89+
const value = createInput(input);
90+
const bits = Number(length);
91+
run = () => truncateToBitLength(bits, value);
92+
break;
93+
}
94+
case 'usages': {
95+
const usages = {
96+
0: [],
97+
1: ['sign'],
98+
2: ['sign', 'verify'],
99+
4: ['encrypt', 'decrypt', 'wrapKey', 'unwrapKey'],
100+
};
101+
const mask = getUsagesMask(usages[input]);
102+
run = () => getUsagesFromMask(mask);
103+
break;
104+
}
105+
default:
106+
throw new Error(`Unsupported operation: ${operation}`);
107+
}
108+
109+
let result;
110+
bench.start();
111+
for (let i = 0; i < n; i++)
112+
result = run();
113+
bench.end(n);
114+
115+
if (result === undefined)
116+
throw new Error('Missing benchmark result');
117+
}

‎benchmark/misc/webcrypto-webidl.js‎

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,13 @@ const bench = common.createBenchmark(main, {
66
op: [
77
'normalizeAlgorithm-string',
88
'normalizeAlgorithm-dict',
9+
'normalizeAlgorithm-validate-aes-gcm',
10+
'normalizeAlgorithm-validate-aes-cbc',
11+
'normalizeAlgorithm-validate-aes-ctr',
12+
'normalizeAlgorithm-validate-aes-generate',
13+
'normalizeAlgorithm-validate-hkdf',
14+
'normalizeAlgorithm-validate-hmac',
15+
'normalizeAlgorithm-validate-rsa',
916
'webidl-dict',
1017
'webidl-algorithm-identifier-string',
1118
'webidl-algorithm-identifier-object',
@@ -17,7 +24,7 @@ const bench = common.createBenchmark(main, {
1724
}, { flags: ['--expose-internals'] });
1825

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

2229
switch (op) {
2330
case 'normalizeAlgorithm-string': {
@@ -37,6 +44,54 @@ function main({ n, op }) {
3744
bench.end(n);
3845
break;
3946
}
47+
case 'normalizeAlgorithm-validate-aes-gcm':
48+
case 'normalizeAlgorithm-validate-aes-cbc':
49+
case 'normalizeAlgorithm-validate-aes-ctr':
50+
case 'normalizeAlgorithm-validate-aes-generate':
51+
case 'normalizeAlgorithm-validate-hkdf':
52+
case 'normalizeAlgorithm-validate-hmac':
53+
case 'normalizeAlgorithm-validate-rsa': {
54+
const cases = {
55+
'aes-gcm': [
56+
{ name: 'AES-GCM', iv: new Uint8Array(12), tagLength: 128 },
57+
'encrypt',
58+
],
59+
'aes-cbc': [
60+
{ name: 'AES-CBC', iv: new Uint8Array(16) },
61+
'encrypt',
62+
],
63+
'aes-ctr': [
64+
{ name: 'AES-CTR', counter: new Uint8Array(16), length: 64 },
65+
'encrypt',
66+
],
67+
'aes-generate': [{ name: 'AES-GCM', length: 256 }, 'generateKey'],
68+
'hkdf': [
69+
{
70+
name: 'HKDF', hash: 'SHA-256',
71+
salt: new Uint8Array(32), info: new Uint8Array(32),
72+
},
73+
'deriveBits',
74+
],
75+
'hmac': [{ name: 'HMAC', hash: 'SHA-256', length: 256 }, 'importKey'],
76+
'rsa': [
77+
{
78+
name: 'RSA-PSS', hash: 'SHA-256', modulusLength: 2048,
79+
publicExponent: new Uint8Array([1, 0, 1]),
80+
},
81+
'generateKey',
82+
],
83+
};
84+
const name = op.slice('normalizeAlgorithm-validate-'.length);
85+
const [input, operation] = cases[name];
86+
bench.start();
87+
for (let i = 0; i < n; i++) {
88+
const normalized = normalizeAlgorithm(input, operation);
89+
// Older revisions validate inside normalizeAlgorithm.
90+
validateAlgorithm?.(normalized, operation);
91+
}
92+
bench.end(n);
93+
break;
94+
}
4095
case 'webidl-dict': {
4196
// WebIDL dictionary converter in isolation.
4297
const webidl = require('internal/crypto/webidl');
@@ -85,7 +140,7 @@ function main({ n, op }) {
85140
break;
86141
}
87142
case 'webidl-dict-ensure-sha': {
88-
// Exercises ensureSHA on a hash member.
143+
// Converts a dictionary containing a hash identifier.
89144
const webidl = require('internal/crypto/webidl');
90145
const input = { name: 'RSASSA-PKCS1-v1_5', hash: 'SHA-256' };
91146
const opts = { prefix: 'test', context: 'test' };

0 commit comments

Comments
 (0)