• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Flags: --expose-internals
2'use strict';
3const common = require('../common');
4if (!common.hasCrypto)
5  common.skip('missing crypto');
6
7const assert = require('assert');
8const spawnSync = require('child_process').spawnSync;
9const path = require('path');
10const fixtures = require('../common/fixtures');
11const { internalBinding } = require('internal/test/binding');
12const { testFipsCrypto } = internalBinding('crypto');
13
14const FIPS_ENABLED = 1;
15const FIPS_DISABLED = 0;
16const FIPS_ERROR_STRING2 =
17  'Error [ERR_CRYPTO_FIPS_FORCED]: Cannot set FIPS mode, it was forced with ' +
18  '--force-fips at startup.';
19const FIPS_UNSUPPORTED_ERROR_STRING = 'fips mode not supported';
20const FIPS_ENABLE_ERROR_STRING = 'OpenSSL error when trying to enable FIPS:';
21
22const CNF_FIPS_ON = fixtures.path('openssl_fips_enabled.cnf');
23const CNF_FIPS_OFF = fixtures.path('openssl_fips_disabled.cnf');
24
25let num_children_ok = 0;
26
27function sharedOpenSSL() {
28  return process.config.variables.node_shared_openssl;
29}
30
31function testHelper(stream, args, expectedOutput, cmd, env) {
32  const fullArgs = args.concat(['-e', `console.log(${cmd})`]);
33  const child = spawnSync(process.execPath, fullArgs, {
34    cwd: path.dirname(process.execPath),
35    env: env
36  });
37
38  console.error(
39    `Spawned child [pid:${child.pid}] with cmd '${cmd}' expect %j with args '${
40      args}' OPENSSL_CONF=%j`, expectedOutput, env.OPENSSL_CONF);
41
42  function childOk(child) {
43    console.error(`Child #${++num_children_ok} [pid:${child.pid}] OK.`);
44  }
45
46  function responseHandler(buffer, expectedOutput) {
47    const response = buffer.toString();
48    assert.notStrictEqual(response.length, 0);
49    if (FIPS_ENABLED !== expectedOutput && FIPS_DISABLED !== expectedOutput) {
50      // In the case of expected errors just look for a substring.
51      assert.ok(response.includes(expectedOutput));
52    } else {
53      const getFipsValue = Number(response);
54      if (!Number.isNaN(getFipsValue))
55        // Normal path where we expect either FIPS enabled or disabled.
56        assert.strictEqual(getFipsValue, expectedOutput);
57    }
58    childOk(child);
59  }
60
61  responseHandler(child[stream], expectedOutput);
62}
63
64// --enable-fips should raise an error if OpenSSL is not FIPS enabled.
65testHelper(
66  testFipsCrypto() ? 'stdout' : 'stderr',
67  ['--enable-fips'],
68  testFipsCrypto() ? FIPS_ENABLED : FIPS_ENABLE_ERROR_STRING,
69  'process.versions',
70  process.env);
71
72// --force-fips should raise an error if OpenSSL is not FIPS enabled.
73testHelper(
74  testFipsCrypto() ? 'stdout' : 'stderr',
75  ['--force-fips'],
76  testFipsCrypto() ? FIPS_ENABLED : FIPS_ENABLE_ERROR_STRING,
77  'process.versions',
78  process.env);
79
80// By default FIPS should be off in both FIPS and non-FIPS builds
81// unless Node.js was configured using --shared-openssl in
82// which case it may be enabled by the system.
83if (!sharedOpenSSL()) {
84  testHelper(
85    'stdout',
86    [],
87    FIPS_DISABLED,
88    'require("crypto").getFips()',
89    { ...process.env, 'OPENSSL_CONF': ' ' });
90}
91
92// This should succeed for both FIPS and non-FIPS builds in combination with
93// OpenSSL 1.1.1 or OpenSSL 3.0
94const test_result = testFipsCrypto();
95assert.ok(test_result === 1 || test_result === 0);
96
97// If Node was configured using --shared-openssl fips support might be
98// available depending on how OpenSSL was built. If fips support is
99// available the tests that toggle the fips_mode on/off using the config
100// file option will succeed and return 1 instead of 0.
101//
102// Note that this case is different from when calling the fips setter as the
103// configuration file is handled by OpenSSL, so it is not possible for us
104// to try to call the fips setter, to try to detect this situation, as
105// that would throw an error:
106// ("Error: Cannot set FIPS mode in a non-FIPS build.").
107// Due to this uncertainty the following tests are skipped when configured
108// with --shared-openssl.
109if (!sharedOpenSSL() && !common.hasOpenSSL3) {
110  // OpenSSL config file should be able to turn on FIPS mode
111  testHelper(
112    'stdout',
113    [`--openssl-config=${CNF_FIPS_ON}`],
114    testFipsCrypto() ? FIPS_ENABLED : FIPS_DISABLED,
115    'require("crypto").getFips()',
116    process.env);
117
118  // OPENSSL_CONF should be able to turn on FIPS mode
119  testHelper(
120    'stdout',
121    [],
122    testFipsCrypto() ? FIPS_ENABLED : FIPS_DISABLED,
123    'require("crypto").getFips()',
124    Object.assign({}, process.env, { 'OPENSSL_CONF': CNF_FIPS_ON }));
125
126  // --openssl-config option should override OPENSSL_CONF
127  testHelper(
128    'stdout',
129    [`--openssl-config=${CNF_FIPS_ON}`],
130    testFipsCrypto() ? FIPS_ENABLED : FIPS_DISABLED,
131    'require("crypto").getFips()',
132    Object.assign({}, process.env, { 'OPENSSL_CONF': CNF_FIPS_OFF }));
133}
134
135// OpenSSL 3.x has changed the configuration files so the following tests
136// will not work as expected with that version.
137// TODO(danbev) Revisit these test once FIPS support is available in
138// OpenSSL 3.x.
139if (!common.hasOpenSSL3) {
140  testHelper(
141    'stdout',
142    [`--openssl-config=${CNF_FIPS_OFF}`],
143    FIPS_DISABLED,
144    'require("crypto").getFips()',
145    Object.assign({}, process.env, { 'OPENSSL_CONF': CNF_FIPS_ON }));
146
147  // --enable-fips should take precedence over OpenSSL config file
148  testHelper(
149    testFipsCrypto() ? 'stdout' : 'stderr',
150    ['--enable-fips', `--openssl-config=${CNF_FIPS_OFF}`],
151    testFipsCrypto() ? FIPS_ENABLED : FIPS_UNSUPPORTED_ERROR_STRING,
152    'require("crypto").getFips()',
153    process.env);
154  // --force-fips should take precedence over OpenSSL config file
155  testHelper(
156    testFipsCrypto() ? 'stdout' : 'stderr',
157    ['--force-fips', `--openssl-config=${CNF_FIPS_OFF}`],
158    testFipsCrypto() ? FIPS_ENABLED : FIPS_UNSUPPORTED_ERROR_STRING,
159    'require("crypto").getFips()',
160    process.env);
161  // --enable-fips should turn FIPS mode on
162  testHelper(
163    testFipsCrypto() ? 'stdout' : 'stderr',
164    ['--enable-fips'],
165    testFipsCrypto() ? FIPS_ENABLED : FIPS_UNSUPPORTED_ERROR_STRING,
166    'require("crypto").getFips()',
167    process.env);
168
169  // --force-fips should turn FIPS mode on
170  testHelper(
171    testFipsCrypto() ? 'stdout' : 'stderr',
172    ['--force-fips'],
173    testFipsCrypto() ? FIPS_ENABLED : FIPS_UNSUPPORTED_ERROR_STRING,
174    'require("crypto").getFips()',
175    process.env);
176
177  // OPENSSL_CONF should _not_ make a difference to --enable-fips
178  testHelper(
179    testFipsCrypto() ? 'stdout' : 'stderr',
180    ['--enable-fips'],
181    testFipsCrypto() ? FIPS_ENABLED : FIPS_UNSUPPORTED_ERROR_STRING,
182    'require("crypto").getFips()',
183    Object.assign({}, process.env, { 'OPENSSL_CONF': CNF_FIPS_OFF }));
184
185  // Using OPENSSL_CONF should not make a difference to --force-fips
186  testHelper(
187    testFipsCrypto() ? 'stdout' : 'stderr',
188    ['--force-fips'],
189    testFipsCrypto() ? FIPS_ENABLED : FIPS_UNSUPPORTED_ERROR_STRING,
190    'require("crypto").getFips()',
191    Object.assign({}, process.env, { 'OPENSSL_CONF': CNF_FIPS_OFF }));
192
193  // setFipsCrypto should be able to turn FIPS mode on
194  testHelper(
195    testFipsCrypto() ? 'stdout' : 'stderr',
196    [],
197    testFipsCrypto() ? FIPS_ENABLED : FIPS_UNSUPPORTED_ERROR_STRING,
198    '(require("crypto").setFips(true),' +
199    'require("crypto").getFips())',
200    process.env);
201
202  // setFipsCrypto should be able to turn FIPS mode on and off
203  testHelper(
204    testFipsCrypto() ? 'stdout' : 'stderr',
205    [],
206    testFipsCrypto() ? FIPS_DISABLED : FIPS_UNSUPPORTED_ERROR_STRING,
207    '(require("crypto").setFips(true),' +
208    'require("crypto").setFips(false),' +
209    'require("crypto").getFips())',
210    process.env);
211
212  // setFipsCrypto takes precedence over OpenSSL config file, FIPS on
213  testHelper(
214    testFipsCrypto() ? 'stdout' : 'stderr',
215    [`--openssl-config=${CNF_FIPS_OFF}`],
216    testFipsCrypto() ? FIPS_ENABLED : FIPS_UNSUPPORTED_ERROR_STRING,
217    '(require("crypto").setFips(true),' +
218    'require("crypto").getFips())',
219    process.env);
220
221  // setFipsCrypto takes precedence over OpenSSL config file, FIPS off
222  testHelper(
223    'stdout',
224    [`--openssl-config=${CNF_FIPS_ON}`],
225    FIPS_DISABLED,
226    '(require("crypto").setFips(false),' +
227    'require("crypto").getFips())',
228    process.env);
229
230  // --enable-fips does not prevent use of setFipsCrypto API
231  testHelper(
232    testFipsCrypto() ? 'stdout' : 'stderr',
233    ['--enable-fips'],
234    testFipsCrypto() ? FIPS_DISABLED : FIPS_UNSUPPORTED_ERROR_STRING,
235    '(require("crypto").setFips(false),' +
236    'require("crypto").getFips())',
237    process.env);
238
239  // --force-fips prevents use of setFipsCrypto API
240  testHelper(
241    'stderr',
242    ['--force-fips'],
243    testFipsCrypto() ? FIPS_ERROR_STRING2 : FIPS_UNSUPPORTED_ERROR_STRING,
244    'require("crypto").setFips(false)',
245    process.env);
246
247  // --force-fips makes setFipsCrypto enable a no-op (FIPS stays on)
248  testHelper(
249    testFipsCrypto() ? 'stdout' : 'stderr',
250    ['--force-fips'],
251    testFipsCrypto() ? FIPS_ENABLED : FIPS_UNSUPPORTED_ERROR_STRING,
252    '(require("crypto").setFips(true),' +
253    'require("crypto").getFips())',
254    process.env);
255
256  // --force-fips and --enable-fips order does not matter
257  testHelper(
258    'stderr',
259    ['--force-fips', '--enable-fips'],
260    testFipsCrypto() ? FIPS_ERROR_STRING2 : FIPS_UNSUPPORTED_ERROR_STRING,
261    'require("crypto").setFips(false)',
262    process.env);
263
264  // --enable-fips and --force-fips order does not matter
265  testHelper(
266    'stderr',
267    ['--enable-fips', '--force-fips'],
268    testFipsCrypto() ? FIPS_ERROR_STRING2 : FIPS_UNSUPPORTED_ERROR_STRING,
269    'require("crypto").setFips(false)',
270    process.env);
271}
272