Skip to content

Commit

Permalink
test: move crypto related common utilities in common/crypto
Browse files Browse the repository at this point in the history
Since `common/crypto` already exists, it makes sense to keep
crypto-related utilities there. The only exception being
common.hasCrypto which is needed up front to determine
if tests should be skipped.

Eliminate the redundant check in hasFipsCrypto and just
use crypto.getFips() directly where needed.

PR-URL: #56714
Reviewed-By: Yagiz Nizipli <[email protected]>
Reviewed-By: Luigi Pinca <[email protected]>
  • Loading branch information
jasnell authored Jan 25, 2025
1 parent c752615 commit 761de81
Show file tree
Hide file tree
Showing 89 changed files with 505 additions and 288 deletions.
7 changes: 5 additions & 2 deletions test/addons/openssl-providers/providers.cjs
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
'use strict';

const common = require('../../common');
if (!common.hasCrypto)
if (!common.hasCrypto) {
common.skip('missing crypto');
}
const { hasOpenSSL3 } = require('../../common/crypto');

if (!common.hasOpenSSL3)
if (!hasOpenSSL3) {
common.skip('this test requires OpenSSL 3.x');
}
const assert = require('node:assert');
const { createHash, getCiphers, getHashes } = require('node:crypto');
const { debuglog } = require('node:util');
Expand Down
5 changes: 4 additions & 1 deletion test/benchmark/test-benchmark-crypto.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@ const common = require('../common');
if (!common.hasCrypto)
common.skip('missing crypto');

if (common.hasFipsCrypto)
const { getFips } = require('crypto');

if (getFips()) {
common.skip('some benchmarks are FIPS-incompatible');
}

const runBenchmark = require('../common/benchmark');

Expand Down
17 changes: 0 additions & 17 deletions test/common/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -226,17 +226,6 @@ The TTY file descriptor is assumed to be capable of being writable.

Indicates whether OpenSSL is available.

### `hasFipsCrypto`

* [\<boolean>][<boolean>]

Indicates that Node.js has been linked with a FIPS compatible OpenSSL library,
and that FIPS as been enabled using `--enable-fips`.

To only detect if the OpenSSL library is FIPS compatible, regardless if it has
been enabled or not, then `process.config.variables.openssl_is_fips` can be
used to determine that situation.

### `hasIntl`

* [\<boolean>][<boolean>]
Expand Down Expand Up @@ -417,12 +406,6 @@ Returns `true` if the exit code `exitCode` and/or signal name `signal` represent
the exit code and/or signal name of a node process that aborted, `false`
otherwise.

### `opensslCli`

* [\<boolean>][<boolean>]

Indicates whether 'opensslCli' is supported.

### `platformTimeout(ms)`

* `ms` [\<number>][<number>] | [\<bigint>][<bigint>]
Expand Down
52 changes: 51 additions & 1 deletion test/common/crypto.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
'use strict';

const common = require('../common');
if (!common.hasCrypto)
if (!common.hasCrypto) {
common.skip('missing crypto');
}

const assert = require('assert');
const crypto = require('crypto');
Expand Down Expand Up @@ -98,6 +99,27 @@ const pkcs8EncExp = getRegExpForPEM('ENCRYPTED PRIVATE KEY');
const sec1Exp = getRegExpForPEM('EC PRIVATE KEY');
const sec1EncExp = (cipher) => getRegExpForPEM('EC PRIVATE KEY', cipher);

// Synthesize OPENSSL_VERSION_NUMBER format with the layout 0xMNN00PPSL
const opensslVersionNumber = (major = 0, minor = 0, patch = 0) => {
assert(major >= 0 && major <= 0xf);
assert(minor >= 0 && minor <= 0xff);
assert(patch >= 0 && patch <= 0xff);
return (major << 28) | (minor << 20) | (patch << 4);
};

let OPENSSL_VERSION_NUMBER;
const hasOpenSSL = (major = 0, minor = 0, patch = 0) => {
if (!common.hasCrypto) return false;
if (OPENSSL_VERSION_NUMBER === undefined) {
const regexp = /(?<m>\d+)\.(?<n>\d+)\.(?<p>\d+)/;
const { m, n, p } = process.versions.openssl.match(regexp).groups;
OPENSSL_VERSION_NUMBER = opensslVersionNumber(m, n, p);
}
return OPENSSL_VERSION_NUMBER >= opensslVersionNumber(major, minor, patch);
};

let opensslCli = null;

module.exports = {
modp2buf,
assertApproximateSize,
Expand All @@ -111,4 +133,32 @@ module.exports = {
pkcs8EncExp, // used once
sec1Exp,
sec1EncExp,
hasOpenSSL,
get hasOpenSSL3() {
return hasOpenSSL(3);
},
// opensslCli defined lazily to reduce overhead of spawnSync
get opensslCli() {
if (opensslCli !== null) return opensslCli;

if (process.config.variables.node_shared_openssl) {
// Use external command
opensslCli = 'openssl';
} else {
const path = require('path');
// Use command built from sources included in Node.js repository
opensslCli = path.join(path.dirname(process.execPath), 'openssl-cli');
}

if (exports.isWindows) opensslCli += '.exe';

const { spawnSync } = require('child_process');

const opensslCmd = spawnSync(opensslCli, ['version']);
if (opensslCmd.status !== 0 || opensslCmd.error !== undefined) {
// OpenSSL command cannot be executed
opensslCli = false;
}
return opensslCli;
},
};
52 changes: 0 additions & 52 deletions test/common/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.

/* eslint-disable node-core/crypto-check */
'use strict';
const process = global.process; // Some tests tamper with the process global.

Expand Down Expand Up @@ -57,25 +56,6 @@ const noop = () => {};
const hasCrypto = Boolean(process.versions.openssl) &&
!process.env.NODE_SKIP_CRYPTO;

// Synthesize OPENSSL_VERSION_NUMBER format with the layout 0xMNN00PPSL
const opensslVersionNumber = (major = 0, minor = 0, patch = 0) => {
assert(major >= 0 && major <= 0xf);
assert(minor >= 0 && minor <= 0xff);
assert(patch >= 0 && patch <= 0xff);
return (major << 28) | (minor << 20) | (patch << 4);
};

let OPENSSL_VERSION_NUMBER;
const hasOpenSSL = (major = 0, minor = 0, patch = 0) => {
if (!hasCrypto) return false;
if (OPENSSL_VERSION_NUMBER === undefined) {
const regexp = /(?<m>\d+)\.(?<n>\d+)\.(?<p>\d+)/;
const { m, n, p } = process.versions.openssl.match(regexp).groups;
OPENSSL_VERSION_NUMBER = opensslVersionNumber(m, n, p);
}
return OPENSSL_VERSION_NUMBER >= opensslVersionNumber(major, minor, patch);
};

const hasQuic = hasCrypto && !!process.config.variables.openssl_quic;

function parseTestFlags(filename = process.argv[1]) {
Expand Down Expand Up @@ -220,7 +200,6 @@ if (process.env.NODE_TEST_WITH_ASYNC_HOOKS) {
}).enable();
}

let opensslCli = null;
let inFreeBSDJail = null;
let localhostIPv4 = null;

Expand Down Expand Up @@ -985,7 +964,6 @@ const common = {
getTTYfd,
hasIntl,
hasCrypto,
hasOpenSSL,
hasQuic,
hasMultiLocalhost,
invalidArgTypeHelper,
Expand Down Expand Up @@ -1027,10 +1005,6 @@ const common = {
return require('os').totalmem() > 0x70000000; /* 1.75 Gb */
},

get hasFipsCrypto() {
return hasCrypto && require('crypto').getFips();
},

get hasIPv6() {
const iFaces = require('os').networkInterfaces();
let re;
Expand All @@ -1047,10 +1021,6 @@ const common = {
});
},

get hasOpenSSL3() {
return hasOpenSSL(3);
},

get inFreeBSDJail() {
if (inFreeBSDJail !== null) return inFreeBSDJail;

Expand Down Expand Up @@ -1100,28 +1070,6 @@ const common = {
return localhostIPv4;
},

// opensslCli defined lazily to reduce overhead of spawnSync
get opensslCli() {
if (opensslCli !== null) return opensslCli;

if (process.config.variables.node_shared_openssl) {
// Use external command
opensslCli = 'openssl';
} else {
// Use command built from sources included in Node.js repository
opensslCli = path.join(path.dirname(process.execPath), 'openssl-cli');
}

if (exports.isWindows) opensslCli += '.exe';

const opensslCmd = spawnSync(opensslCli, ['version']);
if (opensslCmd.status !== 0 || opensslCmd.error !== undefined) {
// OpenSSL command cannot be executed
opensslCli = false;
}
return opensslCli;
},

get PORT() {
if (+process.env.TEST_PARALLEL) {
throw new Error('common.PORT cannot be used in a parallelized test');
Expand Down
2 changes: 0 additions & 2 deletions test/common/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ const {
mustNotMutateObjectDeep,
mustSucceed,
nodeProcessAborted,
opensslCli,
parseTestFlags,
PIPE,
platformTimeout,
Expand Down Expand Up @@ -97,7 +96,6 @@ export {
mustNotMutateObjectDeep,
mustSucceed,
nodeProcessAborted,
opensslCli,
parseTestFlags,
PIPE,
platformTimeout,
Expand Down
3 changes: 2 additions & 1 deletion test/parallel/test-cli-node-options.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const { Worker } = require('worker_threads');

const fixtures = require('../common/fixtures');
const tmpdir = require('../common/tmpdir');
const { hasOpenSSL3 } = require('../common/crypto');
tmpdir.refresh();

const printA = path.relative(tmpdir.path, fixtures.path('printA.js'));
Expand Down Expand Up @@ -64,7 +65,7 @@ if (common.isLinux) {
if (common.hasCrypto) {
expectNoWorker('--use-openssl-ca', 'B\n');
expectNoWorker('--use-bundled-ca', 'B\n');
if (!common.hasOpenSSL3)
if (!hasOpenSSL3)
expectNoWorker('--openssl-config=_ossl_cfg', 'B\n');
}

Expand Down
20 changes: 12 additions & 8 deletions test/parallel/test-crypto-authenticated.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,17 @@
// Flags: --no-warnings
'use strict';
const common = require('../common');
if (!common.hasCrypto)
if (!common.hasCrypto) {
common.skip('missing crypto');
}

const assert = require('assert');
const crypto = require('crypto');
const { inspect } = require('util');
const fixtures = require('../common/fixtures');
const { hasOpenSSL3 } = require('../common/crypto');

const isFipsEnabled = crypto.getFips();

//
// Test authenticated encryption modes.
Expand All @@ -53,7 +57,7 @@ for (const test of TEST_CASES) {
continue;
}

if (common.hasFipsCrypto && test.iv.length < 24) {
if (isFipsEnabled && test.iv.length < 24) {
common.printSkipMessage('IV len < 12 bytes unsupported in FIPS mode');
continue;
}
Expand Down Expand Up @@ -95,7 +99,7 @@ for (const test of TEST_CASES) {
}

{
if (isCCM && common.hasFipsCrypto) {
if (isCCM && isFipsEnabled) {
assert.throws(() => {
crypto.createDecipheriv(test.algo,
Buffer.from(test.key, 'hex'),
Expand Down Expand Up @@ -286,7 +290,7 @@ for (const test of TEST_CASES) {
});
}, errMessages.authTagLength);

if (!common.hasFipsCrypto) {
if (!isFipsEnabled) {
assert.throws(() => {
crypto.createDecipheriv('aes-256-ccm',
'FxLKsqdmv0E9xrQhp0b1ZgI0K7JFZJM8',
Expand All @@ -312,7 +316,7 @@ for (const test of TEST_CASES) {
});

// CCM decryption and create(De|C)ipher are unsupported in FIPS mode.
if (!common.hasFipsCrypto) {
if (!isFipsEnabled) {
assert.throws(() => {
crypto.createDecipheriv(`aes-256-${mode}`,
'FxLKsqdmv0E9xrQhp0b1ZgI0K7JFZJM8',
Expand Down Expand Up @@ -388,7 +392,7 @@ for (const test of TEST_CASES) {
cipher.setAAD(Buffer.from('0123456789', 'hex'));
}, /options\.plaintextLength required for CCM mode with AAD/);

if (!common.hasFipsCrypto) {
if (!isFipsEnabled) {
assert.throws(() => {
const cipher = crypto.createDecipheriv('aes-256-ccm',
'FxLKsqdmv0E9xrQhp0b1ZgI0K7JFZJM8',
Expand All @@ -403,7 +407,7 @@ for (const test of TEST_CASES) {

// Test that final() throws in CCM mode when no authentication tag is provided.
{
if (!common.hasFipsCrypto) {
if (!isFipsEnabled) {
const key = Buffer.from('1ed2233fa2223ef5d7df08546049406c', 'hex');
const iv = Buffer.from('7305220bca40d4c90e1791e9', 'hex');
const ct = Buffer.from('8beba09d4d4d861f957d51c0794f4abf8030848e', 'hex');
Expand Down Expand Up @@ -562,7 +566,7 @@ for (const test of TEST_CASES) {
]) {
assert.throws(() => {
cipher.final();
}, common.hasOpenSSL3 ? {
}, hasOpenSSL3 ? {
code: 'ERR_OSSL_TAG_NOT_SET'
} : {
message: /Unsupported state/
Expand Down
10 changes: 6 additions & 4 deletions test/parallel/test-crypto-cipheriv-decipheriv.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ if (!common.hasCrypto)

const assert = require('assert');
const crypto = require('crypto');
const { hasOpenSSL3 } = require('../common/crypto');
const isFipsEnabled = crypto.getFips();

function testCipher1(key, iv) {
// Test encryption and decryption with explicit key and iv
Expand Down Expand Up @@ -150,7 +152,7 @@ testCipher1(Buffer.from('0123456789abcd0123456789'), '12345678');
testCipher1(Buffer.from('0123456789abcd0123456789'), Buffer.from('12345678'));
testCipher2(Buffer.from('0123456789abcd0123456789'), Buffer.from('12345678'));

if (!common.hasFipsCrypto) {
if (!isFipsEnabled) {
testCipher3(Buffer.from('000102030405060708090A0B0C0D0E0F', 'hex'),
Buffer.from('A6A6A6A6A6A6A6A6', 'hex'));
}
Expand Down Expand Up @@ -193,10 +195,10 @@ assert.throws(
errMessage);

// But all other IV lengths should be accepted.
const minIvLength = common.hasOpenSSL3 ? 8 : 1;
const maxIvLength = common.hasOpenSSL3 ? 64 : 256;
const minIvLength = hasOpenSSL3 ? 8 : 1;
const maxIvLength = hasOpenSSL3 ? 64 : 256;
for (let n = minIvLength; n < maxIvLength; n += 1) {
if (common.hasFipsCrypto && n < 12) continue;
if (isFipsEnabled && n < 12) continue;
crypto.createCipheriv('aes-128-gcm', Buffer.alloc(16), Buffer.alloc(n));
}

Expand Down
Loading

0 comments on commit 761de81

Please sign in to comment.