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