diff --git a/doc/api/cli.md b/doc/api/cli.md index 415b65ac5efa..2045ce35eb16 100644 --- a/doc/api/cli.md +++ b/doc/api/cli.md @@ -1232,8 +1232,9 @@ priority than `--dns-result-order`. added: v6.0.0 --> -Enable [FIPS mode][] at startup. A configured provider named `fips` must be -available and initialize successfully. +Enable [FIPS mode][] at startup by adding `fips=yes` to OpenSSL's default +property query. This does not load or validate a FIPS provider or run its +self-tests. Configure OpenSSL to load the provider separately. ### `--enable-fips-indicator-events` @@ -1807,7 +1808,8 @@ changes: --> Enable [FIPS mode][] at startup and prevent it from being disabled from script -code. The same OpenSSL requirements as [`--enable-fips`][] apply. +code. This changes the default property query as described for +[`--enable-fips`][]. An optional mode can be specified using `--force-fips=mode`: diff --git a/doc/api/crypto.md b/doc/api/crypto.md index b14c4876884f..53f87ce0b8f6 100644 --- a/doc/api/crypto.md +++ b/doc/api/crypto.md @@ -6826,9 +6826,9 @@ OpenSSL-backed APIs. This method only affects subsequent algorithm fetches. Node.js initializes some OpenSSL state before application code runs. When the property query must be active from process startup, set `default_properties = fips=yes` in the OpenSSL -configuration or use [`--enable-fips`][] or [`--force-fips`][]. The command-line -flags additionally require a configured provider named `fips` to initialize and -pass its self-test; Node.js fails to start otherwise. +configuration or use [`--enable-fips`][] or [`--force-fips`][]. Like this method, +the command-line flags change the property query without loading or validating +a provider or running its self-tests. Throws an error if OpenSSL cannot change the state. FIPS mode cannot be disabled when Node.js was started with `--force-fips`. @@ -7475,10 +7475,9 @@ default section name. The configuration above enables the `fips=yes` property query at startup. The following controls are also available: -* [`--enable-fips`][] and [`--force-fips`][] enable the property query and - additionally require the configured provider named `fips` to initialize and - pass its self-test. Node.js exits if that check fails. `--force-fips` also - prevents FIPS mode from being disabled from script code. With +* [`--enable-fips`][] and [`--force-fips`][] enable the property query at startup. + They do not load or validate a provider or run its self-tests. `--force-fips` + also prevents FIPS mode from being disabled from script code. With `--force-fips=strict`, Node.js also rejects non-approved operations reported through the OpenSSL FIPS indicator callback. * [`crypto.setFips()`][] changes the FIPS/property-query state. It does not diff --git a/doc/node.1 b/doc/node.1 index 7419eb9a34d2..48603a130d25 100644 --- a/doc/node.1 +++ b/doc/node.1 @@ -750,8 +750,9 @@ The default is \fBverbatim\fR and \fBdns.setDefaultResultOrder()\fR have higher priority than \fB--dns-result-order\fR. . .It Fl -enable-fips -Enable FIPS mode at startup. A configured provider named \fBfips\fR must be -available and initialize successfully. +Enable FIPS mode at startup by adding \fBfips=yes\fR to OpenSSL's default +property query. This does not load or validate a FIPS provider or run its +self-tests. Configure OpenSSL to load the provider separately. . .It Fl -enable-fips-indicator-events Publish OpenSSL FIPS indicator results to the @@ -965,7 +966,8 @@ Disable loading native addons that are not context-aware. . .It Fl -force-fips Ns = Ns Ar [mode] Enable FIPS mode at startup and prevent it from being disabled from script -code. The same OpenSSL requirements as \fB--enable-fips\fR apply. +code. This changes the default property query as described for +\fB--enable-fips\fR. An optional mode can be specified using \fB--force-fips=mode\fR: .Bl -bullet .It diff --git a/src/crypto/crypto_util.cc b/src/crypto/crypto_util.cc index 783620c1210b..f601685fadae 100644 --- a/src/crypto/crypto_util.cc +++ b/src/crypto/crypto_util.cc @@ -448,19 +448,6 @@ std::optional ProcessFipsOptions() { const bool force_fips = per_process::cli_options->force_fips_crypto; if (!enable_fips && !force_fips) return std::nullopt; -#ifndef OPENSSL_IS_BORINGSSL - // Whether FIPS-approved implementations are reachable is decided by the - // OpenSSL configuration, not by Node.js. Refuse to start rather than - // restrict the default property query to a provider that is not there, - // which would leave every operation failing as unsupported. - if (!ncrypto::testFipsEnabled()) { - const std::string option = force_fips ? "--force-fips" : "--enable-fips"; - return option + " requires an active OpenSSL provider named \"fips\". " - "FIPS mode is configured through OpenSSL; see " - "https://nodejs.org/api/crypto.html#fips-mode"; - } -#endif - CryptoErrorList errors{CryptoErrorList::Option::NONE}; if (!ncrypto::setFipsEnabled(true, &errors)) { std::string error = "OpenSSL error when trying to enable FIPS"; diff --git a/test/fixtures/openssl3-conf/fips_no_provider.cnf b/test/fixtures/openssl3-conf/fips_no_provider.cnf new file mode 100644 index 000000000000..ee36d5b31a0a --- /dev/null +++ b/test/fixtures/openssl3-conf/fips_no_provider.cnf @@ -0,0 +1,17 @@ +nodejs_conf = nodejs_init +openssl_conf = nodejs_init + +[nodejs_init] +providers = provider_sect +random = random_sect + +[provider_sect] +default = default_sect + +[default_sect] +activate = 1 + +# Allow startup randomness while other operations use the default properties. +[random_sect] +properties = fips=no +seed_properties = fips=no diff --git a/test/parallel/test-crypto-fips.js b/test/parallel/test-crypto-fips.js index 2ff8e2325b3d..32780057cf47 100644 --- a/test/parallel/test-crypto-fips.js +++ b/test/parallel/test-crypto-fips.js @@ -12,17 +12,13 @@ if (isBoringSSL) const assert = require('assert'); const spawnSync = require('child_process').spawnSync; const path = require('path'); +const fixtures = require('../common/fixtures'); const { spawnSyncAndAssert } = require('../common/child_process'); const { internalBinding } = require('internal/test/binding'); const { testFipsCrypto } = internalBinding('crypto'); const FIPS_ENABLED = 1; const FIPS_DISABLED = 0; -const FIPS_ENABLE_ERROR_STRING = - '--enable-fips requires an active OpenSSL provider named "fips"'; -const FIPS_FORCE_ERROR_STRING = - '--force-fips requires an active OpenSSL provider named "fips"'; - const kNoFailure = 0; const kGenericUserError = 1; @@ -66,32 +62,37 @@ function testHelper(stream, args, expectedStatus, expectedOutput, cmd, env) { responseHandler(child[stream], expectedOutput); } -// --enable-fips should raise an error if OpenSSL is not FIPS enabled. -testHelper( - testFipsCrypto() ? 'stdout' : 'stderr', - ['--enable-fips'], - testFipsCrypto() ? kNoFailure : kGenericUserError, - testFipsCrypto() ? FIPS_ENABLED : FIPS_ENABLE_ERROR_STRING, - 'require("crypto").getFips()', - process.env); - -// --force-fips should raise an error if OpenSSL is not FIPS enabled. -testHelper( - testFipsCrypto() ? 'stdout' : 'stderr', - ['--force-fips'], - testFipsCrypto() ? kNoFailure : kGenericUserError, - testFipsCrypto() ? FIPS_ENABLED : FIPS_FORCE_ERROR_STRING, - 'require("crypto").getFips()', - process.env); - -// Explicit provider mode should preserve the behavior of bare --force-fips. -testHelper( - testFipsCrypto() ? 'stdout' : 'stderr', - ['--force-fips=provider'], - testFipsCrypto() ? kNoFailure : kGenericUserError, - testFipsCrypto() ? FIPS_ENABLED : FIPS_FORCE_ERROR_STRING, - 'require("crypto").getFips()', - process.env); +const fipsFlags = ['--enable-fips', '--force-fips', '--force-fips=provider']; +if (hasOpenSSL(3, 4)) + fipsFlags.push('--force-fips=strict'); + +// The flags change the default property query without requiring a provider +// named "fips". This configuration supplies the RNG needed during startup, +// but has no cryptographic implementations matching fips=yes. +for (const flag of fipsFlags) { + spawnSyncAndAssert(process.execPath, [ + flag, + `--openssl-config=${fixtures.path('openssl3-conf', 'fips_no_provider.cnf')}`, + '-e', ` + const assert = require('assert'); + const crypto = require('crypto'); + assert.strictEqual(crypto.getFips(), 1); + assert.throws(() => crypto.createHash('sha256'), { + code: 'ERR_OSSL_EVP_UNSUPPORTED', + }); + if (${flag === '--enable-fips'}) { + crypto.setFips(false); + assert.strictEqual(crypto.getFips(), 0); + crypto.createHash('sha256'); + } else { + assert.throws(() => crypto.setFips(false), { + code: 'ERR_CRYPTO_FIPS_FORCED', + }); + assert.strictEqual(crypto.getFips(), 1); + } + `, + ], { stdout: '', stderr: '' }); +} { spawnSyncAndAssert( @@ -101,15 +102,7 @@ testHelper( }); } -if (hasOpenSSL(3, 4)) { - testHelper( - testFipsCrypto() ? 'stdout' : 'stderr', - ['--force-fips=strict'], - testFipsCrypto() ? kNoFailure : kGenericUserError, - testFipsCrypto() ? FIPS_ENABLED : FIPS_FORCE_ERROR_STRING, - 'require("crypto").getFips()', - process.env); -} else { +if (!hasOpenSSL(3, 4)) { spawnSyncAndAssert( process.execPath, ['--enable-fips-indicator-events', '-e', '0'], { status: 9,