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