Skip to content

Commit 922f2f0

Browse files
panvadanielleadams
authored andcommitted
crypto: add optional callback to crypto.sign and crypto.verify
PR-URL: #37500 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
1 parent a86334f commit 922f2f0

File tree

11 files changed

+361
-74
lines changed

11 files changed

+361
-74
lines changed

doc/api/crypto.md

+22-4
Original file line numberDiff line numberDiff line change
@@ -3822,10 +3822,13 @@ added: v10.0.0
38223822
Enables the FIPS compliant crypto provider in a FIPS-enabled Node.js build.
38233823
Throws an error if FIPS mode is not available.
38243824

3825-
### `crypto.sign(algorithm, data, key)`
3825+
### `crypto.sign(algorithm, data, key[, callback])`
38263826
<!-- YAML
38273827
added: v12.0.0
38283828
changes:
3829+
- version: REPLACEME
3830+
pr-url: https://github.com/nodejs/node/pull/37500
3831+
description: Optional callback argument added.
38293832
- version:
38303833
- v13.2.0
38313834
- v12.16.0
@@ -3837,7 +3840,10 @@ changes:
38373840
* `algorithm` {string | null | undefined}
38383841
* `data` {ArrayBuffer|Buffer|TypedArray|DataView}
38393842
* `key` {Object|string|ArrayBuffer|Buffer|TypedArray|DataView|KeyObject|CryptoKey}
3840-
* Returns: {Buffer}
3843+
* `callback` {Function}
3844+
* `err` {Error}
3845+
* `signature` {Buffer}
3846+
* Returns: {Buffer} if the `callback` function is not provided.
38413847
<!--lint enable maximum-line-length remark-lint-->
38423848

38433849
Calculates and returns the signature for `data` using the given private key and
@@ -3864,6 +3870,8 @@ additional properties can be passed:
38643870
size, `crypto.constants.RSA_PSS_SALTLEN_MAX_SIGN` (default) sets it to the
38653871
maximum permissible value.
38663872

3873+
If the `callback` function is provided this function uses libuv's threadpool.
3874+
38673875
### `crypto.timingSafeEqual(a, b)`
38683876
<!-- YAML
38693877
added: v6.6.0
@@ -3894,10 +3902,13 @@ Use of `crypto.timingSafeEqual` does not guarantee that the *surrounding* code
38943902
is timing-safe. Care should be taken to ensure that the surrounding code does
38953903
not introduce timing vulnerabilities.
38963904

3897-
### `crypto.verify(algorithm, data, key, signature)`
3905+
### `crypto.verify(algorithm, data, key, signature[, callback])`
38983906
<!-- YAML
38993907
added: v12.0.0
39003908
changes:
3909+
- version: REPLACEME
3910+
pr-url: https://github.com/nodejs/node/pull/37500
3911+
description: Optional callback argument added.
39013912
- version: v15.0.0
39023913
pr-url: https://github.com/nodejs/node/pull/35093
39033914
description: The data, key, and signature arguments can also be ArrayBuffer.
@@ -3913,7 +3924,12 @@ changes:
39133924
* `data` {ArrayBuffer| Buffer|TypedArray|DataView}
39143925
* `key` {Object|string|ArrayBuffer|Buffer|TypedArray|DataView|KeyObject|CryptoKey}
39153926
* `signature` {ArrayBuffer|Buffer|TypedArray|DataView}
3916-
* Returns: {boolean}
3927+
* `callback` {Function}
3928+
* `err` {Error}
3929+
* `result` {boolean}
3930+
* Returns: {boolean} `true` or `false` depending on the validity of the
3931+
signature for the data and public key if the `callback` function is not
3932+
provided.
39173933
<!--lint enable maximum-line-length remark-lint-->
39183934

39193935
Verifies the given signature for `data` using the given key and algorithm. If
@@ -3945,6 +3961,8 @@ The `signature` argument is the previously calculated signature for the `data`.
39453961
Because public keys can be derived from private keys, a private key or a public
39463962
key may be passed for `key`.
39473963

3964+
If the `callback` function is provided this function uses libuv's threadpool.
3965+
39483966
### `crypto.webcrypto`
39493967
<!-- YAML
39503968
added: v15.0.0

lib/internal/crypto/dsa.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ const {
1010
KeyObjectHandle,
1111
SignJob,
1212
kCryptoJobAsync,
13+
kSigEncDER,
1314
kKeyTypePrivate,
1415
kSignJobModeSign,
1516
kSignJobModeVerify,
@@ -254,7 +255,8 @@ function dsaSignVerify(key, data, algorithm, signature) {
254255
normalizeHashName(key.algorithm.hash.name),
255256
undefined, // Salt-length is not used in DSA
256257
undefined, // Padding is not used in DSA
257-
signature));
258+
signature,
259+
kSigEncDER));
258260
}
259261

260262
module.exports = {

lib/internal/crypto/ec.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ const {
1717
kKeyTypePublic,
1818
kSignJobModeSign,
1919
kSignJobModeVerify,
20+
kSigEncP1363,
2021
} = internalBinding('crypto');
2122

2223
const {
@@ -470,7 +471,8 @@ function ecdsaSignVerify(key, data, { name, hash }, signature) {
470471
hashname,
471472
undefined, // Salt length, not used with ECDSA
472473
undefined, // PSS Padding, not used with ECDSA
473-
signature));
474+
signature,
475+
kSigEncP1363));
474476
}
475477

476478
module.exports = {

lib/internal/crypto/rsa.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ const {
3030
} = require('internal/errors');
3131

3232
const {
33+
validateInt32,
3334
validateUint32,
3435
} = require('internal/validators');
3536

@@ -342,7 +343,7 @@ function rsaSignVerify(key, data, { saltLength }, signature) {
342343
// TODO(@jasnell): Validate maximum size of saltLength
343344
// based on the key size:
344345
// Math.ceil((keySizeInBits - 1)/8) - digestSizeInBytes - 2
345-
validateUint32(saltLength, 'algorithm.saltLength');
346+
validateInt32(saltLength, 'algorithm.saltLength', -2);
346347
}
347348

348349
const mode = signature === undefined ? kSignJobModeSign : kSignJobModeVerify;

lib/internal/crypto/sig.js

+96-21
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
'use strict';
22

33
const {
4+
FunctionPrototypeCall,
45
ObjectSetPrototypeOf,
56
ReflectApply,
67
} = primordials;
@@ -14,17 +15,22 @@ const {
1415
} = require('internal/errors');
1516

1617
const {
18+
validateCallback,
1719
validateEncoding,
1820
validateString,
1921
} = require('internal/validators');
2022

2123
const {
2224
Sign: _Sign,
25+
SignJob,
2326
Verify: _Verify,
2427
signOneShot: _signOneShot,
2528
verifyOneShot: _verifyOneShot,
29+
kCryptoJobAsync,
2630
kSigEncDER,
2731
kSigEncP1363,
32+
kSignJobModeSign,
33+
kSignJobModeVerify,
2834
} = internalBinding('crypto');
2935

3036
const {
@@ -34,12 +40,18 @@ const {
3440
} = require('internal/crypto/util');
3541

3642
const {
37-
preparePublicOrPrivateKey,
43+
createPrivateKey,
44+
createPublicKey,
45+
isCryptoKey,
46+
isKeyObject,
3847
preparePrivateKey,
48+
preparePublicOrPrivateKey,
3949
} = require('internal/crypto/keys');
4050

4151
const { Writable } = require('stream');
4252

53+
const { Buffer } = require('buffer');
54+
4355
const {
4456
isArrayBufferView,
4557
} = require('internal/util/types');
@@ -131,31 +143,62 @@ Sign.prototype.sign = function sign(options, encoding) {
131143
return ret;
132144
};
133145

134-
function signOneShot(algorithm, data, key) {
146+
function signOneShot(algorithm, data, key, callback) {
135147
if (algorithm != null)
136148
validateString(algorithm, 'algorithm');
137149

150+
if (callback !== undefined)
151+
validateCallback(callback);
152+
138153
data = getArrayBufferOrView(data, 'data');
139154

140155
if (!key)
141156
throw new ERR_CRYPTO_SIGN_KEY_REQUIRED();
142157

143-
const {
144-
data: keyData,
145-
format: keyFormat,
146-
type: keyType,
147-
passphrase: keyPassphrase
148-
} = preparePrivateKey(key);
149-
150158
// Options specific to RSA
151159
const rsaPadding = getPadding(key);
152160
const pssSaltLength = getSaltLength(key);
153161

154162
// Options specific to (EC)DSA
155163
const dsaSigEnc = getDSASignatureEncoding(key);
156164

157-
return _signOneShot(keyData, keyFormat, keyType, keyPassphrase, data,
158-
algorithm, rsaPadding, pssSaltLength, dsaSigEnc);
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 (key != null && (isKeyObject(key.key) || isCryptoKey(key.key))) {
181+
({ data: keyData } = preparePrivateKey(key.key));
182+
} else {
183+
keyData = createPrivateKey(key)[kHandle];
184+
}
185+
186+
const job = new SignJob(
187+
kCryptoJobAsync,
188+
kSignJobModeSign,
189+
keyData,
190+
data,
191+
algorithm,
192+
pssSaltLength,
193+
rsaPadding,
194+
undefined,
195+
dsaSigEnc);
196+
197+
job.ondone = (error, signature) => {
198+
if (error) return FunctionPrototypeCall(callback, job, error);
199+
FunctionPrototypeCall(callback, job, null, Buffer.from(signature));
200+
};
201+
job.run();
159202
}
160203

161204
function Verify(algorithm, options) {
@@ -197,10 +240,13 @@ Verify.prototype.verify = function verify(options, signature, sigEncoding) {
197240
rsaPadding, pssSaltLength, dsaSigEnc);
198241
};
199242

200-
function verifyOneShot(algorithm, data, key, signature) {
243+
function verifyOneShot(algorithm, data, key, signature, callback) {
201244
if (algorithm != null)
202245
validateString(algorithm, 'algorithm');
203246

247+
if (callback !== undefined)
248+
validateCallback(callback);
249+
204250
data = getArrayBufferOrView(data, 'data');
205251

206252
if (!isArrayBufferView(data)) {
@@ -211,13 +257,6 @@ function verifyOneShot(algorithm, data, key, signature) {
211257
);
212258
}
213259

214-
const {
215-
data: keyData,
216-
format: keyFormat,
217-
type: keyType,
218-
passphrase: keyPassphrase
219-
} = preparePublicOrPrivateKey(key);
220-
221260
// Options specific to RSA
222261
const rsaPadding = getPadding(key);
223262
const pssSaltLength = getSaltLength(key);
@@ -233,8 +272,44 @@ function verifyOneShot(algorithm, data, key, signature) {
233272
);
234273
}
235274

236-
return _verifyOneShot(keyData, keyFormat, keyType, keyPassphrase, signature,
237-
data, algorithm, rsaPadding, pssSaltLength, dsaSigEnc);
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+
}
296+
297+
const job = new SignJob(
298+
kCryptoJobAsync,
299+
kSignJobModeVerify,
300+
keyData,
301+
data,
302+
algorithm,
303+
pssSaltLength,
304+
rsaPadding,
305+
signature,
306+
dsaSigEnc);
307+
308+
job.ondone = (error, result) => {
309+
if (error) return FunctionPrototypeCall(callback, job, error);
310+
FunctionPrototypeCall(callback, job, null, result);
311+
};
312+
job.run();
238313
}
239314

240315
module.exports = {

0 commit comments

Comments
 (0)