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