Commit 96fd36a8b53 for nodejs

commit 96fd36a8b53ad054c28a0736ec0732c35e30d79d
Author: Christian Aurich Zanettini Martins <christian.aurichzm@gmail.com>
Date:   Fri Oct 30 14:34:42 2026 -0300

    test: add test for concurrent first use of OpenSSL

    OpenSSL 3 builds its table of algorithm names on first use. When
    43d3fe95581 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: https://github.com/nodejs/node/pull/66903
    Refs: https://github.com/nodejs/node/pull/66935
    Signed-off-by: Christian Aurich Zanettini Martins <christian.aurichzm@gmail.com>
    PR-URL: https://github.com/nodejs/node/pull/66966
    Reviewed-By: Filip Skokan <panva.ip@gmail.com>

diff --git a/test/parallel/test-crypto-off-thread-first-use.js b/test/parallel/test-crypto-off-thread-first-use.js
new file mode 100644
index 00000000000..51528d4a859
--- /dev/null
+++ b/test/parallel/test-crypto-off-thread-first-use.js
@@ -0,0 +1,59 @@
+'use strict';
+const common = require('../common');
+if (!common.hasCrypto)
+  common.skip('missing crypto');
+
+const { hasOpenSSL } = require('../common/crypto');
+if (!hasOpenSSL(3))
+  common.skip('this test requires OpenSSL >= 3.x');
+
+// OpenSSL 3 builds its table of algorithm names on first use. If several
+// threads use OpenSSL for the first time at once, an RSA-PSS key decoded while
+// the table is still being built can miss the RSA-PSS name, and every later
+// RSA-PSS decode in the process fails as well. Here Workers decode an RSA-PSS
+// certificate while the root certificate loader or a thread pool crypto job
+// uses OpenSSL for the first time.
+
+const assert = require('assert');
+const { X509Certificate } = require('crypto');
+const { once } = require('events');
+const { Worker, parentPort, workerData } = require('worker_threads');
+const fixtures = require('../common/fixtures');
+
+if (workerData) {
+  parentPort.postMessage('ready');
+  Atomics.wait(workerData.barrier, 0, 0);
+  const cert = new X509Certificate(workerData.pem);
+  assert.strictEqual(cert.publicKey.asymmetricKeyType, 'rsa-pss');
+  assert(Buffer.isBuffer(cert.toLegacyObject().pubkey));
+} else if (process.argv[2] === 'child') {
+  // OpenSSL must not have been used in this process before the barrier opens.
+  const pem = fixtures.readKey('rsa_pss_cert_2048.pem');
+  const barrier = new Int32Array(new SharedArrayBuffer(4));
+  const workers = Array.from({ length: 8 }, () =>
+    new Worker(__filename, { workerData: { pem, barrier } }));
+  Promise.all(workers.map((w) => once(w, 'message'))).then(common.mustCall(() => {
+    if (process.argv[3] === 'tls') {
+      require('tls');
+    } else {
+      require('crypto').pbkdf2('password', '0123456789abcdef', 1000, 32,
+                               'sha256', common.mustSucceed());
+    }
+    Atomics.store(barrier, 0, 1);
+    Atomics.notify(barrier, 0);
+  }));
+} else {
+  (async () => {
+    for (const mode of ['tls', 'pbkdf2']) {
+      for (let round = 0; round < 3; round++) {
+        const children = Array.from({ length: 4 }, () =>
+          common.spawnPromisified(process.execPath,
+                                  [__filename, 'child', mode]));
+        for (const { code, signal, stderr } of await Promise.all(children)) {
+          assert.strictEqual(signal, null, `${mode}: ${stderr}`);
+          assert.strictEqual(code, 0, `${mode}: ${stderr}`);
+        }
+      }
+    }
+  })().then(common.mustCall());
+}