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