Skip to content

Commit 9dac99a

Browse files
tniessendanielleadams
authored andcommitted
crypto: fix and simplify prime option validation
PR-URL: #37164 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Rich Trott <[email protected]>
1 parent dc38dd2 commit 9dac99a

File tree

2 files changed

+55
-66
lines changed

2 files changed

+55
-66
lines changed

lib/internal/crypto/random.js

+33-66
Original file line numberDiff line numberDiff line change
@@ -391,14 +391,9 @@ function randomUUID(options) {
391391
return uuid.latin1Slice(0, 36);
392392
}
393393

394-
function generatePrime(size, options, callback) {
395-
validateUint32(size, 'size', true);
396-
if (typeof options === 'function') {
397-
callback = options;
398-
options = {};
399-
}
400-
validateCallback(callback);
394+
function createRandomPrimeJob(type, size, options) {
401395
validateObject(options, 'options');
396+
402397
const {
403398
safe = false,
404399
bigint = false,
@@ -413,7 +408,7 @@ function generatePrime(size, options, callback) {
413408

414409
if (add !== undefined) {
415410
if (typeof add === 'bigint') {
416-
add = Buffer.from(toHexPadded(add), 'hex');
411+
add = unsignedBigIntToBuffer(add, 'options.add');
417412
} else if (!isAnyArrayBuffer(add) && !isArrayBufferView(add)) {
418413
throw new ERR_INVALID_ARG_TYPE(
419414
'options.add',
@@ -430,7 +425,7 @@ function generatePrime(size, options, callback) {
430425

431426
if (rem !== undefined) {
432427
if (typeof rem === 'bigint') {
433-
rem = Buffer.from(toHexPadded(rem), 'hex');
428+
rem = unsignedBigIntToBuffer(rem, 'options.rem');
434429
} else if (!isAnyArrayBuffer(rem) && !isArrayBufferView(rem)) {
435430
throw new ERR_INVALID_ARG_TYPE(
436431
'options.rem',
@@ -445,7 +440,20 @@ function generatePrime(size, options, callback) {
445440
}
446441
}
447442

448-
const job = new RandomPrimeJob(kCryptoJobAsync, size, safe, add, rem);
443+
const job = new RandomPrimeJob(type, size, safe, add, rem);
444+
job.result = bigint ? arrayBufferToUnsignedBigInt : (p) => p;
445+
return job;
446+
}
447+
448+
function generatePrime(size, options, callback) {
449+
validateUint32(size, 'size', true);
450+
if (typeof options === 'function') {
451+
callback = options;
452+
options = {};
453+
}
454+
validateCallback(callback);
455+
456+
const job = createRandomPrimeJob(kCryptoJobAsync, size, options);
449457
job.ondone = (err, prime) => {
450458
if (err) {
451459
callback(err);
@@ -454,79 +462,38 @@ function generatePrime(size, options, callback) {
454462

455463
callback(
456464
undefined,
457-
bigint ?
458-
BigInt(`0x${Buffer.from(prime).toString('hex')}`) :
459-
prime);
465+
job.result(prime));
460466
};
461467
job.run();
462468
}
463469

464470
function generatePrimeSync(size, options = {}) {
465471
validateUint32(size, 'size', true);
466-
validateObject(options, 'options');
467-
const {
468-
safe = false,
469-
bigint = false,
470-
} = options;
471-
let {
472-
add,
473-
rem,
474-
} = options;
475-
validateBoolean(safe, 'options.safe');
476-
validateBoolean(bigint, 'options.bigint');
477-
478-
if (add !== undefined) {
479-
if (typeof add === 'bigint') {
480-
add = Buffer.from(toHexPadded(add), 'hex');
481-
} else if (!isAnyArrayBuffer(add) && !isArrayBufferView(add)) {
482-
throw new ERR_INVALID_ARG_TYPE(
483-
'options.add',
484-
[
485-
'ArrayBuffer',
486-
'TypedArray',
487-
'Buffer',
488-
'DataView',
489-
'bigint',
490-
],
491-
add);
492-
}
493-
}
494-
495-
if (rem !== undefined) {
496-
if (typeof rem === 'bigint') {
497-
rem = Buffer.from(toHexPadded(rem), 'hex');
498-
} else if (!isAnyArrayBuffer(rem) && !isArrayBufferView(rem)) {
499-
throw new ERR_INVALID_ARG_TYPE(
500-
'options.rem',
501-
[
502-
'ArrayBuffer',
503-
'TypedArray',
504-
'Buffer',
505-
'DataView',
506-
'bigint',
507-
],
508-
rem);
509-
}
510-
}
511472

512-
const job = new RandomPrimeJob(kCryptoJobSync, size, safe, add, rem);
473+
const job = createRandomPrimeJob(kCryptoJobSync, size, options);
513474
const [err, prime] = job.run();
514475
if (err)
515476
throw err;
477+
return job.result(prime);
478+
}
516479

517-
return bigint ?
518-
BigInt(`0x${Buffer.from(prime).toString('hex')}`) :
519-
prime;
480+
function arrayBufferToUnsignedBigInt(arrayBuffer) {
481+
return BigInt(`0x${Buffer.from(arrayBuffer).toString('hex')}`);
520482
}
521483

522-
function toHexPadded(bigint) {
484+
function unsignedBigIntToBuffer(bigint, name) {
485+
if (bigint < 0) {
486+
throw new ERR_OUT_OF_RANGE(name, '>= 0', bigint);
487+
}
488+
523489
const hex = bigint.toString(16);
524-
return hex.padStart(hex.length + (hex.length % 2), 0);
490+
const padded = hex.padStart(hex.length + (hex.length % 2), 0);
491+
return Buffer.from(padded, 'hex');
525492
}
526493

527494
function checkPrime(candidate, options = {}, callback) {
528495
if (typeof candidate === 'bigint')
529-
candidate = Buffer.from(toHexPadded(candidate), 'hex');
496+
candidate = unsignedBigIntToBuffer(candidate, 'candidate');
530497
if (!isAnyArrayBuffer(candidate) && !isArrayBufferView(candidate)) {
531498
throw new ERR_INVALID_ARG_TYPE(
532499
'candidate',
@@ -559,7 +526,7 @@ function checkPrime(candidate, options = {}, callback) {
559526

560527
function checkPrimeSync(candidate, options = {}) {
561528
if (typeof candidate === 'bigint')
562-
candidate = Buffer.from(toHexPadded(candidate), 'hex');
529+
candidate = unsignedBigIntToBuffer(candidate, 'candidate');
563530
if (!isAnyArrayBuffer(candidate) && !isArrayBufferView(candidate)) {
564531
throw new ERR_INVALID_ARG_TYPE(
565532
'candidate',

test/parallel/test-crypto-prime.js

+22
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,28 @@ const pCheckPrime = promisify(checkPrime);
7171
});
7272
});
7373

74+
{
75+
// Negative BigInts should not be converted to 0 silently.
76+
77+
assert.throws(() => generatePrime(20, { add: -1n }, common.mustNotCall()), {
78+
code: 'ERR_OUT_OF_RANGE',
79+
message: 'The value of "options.add" is out of range. It must be >= 0. ' +
80+
'Received -1n'
81+
});
82+
83+
assert.throws(() => generatePrime(20, { rem: -1n }, common.mustNotCall()), {
84+
code: 'ERR_OUT_OF_RANGE',
85+
message: 'The value of "options.rem" is out of range. It must be >= 0. ' +
86+
'Received -1n'
87+
});
88+
89+
assert.throws(() => checkPrime(-1n, common.mustNotCall()), {
90+
code: 'ERR_OUT_OF_RANGE',
91+
message: 'The value of "candidate" is out of range. It must be >= 0. ' +
92+
'Received -1n'
93+
});
94+
}
95+
7496
generatePrime(80, common.mustSucceed((prime) => {
7597
assert(checkPrimeSync(prime));
7698
checkPrime(prime, common.mustSucceed((result) => {

0 commit comments

Comments
 (0)