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