node/test/parallel/test-crypto-argon2.js
Ranieri Althoff bdcab711b8
crypto: add argon2() and argon2Sync() methods
Co-authored-by: Filip Skokan <panva.ip@gmail.com>
Co-authored-by: James M Snell <jasnell@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/50353
Reviewed-By: Ethan Arrowood <ethan@arrowood.dev>
Reviewed-By: Filip Skokan <panva.ip@gmail.com>
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
2025-08-19 19:30:38 +00:00

140 lines
5.2 KiB
JavaScript

'use strict';
const common = require('../common');
if (!common.hasCrypto)
common.skip('missing crypto');
const { hasOpenSSL } = require('../common/crypto');
if (!hasOpenSSL(3, 2))
common.skip('requires OpenSSL >= 3.2');
const assert = require('node:assert');
const crypto = require('node:crypto');
function runArgon2(algorithm, options) {
const syncResult = crypto.argon2Sync(algorithm, options);
crypto.argon2(algorithm, options,
common.mustSucceed((asyncResult) => {
assert.deepStrictEqual(asyncResult, syncResult);
}));
return syncResult;
}
const message = Buffer.alloc(32, 0x01);
const nonce = Buffer.alloc(16, 0x02);
const secret = Buffer.alloc(8, 0x03);
const associatedData = Buffer.alloc(12, 0x04);
const defaults = { message, nonce, parallelism: 1, tagLength: 64, memory: 8, passes: 3 };
const good = [
// Test vectors from RFC 9106 https://www.rfc-editor.org/rfc/rfc9106.html#name-test-vectors
// and OpenSSL 3.2 https://github.com/openssl/openssl/blob/6dfa998f7ea150f9c6d4e4727cf6d5c82a68a8da/test/recipes/30-test_evp_data/evpkdf_argon2.txt
//
// OpenSSL defaults are:
// - outlen: 64
// - passes: 3
// - parallelism: 1
// - memory: 8
// https://github.com/openssl/openssl/blob/6dfa998f7ea150f9c6d4e4727cf6d5c82a68a8da/providers/implementations/kdfs/argon2.c#L77-L82
[
'argon2d',
{ secret, associatedData, parallelism: 4, tagLength: 32, memory: 32 },
'512b391b6f1162975371d30919734294f868e3be3984f3c1a13a4db9fabe4acb',
],
[
'argon2i',
{ secret, associatedData, parallelism: 4, tagLength: 32, memory: 32 },
'c814d9d1dc7f37aa13f0d77f2494bda1c8de6b016dd388d29952a4c4672b6ce8',
],
[
'argon2id',
{ secret, associatedData, parallelism: 4, tagLength: 32, memory: 32 },
'0d640df58d78766c08c037a34a8b53c9d01ef0452d75b65eb52520e96b01e659',
],
[
'argon2d',
{ message: '1234567890', nonce: 'saltsalt' },
'd16ad773b1c6400d3193bc3e66271603e9de72bace20af3f89c236f5434cdec9' +
'9072ddfc6b9c77ea9f386c0e8d7cb0c37cec6ec3277a22c92d5be58ef67c7eaa',
],
[
'argon2id',
{ message: '', parallelism: 4, tagLength: 32, memory: 32 },
'0a34f1abde67086c82e785eaf17c68382259a264f4e61b91cd2763cb75ac189a',
],
[
'argon2d',
{ message: '1234567890', nonce: 'saltsalt', parallelism: 2, memory: 65536 },
'5ca0ab135de1241454840172696c301c7b8fd99a788cd11cf9699044cadf7fca' +
'0a6e3762cb3043a71adf6553db3fd7925101b0ccf8868b098492a4addb2486bc',
],
[
'argon2i',
{ parallelism: 4, tagLength: 32, memory: 32 },
'a9a7510e6db4d588ba3414cd0e094d480d683f97b9ccb612a544fe8ef65ba8e0',
],
[
'argon2id',
{ parallelism: 4, tagLength: 32, memory: 32 },
'03aab965c12001c9d7d0d2de33192c0494b684bb148196d73c1df1acaf6d0c2e',
],
[
'argon2d',
{ message: '1234567890', nonce: 'saltsalt', parallelism: 2, tagLength: 128, memory: 65536 },
'a86c83a19f0b234ecba8c275d16d059153f961e4c39ec9b1be98b3e73d791789' +
'363682443ad594334048634e91c493affed0bc29fd329a0e553c00149d6db19a' +
'f4e4a354aec14dbd575d78ba87d4a4bc4746666e7a4e6ee1572bbffc2eba308a' +
'2d825cb7b41fde3a95d5cff0dfa2d0fdd636b32aea8b4a3c532742d330bd1b90',
],
];
// Test vectors that should fail.
const bad = [
['argon2id', { nonce: nonce.subarray(0, 7) }, 'parameters.nonce.byteLength'], // nonce.byteLength < 8
['argon2id', { tagLength: 3 }, 'parameters.tagLength'], // tagLength < 4
['argon2id', { tagLength: 2 ** 32 }, 'parameters.tagLength'], // tagLength > 2^(32)-1
['argon2id', { passes: 0 }, 'parameters.passes'], // passes < 2
['argon2id', { passes: 2 ** 32 }, 'parameters.passes'], // passes > 2^(32)-1
['argon2id', { parallelism: 0 }, 'parameters.parallelism'], // parallelism < 1
['argon2id', { parallelism: 2 ** 24 }, 'parameters.parallelism'], // Parallelism > 2^(24)-1
['argon2id', { parallelism: 4, memory: 16 }, 'parameters.memory'], // Memory < 8 * parallelism
['argon2id', { memory: 2 ** 32 }, 'parameters.memory'], // memory > 2^(32)-1
];
for (const [algorithm, overrides, expected] of good) {
const parameters = { ...defaults, ...overrides };
const actual = runArgon2(algorithm, parameters);
assert.strictEqual(actual.toString('hex'), expected);
}
for (const [algorithm, overrides, param] of bad) {
const expected = {
code: 'ERR_OUT_OF_RANGE',
message: new RegExp(`The value of "${param}" is out of range`),
};
const parameters = { ...defaults, ...overrides };
assert.throws(() => crypto.argon2(algorithm, parameters, () => {}), expected);
assert.throws(() => crypto.argon2Sync(algorithm, parameters), expected);
}
for (const key of Object.keys(defaults)) {
const expected = {
code: 'ERR_INVALID_ARG_TYPE',
message: new RegExp(`"parameters\\.${key}"`),
};
const parameters = { ...defaults };
delete parameters[key];
assert.throws(() => crypto.argon2('argon2id', parameters, () => {}), expected);
assert.throws(() => crypto.argon2Sync('argon2id', parameters), expected);
}
{
const expected = { code: 'ERR_INVALID_ARG_TYPE' };
assert.throws(() => crypto.argon2(), expected);
assert.throws(() => crypto.argon2('argon2id', null), expected);
assert.throws(() => crypto.argon2('argon2id', defaults, null), expected);
assert.throws(() => crypto.argon2('argon2id', defaults, {}), expected);
}