Skip to content

Commit

Permalink
fixup! crypto: add generatePrime/checkPrime
Browse files Browse the repository at this point in the history
  • Loading branch information
jasnell committed Jan 25, 2021
1 parent 6ce845a commit cc70379
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 65 deletions.
40 changes: 24 additions & 16 deletions doc/api/crypto.md
Original file line number Diff line number Diff line change
Expand Up @@ -1972,14 +1972,14 @@ added: REPLACEME
* `options` {Object}
* `checks` {number} The number of Miller-Rabin probabilistic primality
iterations to perform. When the value is `0` (zero), a number of checks
is used that yields a false positive rate of at most 2^-64 for random
input. Care must be used when selecting a number of checks. Refer to the
OpenSSL documentation for the [`BN_is_prime_ex`][] function `nchecks`
is used that yields a false positive rate of at most 2<sup>-64</sup> for
random input. Care must be used when selecting a number of checks. Refer
to the OpenSSL documentation for the [`BN_is_prime_ex`][] function `nchecks`
options for more details. **Defaults**: `0`
* `callback` {Function}
* `err` {Error} Set to an {Error} object if an error occured during check.
* `result` {boolean} `true` if the candidate is a prime with an error
probability less than `0.25^options.checks`.
probability less than `0.25 ** options.checks`.

Checks the primality of the `candidate`.

Expand All @@ -1994,12 +1994,12 @@ added: REPLACEME
* `options` {Object}
* `checks` {number} The number of Miller-Rabin probabilistic primality
iterations to perform. When the value is `0` (zero), a number of checks
is used that yields a false positive rate of at most 2^-64 for random
input. Care must be used when selecting a number of checks. Refer to the
OpenSSL documentation for the [`BN_is_prime_ex`][] function `nchecks`
is used that yields a false positive rate of at most 2<sup>-64</sup> for
random input. Care must be used when selecting a number of checks. Refer
to the OpenSSL documentation for the [`BN_is_prime_ex`][] function `nchecks`
options for more details. **Defaults**: `0`
* Returns: {boolean} `true` if the candidate is a prime with an error
probability less than `0.25^options.checks`.
probability less than `0.25 ** options.checks`.

Checks the primality of the `candidate`.

Expand Down Expand Up @@ -2743,9 +2743,9 @@ added: REPLACEME

* `size` {number} The size (in bits) of the prime to generate.
* `options` {Object}
* `add` {ArrayBuffer|SharedArrayBuffer|TypedArray|Buffer|DataView}
* `rem` {ArrayBuffer|SharedArrayBuffer|TypedArray|Buffer|DataView}
* `safe` {boolean}
* `add` {ArrayBuffer|SharedArrayBuffer|TypedArray|Buffer|DataView|bigint}
* `rem` {ArrayBuffer|SharedArrayBuffer|TypedArray|Buffer|DataView|bigint}
* `safe` {boolean} **Defaults**: `false`.
* `bigint` {boolean} When `true`, the generated prime is returned
as a `bigint`.
* `callback` {Function}
Expand All @@ -2754,7 +2754,7 @@ added: REPLACEME

Generates a pseudo-random prime of `size` bits.

If `options.safe` is true, the prime will be a safe prime -- that is,
If `options.safe` is `true`, the prime will be a safe prime -- that is,
`(prime - 1) / 2` will also be a prime.

If `options.add` and `options.rem` are set, the prime will satisfy the
Expand All @@ -2765,6 +2765,10 @@ will satisfy the condition `prime % add = 3`. Otherwise if `options.safe`
is `false` and `options.rem` is `undefined`, `options.add` will be
ignored.

Both `options.add` and `options.rem` must be encoded as big-endian sequences
if given as an `ArrayBuffer`, `SharedArrayBuffer`, `TypedArray`, `Buffer`, or
`DataView`.

By default, the prime is encoded as a big-endian sequence of octets
in an {ArrayBuffer}. If the `bigint` option is `true`, then a {bigint}
is provided.
Expand All @@ -2776,16 +2780,16 @@ added: REPLACEME

* `size` {number} The size (in bits) of the prime to generate.
* `options` {Object}
* `add` {ArrayBuffer|SharedArrayBuffer|TypedArray|Buffer|DataView}
* `rem` {ArrayBuffer|SharedArrayBuffer|TypedArray|Buffer|DataView}
* `safe` {boolean}
* `add` {ArrayBuffer|SharedArrayBuffer|TypedArray|Buffer|DataView|bigint}
* `rem` {ArrayBuffer|SharedArrayBuffer|TypedArray|Buffer|DataView|bigint}
* `safe` {boolean} **Defaults**: `false`.
* `bigint` {boolean} When `true`, the generated prime is returned
as a `bigint`.
* Returns: {ArrayBuffer|bigint}

Generates a pseudo-random prime of `size` bits.

If `options.safe` is true, the prime will be a safe prime -- that is,
If `options.safe` is `true`, the prime will be a safe prime -- that is,
`(prime - 1)` / 2 will also be a prime.

If `options.add` and `options.rem` are set, the prime will satisfy the
Expand All @@ -2796,6 +2800,10 @@ will satisfy the condition `prime % add = 3`. Otherwise if `options.safe`
is `false` and `options.rem` is `undefined`, `options.add` will be
ignored.

Both `options.add` and `options.rem` must be encoded as big-endian sequences
if given as an `ArrayBuffer`, `SharedArrayBuffer`, `TypedArray`, `Buffer`, or
`DataView`.

By default, the prime is encoded as a big-endian sequence of octets
in an {ArrayBuffer}. If the `bigint` option is `true`, then a {bigint}
is provided.
Expand Down
105 changes: 63 additions & 42 deletions lib/internal/crypto/random.js
Original file line number Diff line number Diff line change
Expand Up @@ -401,35 +401,46 @@ function generatePrime(size, options, callback) {
validateObject(options, 'options');
const {
safe = false,
bigint = false,
} = options;
let {
add,
rem,
bigint = false,
} = options;

validateBoolean(safe, 'options.safe');
validateBoolean(bigint, 'options.bigint');

if (add !== undefined && !isAnyArrayBuffer(add) && !isArrayBufferView(add)) {
throw new ERR_INVALID_ARG_TYPE(
'options.add',
[
'ArrayBuffer',
'TypedArray',
'Buffer',
'DataView'
],
add);
if (add !== undefined) {
if (typeof add === 'bigint') {
add = Buffer.from(toHexPadded(add), 'hex');
} else if (!isAnyArrayBuffer(add) && !isArrayBufferView(add)) {
throw new ERR_INVALID_ARG_TYPE(
'options.add',
[
'ArrayBuffer',
'TypedArray',
'Buffer',
'DataView'
],
add);
}
}

if (rem !== undefined && !isAnyArrayBuffer(rem) && !isArrayBufferView(rem)) {
throw new ERR_INVALID_ARG_TYPE(
'options.rem',
[
'ArrayBuffer',
'TypedArray',
'Buffer',
'DataView'
],
rem);
if (rem !== undefined) {
if (typeof rem === 'bigint') {
rem = Buffer.from(toHexPadded(rem), 'hex');
} else if (!isAnyArrayBuffer(rem) && !isArrayBufferView(rem)) {
throw new ERR_INVALID_ARG_TYPE(
'options.rem',
[
'ArrayBuffer',
'TypedArray',
'Buffer',
'DataView'
],
rem);
}
}

const job = new RandomPrimeJob(kCryptoJobAsync, size, safe, add, rem);
Expand All @@ -453,35 +464,45 @@ function generatePrimeSync(size, options = {}) {
validateObject(options, 'options');
const {
safe = false,
bigint = false,
} = options;
let {
add,
rem,
bigint = false,
} = options;
validateBoolean(safe, 'options.safe');
validateBoolean(bigint, 'options.bigint');

if (add !== undefined && !isAnyArrayBuffer(add) && !isArrayBufferView(add)) {
throw new ERR_INVALID_ARG_TYPE(
'options.add',
[
'ArrayBuffer',
'TypedArray',
'Buffer',
'DataView'
],
add);
if (add !== undefined) {
if (typeof add === 'bigint') {
add = Buffer.from(toHexPadded(add), 'hex');
} else if (!isAnyArrayBuffer(add) && !isArrayBufferView(add)) {
throw new ERR_INVALID_ARG_TYPE(
'options.add',
[
'ArrayBuffer',
'TypedArray',
'Buffer',
'DataView'
],
add);
}
}

if (rem !== undefined && !isAnyArrayBuffer(rem) && !isArrayBufferView(rem)) {
throw new ERR_INVALID_ARG_TYPE(
'options.rem',
[
'ArrayBuffer',
'TypedArray',
'Buffer',
'DataView'
],
rem);
if (rem !== undefined) {
if (typeof rem === 'bigint') {
rem = Buffer.from(toHexPadded(rem), 'hex');
} else if (!isAnyArrayBuffer(rem) && !isArrayBufferView(rem)) {
throw new ERR_INVALID_ARG_TYPE(
'options.rem',
[
'ArrayBuffer',
'TypedArray',
'Buffer',
'DataView'
],
rem);
}
}

const job = new RandomPrimeJob(kCryptoJobSync, size, safe, add, rem);
Expand Down
6 changes: 3 additions & 3 deletions src/crypto/crypto_random.cc
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ Maybe<bool> RandomPrimeTraits::AdditionalConfig(
bool safe = args[offset + 1]->IsTrue();

if (!args[offset + 2]->IsUndefined()) {
params->add.reset(BN_new());
params->add.reset(BN_secure_new());
if (!params->add) {
THROW_ERR_CRYPTO_OPERATION_FAILED(env, "could not generate prime");
return Nothing<bool>();
Expand All @@ -118,7 +118,7 @@ Maybe<bool> RandomPrimeTraits::AdditionalConfig(
}

if (!args[offset + 3]->IsUndefined()) {
params->rem.reset(BN_new());
params->rem.reset(BN_secure_new());
if (!params->rem) {
THROW_ERR_CRYPTO_OPERATION_FAILED(env, "could not generate prime");
return Nothing<bool>();
Expand All @@ -139,7 +139,7 @@ Maybe<bool> RandomPrimeTraits::AdditionalConfig(

params->bits = bits;
params->safe = safe;
params->prime.reset(BN_new());
params->prime.reset(BN_secure_new());
if (!params->prime) {
THROW_ERR_CRYPTO_OPERATION_FAILED(env, "could not generate prime");
return Nothing<bool>();
Expand Down
14 changes: 10 additions & 4 deletions test/parallel/test-crypto-prime.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,8 @@ generatePrime(32, { safe: true }, common.mustSucceed((prime) => {

const add = 12;
const rem = 11;
const add_buf = Buffer.alloc(4);
const rem_buf = Buffer.alloc(4);
add_buf.writeUInt32BE(add);
rem_buf.writeUInt32BE(rem);
const add_buf = Buffer.from([add]);
const rem_buf = Buffer.from([rem]);
generatePrime(
32,
{ add: add_buf, rem: rem_buf },
Expand All @@ -129,6 +127,14 @@ generatePrime(
assert.strictEqual(val % add, rem);
}

{
const prime = generatePrimeSync(32, { add: BigInt(add), rem: BigInt(rem) });
assert(checkPrimeSync(prime));
const buf = Buffer.from(prime);
const val = buf.readUInt32BE();
assert.strictEqual(val % add, rem);
}

[1, 'hello', {}, []].forEach((i) => {
assert.throws(() => checkPrime(i), {
code: 'ERR_INVALID_ARG_TYPE'
Expand Down

0 comments on commit cc70379

Please sign in to comment.