Skip to content

Commit

Permalink
crypto: reconcile oneshot sign/verify sync and async implementations
Browse files Browse the repository at this point in the history
  • Loading branch information
panva committed Apr 7, 2021
1 parent 038608d commit bbad5ba
Show file tree
Hide file tree
Showing 9 changed files with 270 additions and 255 deletions.
141 changes: 141 additions & 0 deletions benchmark/crypto/oneshot-sign-verify.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
'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;
}
}
7 changes: 5 additions & 2 deletions lib/internal/crypto/dsa.js
Original file line number Diff line number Diff line change
Expand Up @@ -251,12 +251,15 @@ function dsaSignVerify(key, data, algorithm, signature) {
kCryptoJobAsync,
signature === undefined ? kSignJobModeSign : kSignJobModeVerify,
key[kKeyObject][kHandle],
undefined,
undefined,
undefined,
data,
normalizeHashName(key.algorithm.hash.name),
undefined, // Salt-length is not used in DSA
undefined, // Padding is not used in DSA
signature,
kSigEncDER));
kSigEncDER,
signature));
}

module.exports = {
Expand Down
7 changes: 5 additions & 2 deletions lib/internal/crypto/ec.js
Original file line number Diff line number Diff line change
Expand Up @@ -467,12 +467,15 @@ function ecdsaSignVerify(key, data, { name, hash }, signature) {
kCryptoJobAsync,
mode,
key[kKeyObject][kHandle],
undefined,
undefined,
undefined,
data,
hashname,
undefined, // Salt length, not used with ECDSA
undefined, // PSS Padding, not used with ECDSA
signature,
kSigEncP1363));
kSigEncP1363,
signature));
}

module.exports = {
Expand Down
4 changes: 4 additions & 0 deletions lib/internal/crypto/rsa.js
Original file line number Diff line number Diff line change
Expand Up @@ -356,10 +356,14 @@ function rsaSignVerify(key, data, { saltLength }, signature) {
kCryptoJobAsync,
signature === undefined ? kSignJobModeSign : kSignJobModeVerify,
key[kKeyObject][kHandle],
undefined,
undefined,
undefined,
data,
normalizeHashName(key.algorithm.hash.name),
saltLength,
padding,
undefined,
signature));
}

Expand Down
91 changes: 39 additions & 52 deletions lib/internal/crypto/sig.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,8 @@ const {
Sign: _Sign,
SignJob,
Verify: _Verify,
signOneShot: _signOneShot,
verifyOneShot: _verifyOneShot,
kCryptoJobAsync,
kCryptoJobSync,
kSigEncDER,
kSigEncP1363,
kSignJobModeSign,
Expand All @@ -40,10 +39,6 @@ const {
} = require('internal/crypto/util');

const {
createPrivateKey,
createPublicKey,
isCryptoKey,
isKeyObject,
preparePrivateKey,
preparePublicOrPrivateKey,
} = require('internal/crypto/keys');
Expand Down Expand Up @@ -162,38 +157,34 @@ function signOneShot(algorithm, data, key, callback) {
// Options specific to (EC)DSA
const dsaSigEnc = getDSASignatureEncoding(key);

if (!callback) {
const {
data: keyData,
format: keyFormat,
type: keyType,
passphrase: keyPassphrase
} = preparePrivateKey(key);

return _signOneShot(keyData, keyFormat, keyType, keyPassphrase, data,
algorithm, rsaPadding, pssSaltLength, dsaSigEnc);
}

let keyData;
if (isKeyObject(key) || isCryptoKey(key)) {
({ data: keyData } = preparePrivateKey(key));
} else if (isKeyObject(key.key) || isCryptoKey(key.key)) {
({ data: keyData } = preparePrivateKey(key.key));
} else {
keyData = createPrivateKey(key)[kHandle];
}
const {
data: keyData,
format: keyFormat,
type: keyType,
passphrase: keyPassphrase
} = preparePrivateKey(key);

const job = new SignJob(
kCryptoJobAsync,
callback ? kCryptoJobAsync : kCryptoJobSync,
kSignJobModeSign,
keyData,
keyFormat,
keyType,
keyPassphrase,
data,
algorithm,
pssSaltLength,
rsaPadding,
undefined,
dsaSigEnc);

if (!callback) {
const { 0: err, 1: signature } = job.run();
if (err !== undefined)
throw err;

return Buffer.from(signature);
}

job.ondone = (error, signature) => {
if (error) return FunctionPrototypeCall(callback, job, error);
FunctionPrototypeCall(callback, job, null, Buffer.from(signature));
Expand Down Expand Up @@ -272,38 +263,34 @@ function verifyOneShot(algorithm, data, key, signature, callback) {
);
}

if (!callback) {
const {
data: keyData,
format: keyFormat,
type: keyType,
passphrase: keyPassphrase
} = preparePublicOrPrivateKey(key);

return _verifyOneShot(keyData, keyFormat, keyType, keyPassphrase,
signature, data, algorithm, rsaPadding,
pssSaltLength, dsaSigEnc);
}

let keyData;
if (isKeyObject(key) || isCryptoKey(key)) {
({ data: keyData } = preparePublicOrPrivateKey(key));
} else if (key != null && (isKeyObject(key.key) || isCryptoKey(key.key))) {
({ data: keyData } = preparePublicOrPrivateKey(key.key));
} else {
keyData = createPublicKey(key)[kHandle];
}
const {
data: keyData,
format: keyFormat,
type: keyType,
passphrase: keyPassphrase
} = preparePublicOrPrivateKey(key);

const job = new SignJob(
kCryptoJobAsync,
callback ? kCryptoJobAsync : kCryptoJobSync,
kSignJobModeVerify,
keyData,
keyFormat,
keyType,
keyPassphrase,
data,
algorithm,
pssSaltLength,
rsaPadding,
signature,
dsaSigEnc);
dsaSigEnc,
signature);

if (!callback) {
const { 0: err, 1: result } = job.run();
if (err !== undefined)
throw err;

return result;
}

job.ondone = (error, result) => {
if (error) return FunctionPrototypeCall(callback, job, error);
Expand Down
Loading

0 comments on commit bbad5ba

Please sign in to comment.