Skip to content

Commit 8d46b14

Browse files
panvanodejs-github-bot
authored andcommitted
benchmark: add crypto class benchmarks
Measure construction and low-cost method paths for crypto classes whose state is stored in native or private slots. Signed-off-by: Filip Skokan <panva.ip@gmail.com> PR-URL: #65518 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
1 parent 76bb3f7 commit 8d46b14

2 files changed

Lines changed: 292 additions & 0 deletions

File tree

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
'use strict';
2+
3+
const common = require('../common.js');
4+
const assert = require('node:assert');
5+
const crypto = require('node:crypto');
6+
const fs = require('node:fs');
7+
const path = require('node:path');
8+
9+
const fixtureDir = path.resolve(__dirname, '../../test/fixtures/keys');
10+
const certificate = fs.readFileSync(path.join(fixtureDir, 'agent1-cert.pem'));
11+
const dhPrime = crypto.getDiffieHellman('modp14').getPrime();
12+
const key = Buffer.alloc(32, 0x01);
13+
const keyObject = crypto.createSecretKey(key);
14+
const hmacAlgorithm = { name: 'HMAC', hash: 'SHA-256' };
15+
const keyUsages = ['sign'];
16+
const iv = Buffer.alloc(16, 0x02);
17+
18+
const iterations = {
19+
Certificate: 1e7,
20+
Cipheriv: 1e5,
21+
Decipheriv: 1e5,
22+
DiffieHellman: 10,
23+
DiffieHellmanGroup: 2e5,
24+
ECDH: 2e5,
25+
Hash: 1e5,
26+
Hmac: 5e4,
27+
KeyObject: 1e5,
28+
Sign: 1e5,
29+
Verify: 1e5,
30+
CryptoKey: 5e4,
31+
X509Certificate: 5e3,
32+
};
33+
34+
const bench = common.createBenchmark(main, {
35+
type: Object.keys(iterations),
36+
n: [...new Set(Object.values(iterations))],
37+
}, {
38+
combinationFilter({ type, n }) {
39+
// Benchmark test mode reduces numeric options to 1.
40+
return n === 1 || iterations[type] === n;
41+
},
42+
});
43+
44+
function construct(type) {
45+
switch (type) {
46+
case 'Certificate':
47+
return new crypto.Certificate();
48+
case 'Cipheriv':
49+
return crypto.createCipheriv('aes-256-ctr', key, iv);
50+
case 'Decipheriv':
51+
return crypto.createDecipheriv('aes-256-ctr', key, iv);
52+
case 'DiffieHellman':
53+
return crypto.createDiffieHellman(dhPrime);
54+
case 'DiffieHellmanGroup':
55+
return crypto.getDiffieHellman('modp14');
56+
case 'ECDH':
57+
return crypto.createECDH('prime256v1');
58+
case 'Hash':
59+
return crypto.createHash('sha256');
60+
case 'Hmac':
61+
return crypto.createHmac('sha256', key);
62+
case 'KeyObject':
63+
return crypto.createSecretKey(key);
64+
case 'Sign':
65+
return crypto.createSign('sha256');
66+
case 'Verify':
67+
return crypto.createVerify('sha256');
68+
case 'CryptoKey':
69+
return keyObject.toCryptoKey(hmacAlgorithm, true, keyUsages);
70+
case 'X509Certificate':
71+
return new crypto.X509Certificate(certificate);
72+
default:
73+
throw new Error(`Unsupported class: ${type}`);
74+
}
75+
}
76+
77+
function main({ type, n }) {
78+
const instances = new Array(n);
79+
80+
bench.start();
81+
for (let i = 0; i < n; ++i)
82+
instances[i] = construct(type);
83+
bench.end(n);
84+
85+
assert.strictEqual(typeof instances[n - 1], 'object');
86+
}

benchmark/crypto/class-methods.js

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
'use strict';
2+
3+
const common = require('../common.js');
4+
const crypto = require('node:crypto');
5+
const fs = require('node:fs');
6+
const path = require('node:path');
7+
8+
const fixtureDir = path.resolve(__dirname, '../../test/fixtures/keys');
9+
const certificate = fs.readFileSync(path.join(fixtureDir, 'agent1-cert.pem'));
10+
const key = Buffer.alloc(32, 0x01);
11+
const hmacAlgorithm = { name: 'HMAC', hash: 'SHA-256' };
12+
const keyUsages = ['sign'];
13+
const iv = Buffer.alloc(16, 0x02);
14+
const input = Buffer.alloc(16, 0x03);
15+
16+
const iterations = {
17+
'Cipheriv-update': 1e6,
18+
'Decipheriv-update': 1e6,
19+
'DiffieHellman-getGenerator': 2e5,
20+
'DiffieHellmanGroup-getGenerator': 2e6,
21+
'ECDH-getPrivateKey': 2e6,
22+
'Hash-update': 5e6,
23+
'Hmac-update': 5e6,
24+
'KeyObject-equals': 2e6,
25+
'KeyObject-symmetricKeySize-first': 1e5,
26+
'KeyObject-type-first': 1e5,
27+
'KeyObject-type': 1e8,
28+
'Sign-update': 5e6,
29+
'Verify-update': 5e6,
30+
'CryptoKey-toKeyObject': 2e5,
31+
'CryptoKey-algorithm-first': 1e5,
32+
'CryptoKey-extractable-first': 1e5,
33+
'CryptoKey-type-first': 1e5,
34+
'CryptoKey-usages-first': 1e5,
35+
'CryptoKey-type': 1e8,
36+
'X509Certificate-checkHost': 1e6,
37+
'X509Certificate-publicKey-first': 5e3,
38+
'X509Certificate-publicKey': 1e8,
39+
'X509Certificate-subject-first': 5e3,
40+
'X509Certificate-subject': 1e8,
41+
};
42+
43+
const bench = common.createBenchmark(main, {
44+
operation: Object.keys(iterations),
45+
n: [...new Set(Object.values(iterations))],
46+
}, {
47+
combinationFilter({ operation, n }) {
48+
// Benchmark test mode reduces numeric options to 1.
49+
return n === 1 || iterations[operation] === n;
50+
},
51+
});
52+
53+
function setup(operation) {
54+
switch (operation) {
55+
case 'Cipheriv-update': {
56+
const cipher = crypto.createCipheriv('aes-256-ctr', key, iv);
57+
return {
58+
run: () => cipher.update(input),
59+
finish: () => cipher.final(),
60+
};
61+
}
62+
case 'Decipheriv-update': {
63+
const decipher = crypto.createDecipheriv('aes-256-ctr', key, iv);
64+
return {
65+
run: () => decipher.update(input),
66+
finish: () => decipher.final(),
67+
};
68+
}
69+
case 'DiffieHellman-getGenerator': {
70+
const dh = crypto.createDiffieHellman(
71+
crypto.getDiffieHellman('modp14').getPrime());
72+
return { run: () => dh.getGenerator() };
73+
}
74+
case 'DiffieHellmanGroup-getGenerator': {
75+
const dh = crypto.getDiffieHellman('modp14');
76+
return { run: () => dh.getGenerator() };
77+
}
78+
case 'ECDH-getPrivateKey': {
79+
const ecdh = crypto.createECDH('prime256v1');
80+
ecdh.generateKeys();
81+
return { run: () => ecdh.getPrivateKey() };
82+
}
83+
case 'Hash-update': {
84+
const hash = crypto.createHash('sha256');
85+
return {
86+
run: () => hash.update(input),
87+
finish: () => hash.digest(),
88+
};
89+
}
90+
case 'Hmac-update': {
91+
const hmac = crypto.createHmac('sha256', key);
92+
return {
93+
run: () => hmac.update(input),
94+
finish: () => hmac.digest(),
95+
};
96+
}
97+
case 'KeyObject-equals': {
98+
const keyObject = crypto.createSecretKey(key);
99+
return { run: () => keyObject.equals(keyObject) };
100+
}
101+
case 'KeyObject-symmetricKeySize-first': {
102+
return { run: () => crypto.createSecretKey(key).symmetricKeySize };
103+
}
104+
case 'KeyObject-type-first': {
105+
return { run: () => crypto.createSecretKey(key).type };
106+
}
107+
case 'KeyObject-type': {
108+
const keyObjects = Array.from(
109+
{ length: 64 }, () => crypto.createSecretKey(key));
110+
for (const keyObject of keyObjects) {
111+
if (keyObject.type !== 'secret')
112+
throw new Error('Unexpected KeyObject type');
113+
}
114+
let index = 0;
115+
return { run: () => keyObjects[index++ & 63].type };
116+
}
117+
case 'Sign-update': {
118+
const sign = crypto.createSign('sha256');
119+
return { run: () => sign.update(input) };
120+
}
121+
case 'Verify-update': {
122+
const verify = crypto.createVerify('sha256');
123+
return { run: () => verify.update(input) };
124+
}
125+
case 'CryptoKey-toKeyObject': {
126+
const keyObject = crypto.createSecretKey(key);
127+
const cryptoKey = keyObject.toCryptoKey(
128+
hmacAlgorithm, true, keyUsages);
129+
return { run: () => crypto.KeyObject.from(cryptoKey) };
130+
}
131+
case 'CryptoKey-algorithm-first':
132+
case 'CryptoKey-extractable-first':
133+
case 'CryptoKey-type-first':
134+
case 'CryptoKey-usages-first': {
135+
const keyObject = crypto.createSecretKey(key);
136+
const property = operation.slice('CryptoKey-'.length, -'-first'.length);
137+
return {
138+
run: () => keyObject.toCryptoKey(
139+
hmacAlgorithm, true, keyUsages)[property],
140+
};
141+
}
142+
case 'CryptoKey-type': {
143+
const keyObject = crypto.createSecretKey(key);
144+
const cryptoKeys = Array.from(
145+
{ length: 64 },
146+
() => keyObject.toCryptoKey(hmacAlgorithm, true, keyUsages));
147+
for (const cryptoKey of cryptoKeys) {
148+
if (cryptoKey.type !== 'secret')
149+
throw new Error('Unexpected CryptoKey type');
150+
}
151+
let index = 0;
152+
return { run: () => cryptoKeys[index++ & 63].type };
153+
}
154+
case 'X509Certificate-checkHost': {
155+
const x509 = new crypto.X509Certificate(certificate);
156+
return { run: () => x509.checkHost('agent1') };
157+
}
158+
case 'X509Certificate-publicKey-first': {
159+
return {
160+
run: () => new crypto.X509Certificate(certificate).publicKey,
161+
};
162+
}
163+
case 'X509Certificate-publicKey': {
164+
const certificates = Array.from(
165+
{ length: 64 }, () => new crypto.X509Certificate(certificate));
166+
for (const x509 of certificates) {
167+
if (x509.publicKey === undefined)
168+
throw new Error('Missing certificate public key');
169+
}
170+
let index = 0;
171+
return { run: () => certificates[index++ & 63].publicKey };
172+
}
173+
case 'X509Certificate-subject-first': {
174+
return {
175+
run: () => new crypto.X509Certificate(certificate).subject,
176+
};
177+
}
178+
case 'X509Certificate-subject': {
179+
const certificates = Array.from(
180+
{ length: 64 }, () => new crypto.X509Certificate(certificate));
181+
for (const x509 of certificates) {
182+
if (x509.subject === undefined)
183+
throw new Error('Missing certificate subject');
184+
}
185+
let index = 0;
186+
return { run: () => certificates[index++ & 63].subject };
187+
}
188+
default:
189+
throw new Error(`Unsupported operation: ${operation}`);
190+
}
191+
}
192+
193+
function main({ operation, n }) {
194+
const state = setup(operation);
195+
let result;
196+
197+
bench.start();
198+
for (let i = 0; i < n; ++i)
199+
result = state.run();
200+
bench.end(n);
201+
202+
if (state.finish)
203+
state.finish();
204+
if (result === state)
205+
throw new Error('Unexpected benchmark result');
206+
}

0 commit comments

Comments
 (0)