Files
node/test/parallel/test-webcrypto-supports-fips.js
T
Filip Skokan acbc3ff133 test,benchmark: drop OpenSSL 1.x-only coverage
Delete test-crypto-ecb.js, which can no longer run anywhere: Blowfish
is available only from OpenSSL's legacy provider and is absent from
BoringSSL.

The addon and cctest version guards become OPENSSL_IS_BORINGSSL checks.
Remove legacy digest-alias coverage and unreachable OpenSSL 1.x
branches. JavaScript test and benchmark gates, skip reasons, and TLS
error expectations that distinguished OpenSSL 1.x from provider-backed
OpenSSL now distinguish BoringSSL, including PKCS12 parsing, named DH
groups, private-key serialization, RSA-PSS parameters, consolidated
provider caches, FIPS availability, and empty KMAC output. Keep the CCM
alternate finalization error only for BoringSSL.

Provider-only test skips use version-neutral terminology or state the
actual minimum OpenSSL version.

Signed-off-by: Filip Skokan <panva.ip@gmail.com>
Assisted-by: Codex
PR-URL: https://github.com/nodejs/node/pull/64777
Fixes: https://github.com/nodejs/node/issues/56733
Refs: https://github.com/nodejs/node/pull/64211
Refs: https://github.com/nodejs/node/pull/63966
Refs: https://github.com/nodejs/node/pull/53329
Refs: https://github.com/nodejs/node/pull/48950
Refs: https://github.com/nodejs/node/issues/45465
Refs: https://github.com/nodejs/node/issues/42827
Refs: https://github.com/nodejs/node/issues/47076
Refs: https://github.com/nodejs/node/pull/62862
Refs: https://github.com/nodejs/node/pull/38512
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Richard Lau <richard.lau@ibm.com>
Reviewed-By: Gürgün Dayıoğlu <hey@gurgun.day>
2026-10-25 22:36:32 +01:00

138 lines
5.3 KiB
JavaScript

// Flags: --expose-internals --no-warnings
'use strict';
const common = require('../common');
if (!common.hasCrypto) common.skip('missing crypto');
const { isBoringSSL } = require('../common/crypto');
if (isBoringSSL) common.skip('requires an OpenSSL provider');
const assert = require('node:assert');
const crypto = require('node:crypto');
const { once } = require('node:events');
const { isMainThread, parentPort, Worker, workerData } = require('node:worker_threads');
const { normalizeAlgorithm } = require('internal/crypto/util');
const { subtle } = globalThis.crypto;
if (!isMainThread && !workerData?.supportsFips)
common.skip('crypto.setFips() is not supported in workers');
async function check() {
const fips = crypto.getFips() === 1;
const turbo = { name: 'TurboSHAKE128', outputLength: 128 };
if (fips) {
const error = { name: 'NotSupportedError', message: 'Unrecognized algorithm name' };
assert.throws(() => normalizeAlgorithm(turbo, 'digest'), error);
await assert.rejects(subtle.digest(turbo, new Uint8Array()), error);
} else {
assert.strictEqual(normalizeAlgorithm(turbo, 'digest').name, turbo.name);
assert.strictEqual((await subtle.digest(turbo, new Uint8Array())).byteLength, 16);
}
assert.strictEqual(SubtleCrypto.supports('digest', turbo), !fips);
for (const [algorithm, usages] of [
[{ name: 'AES-OCB', length: 128 }, ['encrypt']],
[{ name: 'X25519' }, ['deriveBits']],
]) {
const supported = SubtleCrypto.supports('generateKey', algorithm);
const generated = subtle.generateKey(algorithm, true, usages);
if (supported) {
await generated;
} else {
await assert.rejects(generated, {
name: 'NotSupportedError', message: 'Unrecognized algorithm name',
});
}
}
for (const name of ['ECDH', 'ECDSA']) {
for (const namedCurve of ['P-256', 'P-384', 'P-521']) {
const algorithm = { name, namedCurve };
for (const operation of ['generateKey', 'importKey']) {
assert.strictEqual(SubtleCrypto.supports(operation, algorithm), true);
}
}
}
const hashes = crypto.getHashes();
const hashError = { name: 'NotSupportedError', message: 'Unrecognized algorithm name' };
const rsa = {
name: 'RSA-PSS', modulusLength: 1024,
publicExponent: new Uint8Array([1, 0, 1]), hash: 'SHA-256',
};
assert.strictEqual(SubtleCrypto.supports('generateKey', rsa), !fips);
if (fips) {
// Hash normalization precedes the RSA operation's modulus validation.
const error = hashes.includes('sha256') ? {
name: 'OperationError', message: 'algorithm.modulusLength must be at least 2048',
} : hashError;
await assert.rejects(subtle.generateKey(rsa, true, ['sign']), error);
} else {
assert.strictEqual(normalizeAlgorithm(rsa, 'generateKey').modulusLength, 1024);
}
const salt = new Uint8Array(16);
for (const [name, alias] of [
['SHA-1', 'sha1'], ['SHA-256', 'sha256'], ['SHA-384', 'sha384'], ['SHA-512', 'sha512'],
['SHA3-256', 'sha3-256'], ['SHA3-384', 'sha3-384'], ['SHA3-512', 'sha3-512'],
]) {
const available = hashes.includes(alias);
for (const hash of [name, { name }]) {
const hmac = { name: 'HMAC', hash, length: 256 };
const hkdf = { name: 'HKDF', hash, salt, info: new Uint8Array() };
for (const [operations, algorithm] of [
[['digest'], hash],
[['generateKey', 'importKey'], { ...rsa, modulusLength: 2048, hash }],
[['generateKey', 'importKey'], hmac],
[['sign', 'verify'], { name: 'ECDSA', hash }],
[['deriveBits'], hkdf],
[['deriveBits'], { name: 'PBKDF2', hash, salt, iterations: 1 }],
]) {
for (const operation of operations) {
const length = operation === 'deriveBits' ? 128 : undefined;
assert.strictEqual(SubtleCrypto.supports(operation, algorithm, length), available);
if (!available)
assert.throws(() => normalizeAlgorithm(algorithm, operation), hashError);
}
}
assert.strictEqual(SubtleCrypto.supports('deriveKey', hkdf, hmac), available);
if (!available)
assert.throws(() => normalizeAlgorithm(hmac, 'get key length'), hashError);
}
}
}
if (!isMainThread) {
parentPort.on('message', async () => {
await check();
parentPort.postMessage(crypto.getFips());
});
parentPort.postMessage('ready');
} else {
(async () => {
const originalFips = crypto.getFips();
await check();
try {
crypto.setFips(0);
} catch (err) {
if (err.code !== 'ERR_CRYPTO_FIPS_FORCED') throw err;
common.printSkipMessage('FIPS mode cannot be disabled');
return;
}
let worker;
try {
// Start the worker before enabling FIPS: isolate initialization can need
// OpenSSL entropy, which is unavailable without an active FIPS provider.
worker = new Worker(__filename, { workerData: { supportsFips: true } });
worker.on('error', common.mustNotCall());
await once(worker, 'message');
for (const fips of [0, 1, 0]) {
crypto.setFips(fips);
await check();
const response = once(worker, 'message');
worker.postMessage('check');
const [actual] = await response;
assert.strictEqual(actual, fips);
}
} finally {
if (worker !== undefined) await worker.terminate();
crypto.setFips(originalFips);
}
})().then(common.mustCall());
}