Skip to content

Commit 3345905

Browse files
committed
test: reduce expensive crypto test variants
Skip redundant RSA keygen and PBKDF2 WPT variants during regular CI. Keep the full matrix when producing a WPT report. Use fewer scrypt, PBKDF2, and Argon2 derivations in parallel tests. Reuse RSA key material across OAEP hash and PSS salt tests. Signed-off-by: Filip Skokan <panva.ip@gmail.com> Assisted-by: Codex
1 parent 03fcd8b commit 3345905

7 files changed

Lines changed: 176 additions & 165 deletions

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

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,15 @@ if (!hasOpenSSL(3, 2))
1111
const assert = require('node:assert');
1212
const crypto = require('node:crypto');
1313

14-
function runArgon2(algorithm, options) {
14+
function runArgon2(algorithm, options, testAsync = false) {
1515
const syncResult = crypto.argon2Sync(algorithm, options);
1616

17-
crypto.argon2(algorithm, options,
18-
common.mustSucceed((asyncResult) => {
19-
assert.deepStrictEqual(asyncResult, syncResult);
20-
}));
17+
if (testAsync) {
18+
crypto.argon2(algorithm, options,
19+
common.mustSucceed((asyncResult) => {
20+
assert.deepStrictEqual(asyncResult, syncResult);
21+
}));
22+
}
2123

2224
return syncResult;
2325
}
@@ -124,9 +126,11 @@ const bad = [
124126
assert.deepStrictEqual(omitted, explicitEmpty);
125127
}
126128

127-
for (const [algorithm, overrides, expected] of good) {
129+
// The RFC vectors exercise both APIs for each Argon2 algorithm. Other vectors
130+
// check distinct options without repeating the derivation asynchronously.
131+
for (const [index, [algorithm, overrides, expected]] of good.entries()) {
128132
const parameters = { ...defaults, ...overrides };
129-
const actual = runArgon2(algorithm, parameters);
133+
const actual = runArgon2(algorithm, parameters, index < 3);
130134
assert.strictEqual(actual.toString('hex'), expected);
131135
}
132136

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

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,16 @@ const validSalt = fips4 ? '0123456789abcdef' : 'salt';
1616
const validIterations = fips4 ? 1000 : 8;
1717
const validKeyLength = fips4 ? 16 : 8;
1818

19-
function runPBKDF2(password, salt, iterations, keylen, hash) {
19+
function runPBKDF2(password, salt, iterations, keylen, hash, testAsync = true) {
2020
const syncResult =
2121
crypto.pbkdf2Sync(password, salt, iterations, keylen, hash);
2222

23-
crypto.pbkdf2(password, salt, iterations, keylen, hash,
24-
common.mustSucceed((asyncResult) => {
25-
assert.deepStrictEqual(asyncResult, syncResult);
26-
}));
23+
if (testAsync) {
24+
crypto.pbkdf2(password, salt, iterations, keylen, hash,
25+
common.mustSucceed((asyncResult) => {
26+
assert.deepStrictEqual(asyncResult, syncResult);
27+
}));
28+
}
2729

2830
return syncResult;
2931
}
@@ -47,20 +49,22 @@ function assertPBKDF2Fails(password, salt, iterations, keylen, hash) {
4749
);
4850
}
4951

50-
function testPBKDF2(password, salt, iterations, keylen, expected, encoding) {
51-
const actual = runPBKDF2(password, salt, iterations, keylen, 'sha256');
52+
function testPBKDF2(password, salt, iterations, keylen, expected, encoding,
53+
testAsync = false) {
54+
const actual = runPBKDF2(password, salt, iterations, keylen, 'sha256', testAsync);
5255
assert.strictEqual(actual.toString(encoding || 'latin1'), expected);
5356
}
5457

5558
//
56-
// Test PBKDF2 with RFC 6070 test vectors (except #4)
59+
// Test PBKDF2 with RFC 6070 test vectors (except #4). Run one known answer
60+
// through the callback API; the rest only need synchronous derivation.
5761
//
5862

5963
if (fips4) {
6064
testPBKDF2(validPassword, validSalt, validIterations, 32,
6165
'8514638175a45bc45eb1f22f04ff7d27' +
6266
'f4f8be480498c455ff4b494ce8d1e7d2',
63-
'hex');
67+
'hex', true);
6468

6569
for (const args of [
6670
['short', validSalt, validIterations],
@@ -82,7 +86,7 @@ if (fips4) {
8286

8387
testPBKDF2('password', 'salt', 4096, 20,
8488
'\xc5\xe4\x78\xd5\x92\x88\xc8\x41\xaa\x53\x0d\xb6' +
85-
'\x84\x5c\x4c\x8d\x96\x28\x93\xa0');
89+
'\x84\x5c\x4c\x8d\x96\x28\x93\xa0', 'latin1', true);
8690

8791
testPBKDF2('passwordPASSWORDpassword',
8892
'saltSALTsaltSALTsaltSALTsaltSALTsalt',

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

Lines changed: 24 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ const good = [
4343
N: 1024,
4444
p: 16,
4545
r: 8,
46+
testAsync: true,
4647
expected:
4748
'fdbabe1c9d3472007856e7190d01e9fe7c6ad7cbc8237830e77376634b373162' +
4849
'2eaf30d92e22a3886ff109279d9830dac727afb94a83ee6d8360cbdfa2cc0640',
@@ -65,32 +66,11 @@ const good = [
6566
cost: 16,
6667
parallelization: 1,
6768
blockSize: 1,
69+
testAsync: true,
6870
expected:
6971
'77d6576238657b203b19ca42c18a0497f16b4844e3074ae8dfdffa3fede21442' +
7072
'fcd0069ded0948f8326a753a0fc81f17e8d3e0fb2e0d3628cf35e20c38d18906',
7173
},
72-
{
73-
pass: 'password',
74-
salt: 'NaCl',
75-
keylen: 64,
76-
cost: 1024,
77-
parallelization: 16,
78-
blockSize: 8,
79-
expected:
80-
'fdbabe1c9d3472007856e7190d01e9fe7c6ad7cbc8237830e77376634b373162' +
81-
'2eaf30d92e22a3886ff109279d9830dac727afb94a83ee6d8360cbdfa2cc0640',
82-
},
83-
{
84-
pass: 'pleaseletmein',
85-
salt: 'SodiumChloride',
86-
keylen: 64,
87-
cost: 16384,
88-
parallelization: 1,
89-
blockSize: 8,
90-
expected:
91-
'7023bdcb3afd7348461c06cd81fd38ebfda8fbba904f8e3ea9b543f6545da1f2' +
92-
'd5432955613f0fcf62d49705242a9af9e61e85dc0d651e40dfcf017b45575887',
93-
},
9474
];
9575

9676
// Test vectors that contain invalid parameters.
@@ -171,9 +151,11 @@ if (isFips) {
171151
const { pass, salt, keylen, expected } = options;
172152
const actual = crypto.scryptSync(pass, salt, keylen, options);
173153
assert.strictEqual(actual.toString('hex'), expected);
174-
crypto.scrypt(pass, salt, keylen, options, common.mustSucceed((actual) => {
175-
assert.strictEqual(actual.toString('hex'), expected);
176-
}));
154+
if (options.testAsync) {
155+
crypto.scrypt(pass, salt, keylen, options, common.mustSucceed((actual) => {
156+
assert.strictEqual(actual.toString('hex'), expected);
157+
}));
158+
}
177159
}
178160
}
179161

@@ -215,12 +197,11 @@ for (const options of toobig) {
215197
}
216198

217199
if (!isFips) {
218-
const defaults = { N: 16384, p: 1, r: 8 };
219-
const expected = crypto.scryptSync('pass', 'salt', 1, defaults);
220-
const actual = crypto.scryptSync('pass', 'salt', 1);
221-
assert.deepStrictEqual(actual.toString('hex'), expected.toString('hex'));
222-
crypto.scrypt('pass', 'salt', 1, common.mustSucceed((actual) => {
223-
assert.deepStrictEqual(actual.toString('hex'), expected.toString('hex'));
200+
const expected = '4cac4540';
201+
const actual = crypto.scryptSync('pass', 'salt', 4);
202+
assert.strictEqual(actual.toString('hex'), expected);
203+
crypto.scrypt('pass', 'salt', 4, common.mustSucceed((actual) => {
204+
assert.strictEqual(actual.toString('hex'), expected);
224205
}));
225206
}
226207

@@ -241,9 +222,9 @@ for (const { args, expected } of badargs) {
241222
// Values for maxmem that do not fit in 32 bits but that are still safe
242223
// integers should be allowed.
243224
if (!isFips) {
244-
crypto.scrypt('', '', 4, { maxmem: 2 ** 52 },
225+
crypto.scrypt('', '', 4, { N: 16, maxmem: 2 ** 52 },
245226
common.mustSucceed((actual) => {
246-
assert.strictEqual(actual.toString('hex'), 'd72c87d0');
227+
assert.strictEqual(actual.toString('hex'), 'e2b18837');
247228
}));
248229
}
249230

@@ -258,9 +239,12 @@ if (!isFips) {
258239

259240
function testParameter(name, value) {
260241
let accessCount = 0;
242+
// This regression checks getter access, so the derivation can be cheap.
243+
const options = name === 'cost' ? { cost: 16 } : { N: 16 };
261244

262245
// Find out how often the value is accessed.
263246
crypto.scryptSync('', '', 1, {
247+
...options,
264248
get [name]() {
265249
accessCount++;
266250
return value;
@@ -270,6 +254,7 @@ if (!isFips) {
270254
// Try to crash the process on the last access.
271255
assert.throws(() => {
272256
crypto.scryptSync('', '', 1, {
257+
...options,
273258
get [name]() {
274259
if (--accessCount === 0)
275260
return '';
@@ -282,7 +267,7 @@ if (!isFips) {
282267
}
283268

284269
[
285-
['N', 16384], ['cost', 16384],
270+
['N', 16], ['cost', 16],
286271
['r', 8], ['blockSize', 8],
287272
['p', 1], ['parallelization', 1],
288273
].forEach((arg) => testParameter(...arg));
@@ -292,17 +277,18 @@ if (!isFips) {
292277
// IsInt32() assertion. Assert that `-0` produces the same outcome as
293278
// `+0` (which differs by OpenSSL build).
294279
{
280+
const options = { N: 16 };
295281
let posError;
296282
let posResult;
297283
try {
298-
posResult = crypto.scryptSync('', '', 0);
284+
posResult = crypto.scryptSync('', '', 0, options);
299285
} catch (err) {
300286
posError = err;
301287
}
302288
let negError;
303289
let negResult;
304290
try {
305-
negResult = crypto.scryptSync('', '', -0);
291+
negResult = crypto.scryptSync('', '', -0, options);
306292
} catch (err) {
307293
negError = err;
308294
}
@@ -314,9 +300,9 @@ if (!isFips) {
314300

315301
if (isFips) {
316302
assert.throws(
317-
() => crypto.scrypt('', '', -0, () => {}),
303+
() => crypto.scrypt('', '', -0, options, () => {}),
318304
{ code: 'ERR_CRYPTO_INVALID_SCRYPT_PARAMS' });
319305
} else {
320-
crypto.scrypt('', '', -0, common.mustCall());
306+
crypto.scrypt('', '', -0, options, common.mustCall());
321307
}
322308
}

‎test/parallel/test-webcrypto-derivebits-argon2.js‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ for (const { algorithm, length, password, params, tag } of vectors) {
8080
const result = await subtle.deriveBits(parameters, key, length);
8181
assert.deepStrictEqual(result, tag);
8282
}
83-
{
83+
if (algorithm === 'Argon2id') {
8484
const derivedKeyType = { name: 'HMAC', length, hash: 'SHA-256' };
8585

8686
const key = createSecretKey(password)

‎test/parallel/test-webcrypto-encrypt-decrypt.js‎

Lines changed: 45 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -13,95 +13,55 @@ const { subtle } = globalThis.crypto;
1313
// This is only a partial test. The WebCrypto Web Platform Tests
1414
// will provide much greater coverage.
1515

16-
// Test Encrypt/Decrypt RSA-OAEP w/ SHA-2
17-
{
16+
async function testRSAOaep(publicKey, privateKey) {
1817
const buf = globalThis.crypto.getRandomValues(new Uint8Array(50));
19-
20-
async function test() {
21-
const ec = new TextEncoder();
22-
const { publicKey, privateKey } = await subtle.generateKey({
23-
name: 'RSA-OAEP',
24-
modulusLength: 2048,
25-
publicExponent: new Uint8Array([1, 0, 1]),
26-
hash: 'SHA-384',
27-
}, true, ['encrypt', 'decrypt']);
28-
29-
const ciphertext = await subtle.encrypt({
30-
name: 'RSA-OAEP',
31-
label: ec.encode('a label')
32-
}, publicKey, buf);
33-
34-
const plaintext = await subtle.decrypt({
35-
name: 'RSA-OAEP',
36-
label: ec.encode('a label')
37-
}, privateKey, ciphertext);
38-
39-
assert.strictEqual(
40-
Buffer.from(plaintext).toString('hex'),
41-
Buffer.from(buf).toString('hex'));
42-
43-
await assert.rejects(() => subtle.encrypt({
44-
name: 'RSA-OAEP',
45-
}, privateKey, buf), {
46-
name: 'InvalidAccessError',
47-
message: 'Unable to use this key to encrypt'
48-
});
49-
50-
await assert.rejects(() => subtle.decrypt({
51-
name: 'RSA-OAEP',
52-
}, publicKey, ciphertext), {
53-
name: 'InvalidAccessError',
54-
message: 'Unable to use this key to decrypt'
55-
});
56-
}
57-
58-
test().then(common.mustCall());
18+
const label = new TextEncoder().encode('a label');
19+
const ciphertext = await subtle.encrypt({ name: 'RSA-OAEP', label }, publicKey, buf);
20+
const plaintext = await subtle.decrypt({ name: 'RSA-OAEP', label }, privateKey, ciphertext);
21+
22+
assert.strictEqual(
23+
Buffer.from(plaintext).toString('hex'),
24+
Buffer.from(buf).toString('hex'));
25+
26+
await assert.rejects(() => subtle.encrypt({
27+
name: 'RSA-OAEP',
28+
}, privateKey, buf), {
29+
name: 'InvalidAccessError',
30+
message: 'Unable to use this key to encrypt'
31+
});
32+
33+
await assert.rejects(() => subtle.decrypt({
34+
name: 'RSA-OAEP',
35+
}, publicKey, ciphertext), {
36+
name: 'InvalidAccessError',
37+
message: 'Unable to use this key to decrypt'
38+
});
5939
}
6040

61-
// Test Encrypt/Decrypt RSA-OAEP w/ SHA-3
62-
if (!isBoringSSL) {
63-
const buf = globalThis.crypto.getRandomValues(new Uint8Array(50));
64-
65-
async function test() {
66-
const ec = new TextEncoder();
67-
const { publicKey, privateKey } = await subtle.generateKey({
68-
name: 'RSA-OAEP',
69-
modulusLength: 2048,
70-
publicExponent: new Uint8Array([1, 0, 1]),
71-
hash: 'SHA3-384',
72-
}, true, ['encrypt', 'decrypt']);
73-
74-
const ciphertext = await subtle.encrypt({
75-
name: 'RSA-OAEP',
76-
label: ec.encode('a label')
77-
}, publicKey, buf);
78-
79-
const plaintext = await subtle.decrypt({
80-
name: 'RSA-OAEP',
81-
label: ec.encode('a label')
82-
}, privateKey, ciphertext);
83-
84-
assert.strictEqual(
85-
Buffer.from(plaintext).toString('hex'),
86-
Buffer.from(buf).toString('hex'));
87-
88-
await assert.rejects(() => subtle.encrypt({
89-
name: 'RSA-OAEP',
90-
}, privateKey, buf), {
91-
name: 'InvalidAccessError',
92-
message: 'Unable to use this key to encrypt'
93-
});
94-
95-
await assert.rejects(() => subtle.decrypt({
96-
name: 'RSA-OAEP',
97-
}, publicKey, ciphertext), {
98-
name: 'InvalidAccessError',
99-
message: 'Unable to use this key to decrypt'
100-
});
41+
(async function() {
42+
const { publicKey, privateKey } = await subtle.generateKey({
43+
name: 'RSA-OAEP',
44+
modulusLength: 2048,
45+
publicExponent: new Uint8Array([1, 0, 1]),
46+
hash: 'SHA-384',
47+
}, true, ['encrypt', 'decrypt']);
48+
49+
await testRSAOaep(publicKey, privateKey);
50+
51+
if (!isBoringSSL) {
52+
// Import the same key material with SHA-3 to avoid another RSA keygen.
53+
const [spki, pkcs8] = await Promise.all([
54+
subtle.exportKey('spki', publicKey),
55+
subtle.exportKey('pkcs8', privateKey),
56+
]);
57+
const algorithm = { name: 'RSA-OAEP', hash: 'SHA3-384' };
58+
const [sha3PublicKey, sha3PrivateKey] = await Promise.all([
59+
subtle.importKey('spki', spki, algorithm, false, ['encrypt']),
60+
subtle.importKey('pkcs8', pkcs8, algorithm, false, ['decrypt']),
61+
]);
62+
await testRSAOaep(sha3PublicKey, sha3PrivateKey);
10163
}
102-
103-
test().then(common.mustCall());
104-
}
64+
})().then(common.mustCall());
10565

10666
// Test Encrypt/Decrypt AES-CTR
10767
{

0 commit comments

Comments
 (0)