• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict';
2const common = require('../common');
3if (!common.hasCrypto)
4  common.skip('missing crypto');
5
6const assert = require('assert');
7const crypto = require('crypto');
8
9function runPBKDF2(password, salt, iterations, keylen, hash) {
10  const syncResult =
11    crypto.pbkdf2Sync(password, salt, iterations, keylen, hash);
12
13  crypto.pbkdf2(password, salt, iterations, keylen, hash,
14                common.mustSucceed((asyncResult) => {
15                  assert.deepStrictEqual(asyncResult, syncResult);
16                }));
17
18  return syncResult;
19}
20
21function testPBKDF2(password, salt, iterations, keylen, expected, encoding) {
22  const actual = runPBKDF2(password, salt, iterations, keylen, 'sha256');
23  assert.strictEqual(actual.toString(encoding || 'latin1'), expected);
24}
25
26//
27// Test PBKDF2 with RFC 6070 test vectors (except #4)
28//
29
30testPBKDF2('password', 'salt', 1, 20,
31           '\x12\x0f\xb6\xcf\xfc\xf8\xb3\x2c\x43\xe7\x22\x52' +
32           '\x56\xc4\xf8\x37\xa8\x65\x48\xc9');
33
34testPBKDF2('password', 'salt', 2, 20,
35           '\xae\x4d\x0c\x95\xaf\x6b\x46\xd3\x2d\x0a\xdf\xf9' +
36           '\x28\xf0\x6d\xd0\x2a\x30\x3f\x8e');
37
38testPBKDF2('password', 'salt', 4096, 20,
39           '\xc5\xe4\x78\xd5\x92\x88\xc8\x41\xaa\x53\x0d\xb6' +
40           '\x84\x5c\x4c\x8d\x96\x28\x93\xa0');
41
42testPBKDF2('passwordPASSWORDpassword',
43           'saltSALTsaltSALTsaltSALTsaltSALTsalt',
44           4096,
45           25,
46           '\x34\x8c\x89\xdb\xcb\xd3\x2b\x2f\x32\xd8\x14\xb8\x11' +
47           '\x6e\x84\xcf\x2b\x17\x34\x7e\xbc\x18\x00\x18\x1c');
48
49testPBKDF2('pass\0word', 'sa\0lt', 4096, 16,
50           '\x89\xb6\x9d\x05\x16\xf8\x29\x89\x3c\x69\x62\x26\x65' +
51           '\x0a\x86\x87');
52
53testPBKDF2('password', 'salt', 32, 32,
54           '64c486c55d30d4c5a079b8823b7d7cb37ff0556f537da8410233bcec330ed956',
55           'hex');
56
57// Error path should not leak memory (check with valgrind).
58assert.throws(
59  () => crypto.pbkdf2('password', 'salt', 1, 20, 'sha1'),
60  {
61    code: 'ERR_INVALID_ARG_TYPE',
62    name: 'TypeError'
63  }
64);
65
66for (const iterations of [-1, 0, 2147483648]) {
67  assert.throws(
68    () => crypto.pbkdf2Sync('password', 'salt', iterations, 20, 'sha1'),
69    {
70      code: 'ERR_OUT_OF_RANGE',
71      name: 'RangeError',
72    }
73  );
74}
75
76['str', null, undefined, [], {}].forEach((notNumber) => {
77  assert.throws(
78    () => {
79      crypto.pbkdf2Sync('password', 'salt', 1, notNumber, 'sha256');
80    }, {
81      code: 'ERR_INVALID_ARG_TYPE',
82      name: 'TypeError',
83      message: 'The "keylen" argument must be of type number.' +
84               `${common.invalidArgTypeHelper(notNumber)}`
85    });
86});
87
88[Infinity, -Infinity, NaN].forEach((input) => {
89  assert.throws(
90    () => {
91      crypto.pbkdf2('password', 'salt', 1, input, 'sha256',
92                    common.mustNotCall());
93    }, {
94      code: 'ERR_OUT_OF_RANGE',
95      name: 'RangeError',
96      message: 'The value of "keylen" is out of range. It ' +
97               `must be an integer. Received ${input}`
98    });
99});
100
101[-1, 2147483648, 4294967296].forEach((input) => {
102  assert.throws(
103    () => {
104      crypto.pbkdf2('password', 'salt', 1, input, 'sha256',
105                    common.mustNotCall());
106    }, {
107      code: 'ERR_OUT_OF_RANGE',
108      name: 'RangeError',
109    });
110});
111
112// Should not get FATAL ERROR with empty password and salt
113// https://github.com/nodejs/node/issues/8571
114crypto.pbkdf2('', '', 1, 32, 'sha256', common.mustSucceed());
115
116assert.throws(
117  () => crypto.pbkdf2('password', 'salt', 8, 8, common.mustNotCall()),
118  {
119    code: 'ERR_INVALID_ARG_TYPE',
120    name: 'TypeError',
121    message: 'The "digest" argument must be of type string. ' +
122             'Received undefined'
123  });
124
125assert.throws(
126  () => crypto.pbkdf2Sync('password', 'salt', 8, 8),
127  {
128    code: 'ERR_INVALID_ARG_TYPE',
129    name: 'TypeError',
130    message: 'The "digest" argument must be of type string. ' +
131             'Received undefined'
132  });
133
134assert.throws(
135  () => crypto.pbkdf2Sync('password', 'salt', 8, 8, null),
136  {
137    code: 'ERR_INVALID_ARG_TYPE',
138    name: 'TypeError',
139    message: 'The "digest" argument must be of type string. ' +
140             'Received null'
141  });
142[1, {}, [], true, undefined, null].forEach((input) => {
143  assert.throws(
144    () => crypto.pbkdf2(input, 'salt', 8, 8, 'sha256', common.mustNotCall()),
145    {
146      code: 'ERR_INVALID_ARG_TYPE',
147      name: 'TypeError',
148    }
149  );
150
151  assert.throws(
152    () => crypto.pbkdf2('pass', input, 8, 8, 'sha256', common.mustNotCall()),
153    {
154      code: 'ERR_INVALID_ARG_TYPE',
155      name: 'TypeError',
156    }
157  );
158
159  assert.throws(
160    () => crypto.pbkdf2Sync(input, 'salt', 8, 8, 'sha256'),
161    {
162      code: 'ERR_INVALID_ARG_TYPE',
163      name: 'TypeError',
164    }
165  );
166
167  assert.throws(
168    () => crypto.pbkdf2Sync('pass', input, 8, 8, 'sha256'),
169    {
170      code: 'ERR_INVALID_ARG_TYPE',
171      name: 'TypeError',
172    }
173  );
174});
175
176['test', {}, [], true, undefined, null].forEach((i) => {
177  const received = common.invalidArgTypeHelper(i);
178  assert.throws(
179    () => crypto.pbkdf2('pass', 'salt', i, 8, 'sha256', common.mustNotCall()),
180    {
181      code: 'ERR_INVALID_ARG_TYPE',
182      name: 'TypeError',
183      message: `The "iterations" argument must be of type number.${received}`
184    }
185  );
186
187  assert.throws(
188    () => crypto.pbkdf2Sync('pass', 'salt', i, 8, 'sha256'),
189    {
190      code: 'ERR_INVALID_ARG_TYPE',
191      name: 'TypeError',
192      message: `The "iterations" argument must be of type number.${received}`
193    }
194  );
195});
196
197// Any TypedArray should work for password and salt.
198for (const SomeArray of [Uint8Array, Uint16Array, Uint32Array, Float32Array,
199                         Float64Array, ArrayBuffer, SharedArrayBuffer]) {
200  runPBKDF2(new SomeArray(10), 'salt', 8, 8, 'sha256');
201  runPBKDF2('pass', new SomeArray(10), 8, 8, 'sha256');
202}
203
204assert.throws(
205  () => crypto.pbkdf2('pass', 'salt', 8, 8, 'md55', common.mustNotCall()),
206  {
207    code: 'ERR_CRYPTO_INVALID_DIGEST',
208    name: 'TypeError',
209    message: 'Invalid digest: md55'
210  }
211);
212
213assert.throws(
214  () => crypto.pbkdf2Sync('pass', 'salt', 8, 8, 'md55'),
215  {
216    code: 'ERR_CRYPTO_INVALID_DIGEST',
217    name: 'TypeError',
218    message: 'Invalid digest: md55'
219  }
220);
221
222if (!common.hasOpenSSL3) {
223  const kNotPBKDF2Supported = ['shake128', 'shake256'];
224  crypto.getHashes()
225    .filter((hash) => !kNotPBKDF2Supported.includes(hash))
226    .forEach((hash) => {
227      runPBKDF2(new Uint8Array(10), 'salt', 8, 8, hash);
228    });
229}
230
231{
232  // This should not crash.
233  assert.throws(
234    () => crypto.pbkdf2Sync('1', '2', 1, 1, '%'),
235    {
236      code: 'ERR_CRYPTO_INVALID_DIGEST',
237      name: 'TypeError',
238      message: 'Invalid digest: %'
239    }
240  );
241}
242