From 12fa716cf92c8bf7a9fae1aedc7d9d3ea2c546f5 Mon Sep 17 00:00:00 2001 From: Filip Skokan Date: Mon, 5 Jun 2023 20:00:41 +0100 Subject: [PATCH] benchmark: refactor crypto oneshot MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/48267 Reviewed-By: Tobias Nießen Reviewed-By: Yagiz Nizipli --- benchmark/crypto/oneshot-sign-verify.js | 141 ---------------------- benchmark/crypto/oneshot-sign.js | 131 ++++++++++++++++++++ benchmark/crypto/oneshot-verify.js | 151 ++++++++++++++++++++++++ 3 files changed, 282 insertions(+), 141 deletions(-) delete mode 100644 benchmark/crypto/oneshot-sign-verify.js create mode 100644 benchmark/crypto/oneshot-sign.js create mode 100644 benchmark/crypto/oneshot-verify.js diff --git a/benchmark/crypto/oneshot-sign-verify.js b/benchmark/crypto/oneshot-sign-verify.js deleted file mode 100644 index d3c70eb042469e..00000000000000 --- a/benchmark/crypto/oneshot-sign-verify.js +++ /dev/null @@ -1,141 +0,0 @@ -'use strict'; - -const common = require('../common.js'); -const crypto = require('crypto'); -const fs = require('fs'); -const path = require('path'); -const fixtures_keydir = path.resolve(__dirname, '../../test/fixtures/keys/'); -const keyFixtures = { - publicKey: fs.readFileSync(`${fixtures_keydir}/ec_p256_public.pem`) - .toString(), - privateKey: fs.readFileSync(`${fixtures_keydir}/ec_p256_private.pem`) - .toString(), -}; - -const data = crypto.randomBytes(256); - -let pems; -let keyObjects; - -function getKeyObject({ privateKey, publicKey }) { - return { - privateKey: crypto.createPrivateKey(privateKey), - publicKey: crypto.createPublicKey(publicKey), - }; -} - -const bench = common.createBenchmark(main, { - mode: ['sync', 'async-serial', 'async-parallel'], - keyFormat: ['pem', 'keyObject', 'pem.unique', 'keyObject.unique'], - n: [1e3], -}); - -function measureSync(n, privateKey, publicKey, keys) { - bench.start(); - for (let i = 0; i < n; ++i) { - crypto.verify( - 'sha256', - data, - { key: publicKey || keys[i].publicKey, dsaEncoding: 'ieee-p1363' }, - crypto.sign( - 'sha256', - data, - { key: privateKey || keys[i].privateKey, dsaEncoding: 'ieee-p1363' })); - } - bench.end(n); -} - -function measureAsyncSerial(n, privateKey, publicKey, keys) { - let remaining = n; - function done() { - if (--remaining === 0) - bench.end(n); - else - one(); - } - - function one() { - crypto.sign( - 'sha256', - data, - { - key: privateKey || keys[n - remaining].privateKey, - dsaEncoding: 'ieee-p1363', - }, - (err, signature) => { - crypto.verify( - 'sha256', - data, - { - key: publicKey || keys[n - remaining].publicKey, - dsaEncoding: 'ieee-p1363', - }, - signature, - done); - }); - } - bench.start(); - one(); -} - -function measureAsyncParallel(n, privateKey, publicKey, keys) { - let remaining = n; - function done() { - if (--remaining === 0) - bench.end(n); - } - bench.start(); - for (let i = 0; i < n; ++i) { - crypto.sign( - 'sha256', - data, - { key: privateKey || keys[i].privateKey, dsaEncoding: 'ieee-p1363' }, - (err, signature) => { - crypto.verify( - 'sha256', - data, - { key: publicKey || keys[i].publicKey, dsaEncoding: 'ieee-p1363' }, - signature, - done); - }); - } -} - -function main({ n, mode, keyFormat }) { - pems ||= [...Buffer.alloc(n)].map(() => ({ - privateKey: keyFixtures.privateKey, - publicKey: keyFixtures.publicKey, - })); - keyObjects ||= pems.map(getKeyObject); - - let privateKey, publicKey, keys; - - switch (keyFormat) { - case 'keyObject': - ({ publicKey, privateKey } = keyObjects[0]); - break; - case 'pem': - ({ publicKey, privateKey } = pems[0]); - break; - case 'pem.unique': - keys = pems; - break; - case 'keyObject.unique': - keys = keyObjects; - break; - default: - throw new Error('not implemented'); - } - - switch (mode) { - case 'sync': - measureSync(n, privateKey, publicKey, keys); - break; - case 'async-serial': - measureAsyncSerial(n, privateKey, publicKey, keys); - break; - case 'async-parallel': - measureAsyncParallel(n, privateKey, publicKey, keys); - break; - } -} diff --git a/benchmark/crypto/oneshot-sign.js b/benchmark/crypto/oneshot-sign.js new file mode 100644 index 00000000000000..88e67b9dfb1cf9 --- /dev/null +++ b/benchmark/crypto/oneshot-sign.js @@ -0,0 +1,131 @@ +'use strict'; + +const common = require('../common.js'); +const crypto = require('crypto'); +const fs = require('fs'); +const path = require('path'); +const fixtures_keydir = path.resolve(__dirname, '../../test/fixtures/keys/'); + +const keyFixtures = { + ec: fs.readFileSync(`${fixtures_keydir}/ec_p256_private.pem`, 'utf-8'), + rsa: fs.readFileSync(`${fixtures_keydir}/rsa_private_2048.pem`, 'utf-8'), + ed25519: fs.readFileSync(`${fixtures_keydir}/ed25519_private.pem`, 'utf-8'), +}; + +const data = crypto.randomBytes(256); + +let pems; +let keyObjects; + +const bench = common.createBenchmark(main, { + keyType: ['rsa', 'ec', 'ed25519'], + mode: ['sync', 'async', 'async-parallel'], + keyFormat: ['pem', 'der', 'jwk', 'keyObject', 'keyObject.unique'], + 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'); + }, +}); + +function measureSync(n, digest, privateKey, keys) { + bench.start(); + for (let i = 0; i < n; ++i) { + crypto.sign( + digest, + data, + privateKey || keys[i]); + } + bench.end(n); +} + +function measureAsync(n, digest, privateKey, keys) { + let remaining = n; + function done() { + if (--remaining === 0) + bench.end(n); + else + one(); + } + + function one() { + crypto.sign( + digest, + data, + privateKey || keys[n - remaining], + done); + } + bench.start(); + one(); +} + +function measureAsyncParallel(n, digest, privateKey, keys) { + let remaining = n; + function done() { + if (--remaining === 0) + bench.end(n); + } + bench.start(); + for (let i = 0; i < n; ++i) { + crypto.sign( + digest, + data, + privateKey || keys[i], + done); + } +} + +function main({ n, mode, keyFormat, keyType }) { + pems ||= [...Buffer.alloc(n)].map(() => keyFixtures[keyType]); + keyObjects ||= pems.map(crypto.createPrivateKey); + + let privateKey, keys, digest; + + switch (keyType) { + case 'rsa': + case 'ec': + digest = 'sha256'; + break; + case 'ed25519': + break; + default: + throw new Error('not implemented'); + } + + switch (keyFormat) { + case 'keyObject': + privateKey = keyObjects[0]; + break; + case 'pem': + privateKey = pems[0]; + break; + case 'jwk': { + privateKey = { key: keyObjects[0].export({ format: 'jwk' }), format: 'jwk' }; + break; + } + case 'der': { + privateKey = { key: keyObjects[0].export({ format: 'der', type: 'pkcs8' }), format: 'der', type: 'pkcs8' }; + break; + } + case 'keyObject.unique': + keys = keyObjects; + break; + default: + throw new Error('not implemented'); + } + + switch (mode) { + case 'sync': + measureSync(n, digest, privateKey, keys); + break; + case 'async': + measureAsync(n, digest, privateKey, keys); + break; + case 'async-parallel': + measureAsyncParallel(n, digest, privateKey, keys); + break; + } +} diff --git a/benchmark/crypto/oneshot-verify.js b/benchmark/crypto/oneshot-verify.js new file mode 100644 index 00000000000000..121be28d4d9578 --- /dev/null +++ b/benchmark/crypto/oneshot-verify.js @@ -0,0 +1,151 @@ +'use strict'; + +const common = require('../common.js'); +const crypto = require('crypto'); +const fs = require('fs'); +const path = require('path'); +const fixtures_keydir = path.resolve(__dirname, '../../test/fixtures/keys/'); + +function readKey(name) { + return fs.readFileSync(`${fixtures_keydir}/${name}.pem`, 'utf8'); +} + +function readKeyPair(publicKeyName, privateKeyName) { + return { + publicKey: readKey(publicKeyName), + privateKey: readKey(privateKeyName), + }; +} + +const keyFixtures = { + ec: readKeyPair('ec_p256_public', 'ec_p256_private'), + rsa: readKeyPair('rsa_public_2048', 'rsa_private_2048'), + ed25519: readKeyPair('ed25519_public', 'ed25519_private'), +}; + +const data = crypto.randomBytes(256); + +let pems; +let keyObjects; + +const bench = common.createBenchmark(main, { + keyType: ['rsa', 'ec', 'ed25519'], + mode: ['sync', 'async', 'async-parallel'], + keyFormat: ['pem', 'der', 'jwk', 'keyObject', 'keyObject.unique'], + 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'); + }, +}); + +function measureSync(n, digest, signature, publicKey, keys) { + bench.start(); + for (let i = 0; i < n; ++i) { + crypto.verify( + digest, + data, + publicKey || keys[i], + signature); + } + bench.end(n); +} + +function measureAsync(n, digest, signature, publicKey, keys) { + let remaining = n; + function done() { + if (--remaining === 0) + bench.end(n); + else + one(); + } + + function one() { + crypto.verify( + digest, + data, + publicKey || keys[n - remaining], + signature, + done); + } + bench.start(); + one(); +} + +function measureAsyncParallel(n, digest, signature, publicKey, keys) { + let remaining = n; + function done() { + if (--remaining === 0) + bench.end(n); + } + bench.start(); + for (let i = 0; i < n; ++i) { + crypto.verify( + digest, + data, + publicKey || keys[i], + signature, + done); + } +} + +function main({ n, mode, keyFormat, keyType }) { + pems ||= [...Buffer.alloc(n)].map(() => keyFixtures[keyType].publicKey); + keyObjects ||= pems.map(crypto.createPublicKey); + + let publicKey, keys, digest; + + switch (keyType) { + case 'rsa': + case 'ec': + digest = 'sha256'; + break; + case 'ed25519': + break; + default: + throw new Error('not implemented'); + } + + switch (keyFormat) { + case 'keyObject': + publicKey = keyObjects[0]; + break; + case 'pem': + publicKey = pems[0]; + break; + case 'jwk': { + publicKey = { key: keyObjects[0].export({ format: 'jwk' }), format: 'jwk' }; + break; + } + case 'der': { + publicKey = { key: keyObjects[0].export({ format: 'der', type: 'spki' }), format: 'der', type: 'spki' }; + break; + } + case 'keyObject.unique': + keys = keyObjects; + break; + default: + throw new Error('not implemented'); + } + + + const { privateKey } = keyFixtures[keyType]; + const signature = crypto.sign(digest, data, privateKey); + + switch (mode) { + case 'sync': + measureSync(n, digest, signature, publicKey, keys); + break; + case 'async': + measureAsync(n, digest, signature, publicKey, keys); + break; + case 'async-parallel': + measureAsyncParallel(n, digest, signature, publicKey, keys); + break; + default: + throw new Error('not implemented'); + } +}