Skip to content

Commit 1994eaa

Browse files
jasnelladuh95
authored andcommitted
crypto: make generatePrime/checkPrime interruptible
The `generatePrime` and `checkPrime` functions in the `crypto` module are only somewhat interruptible. This change makes it possible to interrupt these more reliably. Note that generating overly large primes can still take a long time and may not be interruptible as this mechanism relies on a callback to check for stopping conditions but OpenSSL may perform a long running operation without calling the callback right away. Fixes: #56449 PR-URL: #56460 Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
1 parent 5e1ddd5 commit 1994eaa

File tree

3 files changed

+59
-11
lines changed

3 files changed

+59
-11
lines changed

doc/api/crypto.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3934,6 +3934,13 @@ By default, the prime is encoded as a big-endian sequence of octets
39343934
in an {ArrayBuffer}. If the `bigint` option is `true`, then a {bigint}
39353935
is provided.
39363936

3937+
The `size` of the prime will have a direct impact on how long it takes to
3938+
generate the prime. The larger the size, the longer it will take. Because
3939+
we use OpenSSL's `BN_generate_prime_ex` function, which provides only
3940+
minimal control over our ability to interrupt the generation process,
3941+
it is not recommended to generate overly large primes, as doing so may make
3942+
the process unresponsive.
3943+
39373944
### `crypto.generatePrimeSync(size[, options])`
39383945

39393946
<!-- YAML
@@ -3975,6 +3982,13 @@ By default, the prime is encoded as a big-endian sequence of octets
39753982
in an {ArrayBuffer}. If the `bigint` option is `true`, then a {bigint}
39763983
is provided.
39773984

3985+
The `size` of the prime will have a direct impact on how long it takes to
3986+
generate the prime. The larger the size, the longer it will take. Because
3987+
we use OpenSSL's `BN_generate_prime_ex` function, which provides only
3988+
minimal control over our ability to interrupt the generation process,
3989+
it is not recommended to generate overly large primes, as doing so may make
3990+
the process unresponsive.
3991+
39783992
### `crypto.getCipherInfo(nameOrNid[, options])`
39793993

39803994
<!-- YAML

src/crypto/crypto_random.cc

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,25 @@ using v8::Uint32;
2828
using v8::Value;
2929

3030
namespace crypto {
31+
namespace {
32+
using BNGENCBPointer = DeleteFnPtr<BN_GENCB, BN_GENCB_free>;
33+
34+
BNGENCBPointer getBN_GENCB(Environment* env) {
35+
// The callback is used to check if the operation should be stopped.
36+
// Currently, the only check we perform is if env->is_stopping()
37+
// is true.
38+
BNGENCBPointer cb(BN_GENCB_new());
39+
BN_GENCB_set(
40+
cb.get(),
41+
[](int a, int b, BN_GENCB* cb) -> int {
42+
Environment* env = static_cast<Environment*>(BN_GENCB_get_arg(cb));
43+
return env->is_stopping() ? 0 : 1;
44+
},
45+
env);
46+
return cb;
47+
}
48+
49+
} // namespace
3150
MaybeLocal<Value> RandomBytesTraits::EncodeOutput(
3251
Environment* env, const RandomBytesConfig& params, ByteSource* unused) {
3352
return v8::Undefined(env->isolate());
@@ -150,13 +169,14 @@ bool RandomPrimeTraits::DeriveBits(Environment* env,
150169
// Make sure the CSPRNG is properly seeded.
151170
CHECK(ncrypto::CSPRNG(nullptr, 0));
152171

153-
if (BN_generate_prime_ex(
154-
params.prime.get(),
155-
params.bits,
156-
params.safe ? 1 : 0,
157-
params.add.get(),
158-
params.rem.get(),
159-
nullptr) == 0) {
172+
BNGENCBPointer cb = getBN_GENCB(env);
173+
174+
if (BN_generate_prime_ex(params.prime.get(),
175+
params.bits,
176+
params.safe ? 1 : 0,
177+
params.add.get(),
178+
params.rem.get(),
179+
cb.get()) == 0) {
160180
return false;
161181
}
162182

@@ -189,12 +209,10 @@ bool CheckPrimeTraits::DeriveBits(
189209
ByteSource* out) {
190210

191211
BignumCtxPointer ctx(BN_CTX_new());
212+
BNGENCBPointer cb = getBN_GENCB(env);
192213

193214
int ret = BN_is_prime_ex(
194-
params.candidate.get(),
195-
params.checks,
196-
ctx.get(),
197-
nullptr);
215+
params.candidate.get(), params.checks, ctx.get(), cb.get());
198216
if (ret < 0) return false;
199217
ByteSource::Builder buf(1);
200218
buf.data<char>()[0] = ret;

test/parallel/test-crypto-prime.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ const {
1414
checkPrimeSync,
1515
} = require('crypto');
1616

17+
const { Worker } = require('worker_threads');
18+
1719
const { promisify } = require('util');
1820
const pgeneratePrime = promisify(generatePrime);
1921
const pCheckPrime = promisify(checkPrime);
@@ -295,3 +297,17 @@ assert.throws(() => {
295297
checkPrime(prime, common.mustSucceed(assert));
296298
}));
297299
}
300+
301+
{
302+
// Verify that generatePrime can be reasonably interrupted.
303+
const worker = new Worker(`
304+
const { generatePrime } = require('crypto');
305+
generatePrime(2048, () => {
306+
throw new Error('should not be called');
307+
});
308+
process.exit(42);
309+
`, { eval: true });
310+
311+
worker.on('error', common.mustNotCall());
312+
worker.on('exit', common.mustCall((exitCode) => assert.strictEqual(exitCode, 42)));
313+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy