1'use strict'; 2 3const common = require('../common'); 4 5if (!common.hasCrypto) 6 common.skip('missing crypto'); 7 8const assert = require('assert'); 9const { subtle } = require('crypto').webcrypto; 10 11function getDeriveKeyInfo(name, length, hash, ...usages) { 12 return [{ name, length, hash }, usages]; 13} 14 15const kDerivedKeyTypes = [ 16 ['AES-CBC', 128, undefined, 'encrypt', 'decrypt'], 17 ['AES-CBC', 256, undefined, 'encrypt', 'decrypt'], 18 ['AES-CTR', 128, undefined, 'encrypt', 'decrypt'], 19 ['AES-CTR', 256, undefined, 'encrypt', 'decrypt'], 20 ['AES-GCM', 128, undefined, 'encrypt', 'decrypt'], 21 ['AES-GCM', 256, undefined, 'encrypt', 'decrypt'], 22 ['AES-KW', 128, undefined, 'wrapKey', 'unwrapKey'], 23 ['AES-KW', 256, undefined, 'wrapKey', 'unwrapKey'], 24 ['HMAC', 256, 'SHA-1', 'sign', 'verify'], 25 ['HMAC', 256, 'SHA-256', 'sign', 'verify'], 26 ['HMAC', 256, 'SHA-384', 'sign', 'verify'], 27 ['HMAC', 256, 'SHA-512', 'sign', 'verify'], 28]; 29 30const kDerivedKeys = { 31 short: '5040737377307264', 32 long: '55736572732073686f756c64207069636b206c6f6e6720706173737068726' + 33 '173657320286e6f74207573652073686f72742070617373776f7264732921', 34 empty: '' 35}; 36 37const kSalts = { 38 normal: '536f6469756d2043686c6f7269646520636f6d706f756e64', 39 empty: '' 40}; 41 42const kInfos = { 43 normal: '484b444620657874726120696e666f', 44 empty: '' 45}; 46 47const kDerivations = { 48 short: { 49 normal: { 50 'SHA-384': { 51 normal: '19ba74368e6b993390f27fe9a7d02bc3' + 52 '38173f72be71a19fc744fcdb3fd4b84b', 53 empty: '97601f4e0c53a5d3f3a2810099bc6820' + 54 'ec50083434769b59fc24a417a9543734' 55 }, 56 'SHA-512': { 57 normal: '4bbd6db2435fb696157f6089c977c3c7' + 58 '3f3eac5ef3dd6baae604cb53bfbb153e', 59 empty: '2f3157e7fe0c10b01298c8f0886a90ed' + 60 'cf80abdef5dbc1df2b1482532b52b934' 61 }, 62 'SHA-1': { 63 normal: '05ad22ed2138c9600e4d9e2725ded301' + 64 'f5d287fbfb5702f999bc6536d3edef98', 65 empty: 'd51b6fb7e599ca30c5ee264593e4b85f' + 66 '2220c7c3ab003157bff8cb4f369c7560' 67 }, 68 'SHA-256': { 69 normal: '2af5901e28849c28443857386aa1ac3b' + 70 'b127e92631c1c051482d6690941772b4', 71 empty: '9e4b719033742101e90f1ad61e2ff3b4' + 72 '256863667296d74389f1f02af2c4e6a6' 73 } 74 }, 75 empty: { 76 'SHA-384': { 77 normal: 'fb482ff22c4f8d466c4dfe6e29f2cc2e' + 78 'cdabf5884328fbf08a738fd945f166cb', 79 empty: '1e023c17b340533ceaef39230cb8b3bb' + 80 'dbf663a13d6075d0dd326c049478fba5' 81 }, 82 'SHA-512': { 83 normal: 'f17b5bdcd8d7d3d4601036a19436317d' + 84 '1644f9a4e0956efc0e372b83acdacfdb', 85 empty: 'c7b474942f31f83faf5d14731802b1bd' + 86 '49478549cb3a8f3dbfedc4d3209cf5b6' 87 }, 88 'SHA-1': { 89 normal: 'c126f1e6f25a9de42cf7d427059a52ed' + 90 '9601f29a5815cbfbc64bc7f668c6a341', 91 empty: '3215c3f08de70549b051b7033745a818' + 92 '4f8cbaa6b1735330d2bcb6b16f4642ef' 93 }, 94 'SHA-256': { 95 normal: '733c8b6bcfac875c7f08982a6e3ffb56' + 96 '0acea6f165476eb83460b9353ed41dfe', 97 empty: 'c8e12774135305c9147f2cc4766e5ead' + 98 '25d8f457b9a1953d52677361ced558fb' 99 } 100 } 101 }, 102 long: { 103 normal: { 104 'SHA-384': { 105 normal: 'f91571b521f7eef13e573aa46378659e' + 106 'f3b7f36ffdd1bb055db2cd77d260c467', 107 empty: '68af1c2cf6b9370d2054344798bdbb18' + 108 '47ccf407b7652b793dd136d4640e0348' 109 }, 110 'SHA-512': { 111 normal: '710aae2fdf889e45fe0fb995b2c26b33' + 112 'eb988650ec0faef167028a7a6ccb3638', 113 empty: 'e5de568081c71e562750829871c34275' + 114 '8104765ed6f306f0613c9d4bb336f2aa' 115 }, 116 'SHA-1': { 117 normal: '7f957edcbce3cb0b70566e1eb60efd1e' + 118 '405a13304c661d3663778109bf06899c', 119 empty: '3062f3cf1a730b9cef51f02c1dfac85e' + 120 'd91e4b0065eb50ca9fd8b0107e728733' 121 }, 122 'SHA-256': { 123 normal: '31b7d68530a863e717c081ca6917b686' + 124 '50b3dd9a29f30606e2cad199bec14d13', 125 empty: 'e579d1f9e7f08e6f990ffcfcce1ed201' + 126 'c5e37e62cdf606f0ba4aca80427fbc44' 127 } 128 }, 129 empty: { 130 'SHA-384': { 131 normal: '619eb6f9287395bbd5ed6a67c968465a' + 132 'd82b6c559f3c38b604bbb08f58320b03', 133 empty: 'ff447b423d83fe76836c32337228b56b' + 134 '5bd9bf68d58e7dca4b7cca842a45e11a' 135 }, 136 'SHA-512': { 137 normal: '133e8a7f7ff433690cc88432c2a338c2' + 138 '77e5c13756ff878f46753fe6a564e3e5', 139 empty: 'de54f7eec80c9cc66d349fc987f80d46' + 140 '1db2ef4ff4e18505d28bd80cb42c7d76' 141 }, 142 'SHA-1': { 143 normal: 'adb93cdbce79b7d51159b6c0131a2b62' + 144 'f23828d26acd685e34c06535e6f77496', 145 empty: '47710d2a7507e05a1ddcc87a7c2f9061' + 146 '77a266efb9e622510cccb3713cd08d58' 147 }, 148 'SHA-256': { 149 normal: 'a401d7c9158a29e5c7193ab9730f0748' + 150 '851cc5baadb42cad024b6290fe213436', 151 empty: 'b4f7e7557674d501cbfbc0148ad800c0' + 152 '750189fe295a2aca5e1bf4122c85edf9' 153 } 154 } 155 }, 156}; 157 158async function setupBaseKeys() { 159 const promises = []; 160 161 const baseKeys = {}; 162 const noBits = {}; 163 const noKey = {}; 164 let wrongKey = null; 165 166 Object.keys(kDerivedKeys).forEach((size) => { 167 const keyData = Buffer.from(kDerivedKeys[size], 'hex'); 168 promises.push( 169 subtle.importKey( 170 'raw', 171 keyData, 172 { name: 'HKDF' }, 173 false, 174 ['deriveKey', 'deriveBits']) 175 .then( 176 (baseKey) => baseKeys[size] = baseKey, 177 (err) => assert.ifError(err))); 178 179 promises.push( 180 subtle.importKey( 181 'raw', 182 keyData, 183 { name: 'HKDF' }, 184 false, ['deriveBits']) 185 .then( 186 (baseKey) => noKey[size] = baseKey, 187 (err) => assert.ifError(err))); 188 189 promises.push( 190 subtle.importKey( 191 'raw', 192 keyData, 193 { name: 'HKDF' }, 194 false, ['deriveKey']) 195 .then( 196 (baseKey) => noBits[size] = baseKey, 197 (err) => assert.ifError(err))); 198 }); 199 200 promises.push( 201 subtle.generateKey( 202 { 203 name: 'ECDH', 204 namedCurve: 'P-521' 205 }, 206 false, 207 ['deriveKey', 'deriveBits']) 208 .then( 209 (baseKey) => wrongKey = baseKey.privateKey, 210 (err) => assert.ifError(err))); 211 212 await Promise.all(promises); 213 214 return { 215 baseKeys, 216 noBits, 217 noKey, 218 wrongKey 219 }; 220} 221 222async function testDeriveBits( 223 baseKeys, 224 size, 225 saltSize, 226 hash, 227 infoSize) { 228 const algorithm = { 229 name: 'HKDF', 230 salt: Buffer.from(kSalts[saltSize], 'hex'), 231 info: Buffer.from(kInfos[infoSize], 'hex'), 232 hash 233 }; 234 235 const bits = await subtle.deriveBits( 236 algorithm, 237 baseKeys[size], 238 256); 239 240 assert(bits instanceof ArrayBuffer); 241 assert.strictEqual( 242 Buffer.from(bits).toString('hex'), 243 kDerivations[size][saltSize][hash][infoSize]); 244} 245 246async function testDeriveBitsBadLengths( 247 baseKeys, 248 size, 249 saltSize, 250 hash, 251 infoSize) { 252 const algorithm = { 253 name: 'HKDF', 254 salt: Buffer.from(kSalts[saltSize], 'hex'), 255 info: Buffer.from(kInfos[infoSize], 'hex'), 256 hash 257 }; 258 259 return Promise.all([ 260 assert.rejects( 261 subtle.deriveBits(algorithm, baseKeys[size], undefined), { 262 name: 'OperationError', 263 }), 264 assert.rejects( 265 subtle.deriveBits(algorithm, baseKeys[size], 0), { 266 message: /length cannot be zero/, 267 name: 'OperationError', 268 }), 269 assert.rejects( 270 subtle.deriveBits(algorithm, baseKeys[size], null), { 271 message: 'length cannot be null', 272 name: 'OperationError', 273 }), 274 assert.rejects( 275 subtle.deriveBits(algorithm, baseKeys[size], 15), { 276 message: /length must be a multiple of 8/, 277 name: 'OperationError', 278 }), 279 ]); 280} 281 282async function testDeriveBitsBadHash( 283 baseKeys, 284 size, 285 saltSize, 286 hash, 287 infoSize) { 288 const salt = Buffer.from(kSalts[saltSize], 'hex'); 289 const info = Buffer.from(kInfos[infoSize], 'hex'); 290 const algorithm = { name: 'HKDF', salt, info }; 291 292 return Promise.all([ 293 assert.rejects( 294 subtle.deriveBits( 295 { 296 ...algorithm, 297 hash: hash.substring(0, 3) + hash.substring(4) 298 }, baseKeys[size], 256), { 299 message: /Unrecognized algorithm name/, 300 name: 'NotSupportedError', 301 }), 302 assert.rejects( 303 subtle.deriveBits( 304 { 305 ...algorithm, 306 hash: 'PBKDF2' 307 }, 308 baseKeys[size], 256), { 309 message: /Unrecognized algorithm name/, 310 name: 'NotSupportedError', 311 }), 312 ]); 313} 314 315async function testDeriveBitsBadUsage( 316 noBits, 317 size, 318 saltSize, 319 hash, 320 infoSize) { 321 const algorithm = { 322 name: 'HKDF', 323 salt: Buffer.from(kSalts[saltSize], 'hex'), 324 info: Buffer.from(kInfos[infoSize], 'hex'), 325 hash 326 }; 327 328 return assert.rejects( 329 subtle.deriveBits(algorithm, noBits[size], 256), { 330 message: /baseKey does not have deriveBits usage/ 331 }); 332} 333 334async function testDeriveBitsMissingSalt( 335 baseKeys, 336 size, 337 saltSize, 338 hash, 339 infoSize) { 340 const algorithm = { 341 name: 'HKDF', 342 info: Buffer.from(kInfos[infoSize], 'hex'), 343 hash 344 }; 345 346 return assert.rejects( 347 subtle.deriveBits(algorithm, baseKeys[size], 0), { 348 code: 'ERR_MISSING_OPTION' 349 }); 350} 351 352async function testDeriveBitsMissingInfo( 353 baseKeys, 354 size, 355 saltSize, 356 hash, 357 infoSize) { 358 const algorithm = { 359 name: 'HKDF', 360 salt: Buffer.from(kSalts[infoSize], 'hex'), 361 hash 362 }; 363 364 return assert.rejects( 365 subtle.deriveBits(algorithm, baseKeys[size], 0), { 366 code: 'ERR_MISSING_OPTION' 367 }); 368} 369 370async function testBitsWrongKeyType( 371 wrongKey, 372 saltSize, 373 hash, 374 infoSize) { 375 const algorithm = { 376 name: 'HKDF', 377 salt: Buffer.from(kSalts[saltSize], 'hex'), 378 info: Buffer.from(kInfos[infoSize], 'hex'), 379 hash 380 }; 381 382 return assert.rejects( 383 subtle.deriveBits(algorithm, wrongKey, 256), { 384 message: /Key algorithm mismatch/ 385 }); 386} 387 388async function testDeriveKey( 389 baseKeys, 390 size, 391 saltSize, 392 hash, 393 infoSize, 394 keyType, 395 usages) { 396 const algorithm = { 397 name: 'HKDF', 398 salt: Buffer.from(kSalts[saltSize], 'hex'), 399 info: Buffer.from(kInfos[infoSize], 'hex'), 400 hash 401 }; 402 403 const key = await subtle.deriveKey( 404 algorithm, 405 baseKeys[size], 406 keyType, 407 true, 408 usages); 409 410 const bits = await subtle.exportKey('raw', key); 411 412 assert.strictEqual( 413 Buffer.from(bits).toString('hex'), 414 kDerivations[size][saltSize][hash][infoSize].slice(0, keyType.length / 4)); 415} 416 417async function testDeriveKeyBadHash( 418 baseKeys, 419 size, 420 saltSize, 421 hash, 422 infoSize, 423 keyType, 424 usages) { 425 const salt = Buffer.from(kSalts[saltSize], 'hex'); 426 const info = Buffer.from(kInfos[infoSize], 'hex'); 427 const algorithm = { name: 'HKDF', salt, info }; 428 429 return Promise.all([ 430 assert.rejects( 431 subtle.deriveKey( 432 { 433 ...algorithm, 434 hash: hash.substring(0, 3) + hash.substring(4) 435 }, 436 baseKeys[size], 437 keyType, 438 true, 439 usages), 440 { 441 message: /Unrecognized algorithm name/, 442 name: 'NotSupportedError', 443 }), 444 assert.rejects( 445 subtle.deriveKey( 446 { 447 ...algorithm, 448 hash: 'PBKDF2' 449 }, 450 baseKeys[size], 451 keyType, 452 true, 453 usages), 454 { 455 message: /Unrecognized algorithm name/, 456 name: 'NotSupportedError', 457 }), 458 ]); 459} 460 461async function testDeriveKeyBadUsage( 462 noKey, 463 size, 464 saltSize, 465 hash, 466 infoSize, 467 keyType, 468 usages) { 469 const algorithm = { 470 name: 'HKDF', 471 salt: Buffer.from(kSalts[saltSize], 'hex'), 472 info: Buffer.from(kInfos[infoSize], 'hex'), 473 hash 474 }; 475 476 return assert.rejects( 477 subtle.deriveKey(algorithm, noKey[size], keyType, true, usages), { 478 message: /baseKey does not have deriveKey usage/ 479 }); 480} 481 482async function testWrongKeyType( 483 wrongKey, 484 saltSize, 485 hash, 486 infoSize, 487 keyType, 488 usages 489) { 490 const algorithm = { 491 name: 'HKDF', 492 salt: Buffer.from(kSalts[saltSize], 'hex'), 493 info: Buffer.from(kInfos[infoSize], 'hex'), 494 hash 495 }; 496 497 return assert.rejects( 498 subtle.deriveKey(algorithm, wrongKey, keyType, true, usages), { 499 message: /Key algorithm mismatch/ 500 }); 501} 502 503(async function() { 504 const { 505 baseKeys, 506 noBits, 507 noKey, 508 wrongKey, 509 } = await setupBaseKeys(); 510 511 const variations = []; 512 513 Object.keys(kDerivations).forEach((size) => { 514 Object.keys(kDerivations[size]).forEach((saltSize) => { 515 Object.keys(kDerivations[size][saltSize]).forEach((hash) => { 516 Object.keys(kDerivations[size][saltSize][hash]).forEach( 517 async (infoSize) => { 518 const args = [baseKeys, size, saltSize, hash, infoSize]; 519 variations.push(testDeriveBits(...args)); 520 variations.push(testDeriveBitsBadLengths(...args)); 521 variations.push(testDeriveBitsMissingSalt(...args)); 522 variations.push(testDeriveBitsMissingInfo(...args)); 523 variations.push(testDeriveBitsBadHash(...args)); 524 variations.push( 525 testDeriveBitsBadUsage( 526 noBits, 527 size, 528 saltSize, 529 hash, 530 infoSize)); 531 variations.push( 532 testBitsWrongKeyType( 533 wrongKey, 534 saltSize, 535 hash, 536 infoSize)); 537 538 kDerivedKeyTypes.forEach((keyType) => { 539 const keyArgs = getDeriveKeyInfo(...keyType); 540 variations.push(testDeriveKey(...args, ...keyArgs)); 541 variations.push(testDeriveKeyBadHash(...args, ...keyArgs)); 542 variations.push(testDeriveKeyBadUsage( 543 noKey, size, saltSize, hash, infoSize, ...keyArgs)); 544 variations.push( 545 testWrongKeyType( 546 wrongKey, 547 saltSize, 548 hash, 549 infoSize, 550 ...keyArgs)); 551 }); 552 }); 553 }); 554 }); 555 }); 556 557 await Promise.all(variations); 558 559})().then(common.mustCall()); 560