From cc70379b2e52f58e53f53ff7815e77f673a74016 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Mon, 25 Jan 2021 15:02:28 -0800 Subject: [PATCH] fixup! crypto: add generatePrime/checkPrime --- doc/api/crypto.md | 40 ++++++----- lib/internal/crypto/random.js | 105 +++++++++++++++++------------ src/crypto/crypto_random.cc | 6 +- test/parallel/test-crypto-prime.js | 14 ++-- 4 files changed, 100 insertions(+), 65 deletions(-) diff --git a/doc/api/crypto.md b/doc/api/crypto.md index 72488a6581bba3..9462cf0ef43ecc 100644 --- a/doc/api/crypto.md +++ b/doc/api/crypto.md @@ -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-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` 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`. @@ -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-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` 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`. @@ -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} @@ -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 @@ -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. @@ -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 @@ -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. diff --git a/lib/internal/crypto/random.js b/lib/internal/crypto/random.js index c68e77c226bfd3..485989067b7337 100644 --- a/lib/internal/crypto/random.js +++ b/lib/internal/crypto/random.js @@ -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); @@ -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); diff --git a/src/crypto/crypto_random.cc b/src/crypto/crypto_random.cc index 690f9061bfa950..01f32832b29843 100644 --- a/src/crypto/crypto_random.cc +++ b/src/crypto/crypto_random.cc @@ -104,7 +104,7 @@ Maybe 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(); @@ -118,7 +118,7 @@ Maybe 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(); @@ -139,7 +139,7 @@ Maybe 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(); diff --git a/test/parallel/test-crypto-prime.js b/test/parallel/test-crypto-prime.js index 209d7413768c08..4d26c46eb5411a 100644 --- a/test/parallel/test-crypto-prime.js +++ b/test/parallel/test-crypto-prime.js @@ -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 }, @@ -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'