Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 86 additions & 0 deletions benchmark/crypto/class-construction.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
'use strict';

const common = require('../common.js');
const assert = require('node:assert');
const crypto = require('node:crypto');
const fs = require('node:fs');
const path = require('node:path');

const fixtureDir = path.resolve(__dirname, '../../test/fixtures/keys');
const certificate = fs.readFileSync(path.join(fixtureDir, 'agent1-cert.pem'));
const dhPrime = crypto.getDiffieHellman('modp14').getPrime();
const key = Buffer.alloc(32, 0x01);
const keyObject = crypto.createSecretKey(key);
const hmacAlgorithm = { name: 'HMAC', hash: 'SHA-256' };
const keyUsages = ['sign'];
const iv = Buffer.alloc(16, 0x02);

const iterations = {
Certificate: 1e7,
Cipheriv: 1e5,
Decipheriv: 1e5,
DiffieHellman: 10,
DiffieHellmanGroup: 2e5,
ECDH: 2e5,
Hash: 1e5,
Hmac: 5e4,
KeyObject: 1e5,
Sign: 1e5,
Verify: 1e5,
CryptoKey: 5e4,
X509Certificate: 5e3,
};

const bench = common.createBenchmark(main, {
type: Object.keys(iterations),
n: [...new Set(Object.values(iterations))],
}, {
combinationFilter({ type, n }) {
// Benchmark test mode reduces numeric options to 1.
return n === 1 || iterations[type] === n;
},
});

function construct(type) {
switch (type) {
case 'Certificate':
return new crypto.Certificate();
case 'Cipheriv':
return crypto.createCipheriv('aes-256-ctr', key, iv);
case 'Decipheriv':
return crypto.createDecipheriv('aes-256-ctr', key, iv);
case 'DiffieHellman':
return crypto.createDiffieHellman(dhPrime);
case 'DiffieHellmanGroup':
return crypto.getDiffieHellman('modp14');
case 'ECDH':
return crypto.createECDH('prime256v1');
case 'Hash':
return crypto.createHash('sha256');
case 'Hmac':
return crypto.createHmac('sha256', key);
case 'KeyObject':
return crypto.createSecretKey(key);
case 'Sign':
return crypto.createSign('sha256');
case 'Verify':
return crypto.createVerify('sha256');
case 'CryptoKey':
return keyObject.toCryptoKey(hmacAlgorithm, true, keyUsages);
case 'X509Certificate':
return new crypto.X509Certificate(certificate);
default:
throw new Error(`Unsupported class: ${type}`);
}
}

function main({ type, n }) {
const instances = new Array(n);

bench.start();
for (let i = 0; i < n; ++i)
instances[i] = construct(type);
bench.end(n);

assert.strictEqual(typeof instances[n - 1], 'object');
}
206 changes: 206 additions & 0 deletions benchmark/crypto/class-methods.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
'use strict';

const common = require('../common.js');
const crypto = require('node:crypto');
const fs = require('node:fs');
const path = require('node:path');

const fixtureDir = path.resolve(__dirname, '../../test/fixtures/keys');
const certificate = fs.readFileSync(path.join(fixtureDir, 'agent1-cert.pem'));
const key = Buffer.alloc(32, 0x01);
const hmacAlgorithm = { name: 'HMAC', hash: 'SHA-256' };
const keyUsages = ['sign'];
const iv = Buffer.alloc(16, 0x02);
const input = Buffer.alloc(16, 0x03);

const iterations = {
'Cipheriv-update': 1e6,
'Decipheriv-update': 1e6,
'DiffieHellman-getGenerator': 2e5,
'DiffieHellmanGroup-getGenerator': 2e6,
'ECDH-getPrivateKey': 2e6,
'Hash-update': 5e6,
'Hmac-update': 5e6,
'KeyObject-equals': 2e6,
'KeyObject-symmetricKeySize-first': 1e5,
'KeyObject-type-first': 1e5,
'KeyObject-type': 1e8,
'Sign-update': 5e6,
'Verify-update': 5e6,
'CryptoKey-toKeyObject': 2e5,
'CryptoKey-algorithm-first': 1e5,
'CryptoKey-extractable-first': 1e5,
'CryptoKey-type-first': 1e5,
'CryptoKey-usages-first': 1e5,
'CryptoKey-type': 1e8,
'X509Certificate-checkHost': 1e6,
'X509Certificate-publicKey-first': 5e3,
'X509Certificate-publicKey': 1e8,
'X509Certificate-subject-first': 5e3,
'X509Certificate-subject': 1e8,
};

const bench = common.createBenchmark(main, {
operation: Object.keys(iterations),
n: [...new Set(Object.values(iterations))],
}, {
combinationFilter({ operation, n }) {
// Benchmark test mode reduces numeric options to 1.
return n === 1 || iterations[operation] === n;
},
});

function setup(operation) {
switch (operation) {
case 'Cipheriv-update': {
const cipher = crypto.createCipheriv('aes-256-ctr', key, iv);
return {
run: () => cipher.update(input),
finish: () => cipher.final(),
};
}
case 'Decipheriv-update': {
const decipher = crypto.createDecipheriv('aes-256-ctr', key, iv);
return {
run: () => decipher.update(input),
finish: () => decipher.final(),
};
}
case 'DiffieHellman-getGenerator': {
const dh = crypto.createDiffieHellman(
crypto.getDiffieHellman('modp14').getPrime());
return { run: () => dh.getGenerator() };
}
case 'DiffieHellmanGroup-getGenerator': {
const dh = crypto.getDiffieHellman('modp14');
return { run: () => dh.getGenerator() };
}
case 'ECDH-getPrivateKey': {
const ecdh = crypto.createECDH('prime256v1');
ecdh.generateKeys();
return { run: () => ecdh.getPrivateKey() };
}
case 'Hash-update': {
const hash = crypto.createHash('sha256');
return {
run: () => hash.update(input),
finish: () => hash.digest(),
};
}
case 'Hmac-update': {
const hmac = crypto.createHmac('sha256', key);
return {
run: () => hmac.update(input),
finish: () => hmac.digest(),
};
}
case 'KeyObject-equals': {
const keyObject = crypto.createSecretKey(key);
return { run: () => keyObject.equals(keyObject) };
}
case 'KeyObject-symmetricKeySize-first': {
return { run: () => crypto.createSecretKey(key).symmetricKeySize };
}
case 'KeyObject-type-first': {
return { run: () => crypto.createSecretKey(key).type };
}
case 'KeyObject-type': {
const keyObjects = Array.from(
{ length: 64 }, () => crypto.createSecretKey(key));
for (const keyObject of keyObjects) {
if (keyObject.type !== 'secret')
throw new Error('Unexpected KeyObject type');
}
let index = 0;
return { run: () => keyObjects[index++ & 63].type };
}
case 'Sign-update': {
const sign = crypto.createSign('sha256');
return { run: () => sign.update(input) };
}
case 'Verify-update': {
const verify = crypto.createVerify('sha256');
return { run: () => verify.update(input) };
}
case 'CryptoKey-toKeyObject': {
const keyObject = crypto.createSecretKey(key);
const cryptoKey = keyObject.toCryptoKey(
hmacAlgorithm, true, keyUsages);
return { run: () => crypto.KeyObject.from(cryptoKey) };
}
case 'CryptoKey-algorithm-first':
case 'CryptoKey-extractable-first':
case 'CryptoKey-type-first':
case 'CryptoKey-usages-first': {
const keyObject = crypto.createSecretKey(key);
const property = operation.slice('CryptoKey-'.length, -'-first'.length);
return {
run: () => keyObject.toCryptoKey(
hmacAlgorithm, true, keyUsages)[property],
};
}
case 'CryptoKey-type': {
const keyObject = crypto.createSecretKey(key);
const cryptoKeys = Array.from(
{ length: 64 },
() => keyObject.toCryptoKey(hmacAlgorithm, true, keyUsages));
for (const cryptoKey of cryptoKeys) {
if (cryptoKey.type !== 'secret')
throw new Error('Unexpected CryptoKey type');
}
let index = 0;
return { run: () => cryptoKeys[index++ & 63].type };
}
case 'X509Certificate-checkHost': {
const x509 = new crypto.X509Certificate(certificate);
return { run: () => x509.checkHost('agent1') };
}
case 'X509Certificate-publicKey-first': {
return {
run: () => new crypto.X509Certificate(certificate).publicKey,
};
}
case 'X509Certificate-publicKey': {
const certificates = Array.from(
{ length: 64 }, () => new crypto.X509Certificate(certificate));
for (const x509 of certificates) {
if (x509.publicKey === undefined)
throw new Error('Missing certificate public key');
}
let index = 0;
return { run: () => certificates[index++ & 63].publicKey };
}
case 'X509Certificate-subject-first': {
return {
run: () => new crypto.X509Certificate(certificate).subject,
};
}
case 'X509Certificate-subject': {
const certificates = Array.from(
{ length: 64 }, () => new crypto.X509Certificate(certificate));
for (const x509 of certificates) {
if (x509.subject === undefined)
throw new Error('Missing certificate subject');
}
let index = 0;
return { run: () => certificates[index++ & 63].subject };
}
default:
throw new Error(`Unsupported operation: ${operation}`);
}
}

function main({ operation, n }) {
const state = setup(operation);
let result;

bench.start();
for (let i = 0; i < n; ++i)
result = state.run();
bench.end(n);

if (state.finish)
state.finish();
if (result === state)
throw new Error('Unexpected benchmark result');
}
2 changes: 1 addition & 1 deletion lib/eslint.config_partial.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -445,8 +445,8 @@ export default [
'node-core/lowercase-name-for-primitive': 'error',
'node-core/non-ascii-character': 'error',
'node-core/no-array-destructuring': 'error',
'node-core/no-crypto-class-instanceof': 'error',
'node-core/no-cryptokey-public-accessors': 'error',
'node-core/no-keyobject-cryptokey-instanceof': 'error',
'node-core/no-keyobject-public-accessors': 'error',
'node-core/prefer-primordials': [
'error',
Expand Down
Loading
Loading