mirror of
https://github.com/nodejs/node.git
synced 2024-11-21 10:59:27 +00:00
crypto: add generatePrime/checkPrime
APIs for generating and checking pseudo-random primes Signed-off-by: James M Snell <jasnell@gmail.com> PR-URL: https://github.com/nodejs/node/pull/36997 Reviewed-By: Tobias Nießen <tniessen@tnie.de>
This commit is contained in:
parent
ecb78188a3
commit
bb13469acb
@ -1961,6 +1961,48 @@ is currently in use. Setting to true requires a FIPS build of Node.js.
|
||||
This property is deprecated. Please use `crypto.setFips()` and
|
||||
`crypto.getFips()` instead.
|
||||
|
||||
### `crypto.checkPrime(candidate[, options, [callback]])`
|
||||
<!-- YAML
|
||||
added: REPLACEME
|
||||
-->
|
||||
|
||||
* `candidate` {ArrayBuffer|SharedArrayBuffer|TypedArray|Buffer|DataView|bigint}
|
||||
A possible prime encoded as a sequence of big endian octets of arbitrary
|
||||
length.
|
||||
* `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<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`.
|
||||
|
||||
Checks the primality of the `candidate`.
|
||||
|
||||
### `crypto.checkPrimeSync(candidate[, options])`
|
||||
<!-- YAML
|
||||
added: REPLACEME
|
||||
-->
|
||||
|
||||
* `candidate` {ArrayBuffer|SharedArrayBuffer|TypedArray|Buffer|DataView|bigint}
|
||||
A possible prime encoded as a sequence of big endian octets of arbitrary
|
||||
length.
|
||||
* `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<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`.
|
||||
|
||||
Checks the primality of the `candidate`.
|
||||
|
||||
### `crypto.createCipher(algorithm, password[, options])`
|
||||
<!-- YAML
|
||||
added: v0.1.94
|
||||
@ -2694,6 +2736,78 @@ The return value `{ publicKey, privateKey }` represents the generated key pair.
|
||||
When PEM encoding was selected, the respective key will be a string, otherwise
|
||||
it will be a buffer containing the data encoded as DER.
|
||||
|
||||
### `crypto.generatePrime(size[, options[, callback]])`
|
||||
<!-- YAML
|
||||
added: REPLACEME
|
||||
-->
|
||||
|
||||
* `size` {number} The size (in bits) of the prime to generate.
|
||||
* `options` {Object}
|
||||
* `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}
|
||||
* `err` {Error}
|
||||
* `prime` {ArrayBuffer|bigint}
|
||||
|
||||
Generates a pseudo-random prime of `size` bits.
|
||||
|
||||
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
|
||||
condition that `prime % add = rem`. The `options.rem` is ignored if
|
||||
`options.add` is not given. If `options.safe` is `true`, `options.add`
|
||||
is given, and `options.rem` is `undefined`, then the prime generated
|
||||
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.
|
||||
|
||||
### `crypto.generatePrimeSync(size[, options])`
|
||||
<!-- YAML
|
||||
added: REPLACEME
|
||||
-->
|
||||
|
||||
* `size` {number} The size (in bits) of the prime to generate.
|
||||
* `options` {Object}
|
||||
* `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,
|
||||
`(prime - 1)` / 2 will also be a prime.
|
||||
|
||||
If `options.add` and `options.rem` are set, the prime will satisfy the
|
||||
condition that `prime % add = rem`. The `options.rem` is ignored if
|
||||
`options.add` is not given. If `options.safe` is `true`, `options.add`
|
||||
is given, and `options.rem` is `undefined`, then the prime generated
|
||||
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.
|
||||
|
||||
### `crypto.getCiphers()`
|
||||
<!-- YAML
|
||||
added: v0.9.3
|
||||
@ -4234,6 +4348,7 @@ See the [list of SSL OP Flags][] for details.
|
||||
[RFC 4122]: https://www.rfc-editor.org/rfc/rfc4122.txt
|
||||
[RFC 5208]: https://www.rfc-editor.org/rfc/rfc5208.txt
|
||||
[Web Crypto API documentation]: webcrypto.md
|
||||
[`BN_is_prime_ex`]: https://www.openssl.org/docs/man1.1.1/man3/BN_is_prime_ex.html
|
||||
[`Buffer`]: buffer.md
|
||||
[`EVP_BytesToKey`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_BytesToKey.html
|
||||
[`KeyObject`]: #crypto_class_keyobject
|
||||
|
@ -50,6 +50,10 @@ const {
|
||||
timingSafeEqual,
|
||||
} = internalBinding('crypto');
|
||||
const {
|
||||
checkPrime,
|
||||
checkPrimeSync,
|
||||
generatePrime,
|
||||
generatePrimeSync,
|
||||
randomBytes,
|
||||
randomFill,
|
||||
randomFillSync,
|
||||
@ -170,6 +174,8 @@ function createVerify(algorithm, options) {
|
||||
|
||||
module.exports = {
|
||||
// Methods
|
||||
checkPrime,
|
||||
checkPrimeSync,
|
||||
createCipheriv,
|
||||
createDecipheriv,
|
||||
createDiffieHellman,
|
||||
@ -183,6 +189,8 @@ module.exports = {
|
||||
createSign,
|
||||
createVerify,
|
||||
diffieHellman,
|
||||
generatePrime,
|
||||
generatePrimeSync,
|
||||
getCiphers,
|
||||
getCipherInfo,
|
||||
getCurves,
|
||||
|
@ -1,6 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const {
|
||||
BigInt,
|
||||
FunctionPrototypeBind,
|
||||
FunctionPrototypeCall,
|
||||
MathMin,
|
||||
@ -10,6 +11,8 @@ const {
|
||||
|
||||
const {
|
||||
RandomBytesJob,
|
||||
RandomPrimeJob,
|
||||
CheckPrimeJob,
|
||||
kCryptoJobAsync,
|
||||
kCryptoJobSync,
|
||||
secureBuffer,
|
||||
@ -34,6 +37,7 @@ const {
|
||||
validateBoolean,
|
||||
validateCallback,
|
||||
validateObject,
|
||||
validateUint32,
|
||||
} = require('internal/validators');
|
||||
|
||||
const {
|
||||
@ -387,11 +391,212 @@ function randomUUID(options) {
|
||||
return uuid.latin1Slice(0, 36);
|
||||
}
|
||||
|
||||
function generatePrime(size, options, callback) {
|
||||
validateUint32(size, 'size', true);
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = {};
|
||||
}
|
||||
validateCallback(callback);
|
||||
validateObject(options, 'options');
|
||||
const {
|
||||
safe = false,
|
||||
bigint = false,
|
||||
} = options;
|
||||
let {
|
||||
add,
|
||||
rem,
|
||||
} = options;
|
||||
|
||||
validateBoolean(safe, 'options.safe');
|
||||
validateBoolean(bigint, 'options.bigint');
|
||||
|
||||
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',
|
||||
'bigint',
|
||||
],
|
||||
add);
|
||||
}
|
||||
}
|
||||
|
||||
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',
|
||||
'bigint',
|
||||
],
|
||||
rem);
|
||||
}
|
||||
}
|
||||
|
||||
const job = new RandomPrimeJob(kCryptoJobAsync, size, safe, add, rem);
|
||||
job.ondone = (err, prime) => {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
callback(
|
||||
undefined,
|
||||
bigint ?
|
||||
BigInt(`0x${Buffer.from(prime).toString('hex')}`) :
|
||||
prime);
|
||||
};
|
||||
job.run();
|
||||
}
|
||||
|
||||
function generatePrimeSync(size, options = {}) {
|
||||
validateUint32(size, 'size', true);
|
||||
validateObject(options, 'options');
|
||||
const {
|
||||
safe = false,
|
||||
bigint = false,
|
||||
} = options;
|
||||
let {
|
||||
add,
|
||||
rem,
|
||||
} = options;
|
||||
validateBoolean(safe, 'options.safe');
|
||||
validateBoolean(bigint, 'options.bigint');
|
||||
|
||||
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',
|
||||
'bigint',
|
||||
],
|
||||
add);
|
||||
}
|
||||
}
|
||||
|
||||
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',
|
||||
'bigint',
|
||||
],
|
||||
rem);
|
||||
}
|
||||
}
|
||||
|
||||
const job = new RandomPrimeJob(kCryptoJobSync, size, safe, add, rem);
|
||||
const [err, prime] = job.run();
|
||||
if (err)
|
||||
throw err;
|
||||
|
||||
return bigint ?
|
||||
BigInt(`0x${Buffer.from(prime).toString('hex')}`) :
|
||||
prime;
|
||||
}
|
||||
|
||||
function toHexPadded(bigint) {
|
||||
const hex = bigint.toString(16);
|
||||
return hex.padStart(hex.length + (hex.length % 2), 0);
|
||||
}
|
||||
|
||||
function checkPrime(candidate, options = {}, callback) {
|
||||
if (typeof candidate === 'bigint')
|
||||
candidate = Buffer.from(toHexPadded(candidate), 'hex');
|
||||
if (!isAnyArrayBuffer(candidate) && !isArrayBufferView(candidate)) {
|
||||
throw new ERR_INVALID_ARG_TYPE(
|
||||
'candidate',
|
||||
[
|
||||
'ArrayBuffer',
|
||||
'TypedArray',
|
||||
'Buffer',
|
||||
'DataView',
|
||||
'bigint',
|
||||
],
|
||||
candidate
|
||||
);
|
||||
}
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = {};
|
||||
}
|
||||
validateCallback(callback);
|
||||
validateObject(options, 'options');
|
||||
const {
|
||||
checks = 0,
|
||||
} = options;
|
||||
|
||||
validateUint32(checks, 'options.checks');
|
||||
|
||||
const job = new CheckPrimeJob(kCryptoJobAsync, candidate, checks);
|
||||
job.ondone = callback;
|
||||
job.run();
|
||||
}
|
||||
|
||||
function checkPrimeSync(candidate, options = {}) {
|
||||
if (typeof candidate === 'bigint')
|
||||
candidate = Buffer.from(toHexPadded(candidate), 'hex');
|
||||
if (!isAnyArrayBuffer(candidate) && !isArrayBufferView(candidate)) {
|
||||
throw new ERR_INVALID_ARG_TYPE(
|
||||
'candidate',
|
||||
[
|
||||
'ArrayBuffer',
|
||||
'TypedArray',
|
||||
'Buffer',
|
||||
'DataView',
|
||||
'bigint',
|
||||
],
|
||||
candidate
|
||||
);
|
||||
}
|
||||
validateObject(options, 'options');
|
||||
const {
|
||||
checks = 0,
|
||||
} = options;
|
||||
|
||||
validateUint32(checks, 'options.checks');
|
||||
|
||||
const job = new CheckPrimeJob(kCryptoJobSync, candidate, checks);
|
||||
const [err, result] = job.run();
|
||||
if (err)
|
||||
throw err;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
checkPrime,
|
||||
checkPrimeSync,
|
||||
randomBytes,
|
||||
randomFill,
|
||||
randomFillSync,
|
||||
randomInt,
|
||||
getRandomValues,
|
||||
randomUUID,
|
||||
generatePrime,
|
||||
generatePrimeSync,
|
||||
};
|
||||
|
@ -84,6 +84,7 @@ namespace node {
|
||||
|
||||
#if HAVE_OPENSSL
|
||||
#define NODE_ASYNC_CRYPTO_PROVIDER_TYPES(V) \
|
||||
V(CHECKPRIMEREQUEST) \
|
||||
V(PBKDF2REQUEST) \
|
||||
V(KEYPAIRGENREQUEST) \
|
||||
V(KEYGENREQUEST) \
|
||||
@ -92,6 +93,7 @@ namespace node {
|
||||
V(DERIVEBITSREQUEST) \
|
||||
V(HASHREQUEST) \
|
||||
V(RANDOMBYTESREQUEST) \
|
||||
V(RANDOMPRIMEREQUEST) \
|
||||
V(SCRYPTREQUEST) \
|
||||
V(SIGNREQUEST) \
|
||||
V(TLSWRAP) \
|
||||
|
@ -7,14 +7,20 @@
|
||||
#include "threadpoolwork-inl.h"
|
||||
#include "v8.h"
|
||||
|
||||
#include <openssl/bn.h>
|
||||
|
||||
namespace node {
|
||||
|
||||
using v8::ArrayBuffer;
|
||||
using v8::BackingStore;
|
||||
using v8::False;
|
||||
using v8::FunctionCallbackInfo;
|
||||
using v8::Just;
|
||||
using v8::Local;
|
||||
using v8::Maybe;
|
||||
using v8::Nothing;
|
||||
using v8::Object;
|
||||
using v8::True;
|
||||
using v8::Uint32;
|
||||
using v8::Value;
|
||||
|
||||
@ -64,9 +70,170 @@ bool RandomBytesTraits::DeriveBits(
|
||||
return RAND_bytes(params.buffer, params.size) != 0;
|
||||
}
|
||||
|
||||
void RandomPrimeConfig::MemoryInfo(MemoryTracker* tracker) const {
|
||||
tracker->TrackFieldWithSize("prime", prime ? bits * 8 : 0);
|
||||
}
|
||||
|
||||
Maybe<bool> RandomPrimeTraits::EncodeOutput(
|
||||
Environment* env,
|
||||
const RandomPrimeConfig& params,
|
||||
ByteSource* unused,
|
||||
v8::Local<v8::Value>* result) {
|
||||
size_t size = BN_num_bytes(params.prime.get());
|
||||
std::shared_ptr<BackingStore> store =
|
||||
ArrayBuffer::NewBackingStore(env->isolate(), size);
|
||||
BN_bn2binpad(
|
||||
params.prime.get(),
|
||||
reinterpret_cast<unsigned char*>(store->Data()),
|
||||
size);
|
||||
*result = ArrayBuffer::New(env->isolate(), store);
|
||||
return Just(true);
|
||||
}
|
||||
|
||||
Maybe<bool> RandomPrimeTraits::AdditionalConfig(
|
||||
CryptoJobMode mode,
|
||||
const FunctionCallbackInfo<Value>& args,
|
||||
unsigned int offset,
|
||||
RandomPrimeConfig* params) {
|
||||
ClearErrorOnReturn clear_error;
|
||||
Environment* env = Environment::GetCurrent(args);
|
||||
CHECK(args[offset]->IsUint32()); // Size
|
||||
CHECK(args[offset + 1]->IsBoolean()); // Safe
|
||||
|
||||
const uint32_t size = args[offset].As<Uint32>()->Value();
|
||||
bool safe = args[offset + 1]->IsTrue();
|
||||
|
||||
if (!args[offset + 2]->IsUndefined()) {
|
||||
params->add.reset(BN_secure_new());
|
||||
if (!params->add) {
|
||||
THROW_ERR_CRYPTO_OPERATION_FAILED(env, "could not generate prime");
|
||||
return Nothing<bool>();
|
||||
}
|
||||
ArrayBufferOrViewContents<unsigned char> add(args[offset + 2]);
|
||||
BN_bin2bn(add.data(), add.size(), params->add.get());
|
||||
if (!params->add) {
|
||||
THROW_ERR_INVALID_ARG_VALUE(env, "invalid options.add");
|
||||
return Nothing<bool>();
|
||||
}
|
||||
}
|
||||
|
||||
if (!args[offset + 3]->IsUndefined()) {
|
||||
params->rem.reset(BN_secure_new());
|
||||
if (!params->rem) {
|
||||
THROW_ERR_CRYPTO_OPERATION_FAILED(env, "could not generate prime");
|
||||
return Nothing<bool>();
|
||||
}
|
||||
ArrayBufferOrViewContents<unsigned char> rem(args[offset + 3]);
|
||||
BN_bin2bn(rem.data(), rem.size(), params->rem.get());
|
||||
if (!params->rem) {
|
||||
THROW_ERR_INVALID_ARG_VALUE(env, "invalid options.rem");
|
||||
return Nothing<bool>();
|
||||
}
|
||||
}
|
||||
|
||||
int bits = static_cast<int>(size);
|
||||
if (bits < 0) {
|
||||
THROW_ERR_OUT_OF_RANGE(env, "invalid size");
|
||||
return Nothing<bool>();
|
||||
}
|
||||
|
||||
params->bits = bits;
|
||||
params->safe = safe;
|
||||
params->prime.reset(BN_secure_new());
|
||||
if (!params->prime) {
|
||||
THROW_ERR_CRYPTO_OPERATION_FAILED(env, "could not generate prime");
|
||||
return Nothing<bool>();
|
||||
}
|
||||
|
||||
return Just(true);
|
||||
}
|
||||
|
||||
bool RandomPrimeTraits::DeriveBits(
|
||||
Environment* env,
|
||||
const RandomPrimeConfig& params,
|
||||
ByteSource* unused) {
|
||||
|
||||
CheckEntropy();
|
||||
|
||||
if (BN_generate_prime_ex(
|
||||
params.prime.get(),
|
||||
params.bits,
|
||||
params.safe ? 1 : 0,
|
||||
params.add.get(),
|
||||
params.rem.get(),
|
||||
nullptr) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CheckPrimeConfig::MemoryInfo(MemoryTracker* tracker) const {
|
||||
tracker->TrackFieldWithSize(
|
||||
"prime", candidate ? BN_num_bytes(candidate.get()) : 0);
|
||||
}
|
||||
|
||||
Maybe<bool> CheckPrimeTraits::AdditionalConfig(
|
||||
CryptoJobMode mode,
|
||||
const FunctionCallbackInfo<Value>& args,
|
||||
unsigned int offset,
|
||||
CheckPrimeConfig* params) {
|
||||
Environment* env = Environment::GetCurrent(args);
|
||||
|
||||
ArrayBufferOrViewContents<unsigned char> candidate(args[offset]);
|
||||
|
||||
params->candidate =
|
||||
BignumPointer(BN_bin2bn(
|
||||
candidate.data(),
|
||||
candidate.size(),
|
||||
nullptr));
|
||||
|
||||
CHECK(args[offset + 1]->IsUint32()); // Checks
|
||||
|
||||
const int checks = static_cast<int>(args[offset + 1].As<Uint32>()->Value());
|
||||
if (checks < 0) {
|
||||
THROW_ERR_OUT_OF_RANGE(env, "invalid options.checks");
|
||||
return Nothing<bool>();
|
||||
}
|
||||
|
||||
params->checks = checks;
|
||||
|
||||
return Just(true);
|
||||
}
|
||||
|
||||
bool CheckPrimeTraits::DeriveBits(
|
||||
Environment* env,
|
||||
const CheckPrimeConfig& params,
|
||||
ByteSource* out) {
|
||||
|
||||
BignumCtxPointer ctx(BN_CTX_new());
|
||||
|
||||
int ret = BN_is_prime_ex(
|
||||
params.candidate.get(),
|
||||
params.checks,
|
||||
ctx.get(),
|
||||
nullptr);
|
||||
if (ret < 0) return false;
|
||||
char* data = MallocOpenSSL<char>(1);
|
||||
data[0] = ret;
|
||||
*out = ByteSource::Allocated(data, 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
Maybe<bool> CheckPrimeTraits::EncodeOutput(
|
||||
Environment* env,
|
||||
const CheckPrimeConfig& params,
|
||||
ByteSource* out,
|
||||
v8::Local<v8::Value>* result) {
|
||||
*result = out->get()[0] ? True(env->isolate()) : False(env->isolate());
|
||||
return Just(true);
|
||||
}
|
||||
|
||||
namespace Random {
|
||||
void Initialize(Environment* env, Local<Object> target) {
|
||||
RandomBytesJob::Initialize(env, target);
|
||||
RandomPrimeJob::Initialize(env, target);
|
||||
CheckPrimeJob::Initialize(env, target);
|
||||
}
|
||||
} // namespace Random
|
||||
} // namespace crypto
|
||||
|
@ -47,6 +47,79 @@ struct RandomBytesTraits final {
|
||||
|
||||
using RandomBytesJob = DeriveBitsJob<RandomBytesTraits>;
|
||||
|
||||
struct RandomPrimeConfig final : public MemoryRetainer {
|
||||
BignumPointer prime;
|
||||
BignumPointer rem;
|
||||
BignumPointer add;
|
||||
int bits;
|
||||
bool safe;
|
||||
void MemoryInfo(MemoryTracker* tracker) const override;
|
||||
SET_MEMORY_INFO_NAME(RandomPrimeConfig);
|
||||
SET_SELF_SIZE(RandomPrimeConfig);
|
||||
};
|
||||
|
||||
struct RandomPrimeTraits final {
|
||||
using AdditionalParameters = RandomPrimeConfig;
|
||||
static constexpr const char* JobName = "RandomPrimeJob";
|
||||
static constexpr AsyncWrap::ProviderType Provider =
|
||||
AsyncWrap::PROVIDER_RANDOMPRIMEREQUEST;
|
||||
|
||||
static v8::Maybe<bool> AdditionalConfig(
|
||||
CryptoJobMode mode,
|
||||
const v8::FunctionCallbackInfo<v8::Value>& args,
|
||||
unsigned int offset,
|
||||
RandomPrimeConfig* params);
|
||||
|
||||
static bool DeriveBits(
|
||||
Environment* env,
|
||||
const RandomPrimeConfig& params,
|
||||
ByteSource* out_);
|
||||
|
||||
static v8::Maybe<bool> EncodeOutput(
|
||||
Environment* env,
|
||||
const RandomPrimeConfig& params,
|
||||
ByteSource* unused,
|
||||
v8::Local<v8::Value>* result);
|
||||
};
|
||||
|
||||
using RandomPrimeJob = DeriveBitsJob<RandomPrimeTraits>;
|
||||
|
||||
struct CheckPrimeConfig final : public MemoryRetainer {
|
||||
BignumPointer candidate;
|
||||
int checks = 1;
|
||||
|
||||
void MemoryInfo(MemoryTracker* tracker) const override;
|
||||
SET_MEMORY_INFO_NAME(CheckPrimeConfig);
|
||||
SET_SELF_SIZE(CheckPrimeConfig);
|
||||
};
|
||||
|
||||
struct CheckPrimeTraits final {
|
||||
using AdditionalParameters = CheckPrimeConfig;
|
||||
static constexpr const char* JobName = "CheckPrimeJob";
|
||||
|
||||
static constexpr AsyncWrap::ProviderType Provider =
|
||||
AsyncWrap::PROVIDER_CHECKPRIMEREQUEST;
|
||||
|
||||
static v8::Maybe<bool> AdditionalConfig(
|
||||
CryptoJobMode mode,
|
||||
const v8::FunctionCallbackInfo<v8::Value>& args,
|
||||
unsigned int offset,
|
||||
CheckPrimeConfig* params);
|
||||
|
||||
static bool DeriveBits(
|
||||
Environment* env,
|
||||
const CheckPrimeConfig& params,
|
||||
ByteSource* out);
|
||||
|
||||
static v8::Maybe<bool> EncodeOutput(
|
||||
Environment* env,
|
||||
const CheckPrimeConfig& params,
|
||||
ByteSource* out,
|
||||
v8::Local<v8::Value>* result);
|
||||
};
|
||||
|
||||
using CheckPrimeJob = DeriveBitsJob<CheckPrimeTraits>;
|
||||
|
||||
namespace Random {
|
||||
void Initialize(Environment* env, v8::Local<v8::Object> target);
|
||||
} // namespace Random
|
||||
|
@ -59,6 +59,7 @@ using EVPMDPointer = DeleteFnPtr<EVP_MD_CTX, EVP_MD_CTX_free>;
|
||||
using RSAPointer = DeleteFnPtr<RSA, RSA_free>;
|
||||
using ECPointer = DeleteFnPtr<EC_KEY, EC_KEY_free>;
|
||||
using BignumPointer = DeleteFnPtr<BIGNUM, BN_free>;
|
||||
using BignumCtxPointer = DeleteFnPtr<BN_CTX, BN_CTX_free>;
|
||||
using NetscapeSPKIPointer = DeleteFnPtr<NETSCAPE_SPKI, NETSCAPE_SPKI_free>;
|
||||
using ECGroupPointer = DeleteFnPtr<EC_GROUP, EC_GROUP_free>;
|
||||
using ECPointPointer = DeleteFnPtr<EC_POINT, EC_POINT_free>;
|
||||
@ -648,6 +649,7 @@ std::vector<T> CopyBuffer(v8::Local<v8::Value> buf) {
|
||||
v8::MaybeLocal<v8::Value> EncodeBignum(
|
||||
Environment* env,
|
||||
const BIGNUM* bn,
|
||||
int size,
|
||||
v8::Local<v8::Value>* error);
|
||||
|
||||
v8::Maybe<bool> SetEncodedValue(
|
||||
|
195
test/parallel/test-crypto-prime.js
Normal file
195
test/parallel/test-crypto-prime.js
Normal file
@ -0,0 +1,195 @@
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
if (!common.hasCrypto)
|
||||
common.skip('missing crypto');
|
||||
|
||||
const assert = require('assert');
|
||||
|
||||
const {
|
||||
generatePrime,
|
||||
generatePrimeSync,
|
||||
checkPrime,
|
||||
checkPrimeSync,
|
||||
} = require('crypto');
|
||||
|
||||
const { promisify } = require('util');
|
||||
const pgeneratePrime = promisify(generatePrime);
|
||||
const pCheckPrime = promisify(checkPrime);
|
||||
|
||||
['hello', false, {}, []].forEach((i) => {
|
||||
assert.throws(() => generatePrime(i), {
|
||||
code: 'ERR_INVALID_ARG_TYPE'
|
||||
});
|
||||
assert.throws(() => generatePrimeSync(i), {
|
||||
code: 'ERR_INVALID_ARG_TYPE'
|
||||
});
|
||||
});
|
||||
|
||||
['hello', false, 123].forEach((i) => {
|
||||
assert.throws(() => generatePrime(80, i, common.mustNotCall()), {
|
||||
code: 'ERR_INVALID_ARG_TYPE'
|
||||
});
|
||||
assert.throws(() => generatePrimeSync(80, i), {
|
||||
code: 'ERR_INVALID_ARG_TYPE'
|
||||
});
|
||||
});
|
||||
|
||||
['hello', false, 123].forEach((i) => {
|
||||
assert.throws(() => generatePrime(80, {}), {
|
||||
code: 'ERR_INVALID_CALLBACK'
|
||||
});
|
||||
});
|
||||
|
||||
[-1, 0].forEach((i) => {
|
||||
assert.throws(() => generatePrime(i, common.mustNotCall()), {
|
||||
code: 'ERR_OUT_OF_RANGE'
|
||||
});
|
||||
assert.throws(() => generatePrimeSync(i), {
|
||||
code: 'ERR_OUT_OF_RANGE'
|
||||
});
|
||||
});
|
||||
|
||||
['test', -1, {}, []].forEach((i) => {
|
||||
assert.throws(() => generatePrime(8, { safe: i }, common.mustNotCall()), {
|
||||
code: 'ERR_INVALID_ARG_TYPE'
|
||||
});
|
||||
assert.throws(() => generatePrime(8, { rem: i }, common.mustNotCall()), {
|
||||
code: 'ERR_INVALID_ARG_TYPE'
|
||||
});
|
||||
assert.throws(() => generatePrime(8, { add: i }, common.mustNotCall()), {
|
||||
code: 'ERR_INVALID_ARG_TYPE'
|
||||
});
|
||||
assert.throws(() => generatePrimeSync(8, { safe: i }), {
|
||||
code: 'ERR_INVALID_ARG_TYPE'
|
||||
});
|
||||
assert.throws(() => generatePrimeSync(8, { rem: i }), {
|
||||
code: 'ERR_INVALID_ARG_TYPE'
|
||||
});
|
||||
assert.throws(() => generatePrimeSync(8, { add: i }), {
|
||||
code: 'ERR_INVALID_ARG_TYPE'
|
||||
});
|
||||
});
|
||||
|
||||
generatePrime(80, common.mustSucceed((prime) => {
|
||||
assert(checkPrimeSync(prime));
|
||||
checkPrime(prime, common.mustSucceed((result) => {
|
||||
assert(result);
|
||||
}));
|
||||
}));
|
||||
|
||||
assert(checkPrimeSync(generatePrimeSync(80)));
|
||||
|
||||
generatePrime(80, {}, common.mustSucceed((prime) => {
|
||||
assert(checkPrimeSync(prime));
|
||||
}));
|
||||
|
||||
assert(checkPrimeSync(generatePrimeSync(80, {})));
|
||||
|
||||
generatePrime(32, { safe: true }, common.mustSucceed((prime) => {
|
||||
assert(checkPrimeSync(prime));
|
||||
const buf = Buffer.from(prime);
|
||||
const val = buf.readUInt32BE();
|
||||
const check = (val - 1) / 2;
|
||||
buf.writeUInt32BE(check);
|
||||
assert(checkPrimeSync(buf));
|
||||
}));
|
||||
|
||||
{
|
||||
const prime = generatePrimeSync(32, { safe: true });
|
||||
assert(checkPrimeSync(prime));
|
||||
const buf = Buffer.from(prime);
|
||||
const val = buf.readUInt32BE();
|
||||
const check = (val - 1) / 2;
|
||||
buf.writeUInt32BE(check);
|
||||
assert(checkPrimeSync(buf));
|
||||
}
|
||||
|
||||
const add = 12;
|
||||
const rem = 11;
|
||||
const add_buf = Buffer.from([add]);
|
||||
const rem_buf = Buffer.from([rem]);
|
||||
generatePrime(
|
||||
32,
|
||||
{ add: add_buf, rem: rem_buf },
|
||||
common.mustSucceed((prime) => {
|
||||
assert(checkPrimeSync(prime));
|
||||
const buf = Buffer.from(prime);
|
||||
const val = buf.readUInt32BE();
|
||||
assert.strictEqual(val % add, rem);
|
||||
}));
|
||||
|
||||
{
|
||||
const prime = generatePrimeSync(32, { add: add_buf, rem: rem_buf });
|
||||
assert(checkPrimeSync(prime));
|
||||
const buf = Buffer.from(prime);
|
||||
const val = buf.readUInt32BE();
|
||||
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'
|
||||
});
|
||||
});
|
||||
|
||||
['hello', {}, []].forEach((i) => {
|
||||
assert.throws(() => checkPrime(2, { checks: i }), {
|
||||
code: 'ERR_INVALID_ARG_TYPE'
|
||||
}, common.mustNotCall());
|
||||
assert.throws(() => checkPrimeSync(2, { checks: i }), {
|
||||
code: 'ERR_INVALID_ARG_TYPE'
|
||||
});
|
||||
});
|
||||
|
||||
assert(!checkPrimeSync(Buffer.from([0x1])));
|
||||
assert(checkPrimeSync(Buffer.from([0x2])));
|
||||
assert(checkPrimeSync(Buffer.from([0x3])));
|
||||
assert(!checkPrimeSync(Buffer.from([0x4])));
|
||||
|
||||
assert(
|
||||
!checkPrimeSync(
|
||||
Buffer.from([0x1]),
|
||||
{
|
||||
fast: true,
|
||||
trialDivision: true,
|
||||
checks: 10
|
||||
}));
|
||||
|
||||
(async function() {
|
||||
const prime = await pgeneratePrime(36);
|
||||
assert(await pCheckPrime(prime));
|
||||
})().then(common.mustCall());
|
||||
|
||||
assert.throws(() => {
|
||||
generatePrimeSync(32, { bigint: '' });
|
||||
}, { code: 'ERR_INVALID_ARG_TYPE' });
|
||||
|
||||
assert.throws(() => {
|
||||
generatePrime(32, { bigint: '' }, common.mustNotCall());
|
||||
}, { code: 'ERR_INVALID_ARG_TYPE' });
|
||||
|
||||
{
|
||||
const prime = generatePrimeSync(3, { bigint: true });
|
||||
assert.strictEqual(typeof prime, 'bigint');
|
||||
assert.strictEqual(prime, 7n);
|
||||
assert(checkPrimeSync(prime));
|
||||
checkPrime(prime, common.mustSucceed(assert));
|
||||
}
|
||||
|
||||
{
|
||||
generatePrime(3, { bigint: true }, common.mustSucceed((prime) => {
|
||||
assert.strictEqual(typeof prime, 'bigint');
|
||||
assert.strictEqual(prime, 7n);
|
||||
assert(checkPrimeSync(prime));
|
||||
checkPrime(prime, common.mustSucceed(assert));
|
||||
}));
|
||||
}
|
@ -70,6 +70,8 @@ const { getSystemErrorName } = require('util');
|
||||
delete providers.SIGINTWATCHDOG;
|
||||
delete providers.WORKERHEAPSNAPSHOT;
|
||||
delete providers.FIXEDSIZEBLOBCOPY;
|
||||
delete providers.RANDOMPRIMEREQUEST;
|
||||
delete providers.CHECKPRIMEREQUEST;
|
||||
|
||||
const objKeys = Object.keys(providers);
|
||||
if (objKeys.length > 0)
|
||||
|
Loading…
Reference in New Issue
Block a user