Skip to content

Commit bdbe986

Browse files
christianaurichzmaduh95
authored andcommitted
test: add test for concurrent first use of OpenSSL
OpenSSL 3 builds its table of algorithm names on first use. When 43d3fe9 moved that first use after other threads had started, an RSA-PSS key decoded while the table was being built could fail with `ERR_OSSL_EVP_DECODE_ERROR`, and every later RSA-PSS decode in the process failed too. #66903 reverted that change. The new test fails if it comes back: Workers decode an RSA-PSS certificate while the root certificate loader or a thread pool crypto job uses OpenSSL for the first time. It is based on a reproduction by Filip Skokan in #66903. Refs: #66903 Refs: #66935 Signed-off-by: Christian Aurich Zanettini Martins <christian.aurichzm@gmail.com> PR-URL: #66966 Reviewed-By: Filip Skokan <panva.ip@gmail.com>
1 parent 883c6a6 commit bdbe986

1 file changed

Lines changed: 59 additions & 0 deletions

File tree

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
'use strict';
2+
const common = require('../common');
3+
if (!common.hasCrypto)
4+
common.skip('missing crypto');
5+
6+
const { hasOpenSSL } = require('../common/crypto');
7+
if (!hasOpenSSL(3))
8+
common.skip('this test requires OpenSSL >= 3.x');
9+
10+
// OpenSSL 3 builds its table of algorithm names on first use. If several
11+
// threads use OpenSSL for the first time at once, an RSA-PSS key decoded while
12+
// the table is still being built can miss the RSA-PSS name, and every later
13+
// RSA-PSS decode in the process fails as well. Here Workers decode an RSA-PSS
14+
// certificate while the root certificate loader or a thread pool crypto job
15+
// uses OpenSSL for the first time.
16+
17+
const assert = require('assert');
18+
const { X509Certificate } = require('crypto');
19+
const { once } = require('events');
20+
const { Worker, parentPort, workerData } = require('worker_threads');
21+
const fixtures = require('../common/fixtures');
22+
23+
if (workerData) {
24+
parentPort.postMessage('ready');
25+
Atomics.wait(workerData.barrier, 0, 0);
26+
const cert = new X509Certificate(workerData.pem);
27+
assert.strictEqual(cert.publicKey.asymmetricKeyType, 'rsa-pss');
28+
assert(Buffer.isBuffer(cert.toLegacyObject().pubkey));
29+
} else if (process.argv[2] === 'child') {
30+
// OpenSSL must not have been used in this process before the barrier opens.
31+
const pem = fixtures.readKey('rsa_pss_cert_2048.pem');
32+
const barrier = new Int32Array(new SharedArrayBuffer(4));
33+
const workers = Array.from({ length: 8 }, () =>
34+
new Worker(__filename, { workerData: { pem, barrier } }));
35+
Promise.all(workers.map((w) => once(w, 'message'))).then(common.mustCall(() => {
36+
if (process.argv[3] === 'tls') {
37+
require('tls');
38+
} else {
39+
require('crypto').pbkdf2('password', '0123456789abcdef', 1000, 32,
40+
'sha256', common.mustSucceed());
41+
}
42+
Atomics.store(barrier, 0, 1);
43+
Atomics.notify(barrier, 0);
44+
}));
45+
} else {
46+
(async () => {
47+
for (const mode of ['tls', 'pbkdf2']) {
48+
for (let round = 0; round < 3; round++) {
49+
const children = Array.from({ length: 4 }, () =>
50+
common.spawnPromisified(process.execPath,
51+
[__filename, 'child', mode]));
52+
for (const { code, signal, stderr } of await Promise.all(children)) {
53+
assert.strictEqual(signal, null, `${mode}: ${stderr}`);
54+
assert.strictEqual(code, 0, `${mode}: ${stderr}`);
55+
}
56+
}
57+
}
58+
})().then(common.mustCall());
59+
}

0 commit comments

Comments
 (0)