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