Skip to content

Commit 476c6f8

Browse files
richardlauaddaleax
authored andcommitted
crypto: avoid hang when no algorithm available
Avoid an endless loop if no algorithm is available to seed the cryptographically secure pseudorandom number generator (CSPRNG). Co-authored-by: Anna Henningsen <[email protected]> PR-URL: #46237 Fixes: #46200 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Ben Noordhuis <[email protected]>
1 parent 653b108 commit 476c6f8

File tree

3 files changed

+64
-0
lines changed

3 files changed

+64
-0
lines changed

src/crypto/crypto_util.cc

+14
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,20 @@ MUST_USE_RESULT CSPRNGResult CSPRNG(void* buffer, size_t length) {
6565
if (1 == RAND_status())
6666
if (1 == RAND_bytes(static_cast<unsigned char*>(buffer), length))
6767
return {true};
68+
#if OPENSSL_VERSION_MAJOR >= 3
69+
const auto code = ERR_peek_last_error();
70+
// A misconfigured OpenSSL 3 installation may report 1 from RAND_poll()
71+
// and RAND_status() but fail in RAND_bytes() if it cannot look up
72+
// a matching algorithm for the CSPRNG.
73+
if (ERR_GET_LIB(code) == ERR_LIB_RAND) {
74+
const auto reason = ERR_GET_REASON(code);
75+
if (reason == RAND_R_ERROR_INSTANTIATING_DRBG ||
76+
reason == RAND_R_UNABLE_TO_FETCH_DRBG ||
77+
reason == RAND_R_UNABLE_TO_CREATE_DRBG) {
78+
return {false};
79+
}
80+
}
81+
#endif
6882
} while (1 == RAND_poll());
6983

7084
return {false};
+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
nodejs_conf = nodejs_init
2+
3+
[nodejs_init]
4+
providers = provider_sect
5+
6+
# List of providers to load
7+
[provider_sect]
8+
base = base_sect
9+
10+
[base_sect]
11+
activate = 1
12+
+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
if (!common.hasCrypto)
5+
common.skip('missing crypto');
6+
7+
if (!common.hasOpenSSL3)
8+
common.skip('this test requires OpenSSL 3.x');
9+
10+
const assert = require('node:assert/strict');
11+
const crypto = require('node:crypto');
12+
13+
if (common.isMainThread) {
14+
// TODO(richardlau): Decide if `crypto.setFips` should error if the
15+
// provider named "fips" is not available.
16+
crypto.setFips(1);
17+
crypto.randomBytes(20, common.mustCall((err) => {
18+
// crypto.randomBytes should either succeed or fail but not hang.
19+
if (err) {
20+
assert.match(err.message, /digital envelope routines::unsupported/);
21+
const expected = /random number generator::unable to fetch drbg/;
22+
assert(err.opensslErrorStack.some((msg) => expected.test(msg)),
23+
`did not find ${expected} in ${err.opensslErrorStack}`);
24+
}
25+
}));
26+
}
27+
28+
{
29+
// Startup test. Should not hang.
30+
const { path } = require('../common/fixtures');
31+
const { spawnSync } = require('node:child_process');
32+
const baseConf = path('openssl3-conf', 'base_only.cnf');
33+
const cp = spawnSync(process.execPath,
34+
[ `--openssl-config=${baseConf}`, '-p', '"hello"' ],
35+
{ encoding: 'utf8' });
36+
assert(common.nodeProcessAborted(cp.status, cp.signal),
37+
`process did not abort, code:${cp.status} signal:${cp.signal}`);
38+
}

0 commit comments

Comments
 (0)