test: update tests to run with OpenSSL >= 3.0 FIPS mode · nodejs/node@810a6b0 · GitHub
Skip to content

Commit 810a6b0

Browse files
panvaaduh95
authored andcommitted
test: update tests to run with OpenSSL >= 3.0 FIPS mode
Signed-off-by: Filip Skokan <panva.ip@gmail.com> PR-URL: #64960 Backport-PR-URL: #66233 Fixes: #48379 Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
1 parent c555ba0 commit 810a6b0

130 files changed

Lines changed: 2836 additions & 1018 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎lib/internal/crypto/webcrypto.js‎

Lines changed: 1 addition & 0 deletions

‎test/common/crypto.js‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,14 @@ function assertApproximateSize(key, expectedSize) {
5050
function testEncryptDecrypt(publicKey, privateKey) {
5151
const message = 'Hello Node.js world!';
5252
const plaintext = Buffer.from(message, 'utf8');
53+
const withOaepHash = (key) => {
54+
if (!hasFIPS(3)) return key;
55+
if (key?.key !== undefined) return { ...key, oaepHash: 'sha256' };
56+
return { key, oaepHash: 'sha256' };
57+
};
5358
for (const key of [publicKey, privateKey]) {
54-
const ciphertext = publicEncrypt(key, plaintext);
55-
const received = privateDecrypt(privateKey, ciphertext);
59+
const ciphertext = publicEncrypt(withOaepHash(key), plaintext);
60+
const received = privateDecrypt(withOaepHash(privateKey), ciphertext);
5661
assert.strictEqual(received.toString('utf8'), message);
5762
}
5863
}

‎test/fixtures/keys/Makefile‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ all: \
88
ca5-cert.pem \
99
ca6-cert.pem \
1010
agent1-cert.pem \
11+
agent1-fips.pfx \
1112
agent1.pfx \
1213
agent2-cert.pem \
1314
agent3-cert.pem \
@@ -39,6 +40,7 @@ all: \
3940
dsa_private_encrypted_1025.pem \
4041
dsa_public_1025.pem \
4142
ec-cert.pem \
43+
ec-fips.pfx \
4244
ec.pfx \
4345
fake-cnnic-root-cert.pem \
4446
intermediate-ca-cert.pem \
@@ -445,6 +447,20 @@ agent1.pfx: agent1-cert.pem agent1-key.pem ca1-cert.pem
445447
-out agent1.pfx \
446448
-password pass:sample
447449

450+
# PKCS12KDF is unavailable under FIPS properties. Use PBMAC1 with PBKDF2
451+
# instead, alongside AES-256/PBKDF2 key protection.
452+
agent1-fips.pfx: agent1-cert.pem agent1-key.pem ca1-cert.pem
453+
openssl pkcs12 -export \
454+
-keypbe AES-256-CBC \
455+
-certpbe AES-256-CBC \
456+
-iter 2048 \
457+
-pbmac1_pbkdf2 \
458+
-in agent1-cert.pem \
459+
-inkey agent1-key.pem \
460+
-certfile ca1-cert.pem \
461+
-out agent1-fips.pfx \
462+
-password pass:password
463+
448464
agent1-verify: agent1-cert.pem ca1-cert.pem
449465
openssl verify -CAfile ca1-cert.pem agent1-cert.pem
450466

@@ -788,6 +804,18 @@ ec.pfx: ec-cert.pem ec-key.pem
788804
-out ec.pfx \
789805
-password pass:
790806

807+
# See agent1-fips.pfx for why the FIPS fixture uses PBMAC1.
808+
ec-fips.pfx: ec-cert.pem ec-key.pem
809+
openssl pkcs12 -export \
810+
-keypbe AES-256-CBC \
811+
-certpbe AES-256-CBC \
812+
-iter 2048 \
813+
-pbmac1_pbkdf2 \
814+
-in ec-cert.pem \
815+
-inkey ec-key.pem \
816+
-out ec-fips.pfx \
817+
-password pass:password
818+
791819
dh512.pem:
792820
openssl dhparam -out dh512.pem 512
793821

‎test/fixtures/keys/agent1-fips.pfx‎

3.72 KB
Binary file not shown.

‎test/fixtures/keys/ec-fips.pfx‎

1.23 KB
Binary file not shown.

‎test/parallel/test-crypto-argon2-job.js‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@ const common = require('../common');
44
if (!common.hasCrypto)
55
common.skip('missing crypto');
66

7-
const { hasOpenSSL } = require('../common/crypto');
7+
const { hasFIPS, hasOpenSSL } = require('../common/crypto');
88

99
if (!hasOpenSSL(3, 2))
1010
common.skip('requires OpenSSL >= 3.2');
11+
if (hasFIPS(3))
12+
common.skip('Argon2 is not available in FIPS mode');
1113

1214
// Exercises the native Argon2 job directly via internalBinding, bypassing
1315
// the JS validators, to ensure that if invalid parameters ever reach the

‎test/parallel/test-crypto-argon2.js‎

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ const common = require('../common');
33
if (!common.hasCrypto)
44
common.skip('missing crypto');
55

6-
const { hasOpenSSL } = require('../common/crypto');
6+
const { hasFIPS, hasOpenSSL } = require('../common/crypto');
77

88
if (!hasOpenSSL(3, 2))
99
common.skip('requires OpenSSL >= 3.2');
@@ -28,6 +28,19 @@ const secret = Buffer.alloc(8, 0x03);
2828
const associatedData = Buffer.alloc(12, 0x04);
2929
const defaults = { message, nonce, parallelism: 1, tagLength: 64, memory: 8, passes: 3 };
3030

31+
if (hasFIPS(3)) {
32+
assert.throws(() => crypto.argon2Sync('argon2id', defaults), {
33+
name: 'Error',
34+
message: /:digital envelope routines::unsupported$/,
35+
});
36+
crypto.argon2('argon2id', defaults, common.mustCall((err, result) => {
37+
assert.strictEqual(err?.name, 'Error');
38+
assert.match(err.message, /:digital envelope routines::unsupported$/);
39+
assert.strictEqual(result, undefined);
40+
}));
41+
return;
42+
}
43+
3144
const good = [
3245
// Test vectors from RFC 9106 https://www.rfc-editor.org/rfc/rfc9106.html#name-test-vectors
3346
// and OpenSSL 3.2 https://github.com/openssl/openssl/blob/6dfa998f7ea150f9c6d4e4727cf6d5c82a68a8da/test/recipes/30-test_evp_data/evpkdf_argon2.txt

‎test/parallel/test-crypto-async-sign-verify.js‎

Lines changed: 46 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@ const common = require('../common');
33
if (!common.hasCrypto)
44
common.skip('missing crypto');
55

6-
const { hasOpenSSL3 } = require('../common/crypto');
6+
const { hasOpenSSL, hasFIPS } = require('../common/crypto');
77
const assert = require('assert');
88
const util = require('util');
99
const crypto = require('crypto');
1010
const fixtures = require('../common/fixtures');
1111

12+
const fips3 = hasFIPS(3);
13+
1214
function test(
1315
publicFixture,
1416
privateFixture,
@@ -65,6 +67,17 @@ function test(
6567
}
6668
}
6769

70+
function testSignFailure(privateFixture, algorithm, options, code, message) {
71+
const key = { key: fixtures.readKey(privateFixture), ...options };
72+
const data = Buffer.from('Hello world');
73+
assert.throws(() => crypto.sign(algorithm, data, key), { code });
74+
crypto.sign(algorithm, data, key, common.mustCall((err) => {
75+
// Async crypto jobs in v24 preserve the OpenSSL message without a code.
76+
assert.strictEqual(err?.name, 'Error');
77+
assert.strictEqual(err.message, message);
78+
}));
79+
}
80+
6881
// RSA w/ default padding
6982
test('rsa_public.pem', 'rsa_private.pem', 'sha256', true);
7083
test('rsa_public.pem', 'rsa_private.pem', 'sha256', true,
@@ -94,14 +107,20 @@ if (!process.features.openssl_is_boringssl) {
94107
test('ed448_public.pem', 'ed448_private.pem', undefined, true);
95108

96109
// ECDSA w/ der signature encoding
97-
test('ec_secp256k1_public.pem', 'ec_secp256k1_private.pem', 'sha384',
98-
false);
99-
test('ec_secp256k1_public.pem', 'ec_secp256k1_private.pem', 'sha384',
100-
false, { dsaEncoding: 'der' });
101-
102-
// ECDSA w/ ieee-p1363 signature encoding
103-
test('ec_secp256k1_public.pem', 'ec_secp256k1_private.pem', 'sha384', false,
104-
{ dsaEncoding: 'ieee-p1363' });
110+
if (fips3) {
111+
testSignFailure('ec_secp256k1_private.pem', 'sha384', {},
112+
'ERR_OSSL_EVP_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE',
113+
'error:03000096:digital envelope routines::operation not supported for this keytype');
114+
} else {
115+
test('ec_secp256k1_public.pem', 'ec_secp256k1_private.pem', 'sha384',
116+
false);
117+
test('ec_secp256k1_public.pem', 'ec_secp256k1_private.pem', 'sha384',
118+
false, { dsaEncoding: 'der' });
119+
120+
// ECDSA w/ ieee-p1363 signature encoding
121+
test('ec_secp256k1_public.pem', 'ec_secp256k1_private.pem', 'sha384', false,
122+
{ dsaEncoding: 'ieee-p1363' });
123+
}
105124

106125
// DSA w/ der signature encoding
107126
test('dsa_public.pem', 'dsa_private.pem', 'sha256',
@@ -156,7 +175,7 @@ MCowBQYDK2VuAyEA6pwGRbadNQAI/tYN8+/p/0/hbsdHfOEGr1ADiLVk/Gc=
156175
const signature = crypto.randomBytes(16);
157176

158177
let expected = /no default digest/;
159-
if (hasOpenSSL3 || process.features.openssl_is_boringssl) {
178+
if (hasOpenSSL(3) || process.features.openssl_is_boringssl) {
160179
expected = /operation[\s_]not[\s_]supported[\s_]for[\s_]this[\s_]keytype/i;
161180
}
162181

@@ -167,11 +186,21 @@ MCowBQYDK2VuAyEA6pwGRbadNQAI/tYN8+/p/0/hbsdHfOEGr1ADiLVk/Gc=
167186
}
168187

169188
{
170-
const { privateKey } = crypto.generateKeyPairSync('rsa', {
171-
modulusLength: 512
172-
});
173-
crypto.sign('sha512', 'message', privateKey, common.mustCall((err) => {
174-
assert.ok(err);
175-
assert.match(err.message, /digest[\s_]too[\s_]big[\s_]for[\s_]rsa[\s_]key/i);
176-
}));
189+
if (fips3) {
190+
crypto.generateKeyPair('rsa', { modulusLength: 512 },
191+
common.mustCall((err) => {
192+
assert.strictEqual(err?.name, 'Error');
193+
assert.strictEqual(
194+
err.message, 'error:020000AE:rsa routines::invalid modulus');
195+
}));
196+
} else {
197+
const { privateKey } = crypto.generateKeyPairSync('rsa', {
198+
modulusLength: 512
199+
});
200+
crypto.sign('sha512', 'message', privateKey, common.mustCall((err) => {
201+
assert.ok(err);
202+
assert.match(
203+
err.message, /digest[\s_]too[\s_]big[\s_]for[\s_]rsa[\s_]key/i);
204+
}));
205+
}
177206
}

‎test/parallel/test-crypto-authenticated-stream.js‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ if (!common.hasCrypto)
66

77
const assert = require('assert');
88
const crypto = require('crypto');
9+
const { hasFIPS } = require('../common/crypto');
910
const fs = require('fs');
1011
const stream = require('stream');
1112
const tmpdir = require('../common/tmpdir');
@@ -120,6 +121,16 @@ function test(config) {
120121
return;
121122
}
122123

124+
if (hasFIPS(3)) {
125+
assert.throws(() => crypto.createDecipheriv(
126+
config.cipher, config.key, config.iv, {
127+
authTagLength: config.authTagLength,
128+
}), {
129+
code: 'ERR_CRYPTO_UNSUPPORTED_OPERATION',
130+
});
131+
return;
132+
}
133+
123134
direct(config);
124135
mstream(config);
125136
fstream(config);

‎test/parallel/test-crypto-authenticated.js‎

Lines changed: 24 additions & 8 deletions

0 commit comments

Comments
 (0)