crypto: accept key data in crypto.diffieHellman() and cleanup DH jobs · nodejs/node@b9d5e87 · GitHub
Skip to content

Commit b9d5e87

Browse files
panvasxa
authored andcommitted
crypto: accept key data in crypto.diffieHellman() and cleanup DH jobs
Signed-off-by: Filip Skokan <panva.ip@gmail.com> PR-URL: #62527 Backport-PR-URL: #63563 Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent b46d52b commit b9d5e87

11 files changed

Lines changed: 474 additions & 204 deletions

File tree

doc/api/crypto.md

Lines changed: 12 additions & 3 deletions

lib/internal/crypto/diffiehellman.js

Lines changed: 61 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ const {
1717
DiffieHellman: _DiffieHellman,
1818
DiffieHellmanGroup: _DiffieHellmanGroup,
1919
ECDH: _ECDH,
20-
ECDHBitsJob,
2120
ECDHConvertKey: _ECDHConvertKey,
2221
kCryptoJobAsync,
2322
kCryptoJobSync,
@@ -51,9 +50,11 @@ const {
5150
} = require('internal/util');
5251

5352
const {
54-
KeyObject,
53+
isKeyObject,
5554
kAlgorithm,
5655
kKeyType,
56+
preparePrivateKey,
57+
preparePublicOrPrivateKey,
5758
} = require('internal/crypto/keys');
5859

5960
const {
@@ -281,31 +282,65 @@ function diffieHellman(options, callback) {
281282
validateFunction(callback, 'callback');
282283

283284
const { privateKey, publicKey } = options;
284-
if (!(privateKey instanceof KeyObject))
285+
286+
// TODO(@panva): remove these non-semver-major error code preserving measures
287+
// in a semver-major followup, the final state is just preparePublicOrPrivateKey
288+
// and preparePrivateKey
289+
if (privateKey == null)
285290
throw new ERR_INVALID_ARG_VALUE('options.privateKey', privateKey);
286291

287-
if (!(publicKey instanceof KeyObject))
292+
if (publicKey == null)
288293
throw new ERR_INVALID_ARG_VALUE('options.publicKey', publicKey);
289294

290-
if (privateKey.type !== 'private')
291-
throw new ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE(privateKey.type, 'private');
295+
if (isKeyObject(privateKey)) {
296+
if (privateKey.type !== 'private')
297+
throw new ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE(privateKey.type, 'private');
298+
}
292299

293-
if (publicKey.type !== 'public' && publicKey.type !== 'private') {
294-
throw new ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE(publicKey.type,
295-
'private or public');
300+
if (isKeyObject(publicKey)) {
301+
if (publicKey.type !== 'public' && publicKey.type !== 'private') {
302+
throw new ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE(publicKey.type,
303+
'private or public');
304+
}
296305
}
297306

298-
const privateType = privateKey.asymmetricKeyType;
299-
const publicType = publicKey.asymmetricKeyType;
300-
if (privateType !== publicType || !dhEnabledKeyTypes.has(privateType)) {
301-
throw new ERR_CRYPTO_INCOMPATIBLE_KEY('key types for Diffie-Hellman',
302-
`${privateType} and ${publicType}`);
307+
if (isKeyObject(privateKey) && isKeyObject(publicKey)) {
308+
const privateType = privateKey.asymmetricKeyType;
309+
const publicType = publicKey.asymmetricKeyType;
310+
if (privateType !== publicType || !dhEnabledKeyTypes.has(privateType)) {
311+
throw new ERR_CRYPTO_INCOMPATIBLE_KEY('key types for Diffie-Hellman',
312+
`${privateType} and ${publicType}`);
313+
}
303314
}
304315

316+
const {
317+
data: pubData,
318+
format: pubFormat,
319+
type: pubType,
320+
passphrase: pubPassphrase,
321+
namedCurve: pubNamedCurve,
322+
} = preparePublicOrPrivateKey(publicKey, 'options.publicKey');
323+
324+
const {
325+
data: privData,
326+
format: privFormat,
327+
type: privType,
328+
passphrase: privPassphrase,
329+
namedCurve: privNamedCurve,
330+
} = preparePrivateKey(privateKey, 'options.privateKey');
331+
305332
const job = new DHBitsJob(
306333
callback ? kCryptoJobAsync : kCryptoJobSync,
307-
publicKey[kHandle],
308-
privateKey[kHandle]);
334+
pubData,
335+
pubFormat,
336+
pubType,
337+
pubPassphrase,
338+
pubNamedCurve,
339+
privData,
340+
privFormat,
341+
privType,
342+
privPassphrase,
343+
privNamedCurve);
309344

310345
if (!callback) {
311346
const { 0: err, 1: secret } = job.run();
@@ -346,10 +381,18 @@ async function ecdhDeriveBits(algorithm, baseKey, length) {
346381
throw lazyDOMException('Named curve mismatch', 'InvalidAccessError');
347382
}
348383

349-
const bits = await jobPromise(() => new ECDHBitsJob(
384+
const bits = await jobPromise(() => new DHBitsJob(
350385
kCryptoJobAsync,
351386
key[kKeyObject][kHandle],
352-
baseKey[kKeyObject][kHandle]));
387+
undefined,
388+
undefined,
389+
undefined,
390+
undefined,
391+
baseKey[kKeyObject][kHandle],
392+
undefined,
393+
undefined,
394+
undefined,
395+
undefined));
353396

354397
// If a length is not specified, return the full derived secret
355398
if (length === null)

lib/internal/crypto/keys.js

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -467,9 +467,9 @@ function parseKeyType(typeStr, required, keyType, isPublic, optionName) {
467467
throw new ERR_INVALID_ARG_VALUE(optionName, typeStr);
468468
}
469469

470-
function option(name, objName) {
471-
return objName === undefined ?
472-
`options.${name}` : `options.${objName}.${name}`;
470+
function option(name, prefix) {
471+
return prefix === undefined ?
472+
`options.${name}` : `${prefix}.${name}`;
473473
}
474474

475475
function parseKeyFormatAndType(enc, keyType, isPublic, objName) {
@@ -629,7 +629,7 @@ function getKeyTypes(allowKeyObject, bufferOnly = false) {
629629
}
630630

631631

632-
function prepareAsymmetricKey(key, ctx) {
632+
function prepareAsymmetricKey(key, ctx, name = 'key') {
633633
if (isKeyObject(key)) {
634634
// Best case: A key object, as simple as that.
635635
return { data: getKeyObjectHandle(key, ctx) };
@@ -640,7 +640,7 @@ function prepareAsymmetricKey(key, ctx) {
640640
}
641641
if (isStringOrBuffer(key)) {
642642
// Expect PEM by default, mostly for backward compatibility.
643-
return { format: kKeyFormatPEM, data: getArrayBufferOrView(key, 'key') };
643+
return { format: kKeyFormatPEM, data: getArrayBufferOrView(key, name) };
644644
}
645645
if (typeof key === 'object') {
646646
const { key: data, encoding, format } = key;
@@ -655,7 +655,7 @@ function prepareAsymmetricKey(key, ctx) {
655655
return { data: getKeyObjectHandle(data[kKeyObject], ctx) };
656656
}
657657
if (format === 'jwk') {
658-
validateObject(data, 'key.key');
658+
validateObject(data, `${name}.key`);
659659
return { data, format: kKeyFormatJWK };
660660
} else if (format === 'raw-public' || format === 'raw-private' ||
661661
format === 'raw-seed') {
@@ -681,31 +681,31 @@ function prepareAsymmetricKey(key, ctx) {
681681
// Either PEM or DER using PKCS#1 or SPKI.
682682
if (!isStringOrBuffer(data)) {
683683
throw new ERR_INVALID_ARG_TYPE(
684-
'key.key',
684+
`${name}.key`,
685685
getKeyTypes(ctx !== kCreatePrivate),
686686
data);
687687
}
688688

689689
const isPublic =
690690
(ctx === kConsumePrivate || ctx === kCreatePrivate) ? false : undefined;
691691
return {
692-
data: getArrayBufferOrView(data, 'key', encoding),
693-
...parseKeyEncoding(key, undefined, isPublic),
692+
data: getArrayBufferOrView(data, `${name}.key`, encoding),
693+
...parseKeyEncoding(key, undefined, isPublic, name),
694694
};
695695
}
696696

697697
throw new ERR_INVALID_ARG_TYPE(
698-
'key',
698+
name,
699699
getKeyTypes(ctx !== kCreatePrivate),
700700
key);
701701
}
702702

703-
function preparePrivateKey(key) {
704-
return prepareAsymmetricKey(key, kConsumePrivate);
703+
function preparePrivateKey(key, name) {
704+
return prepareAsymmetricKey(key, kConsumePrivate, name);
705705
}
706706

707-
function preparePublicOrPrivateKey(key) {
708-
return prepareAsymmetricKey(key, kConsumePublic);
707+
function preparePublicOrPrivateKey(key, name) {
708+
return prepareAsymmetricKey(key, kConsumePublic, name);
709709
}
710710

711711
function prepareSecretKey(key, encoding, bufferOnly = false) {

lib/internal/crypto/sig.js

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -130,19 +130,19 @@ function getIntOption(name, options) {
130130
return undefined;
131131
}
132132

133-
Sign.prototype.sign = function sign(options, encoding) {
134-
if (!options)
133+
Sign.prototype.sign = function sign(privateKey, encoding) {
134+
if (!privateKey)
135135
throw new ERR_CRYPTO_SIGN_KEY_REQUIRED();
136136

137137
const { data, format, type, passphrase, namedCurve } =
138-
preparePrivateKey(options, true);
138+
preparePrivateKey(privateKey, 'privateKey');
139139

140140
// Options specific to RSA
141-
const rsaPadding = getPadding(options);
142-
const pssSaltLength = getSaltLength(options);
141+
const rsaPadding = getPadding(privateKey);
142+
const pssSaltLength = getSaltLength(privateKey);
143143

144144
// Options specific to (EC)DSA
145-
const dsaSigEnc = getDSASignatureEncoding(options);
145+
const dsaSigEnc = getDSASignatureEncoding(privateKey);
146146

147147
const ret = this[kHandle].sign(data, format, type,
148148
passphrase, namedCurve,
@@ -232,21 +232,21 @@ ObjectSetPrototypeOf(Verify, Writable);
232232
Verify.prototype._write = Sign.prototype._write;
233233
Verify.prototype.update = Sign.prototype.update;
234234

235-
Verify.prototype.verify = function verify(options, signature, sigEncoding) {
235+
Verify.prototype.verify = function verify(key, signature, sigEncoding) {
236236
const {
237237
data,
238238
format,
239239
type,
240240
passphrase,
241241
namedCurve,
242-
} = preparePublicOrPrivateKey(options, true);
242+
} = preparePublicOrPrivateKey(key, 'key');
243243

244244
// Options specific to RSA
245-
const rsaPadding = getPadding(options);
246-
const pssSaltLength = getSaltLength(options);
245+
const rsaPadding = getPadding(key);
246+
const pssSaltLength = getSaltLength(key);
247247

248248
// Options specific to (EC)DSA
249-
const dsaSigEnc = getDSASignatureEncoding(options);
249+
const dsaSigEnc = getDSASignatureEncoding(key);
250250

251251
signature = getArrayBufferOrView(signature, 'signature', sigEncoding);
252252

src/crypto/crypto_dh.cc

Lines changed: 8 additions & 12 deletions

0 commit comments

Comments
 (0)