• 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').scrypt !== '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 GB 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
148for (const options of good) {
149  const { pass, salt, keylen, expected } = options;
150  const actual = crypto.scryptSync(pass, salt, keylen, options);
151  assert.strictEqual(actual.toString('hex'), expected);
152  crypto.scrypt(pass, salt, keylen, options, common.mustCall((err, actual) => {
153    assert.ifError(err);
154    assert.strictEqual(actual.toString('hex'), expected);
155  }));
156}
157
158for (const options of bad) {
159  const expected = {
160    code: 'ERR_CRYPTO_SCRYPT_INVALID_PARAMETER',
161    message: 'Invalid scrypt parameter',
162    name: 'Error',
163  };
164  assert.throws(() => crypto.scrypt('pass', 'salt', 1, options, () => {}),
165                expected);
166  assert.throws(() => crypto.scryptSync('pass', 'salt', 1, options),
167                expected);
168}
169
170for (const options of toobig) {
171  const expected = {
172    message: new RegExp('error:[^:]+:digital envelope routines:' +
173                        '(?:EVP_PBE_scrypt|scrypt_alg):memory limit exceeded'),
174    name: 'Error',
175  };
176  assert.throws(() => crypto.scrypt('pass', 'salt', 1, options, () => {}),
177                expected);
178  assert.throws(() => crypto.scryptSync('pass', 'salt', 1, options),
179                expected);
180}
181
182{
183  const defaults = { N: 16384, p: 1, r: 8 };
184  const expected = crypto.scryptSync('pass', 'salt', 1, defaults);
185  const actual = crypto.scryptSync('pass', 'salt', 1);
186  assert.deepStrictEqual(actual.toString('hex'), expected.toString('hex'));
187  crypto.scrypt('pass', 'salt', 1, common.mustCall((err, actual) => {
188    assert.ifError(err);
189    assert.deepStrictEqual(actual.toString('hex'), expected.toString('hex'));
190  }));
191}
192
193{
194  const defaultEncoding = crypto.DEFAULT_ENCODING;
195  const defaults = { N: 16384, p: 1, r: 8 };
196  const expected = crypto.scryptSync('pass', 'salt', 1, defaults);
197
198  const testEncoding = 'latin1';
199  crypto.DEFAULT_ENCODING = testEncoding;
200  const actual = crypto.scryptSync('pass', 'salt', 1);
201  assert.deepStrictEqual(actual, expected.toString(testEncoding));
202
203  crypto.scrypt('pass', 'salt', 1, common.mustCall((err, actual) => {
204    assert.ifError(err);
205    assert.deepStrictEqual(actual, expected.toString(testEncoding));
206  }));
207
208  crypto.DEFAULT_ENCODING = defaultEncoding;
209}
210
211for (const { args, expected } of badargs) {
212  assert.throws(() => crypto.scrypt(...args), expected);
213  assert.throws(() => crypto.scryptSync(...args), expected);
214}
215
216{
217  const expected = { code: 'ERR_INVALID_CALLBACK' };
218  assert.throws(() => crypto.scrypt('', '', 42, null), expected);
219  assert.throws(() => crypto.scrypt('', '', 42, {}, null), expected);
220  assert.throws(() => crypto.scrypt('', '', 42, {}), expected);
221  assert.throws(() => crypto.scrypt('', '', 42, {}, {}), expected);
222}
223
224{
225  // Values for maxmem that do not fit in 32 bits but that are still safe
226  // integers should be allowed.
227  crypto.scrypt('', '', 4, { maxmem: 2 ** 52 },
228                common.mustCall((err, actual) => {
229                  assert.ifError(err);
230                  assert.strictEqual(actual.toString('hex'), 'd72c87d0');
231                }));
232
233  // Values that exceed Number.isSafeInteger should not be allowed.
234  assert.throws(() => crypto.scryptSync('', '', 0, { maxmem: 2 ** 53 }), {
235    code: 'ERR_OUT_OF_RANGE'
236  });
237}
238
239{
240  // Regression test for https://github.com/nodejs/node/issues/28836.
241
242  function testParameter(name, value) {
243    let accessCount = 0;
244
245    // Find out how often the value is accessed.
246    crypto.scryptSync('', '', 1, {
247      get [name]() {
248        accessCount++;
249        return value;
250      }
251    });
252
253    // Try to crash the process on the last access.
254    assert.throws(() => {
255      crypto.scryptSync('', '', 1, {
256        get [name]() {
257          if (--accessCount === 0)
258            return '';
259          return value;
260        }
261      });
262    }, {
263      code: 'ERR_INVALID_ARG_TYPE'
264    });
265  }
266
267  [
268    ['N', 16384], ['cost', 16384],
269    ['r', 8], ['blockSize', 8],
270    ['p', 1], ['parallelization', 1]
271  ].forEach((arg) => testParameter(...arg));
272}
273