Skip to content

Commit e8cb644

Browse files
committed
crypto: reconcile oneshot sign/verify sync and async implementations
PR-URL: #37816 Reviewed-By: Daniel Bevenius <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Matteo Collina <[email protected]>
1 parent d861324 commit e8cb644

File tree

9 files changed

+270
-255
lines changed

9 files changed

+270
-255
lines changed
+141
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
'use strict';
2+
3+
const common = require('../common.js');
4+
const crypto = require('crypto');
5+
const fs = require('fs');
6+
const path = require('path');
7+
const fixtures_keydir = path.resolve(__dirname, '../../test/fixtures/keys/');
8+
const keyFixtures = {
9+
publicKey: fs.readFileSync(`${fixtures_keydir}/ec_p256_public.pem`)
10+
.toString(),
11+
privateKey: fs.readFileSync(`${fixtures_keydir}/ec_p256_private.pem`)
12+
.toString()
13+
};
14+
15+
const data = crypto.randomBytes(256);
16+
17+
let pems;
18+
let keyObjects;
19+
20+
function getKeyObject({ privateKey, publicKey }) {
21+
return {
22+
privateKey: crypto.createPrivateKey(privateKey),
23+
publicKey: crypto.createPublicKey(publicKey)
24+
};
25+
}
26+
27+
const bench = common.createBenchmark(main, {
28+
mode: ['sync', 'async-serial', 'async-parallel'],
29+
keyFormat: ['pem', 'keyObject', 'pem.unique', 'keyObject.unique'],
30+
n: [1e3],
31+
});
32+
33+
function measureSync(n, privateKey, publicKey, keys) {
34+
bench.start();
35+
for (let i = 0; i < n; ++i) {
36+
crypto.verify(
37+
'sha256',
38+
data,
39+
{ key: publicKey || keys[i].publicKey, dsaEncoding: 'ieee-p1363' },
40+
crypto.sign(
41+
'sha256',
42+
data,
43+
{ key: privateKey || keys[i].privateKey, dsaEncoding: 'ieee-p1363' }));
44+
}
45+
bench.end(n);
46+
}
47+
48+
function measureAsyncSerial(n, privateKey, publicKey, keys) {
49+
let remaining = n;
50+
function done() {
51+
if (--remaining === 0)
52+
bench.end(n);
53+
else
54+
one();
55+
}
56+
57+
function one() {
58+
crypto.sign(
59+
'sha256',
60+
data,
61+
{
62+
key: privateKey || keys[n - remaining].privateKey,
63+
dsaEncoding: 'ieee-p1363'
64+
},
65+
(err, signature) => {
66+
crypto.verify(
67+
'sha256',
68+
data,
69+
{
70+
key: publicKey || keys[n - remaining].publicKey,
71+
dsaEncoding: 'ieee-p1363'
72+
},
73+
signature,
74+
done);
75+
});
76+
}
77+
bench.start();
78+
one();
79+
}
80+
81+
function measureAsyncParallel(n, privateKey, publicKey, keys) {
82+
let remaining = n;
83+
function done() {
84+
if (--remaining === 0)
85+
bench.end(n);
86+
}
87+
bench.start();
88+
for (let i = 0; i < n; ++i) {
89+
crypto.sign(
90+
'sha256',
91+
data,
92+
{ key: privateKey || keys[i].privateKey, dsaEncoding: 'ieee-p1363' },
93+
(err, signature) => {
94+
crypto.verify(
95+
'sha256',
96+
data,
97+
{ key: publicKey || keys[i].publicKey, dsaEncoding: 'ieee-p1363' },
98+
signature,
99+
done);
100+
});
101+
}
102+
}
103+
104+
function main({ n, mode, keyFormat }) {
105+
pems ||= [...Buffer.alloc(n)].map(() => ({
106+
privateKey: keyFixtures.privateKey,
107+
publicKey: keyFixtures.publicKey
108+
}));
109+
keyObjects ||= pems.map(getKeyObject);
110+
111+
let privateKey, publicKey, keys;
112+
113+
switch (keyFormat) {
114+
case 'keyObject':
115+
({ publicKey, privateKey } = keyObjects[0]);
116+
break;
117+
case 'pem':
118+
({ publicKey, privateKey } = pems[0]);
119+
break;
120+
case 'pem.unique':
121+
keys = pems;
122+
break;
123+
case 'keyObject.unique':
124+
keys = keyObjects;
125+
break;
126+
default:
127+
throw new Error('not implemented');
128+
}
129+
130+
switch (mode) {
131+
case 'sync':
132+
measureSync(n, privateKey, publicKey, keys);
133+
break;
134+
case 'async-serial':
135+
measureAsyncSerial(n, privateKey, publicKey, keys);
136+
break;
137+
case 'async-parallel':
138+
measureAsyncParallel(n, privateKey, publicKey, keys);
139+
break;
140+
}
141+
}

lib/internal/crypto/dsa.js

+5-2
Original file line numberDiff line numberDiff line change
@@ -251,12 +251,15 @@ function dsaSignVerify(key, data, algorithm, signature) {
251251
kCryptoJobAsync,
252252
signature === undefined ? kSignJobModeSign : kSignJobModeVerify,
253253
key[kKeyObject][kHandle],
254+
undefined,
255+
undefined,
256+
undefined,
254257
data,
255258
normalizeHashName(key.algorithm.hash.name),
256259
undefined, // Salt-length is not used in DSA
257260
undefined, // Padding is not used in DSA
258-
signature,
259-
kSigEncDER));
261+
kSigEncDER,
262+
signature));
260263
}
261264

262265
module.exports = {

lib/internal/crypto/ec.js

+5-2
Original file line numberDiff line numberDiff line change
@@ -467,12 +467,15 @@ function ecdsaSignVerify(key, data, { name, hash }, signature) {
467467
kCryptoJobAsync,
468468
mode,
469469
key[kKeyObject][kHandle],
470+
undefined,
471+
undefined,
472+
undefined,
470473
data,
471474
hashname,
472475
undefined, // Salt length, not used with ECDSA
473476
undefined, // PSS Padding, not used with ECDSA
474-
signature,
475-
kSigEncP1363));
477+
kSigEncP1363,
478+
signature));
476479
}
477480

478481
module.exports = {

lib/internal/crypto/rsa.js

+4
Original file line numberDiff line numberDiff line change
@@ -356,10 +356,14 @@ function rsaSignVerify(key, data, { saltLength }, signature) {
356356
kCryptoJobAsync,
357357
signature === undefined ? kSignJobModeSign : kSignJobModeVerify,
358358
key[kKeyObject][kHandle],
359+
undefined,
360+
undefined,
361+
undefined,
359362
data,
360363
normalizeHashName(key.algorithm.hash.name),
361364
saltLength,
362365
padding,
366+
undefined,
363367
signature));
364368
}
365369

lib/internal/crypto/sig.js

+39-52
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,8 @@ const {
2424
Sign: _Sign,
2525
SignJob,
2626
Verify: _Verify,
27-
signOneShot: _signOneShot,
28-
verifyOneShot: _verifyOneShot,
2927
kCryptoJobAsync,
28+
kCryptoJobSync,
3029
kSigEncDER,
3130
kSigEncP1363,
3231
kSignJobModeSign,
@@ -40,10 +39,6 @@ const {
4039
} = require('internal/crypto/util');
4140

4241
const {
43-
createPrivateKey,
44-
createPublicKey,
45-
isCryptoKey,
46-
isKeyObject,
4742
preparePrivateKey,
4843
preparePublicOrPrivateKey,
4944
} = require('internal/crypto/keys');
@@ -162,38 +157,34 @@ function signOneShot(algorithm, data, key, callback) {
162157
// Options specific to (EC)DSA
163158
const dsaSigEnc = getDSASignatureEncoding(key);
164159

165-
if (!callback) {
166-
const {
167-
data: keyData,
168-
format: keyFormat,
169-
type: keyType,
170-
passphrase: keyPassphrase
171-
} = preparePrivateKey(key);
172-
173-
return _signOneShot(keyData, keyFormat, keyType, keyPassphrase, data,
174-
algorithm, rsaPadding, pssSaltLength, dsaSigEnc);
175-
}
176-
177-
let keyData;
178-
if (isKeyObject(key) || isCryptoKey(key)) {
179-
({ data: keyData } = preparePrivateKey(key));
180-
} else if (isKeyObject(key.key) || isCryptoKey(key.key)) {
181-
({ data: keyData } = preparePrivateKey(key.key));
182-
} else {
183-
keyData = createPrivateKey(key)[kHandle];
184-
}
160+
const {
161+
data: keyData,
162+
format: keyFormat,
163+
type: keyType,
164+
passphrase: keyPassphrase
165+
} = preparePrivateKey(key);
185166

186167
const job = new SignJob(
187-
kCryptoJobAsync,
168+
callback ? kCryptoJobAsync : kCryptoJobSync,
188169
kSignJobModeSign,
189170
keyData,
171+
keyFormat,
172+
keyType,
173+
keyPassphrase,
190174
data,
191175
algorithm,
192176
pssSaltLength,
193177
rsaPadding,
194-
undefined,
195178
dsaSigEnc);
196179

180+
if (!callback) {
181+
const { 0: err, 1: signature } = job.run();
182+
if (err !== undefined)
183+
throw err;
184+
185+
return Buffer.from(signature);
186+
}
187+
197188
job.ondone = (error, signature) => {
198189
if (error) return FunctionPrototypeCall(callback, job, error);
199190
FunctionPrototypeCall(callback, job, null, Buffer.from(signature));
@@ -272,38 +263,34 @@ function verifyOneShot(algorithm, data, key, signature, callback) {
272263
);
273264
}
274265

275-
if (!callback) {
276-
const {
277-
data: keyData,
278-
format: keyFormat,
279-
type: keyType,
280-
passphrase: keyPassphrase
281-
} = preparePublicOrPrivateKey(key);
282-
283-
return _verifyOneShot(keyData, keyFormat, keyType, keyPassphrase,
284-
signature, data, algorithm, rsaPadding,
285-
pssSaltLength, dsaSigEnc);
286-
}
287-
288-
let keyData;
289-
if (isKeyObject(key) || isCryptoKey(key)) {
290-
({ data: keyData } = preparePublicOrPrivateKey(key));
291-
} else if (key != null && (isKeyObject(key.key) || isCryptoKey(key.key))) {
292-
({ data: keyData } = preparePublicOrPrivateKey(key.key));
293-
} else {
294-
keyData = createPublicKey(key)[kHandle];
295-
}
266+
const {
267+
data: keyData,
268+
format: keyFormat,
269+
type: keyType,
270+
passphrase: keyPassphrase
271+
} = preparePublicOrPrivateKey(key);
296272

297273
const job = new SignJob(
298-
kCryptoJobAsync,
274+
callback ? kCryptoJobAsync : kCryptoJobSync,
299275
kSignJobModeVerify,
300276
keyData,
277+
keyFormat,
278+
keyType,
279+
keyPassphrase,
301280
data,
302281
algorithm,
303282
pssSaltLength,
304283
rsaPadding,
305-
signature,
306-
dsaSigEnc);
284+
dsaSigEnc,
285+
signature);
286+
287+
if (!callback) {
288+
const { 0: err, 1: result } = job.run();
289+
if (err !== undefined)
290+
throw err;
291+
292+
return result;
293+
}
307294

308295
job.ondone = (error, result) => {
309296
if (error) return FunctionPrototypeCall(callback, job, error);

0 commit comments

Comments
 (0)