Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions doc/api/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`

Expand Down Expand Up @@ -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`:

Expand Down
13 changes: 6 additions & 7 deletions doc/api/crypto.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand Down Expand Up @@ -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
Expand Down
8 changes: 5 additions & 3 deletions doc/node.1
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
13 changes: 0 additions & 13 deletions src/crypto/crypto_util.cc
Original file line number Diff line number Diff line change
Expand Up @@ -448,19 +448,6 @@ std::optional<std::string> 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";
Expand Down
17 changes: 17 additions & 0 deletions test/fixtures/openssl3-conf/fips_no_provider.cnf
Original file line number Diff line number Diff line change
@@ -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
73 changes: 33 additions & 40 deletions test/parallel/test-crypto-fips.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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(
Expand All @@ -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,
Expand Down
Loading