• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Flags: --expose-internals
2'use strict';
3const common = require('../common');
4if (!common.hasCrypto)
5  common.skip('missing crypto');
6
7const assert = require('assert');
8const crypto = require('crypto');
9
10const { internalBinding } = require('internal/test/binding');
11if (typeof internalBinding('crypto').ScryptJob !== 'function')
12  common.skip('no scrypt support');
13
14const good = [
15  // Zero-length key is legal, functions as a parameter validation check.
16  {
17    pass: '',
18    salt: '',
19    keylen: 0,
20    N: 16,
21    p: 1,
22    r: 1,
23    expected: '',
24  },
25  // Test vectors from https://tools.ietf.org/html/rfc7914#page-13 that
26  // should pass.  Note that the test vector with N=1048576 is omitted
27  // because it takes too long to complete and uses over 1 GiB of memory.
28  {
29    pass: '',
30    salt: '',
31    keylen: 64,
32    N: 16,
33    p: 1,
34    r: 1,
35    expected:
36        '77d6576238657b203b19ca42c18a0497f16b4844e3074ae8dfdffa3fede21442' +
37        'fcd0069ded0948f8326a753a0fc81f17e8d3e0fb2e0d3628cf35e20c38d18906',
38  },
39  {
40    pass: 'password',
41    salt: 'NaCl',
42    keylen: 64,
43    N: 1024,
44    p: 16,
45    r: 8,
46    expected:
47        'fdbabe1c9d3472007856e7190d01e9fe7c6ad7cbc8237830e77376634b373162' +
48        '2eaf30d92e22a3886ff109279d9830dac727afb94a83ee6d8360cbdfa2cc0640',
49  },
50  {
51    pass: 'pleaseletmein',
52    salt: 'SodiumChloride',
53    keylen: 64,
54    N: 16384,
55    p: 1,
56    r: 8,
57    expected:
58        '7023bdcb3afd7348461c06cd81fd38ebfda8fbba904f8e3ea9b543f6545da1f2' +
59        'd5432955613f0fcf62d49705242a9af9e61e85dc0d651e40dfcf017b45575887',
60  },
61  {
62    pass: '',
63    salt: '',
64    keylen: 64,
65    cost: 16,
66    parallelization: 1,
67    blockSize: 1,
68    expected:
69        '77d6576238657b203b19ca42c18a0497f16b4844e3074ae8dfdffa3fede21442' +
70        'fcd0069ded0948f8326a753a0fc81f17e8d3e0fb2e0d3628cf35e20c38d18906',
71  },
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  },
94];
95
96// Test vectors that should fail.
97const bad = [
98  { N: 1, p: 1, r: 1 },         // N < 2
99  { N: 3, p: 1, r: 1 },         // Not power of 2.
100  { N: 1, cost: 1 },            // Both N and cost
101  { p: 1, parallelization: 1 }, // Both p and parallelization
102  { r: 1, blockSize: 1 },        // Both r and blocksize
103];
104
105// Test vectors where 128*N*r exceeds maxmem.
106const toobig = [
107  { N: 2 ** 16, p: 1, r: 1 },   // N >= 2**(r*16)
108  { N: 2, p: 2 ** 30, r: 1 },   // p > (2**30-1)/r
109  { N: 2 ** 20, p: 1, r: 8 },
110  { N: 2 ** 10, p: 1, r: 8, maxmem: 2 ** 20 },
111];
112
113const badargs = [
114  {
115    args: [],
116    expected: { code: 'ERR_INVALID_ARG_TYPE', message: /"password"/ },
117  },
118  {
119    args: [null],
120    expected: { code: 'ERR_INVALID_ARG_TYPE', message: /"password"/ },
121  },
122  {
123    args: [''],
124    expected: { code: 'ERR_INVALID_ARG_TYPE', message: /"salt"/ },
125  },
126  {
127    args: ['', null],
128    expected: { code: 'ERR_INVALID_ARG_TYPE', message: /"salt"/ },
129  },
130  {
131    args: ['', ''],
132    expected: { code: 'ERR_INVALID_ARG_TYPE', message: /"keylen"/ },
133  },
134  {
135    args: ['', '', null],
136    expected: { code: 'ERR_INVALID_ARG_TYPE', message: /"keylen"/ },
137  },
138  {
139    args: ['', '', .42],
140    expected: { code: 'ERR_OUT_OF_RANGE', message: /"keylen"/ },
141  },
142  {
143    args: ['', '', -42],
144    expected: { code: 'ERR_OUT_OF_RANGE', message: /"keylen"/ },
145  },
146  {
147    args: ['', '', 2 ** 31],
148    expected: { code: 'ERR_OUT_OF_RANGE', message: /"keylen"/ },
149  },
150  {
151    args: ['', '', 2147485780],
152    expected: { code: 'ERR_OUT_OF_RANGE', message: /"keylen"/ },
153  },
154  {
155    args: ['', '', 2 ** 32],
156    expected: { code: 'ERR_OUT_OF_RANGE', message: /"keylen"/ },
157  },
158];
159
160for (const options of good) {
161  const { pass, salt, keylen, expected } = options;
162  const actual = crypto.scryptSync(pass, salt, keylen, options);
163  assert.strictEqual(actual.toString('hex'), expected);
164  crypto.scrypt(pass, salt, keylen, options, common.mustSucceed((actual) => {
165    assert.strictEqual(actual.toString('hex'), expected);
166  }));
167}
168
169for (const options of bad) {
170  const expected = {
171    message: /Invalid scrypt param/,
172  };
173  assert.throws(() => crypto.scrypt('pass', 'salt', 1, options, () => {}),
174                expected);
175  assert.throws(() => crypto.scryptSync('pass', 'salt', 1, options),
176                expected);
177}
178
179for (const options of toobig) {
180  const expected = {
181    message: /Invalid scrypt param/
182  };
183  assert.throws(() => crypto.scrypt('pass', 'salt', 1, options, () => {}),
184                expected);
185  assert.throws(() => crypto.scryptSync('pass', 'salt', 1, options),
186                expected);
187}
188
189{
190  const defaults = { N: 16384, p: 1, r: 8 };
191  const expected = crypto.scryptSync('pass', 'salt', 1, defaults);
192  const actual = crypto.scryptSync('pass', 'salt', 1);
193  assert.deepStrictEqual(actual.toString('hex'), expected.toString('hex'));
194  crypto.scrypt('pass', 'salt', 1, common.mustSucceed((actual) => {
195    assert.deepStrictEqual(actual.toString('hex'), expected.toString('hex'));
196  }));
197}
198
199{
200  const defaultEncoding = crypto.DEFAULT_ENCODING;
201  const defaults = { N: 16384, p: 1, r: 8 };
202  const expected = crypto.scryptSync('pass', 'salt', 1, defaults);
203
204  const testEncoding = 'latin1';
205  crypto.DEFAULT_ENCODING = testEncoding;
206  const actual = crypto.scryptSync('pass', 'salt', 1);
207  assert.deepStrictEqual(actual, expected.toString(testEncoding));
208
209  crypto.scrypt('pass', 'salt', 1, common.mustSucceed((actual) => {
210    assert.deepStrictEqual(actual, expected.toString(testEncoding));
211  }));
212
213  crypto.DEFAULT_ENCODING = defaultEncoding;
214}
215
216for (const { args, expected } of badargs) {
217  assert.throws(() => crypto.scrypt(...args), expected);
218  assert.throws(() => crypto.scryptSync(...args), expected);
219}
220
221{
222  const expected = { code: 'ERR_INVALID_ARG_TYPE' };
223  assert.throws(() => crypto.scrypt('', '', 42, null), expected);
224  assert.throws(() => crypto.scrypt('', '', 42, {}, null), expected);
225  assert.throws(() => crypto.scrypt('', '', 42, {}), expected);
226  assert.throws(() => crypto.scrypt('', '', 42, {}, {}), expected);
227}
228
229{
230  // Values for maxmem that do not fit in 32 bits but that are still safe
231  // integers should be allowed.
232  crypto.scrypt('', '', 4, { maxmem: 2 ** 52 },
233                common.mustSucceed((actual) => {
234                  assert.strictEqual(actual.toString('hex'), 'd72c87d0');
235                }));
236
237  // Values that exceed Number.isSafeInteger should not be allowed.
238  assert.throws(() => crypto.scryptSync('', '', 0, { maxmem: 2 ** 53 }), {
239    code: 'ERR_OUT_OF_RANGE'
240  });
241}
242
243{
244  // Regression test for https://github.com/nodejs/node/issues/28836.
245
246  function testParameter(name, value) {
247    let accessCount = 0;
248
249    // Find out how often the value is accessed.
250    crypto.scryptSync('', '', 1, {
251      get [name]() {
252        accessCount++;
253        return value;
254      }
255    });
256
257    // Try to crash the process on the last access.
258    assert.throws(() => {
259      crypto.scryptSync('', '', 1, {
260        get [name]() {
261          if (--accessCount === 0)
262            return '';
263          return value;
264        }
265      });
266    }, {
267      code: 'ERR_INVALID_ARG_TYPE'
268    });
269  }
270
271  [
272    ['N', 16384], ['cost', 16384],
273    ['r', 8], ['blockSize', 8],
274    ['p', 1], ['parallelization', 1],
275  ].forEach((arg) => testParameter(...arg));
276}
277