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
39 changes: 38 additions & 1 deletion benchmark/crypto/create-keyobject.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,22 @@ if (hasOpenSSL(3, 5)) {

const bench = common.createBenchmark(main, {
keyType: Object.keys(keyFixtures),
keyFormat: ['pkcs8', 'spki', 'der-pkcs8', 'der-spki', 'jwk-public', 'jwk-private'],
keyFormat: ['pkcs8', 'spki', 'der-pkcs8', 'der-spki', 'jwk-public', 'jwk-private',
'raw-public', 'raw-private', 'raw-seed'],
n: [1e3],
}, {
combinationFilter(p) {
// raw-private is not supported for rsa and ml-dsa
if (p.keyFormat === 'raw-private')
return p.keyType !== 'rsa' && !p.keyType.startsWith('ml-');
// raw-public is not supported by rsa
if (p.keyFormat === 'raw-public')
return p.keyType !== 'rsa';
// raw-seed is only supported for ml-dsa
if (p.keyFormat === 'raw-seed')
return p.keyType.startsWith('ml-');
return true;
},
});

function measure(n, fn, input) {
Expand Down Expand Up @@ -82,6 +96,29 @@ function main({ n, keyFormat, keyType }) {
fn = crypto.createPrivateKey;
break;
}
case 'raw-public': {
const exportedKey = keyPair.publicKey.export({ format: 'raw-public' });
key = { key: exportedKey, format: 'raw-public', asymmetricKeyType: keyType };
if (keyType === 'ec') key.namedCurve = keyPair.publicKey.asymmetricKeyDetails.namedCurve;
fn = crypto.createPublicKey;
break;
}
case 'raw-private': {
const exportedKey = keyPair.privateKey.export({ format: 'raw-private' });
key = { key: exportedKey, format: 'raw-private', asymmetricKeyType: keyType };
if (keyType === 'ec') key.namedCurve = keyPair.privateKey.asymmetricKeyDetails.namedCurve;
fn = crypto.createPrivateKey;
break;
}
case 'raw-seed': {
key = {
key: keyPair.privateKey.export({ format: 'raw-seed' }),
format: 'raw-seed',
asymmetricKeyType: keyType,
};
fn = crypto.createPrivateKey;
break;
}
default:
throw new Error('not implemented');
}
Expand Down
147 changes: 112 additions & 35 deletions benchmark/crypto/kem.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,29 @@ function readKey(name) {
return fs.readFileSync(`${fixtures_keydir}/${name}.pem`, 'utf8');
}

function readKeyPair(publicKeyName, privateKeyName) {
return {
publicKey: readKey(publicKeyName),
privateKey: readKey(privateKeyName),
};
}

const keyFixtures = {};

if (hasOpenSSL(3, 5)) {
keyFixtures['ml-kem-512'] = readKey('ml_kem_512_private');
keyFixtures['ml-kem-768'] = readKey('ml_kem_768_private');
keyFixtures['ml-kem-1024'] = readKey('ml_kem_1024_private');
keyFixtures['ml-kem-512'] = readKeyPair('ml_kem_512_public', 'ml_kem_512_private');
keyFixtures['ml-kem-768'] = readKeyPair('ml_kem_768_public', 'ml_kem_768_private');
keyFixtures['ml-kem-1024'] = readKeyPair('ml_kem_1024_public', 'ml_kem_1024_private');
}
if (hasOpenSSL(3, 2)) {
keyFixtures['p-256'] = readKey('ec_p256_private');
keyFixtures['p-384'] = readKey('ec_p384_private');
keyFixtures['p-521'] = readKey('ec_p521_private');
keyFixtures.x25519 = readKey('x25519_private');
keyFixtures.x448 = readKey('x448_private');
keyFixtures['p-256'] = readKeyPair('ec_p256_public', 'ec_p256_private');
keyFixtures['p-384'] = readKeyPair('ec_p384_public', 'ec_p384_private');
keyFixtures['p-521'] = readKeyPair('ec_p521_public', 'ec_p521_private');
keyFixtures.x25519 = readKeyPair('x25519_public', 'x25519_private');
keyFixtures.x448 = readKeyPair('x448_public', 'x448_private');
}
if (hasOpenSSL(3, 0)) {
keyFixtures.rsa = readKey('rsa_private_2048');
keyFixtures.rsa = readKeyPair('rsa_public_2048', 'rsa_private_2048');
}

if (Object.keys(keyFixtures).length === 0) {
Expand All @@ -37,32 +44,46 @@ if (Object.keys(keyFixtures).length === 0) {
const bench = common.createBenchmark(main, {
keyType: Object.keys(keyFixtures),
mode: ['sync', 'async', 'async-parallel'],
keyFormat: ['keyObject', 'keyObject.unique'],
keyFormat: ['keyObject', 'keyObject.unique', 'pem', 'der', 'jwk',
'raw-public', 'raw-private', 'raw-seed'],
op: ['encapsulate', 'decapsulate'],
n: [1e3],
}, {
combinationFilter(p) {
// "keyObject.unique" allows to compare the result with "keyObject" to
// assess whether mutexes over the key material impact the operation
return p.keyFormat !== 'keyObject.unique' ||
(p.keyFormat === 'keyObject.unique' && p.mode === 'async-parallel');
if (p.keyFormat === 'keyObject.unique')
return p.mode === 'async-parallel';
// JWK is not supported for ml-kem for now
if (p.keyFormat === 'jwk')
return !p.keyType.startsWith('ml-');
// raw-public is only supported for encapsulate, not rsa
if (p.keyFormat === 'raw-public')
return p.keyType !== 'rsa' && p.op === 'encapsulate';
// raw-private is not supported for rsa and ml-kem, only for decapsulate
if (p.keyFormat === 'raw-private')
return p.keyType !== 'rsa' && !p.keyType.startsWith('ml-') && p.op === 'decapsulate';
// raw-seed is only supported for ml-kem
if (p.keyFormat === 'raw-seed')
return p.keyType.startsWith('ml-');
return true;
},
});

function measureSync(n, op, privateKey, keys, ciphertexts) {
function measureSync(n, op, key, keys, ciphertexts) {
bench.start();
for (let i = 0; i < n; ++i) {
const key = privateKey || keys[i];
const k = key || keys[i];
if (op === 'encapsulate') {
crypto.encapsulate(key);
crypto.encapsulate(k);
} else {
crypto.decapsulate(key, ciphertexts[i]);
crypto.decapsulate(k, ciphertexts[i]);
}
}
bench.end(n);
}

function measureAsync(n, op, privateKey, keys, ciphertexts) {
function measureAsync(n, op, key, keys, ciphertexts) {
let remaining = n;
function done() {
if (--remaining === 0)
Expand All @@ -72,44 +93,98 @@ function measureAsync(n, op, privateKey, keys, ciphertexts) {
}

function one() {
const key = privateKey || keys[n - remaining];
const k = key || keys[n - remaining];
if (op === 'encapsulate') {
crypto.encapsulate(key, done);
crypto.encapsulate(k, done);
} else {
crypto.decapsulate(key, ciphertexts[n - remaining], done);
crypto.decapsulate(k, ciphertexts[n - remaining], done);
}
}
bench.start();
one();
}

function measureAsyncParallel(n, op, privateKey, keys, ciphertexts) {
function measureAsyncParallel(n, op, key, keys, ciphertexts) {
let remaining = n;
function done() {
if (--remaining === 0)
bench.end(n);
}
bench.start();
for (let i = 0; i < n; ++i) {
const key = privateKey || keys[i];
const k = key || keys[i];
if (op === 'encapsulate') {
crypto.encapsulate(key, done);
crypto.encapsulate(k, done);
} else {
crypto.decapsulate(key, ciphertexts[i], done);
crypto.decapsulate(k, ciphertexts[i], done);
}
}
}

function main({ n, mode, keyFormat, keyType, op }) {
const pems = [...Buffer.alloc(n)].map(() => keyFixtures[keyType]);
const keyObjects = pems.map(crypto.createPrivateKey);
const isEncapsulate = op === 'encapsulate';
const pemSource = isEncapsulate ?
keyFixtures[keyType].publicKey :
keyFixtures[keyType].privateKey;
const createKeyFn = isEncapsulate ? crypto.createPublicKey : crypto.createPrivateKey;
const pems = [...Buffer.alloc(n)].map(() => pemSource);
const keyObjects = pems.map(createKeyFn);

let privateKey, keys, ciphertexts;
// Warm up OpenSSL's provider operation cache for each key object
if (isEncapsulate) {
for (const keyObject of keyObjects) {
crypto.encapsulate(keyObject);
}
} else {
const warmupCiphertext = crypto.encapsulate(keyObjects[0]).ciphertext;
for (const keyObject of keyObjects) {
crypto.decapsulate(keyObject, warmupCiphertext);
}
}

const asymmetricKeyType = keyObjects[0].asymmetricKeyType;
let key, keys, ciphertexts;

switch (keyFormat) {
case 'keyObject':
privateKey = keyObjects[0];
key = keyObjects[0];
break;
case 'pem':
key = pems[0];
break;
case 'jwk': {
key = { key: keyObjects[0].export({ format: 'jwk' }), format: 'jwk' };
break;
}
case 'der': {
const type = isEncapsulate ? 'spki' : 'pkcs8';
key = { key: keyObjects[0].export({ format: 'der', type }), format: 'der', type };
break;
}
case 'raw-public': {
const exportedKey = keyObjects[0].export({ format: 'raw-public' });
const keyOpts = { key: exportedKey, format: 'raw-public', asymmetricKeyType };
if (asymmetricKeyType === 'ec') keyOpts.namedCurve = keyObjects[0].asymmetricKeyDetails.namedCurve;
key = keyOpts;
break;
}
case 'raw-private': {
const exportedKey = keyObjects[0].export({ format: 'raw-private' });
const keyOpts = { key: exportedKey, format: 'raw-private', asymmetricKeyType };
if (asymmetricKeyType === 'ec') keyOpts.namedCurve = keyObjects[0].asymmetricKeyDetails.namedCurve;
key = keyOpts;
break;
}
case 'raw-seed': {
// raw-seed requires a private key to export from
const privateKeyObject = crypto.createPrivateKey(keyFixtures[keyType].privateKey);
key = {
key: privateKeyObject.export({ format: 'raw-seed' }),
format: 'raw-seed',
asymmetricKeyType,
};
break;
}
case 'keyObject.unique':
keys = keyObjects;
break;
Expand All @@ -118,23 +193,25 @@ function main({ n, mode, keyFormat, keyType, op }) {
}

// Pre-generate ciphertexts for decapsulate operations
if (op === 'decapsulate') {
if (privateKey) {
ciphertexts = [...Buffer.alloc(n)].map(() => crypto.encapsulate(privateKey).ciphertext);
if (!isEncapsulate) {
const encapKey = crypto.createPublicKey(
crypto.createPrivateKey(keyFixtures[keyType].privateKey));
if (key) {
ciphertexts = [...Buffer.alloc(n)].map(() => crypto.encapsulate(encapKey).ciphertext);
} else {
ciphertexts = keys.map((key) => crypto.encapsulate(key).ciphertext);
ciphertexts = keys.map(() => crypto.encapsulate(encapKey).ciphertext);
}
}

switch (mode) {
case 'sync':
measureSync(n, op, privateKey, keys, ciphertexts);
measureSync(n, op, key, keys, ciphertexts);
break;
case 'async':
measureAsync(n, op, privateKey, keys, ciphertexts);
measureAsync(n, op, key, keys, ciphertexts);
break;
case 'async-parallel':
measureAsyncParallel(n, op, privateKey, keys, ciphertexts);
measureAsyncParallel(n, op, key, keys, ciphertexts);
break;
}
}
34 changes: 31 additions & 3 deletions benchmark/crypto/oneshot-sign.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,21 @@ let keyObjects;
const bench = common.createBenchmark(main, {
keyType: Object.keys(keyFixtures),
mode: ['sync', 'async', 'async-parallel'],
keyFormat: ['pem', 'der', 'jwk', 'keyObject', 'keyObject.unique'],
keyFormat: ['pem', 'der', 'jwk', 'keyObject', 'keyObject.unique', 'raw-private', 'raw-seed'],
n: [1e3],
}, {
combinationFilter(p) {
// "keyObject.unique" allows to compare the result with "keyObject" to
// assess whether mutexes over the key material impact the operation
return p.keyFormat !== 'keyObject.unique' ||
(p.keyFormat === 'keyObject.unique' && p.mode === 'async-parallel');
if (p.keyFormat === 'keyObject.unique')
return p.mode === 'async-parallel';
// raw-private is not supported for rsa and ml-dsa
if (p.keyFormat === 'raw-private')
return p.keyType !== 'rsa' && !p.keyType.startsWith('ml-');
// raw-seed is only supported for ml-dsa
if (p.keyFormat === 'raw-seed')
return p.keyType.startsWith('ml-');
return true;
},
});

Expand Down Expand Up @@ -91,6 +98,12 @@ function main({ n, mode, keyFormat, keyType }) {
pems ||= [...Buffer.alloc(n)].map(() => keyFixtures[keyType]);
keyObjects ||= pems.map(crypto.createPrivateKey);

// Warm up OpenSSL's provider operation cache for each key object
for (const keyObject of keyObjects) {
crypto.sign(keyType === 'rsa' || keyType === 'ec' ? 'sha256' : null,
data, keyObject);
}

let privateKey, keys, digest;

switch (keyType) {
Expand Down Expand Up @@ -120,6 +133,21 @@ function main({ n, mode, keyFormat, keyType }) {
privateKey = { key: keyObjects[0].export({ format: 'der', type: 'pkcs8' }), format: 'der', type: 'pkcs8' };
break;
}
case 'raw-private': {
const exportedKey = keyObjects[0].export({ format: 'raw-private' });
const keyOpts = { key: exportedKey, format: 'raw-private', asymmetricKeyType: keyType };
if (keyType === 'ec') keyOpts.namedCurve = keyObjects[0].asymmetricKeyDetails.namedCurve;
privateKey = keyOpts;
break;
}
case 'raw-seed': {
privateKey = {
key: keyObjects[0].export({ format: 'raw-seed' }),
format: 'raw-seed',
asymmetricKeyType: keyType,
};
break;
}
case 'keyObject.unique':
keys = keyObjects;
break;
Expand Down
Loading
Loading