Skip to content

Commit 6b49bc9

Browse files
committed
crypto: add randomPrime/randomPrimeSync/checkPrime
APIs for generating and checking pseudo-random primes Signed-off-by: James M Snell <jasnell@gmail.com>
1 parent a35b32e commit 6b49bc9

File tree

8 files changed

+644
-0
lines changed

8 files changed

+644
-0
lines changed

doc/api/crypto.md

+89
Original file line numberDiff line numberDiff line change
@@ -1961,6 +1961,48 @@ is currently in use. Setting to true requires a FIPS build of Node.js.
19611961
This property is deprecated. Please use `crypto.setFips()` and
19621962
`crypto.getFips()` instead.
19631963

1964+
### `crypto.checkPrime(candidate[, options, [callback]])`
1965+
<!-- YAML
1966+
added: REPLACEME
1967+
-->
1968+
1969+
* `candidate` {ArrayBuffer|SharedArrayBuffer|TypedArray|Buffer|DataView} A
1970+
possible prime encoded as a sequence of big endian octets of arbitrary
1971+
length.
1972+
* `options` {Object}
1973+
* `checks` {number} The number of primality checks to perform. When the
1974+
value is `0` (zero), a number of checks is used that yields a false
1975+
positive rate of at most 2^-64 for random input. Care must be used
1976+
when selecting a number of checks. Refer to the OpenSSL documentation
1977+
for the [`BN_is_prime_ex`][] function `nchecks` options for more details.
1978+
**Defaults**: `0`
1979+
* `callback` {Function}
1980+
* `err` {Error} Set to an {Error} object if an error occured during check.
1981+
* `result` {boolean} `true` if the candidate is a prime with an error
1982+
probability less than `0.25^options.checks`.
1983+
1984+
Checks the primality of the `candidate`.
1985+
1986+
### `crypto.checkPrimeSync(candidate[, options])`
1987+
<!-- YAML
1988+
added: REPLACEME
1989+
-->
1990+
1991+
* `candidate` {ArrayBuffer|SharedArrayBuffer|TypedArray|Buffer|DataView} A
1992+
possible prime encoded as a sequence of big endian octets of arbitrary
1993+
length.
1994+
* `options` {Object}
1995+
* `checks` {number} The number of primality checks to perform. When the
1996+
value is `0` (zero), a number of checks is used that yields a false
1997+
positive rate of at most 2^-64 for random input. Care must be used
1998+
when selecting a number of checks. Refer to the OpenSSL documentation
1999+
for the [`BN_is_prime_ex`][] function `nchecks` options for more details.
2000+
**Defaults**: `0`
2001+
* Returns: {boolean} `true` if the candidate is a prime with an error
2002+
probability less than `0.25^options.checks`.
2003+
2004+
Checks the primality of the `candidate`.
2005+
19642006
### `crypto.createCipher(algorithm, password[, options])`
19652007
<!-- YAML
19662008
added: v0.1.94
@@ -3432,6 +3474,52 @@ const n = crypto.randomInt(1, 7);
34323474
console.log(`The dice rolled: ${n}`);
34333475
```
34343476

3477+
### `crypto.randomPrime(size[, options[, callback]])`
3478+
<!-- YAML
3479+
added: REPLACEME
3480+
-->
3481+
3482+
* `size` {number} The size (in bytes) of the prime to generate.
3483+
* `options` {Object}
3484+
* `add` {ArrayBuffer|SharedArrayBuffer|TypedArray|Buffer|DataView}
3485+
* `rem` {ArrayBuffer|SharedArrayBuffer|TypedArray|Buffer|DataView}
3486+
* `safe` {boolean}
3487+
* `callback` {Function}
3488+
* `err` {Error}
3489+
* `prime` {ArrayBuffer}
3490+
3491+
Generates a pseudo-random prime of `size` bytes.
3492+
3493+
If `options.safe` is true, the prime will be a safe prime -- that is,
3494+
prime - 1 / 2 will also be a prime.
3495+
3496+
If `options.add` and `options.rem` are set, the prime will satisfy the
3497+
condition that prime % add = rem.
3498+
3499+
The prime is encoded as a big-endian sequence of octets in an {ArrayBuffer}.
3500+
3501+
### `crypto.randomPrimeSync(size[, options])`
3502+
<!-- YAML
3503+
added: REPLACEME
3504+
-->
3505+
3506+
* `size` {number} The size (in bytes) of the prime to generate.
3507+
* `options` {Object}
3508+
* `add` {ArrayBuffer|SharedArrayBuffer|TypedArray|Buffer|DataView}
3509+
* `rem` {ArrayBuffer|SharedArrayBuffer|TypedArray|Buffer|DataView}
3510+
* `safe` {boolean}
3511+
* Returns: {ArrayBuffer}
3512+
3513+
Generates a pseudo-random prime of `size` bytes.
3514+
3515+
If `options.safe` is true, the prime will be a safe prime -- that is,
3516+
prime - 1 / 2 will also be a prime.
3517+
3518+
If `options.add` and `options.rem` are set, the prime will satisfy the
3519+
condition that prime % add = rem.
3520+
3521+
The prime is encoded as a big-endian sequence of octets in an {ArrayBuffer}.
3522+
34353523
### `crypto.randomUUID([options])`
34363524
<!-- YAML
34373525
added: v15.6.0
@@ -4234,6 +4322,7 @@ See the [list of SSL OP Flags][] for details.
42344322
[RFC 4122]: https://www.rfc-editor.org/rfc/rfc4122.txt
42354323
[RFC 5208]: https://www.rfc-editor.org/rfc/rfc5208.txt
42364324
[Web Crypto API documentation]: webcrypto.md
4325+
[`BN_is_prime_ex`]: https://www.openssl.org/docs/man1.1.1/man3/BN_is_prime_ex.html
42374326
[`Buffer`]: buffer.md
42384327
[`EVP_BytesToKey`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_BytesToKey.html
42394328
[`KeyObject`]: #crypto_class_keyobject

lib/crypto.js

+8
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,14 @@ const {
5050
timingSafeEqual,
5151
} = internalBinding('crypto');
5252
const {
53+
checkPrime,
54+
checkPrimeSync,
5355
randomBytes,
5456
randomFill,
5557
randomFillSync,
5658
randomInt,
59+
randomPrime,
60+
randomPrimeSync,
5761
randomUUID,
5862
} = require('internal/crypto/random');
5963
const {
@@ -170,6 +174,8 @@ function createVerify(algorithm, options) {
170174

171175
module.exports = {
172176
// Methods
177+
checkPrime,
178+
checkPrimeSync,
173179
createCipheriv,
174180
createDecipheriv,
175181
createDiffieHellman,
@@ -204,6 +210,8 @@ module.exports = {
204210
randomFill,
205211
randomFillSync,
206212
randomInt,
213+
randomPrime,
214+
randomPrimeSync,
207215
randomUUID,
208216
scrypt,
209217
scryptSync,

lib/internal/crypto/random.js

+151
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ const {
1010

1111
const {
1212
RandomBytesJob,
13+
RandomPrimeJob,
14+
CheckPrimeJob,
1315
kCryptoJobAsync,
1416
kCryptoJobSync,
1517
secureBuffer,
@@ -34,6 +36,7 @@ const {
3436
validateBoolean,
3537
validateCallback,
3638
validateObject,
39+
validateUint32,
3740
} = require('internal/validators');
3841

3942
const {
@@ -387,11 +390,159 @@ function randomUUID(options) {
387390
return uuid.latin1Slice(0, 36);
388391
}
389392

393+
function randomPrime(size, options, callback) {
394+
validateUint32(size, 'size', true);
395+
if (typeof options === 'function') {
396+
callback = options;
397+
options = {};
398+
}
399+
validateCallback(callback);
400+
validateObject(options, 'options');
401+
const {
402+
safe = false,
403+
add,
404+
rem,
405+
} = options;
406+
validateBoolean(safe, 'options.safe');
407+
408+
if (add !== undefined && !isAnyArrayBuffer(add) && !isArrayBufferView(add)) {
409+
throw new ERR_INVALID_ARG_TYPE(
410+
'options.add',
411+
[
412+
'ArrayBuffer',
413+
'TypedArray',
414+
'Buffer',
415+
'DataView'
416+
],
417+
add);
418+
}
419+
420+
if (rem !== undefined && !isAnyArrayBuffer(rem) && !isArrayBufferView(rem)) {
421+
throw new ERR_INVALID_ARG_TYPE(
422+
'options.rem',
423+
[
424+
'ArrayBuffer',
425+
'TypedArray',
426+
'Buffer',
427+
'DataView'
428+
],
429+
rem);
430+
}
431+
432+
const job = new RandomPrimeJob(kCryptoJobAsync, size, safe, add, rem);
433+
job.ondone = callback;
434+
job.run();
435+
}
436+
437+
function randomPrimeSync(size, options = {}) {
438+
validateUint32(size, 'size', true);
439+
validateObject(options, 'options');
440+
const {
441+
safe = false,
442+
add,
443+
rem,
444+
} = options;
445+
validateBoolean(safe, 'options.safe');
446+
447+
if (add !== undefined && !isAnyArrayBuffer(add) && !isArrayBufferView(add)) {
448+
throw new ERR_INVALID_ARG_TYPE(
449+
'options.add',
450+
[
451+
'ArrayBuffer',
452+
'TypedArray',
453+
'Buffer',
454+
'DataView'
455+
],
456+
add);
457+
}
458+
459+
if (rem !== undefined && !isAnyArrayBuffer(rem) && !isArrayBufferView(rem)) {
460+
throw new ERR_INVALID_ARG_TYPE(
461+
'options.rem',
462+
[
463+
'ArrayBuffer',
464+
'TypedArray',
465+
'Buffer',
466+
'DataView'
467+
],
468+
rem);
469+
}
470+
471+
const job = new RandomPrimeJob(kCryptoJobSync, size, safe, add, rem);
472+
const [err, prime] = job.run();
473+
if (err)
474+
throw err;
475+
476+
return prime;
477+
}
478+
479+
function checkPrime(candidate, options = {}, callback) {
480+
if (!isAnyArrayBuffer(candidate) && !isArrayBufferView(candidate)) {
481+
throw new ERR_INVALID_ARG_TYPE(
482+
'candidate',
483+
[
484+
'ArrayBuffer',
485+
'TypedArray',
486+
'Buffer',
487+
'DataView'
488+
],
489+
candidate
490+
);
491+
}
492+
if (typeof options === 'function') {
493+
callback = options;
494+
options = {};
495+
}
496+
validateCallback(callback);
497+
validateObject(options, 'options');
498+
const {
499+
checks = 0,
500+
} = options;
501+
502+
validateUint32(checks, 'options.checks');
503+
504+
const job = new CheckPrimeJob(kCryptoJobAsync, candidate, checks);
505+
job.ondone = callback;
506+
job.run();
507+
}
508+
509+
function checkPrimeSync(candidate, options = {}) {
510+
if (!isAnyArrayBuffer(candidate) && !isArrayBufferView(candidate)) {
511+
throw new ERR_INVALID_ARG_TYPE(
512+
'candidate',
513+
[
514+
'ArrayBuffer',
515+
'TypedArray',
516+
'Buffer',
517+
'DataView'
518+
],
519+
candidate
520+
);
521+
}
522+
validateObject(options, 'options');
523+
const {
524+
checks = 0,
525+
} = options;
526+
527+
validateUint32(checks, 'options.checks');
528+
529+
const job = new CheckPrimeJob(kCryptoJobSync, candidate, checks);
530+
const [err, result] = job.run();
531+
if (err)
532+
throw err;
533+
534+
return result;
535+
}
536+
390537
module.exports = {
538+
checkPrime,
539+
checkPrimeSync,
391540
randomBytes,
392541
randomFill,
393542
randomFillSync,
394543
randomInt,
395544
getRandomValues,
396545
randomUUID,
546+
randomPrime,
547+
randomPrimeSync,
397548
};

src/async_wrap.h

+2
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ namespace node {
8484

8585
#if HAVE_OPENSSL
8686
#define NODE_ASYNC_CRYPTO_PROVIDER_TYPES(V) \
87+
V(CHECKPRIMEREQUEST) \
8788
V(PBKDF2REQUEST) \
8889
V(KEYPAIRGENREQUEST) \
8990
V(KEYGENREQUEST) \
@@ -92,6 +93,7 @@ namespace node {
9293
V(DERIVEBITSREQUEST) \
9394
V(HASHREQUEST) \
9495
V(RANDOMBYTESREQUEST) \
96+
V(RANDOMPRIMEREQUEST) \
9597
V(SCRYPTREQUEST) \
9698
V(SIGNREQUEST) \
9799
V(TLSWRAP) \

0 commit comments

Comments
 (0)