summaryrefslogtreecommitdiff
path: root/tests/node_compat
diff options
context:
space:
mode:
authorLuca Casonato <hello@lcas.dev>2024-07-05 10:10:22 +0200
committerGitHub <noreply@github.com>2024-07-05 10:10:22 +0200
commit08e5606c3400d3a993c0ce6748901c56fc3db35b (patch)
tree032d3a09c6d22763ceb703e7908ca159d3d7a809 /tests/node_compat
parentb290fd01f3f5d32f9d010fc719ced0240759c049 (diff)
fix(ext/node): rewrite digest handling (#24392)
Previously we had many different code paths all handling digests in different places, all with wildly different digest support. This commit rewrites this to use a single digest handling mechanism for all digest operations. It adds various aliases for digest algorithms, like node does. For example `sha1WithRSAEncryption` is an alias for `sha1`. It also adds support for `md5-sha1` digests in various places.
Diffstat (limited to 'tests/node_compat')
-rw-r--r--tests/node_compat/config.jsonc2
-rw-r--r--tests/node_compat/runner/TODO.md1
-rw-r--r--tests/node_compat/test/parallel/test-crypto-pbkdf2.js251
3 files changed, 253 insertions, 1 deletions
diff --git a/tests/node_compat/config.jsonc b/tests/node_compat/config.jsonc
index 5c0c854a8..f0b667df4 100644
--- a/tests/node_compat/config.jsonc
+++ b/tests/node_compat/config.jsonc
@@ -52,6 +52,7 @@
"test-crypto-dh.js",
"test-crypto-hkdf.js",
"test-crypto-hmac.js",
+ "test-crypto-pbkdf2.js",
"test-crypto-prime.js",
"test-crypto-stream.js",
"test-crypto-x509.js",
@@ -257,6 +258,7 @@
"test-crypto-hash.js",
"test-crypto-hkdf.js",
"test-crypto-hmac.js",
+ "test-crypto-pbkdf2.js",
"test-crypto-prime.js",
"test-crypto-secret-keygen.js",
"test-crypto-stream.js",
diff --git a/tests/node_compat/runner/TODO.md b/tests/node_compat/runner/TODO.md
index a36ad1b63..ec2a2337e 100644
--- a/tests/node_compat/runner/TODO.md
+++ b/tests/node_compat/runner/TODO.md
@@ -505,7 +505,6 @@ NOTE: This file should not be manually edited. Please edit `tests/node_compat/co
- [parallel/test-crypto-op-during-process-exit.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-crypto-op-during-process-exit.js)
- [parallel/test-crypto-padding-aes256.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-crypto-padding-aes256.js)
- [parallel/test-crypto-padding.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-crypto-padding.js)
-- [parallel/test-crypto-pbkdf2.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-crypto-pbkdf2.js)
- [parallel/test-crypto-private-decrypt-gh32240.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-crypto-private-decrypt-gh32240.js)
- [parallel/test-crypto-psychic-signatures.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-crypto-psychic-signatures.js)
- [parallel/test-crypto-publicDecrypt-fails-first-time.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-crypto-publicDecrypt-fails-first-time.js)
diff --git a/tests/node_compat/test/parallel/test-crypto-pbkdf2.js b/tests/node_compat/test/parallel/test-crypto-pbkdf2.js
new file mode 100644
index 000000000..263f73130
--- /dev/null
+++ b/tests/node_compat/test/parallel/test-crypto-pbkdf2.js
@@ -0,0 +1,251 @@
+// deno-fmt-ignore-file
+// deno-lint-ignore-file
+
+// Copyright Joyent and Node contributors. All rights reserved. MIT license.
+// Taken from Node 18.12.1
+// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.
+
+'use strict';
+const common = require('../common');
+if (!common.hasCrypto)
+ common.skip('missing crypto');
+
+const assert = require('assert');
+const crypto = require('crypto');
+
+function runPBKDF2(password, salt, iterations, keylen, hash) {
+ const syncResult =
+ crypto.pbkdf2Sync(password, salt, iterations, keylen, hash);
+
+ crypto.pbkdf2(password, salt, iterations, keylen, hash,
+ common.mustSucceed((asyncResult) => {
+ assert.deepStrictEqual(asyncResult, syncResult);
+ }));
+
+ return syncResult;
+}
+
+function testPBKDF2(password, salt, iterations, keylen, expected, encoding) {
+ const actual = runPBKDF2(password, salt, iterations, keylen, 'sha256');
+ assert.strictEqual(actual.toString(encoding || 'latin1'), expected);
+}
+
+//
+// Test PBKDF2 with RFC 6070 test vectors (except #4)
+//
+
+testPBKDF2('password', 'salt', 1, 20,
+ '\x12\x0f\xb6\xcf\xfc\xf8\xb3\x2c\x43\xe7\x22\x52' +
+ '\x56\xc4\xf8\x37\xa8\x65\x48\xc9');
+
+testPBKDF2('password', 'salt', 2, 20,
+ '\xae\x4d\x0c\x95\xaf\x6b\x46\xd3\x2d\x0a\xdf\xf9' +
+ '\x28\xf0\x6d\xd0\x2a\x30\x3f\x8e');
+
+testPBKDF2('password', 'salt', 4096, 20,
+ '\xc5\xe4\x78\xd5\x92\x88\xc8\x41\xaa\x53\x0d\xb6' +
+ '\x84\x5c\x4c\x8d\x96\x28\x93\xa0');
+
+testPBKDF2('passwordPASSWORDpassword',
+ 'saltSALTsaltSALTsaltSALTsaltSALTsalt',
+ 4096,
+ 25,
+ '\x34\x8c\x89\xdb\xcb\xd3\x2b\x2f\x32\xd8\x14\xb8\x11' +
+ '\x6e\x84\xcf\x2b\x17\x34\x7e\xbc\x18\x00\x18\x1c');
+
+testPBKDF2('pass\0word', 'sa\0lt', 4096, 16,
+ '\x89\xb6\x9d\x05\x16\xf8\x29\x89\x3c\x69\x62\x26\x65' +
+ '\x0a\x86\x87');
+
+testPBKDF2('password', 'salt', 32, 32,
+ '64c486c55d30d4c5a079b8823b7d7cb37ff0556f537da8410233bcec330ed956',
+ 'hex');
+
+// Error path should not leak memory (check with valgrind).
+assert.throws(
+ () => crypto.pbkdf2('password', 'salt', 1, 20, 'sha1'),
+ {
+ code: 'ERR_INVALID_ARG_TYPE',
+ name: 'TypeError'
+ }
+);
+
+for (const iterations of [-1, 0, 2147483648]) {
+ assert.throws(
+ () => crypto.pbkdf2Sync('password', 'salt', iterations, 20, 'sha1'),
+ {
+ code: 'ERR_OUT_OF_RANGE',
+ name: 'RangeError',
+ }
+ );
+}
+
+['str', null, undefined, [], {}].forEach((notNumber) => {
+ assert.throws(
+ () => {
+ crypto.pbkdf2Sync('password', 'salt', 1, notNumber, 'sha256');
+ }, {
+ code: 'ERR_INVALID_ARG_TYPE',
+ name: 'TypeError',
+ message: 'The "keylen" argument must be of type number.' +
+ `${common.invalidArgTypeHelper(notNumber)}`
+ });
+});
+
+[Infinity, -Infinity, NaN].forEach((input) => {
+ assert.throws(
+ () => {
+ crypto.pbkdf2('password', 'salt', 1, input, 'sha256',
+ common.mustNotCall());
+ }, {
+ code: 'ERR_OUT_OF_RANGE',
+ name: 'RangeError',
+ message: 'The value of "keylen" is out of range. It ' +
+ `must be an integer. Received ${input}`
+ });
+});
+
+[-1, 2147483648, 4294967296].forEach((input) => {
+ assert.throws(
+ () => {
+ crypto.pbkdf2('password', 'salt', 1, input, 'sha256',
+ common.mustNotCall());
+ }, {
+ code: 'ERR_OUT_OF_RANGE',
+ name: 'RangeError',
+ });
+});
+
+// Should not get FATAL ERROR with empty password and salt
+// https://github.com/nodejs/node/issues/8571
+crypto.pbkdf2('', '', 1, 32, 'sha256', common.mustSucceed());
+
+assert.throws(
+ () => crypto.pbkdf2('password', 'salt', 8, 8, common.mustNotCall()),
+ {
+ code: 'ERR_INVALID_ARG_TYPE',
+ name: 'TypeError',
+ message: 'The "digest" argument must be of type string. ' +
+ 'Received undefined'
+ });
+
+assert.throws(
+ () => crypto.pbkdf2Sync('password', 'salt', 8, 8),
+ {
+ code: 'ERR_INVALID_ARG_TYPE',
+ name: 'TypeError',
+ message: 'The "digest" argument must be of type string. ' +
+ 'Received undefined'
+ });
+
+assert.throws(
+ () => crypto.pbkdf2Sync('password', 'salt', 8, 8, null),
+ {
+ code: 'ERR_INVALID_ARG_TYPE',
+ name: 'TypeError',
+ message: 'The "digest" argument must be of type string. ' +
+ 'Received null'
+ });
+[1, {}, [], true, undefined, null].forEach((input) => {
+ assert.throws(
+ () => crypto.pbkdf2(input, 'salt', 8, 8, 'sha256', common.mustNotCall()),
+ {
+ code: 'ERR_INVALID_ARG_TYPE',
+ name: 'TypeError',
+ }
+ );
+
+ assert.throws(
+ () => crypto.pbkdf2('pass', input, 8, 8, 'sha256', common.mustNotCall()),
+ {
+ code: 'ERR_INVALID_ARG_TYPE',
+ name: 'TypeError',
+ }
+ );
+
+ assert.throws(
+ () => crypto.pbkdf2Sync(input, 'salt', 8, 8, 'sha256'),
+ {
+ code: 'ERR_INVALID_ARG_TYPE',
+ name: 'TypeError',
+ }
+ );
+
+ assert.throws(
+ () => crypto.pbkdf2Sync('pass', input, 8, 8, 'sha256'),
+ {
+ code: 'ERR_INVALID_ARG_TYPE',
+ name: 'TypeError',
+ }
+ );
+});
+
+['test', {}, [], true, undefined, null].forEach((i) => {
+ const received = common.invalidArgTypeHelper(i);
+ assert.throws(
+ () => crypto.pbkdf2('pass', 'salt', i, 8, 'sha256', common.mustNotCall()),
+ {
+ code: 'ERR_INVALID_ARG_TYPE',
+ name: 'TypeError',
+ message: `The "iterations" argument must be of type number.${received}`
+ }
+ );
+
+ assert.throws(
+ () => crypto.pbkdf2Sync('pass', 'salt', i, 8, 'sha256'),
+ {
+ code: 'ERR_INVALID_ARG_TYPE',
+ name: 'TypeError',
+ message: `The "iterations" argument must be of type number.${received}`
+ }
+ );
+});
+
+// Any TypedArray should work for password and salt.
+// TODO(@lucacasonato): SharedArrayBuffer is not supported
+for (const SomeArray of [Uint8Array, Uint16Array, Uint32Array, Float32Array,
+ Float64Array, ArrayBuffer/*, SharedArrayBuffer*/]) {
+ runPBKDF2(new SomeArray(10), 'salt', 8, 8, 'sha256');
+ runPBKDF2('pass', new SomeArray(10), 8, 8, 'sha256');
+}
+
+// TODO(@lucacasonato): we throw this asynchronously
+// assert.throws(
+// () => crypto.pbkdf2('pass', 'salt', 8, 8, 'md55', common.mustNotCall()),
+// {
+// code: 'ERR_CRYPTO_INVALID_DIGEST',
+// name: 'TypeError',
+// message: 'Invalid digest: md55'
+// }
+// );
+
+assert.throws(
+ () => crypto.pbkdf2Sync('pass', 'salt', 8, 8, 'md55'),
+ {
+ code: 'ERR_CRYPTO_INVALID_DIGEST',
+ name: 'TypeError',
+ message: 'Invalid digest: md55'
+ }
+);
+
+if (!common.hasOpenSSL3) {
+ // TODO(@lucacasonato): we don't support blake2b512 and blake2s256
+ const kNotPBKDF2Supported = ['shake128', 'shake256', 'blake2b512', 'blake2s256'];
+ crypto.getHashes()
+ .filter((hash) => !kNotPBKDF2Supported.includes(hash))
+ .forEach((hash) => {
+ runPBKDF2(new Uint8Array(10), 'salt', 8, 8, hash);
+ });
+}
+
+{
+ // This should not crash.
+ assert.throws(
+ () => crypto.pbkdf2Sync('1', '2', 1, 1, '%'),
+ {
+ code: 'ERR_CRYPTO_INVALID_DIGEST',
+ name: 'TypeError',
+ message: 'Invalid digest: %'
+ }
+ );
+}