1// Flags: --expose-internals 2'use strict'; 3 4const common = require('../common'); 5 6if (!common.hasCrypto) 7 common.skip('missing crypto'); 8 9const assert = require('assert'); 10const { types: { isCryptoKey } } = require('util'); 11const { 12 webcrypto: { subtle, CryptoKey }, 13 createSecretKey, 14 KeyObject, 15} = require('crypto'); 16 17const { bigIntArrayToUnsignedBigInt } = require('internal/crypto/util'); 18 19const allUsages = [ 20 'encrypt', 21 'decrypt', 22 'sign', 23 'verify', 24 'deriveBits', 25 'deriveKey', 26 'wrapKey', 27 'unwrapKey', 28]; 29const vectors = { 30 'AES-CTR': { 31 algorithm: { length: 256 }, 32 result: 'CryptoKey', 33 usages: [ 34 'encrypt', 35 'decrypt', 36 'wrapKey', 37 'unwrapKey', 38 ], 39 }, 40 'AES-CBC': { 41 algorithm: { length: 256 }, 42 result: 'CryptoKey', 43 usages: [ 44 'encrypt', 45 'decrypt', 46 'wrapKey', 47 'unwrapKey', 48 ], 49 }, 50 'AES-GCM': { 51 algorithm: { length: 256 }, 52 result: 'CryptoKey', 53 usages: [ 54 'encrypt', 55 'decrypt', 56 'wrapKey', 57 'unwrapKey', 58 ], 59 }, 60 'AES-KW': { 61 algorithm: { length: 256 }, 62 result: 'CryptoKey', 63 usages: [ 64 'wrapKey', 65 'unwrapKey', 66 ], 67 }, 68 'HMAC': { 69 algorithm: { length: 256, hash: 'SHA-256' }, 70 result: 'CryptoKey', 71 usages: [ 72 'sign', 73 'verify', 74 ], 75 }, 76 'RSASSA-PKCS1-v1_5': { 77 algorithm: { 78 modulusLength: 1024, 79 publicExponent: new Uint8Array([1, 0, 1]), 80 hash: 'SHA-256' 81 }, 82 result: 'CryptoKeyPair', 83 usages: [ 84 'sign', 85 'verify', 86 ], 87 }, 88 'RSA-PSS': { 89 algorithm: { 90 modulusLength: 1024, 91 publicExponent: new Uint8Array([1, 0, 1]), 92 hash: 'SHA-256' 93 }, 94 result: 'CryptoKeyPair', 95 usages: [ 96 'sign', 97 'verify', 98 ], 99 }, 100 'RSA-OAEP': { 101 algorithm: { 102 modulusLength: 1024, 103 publicExponent: new Uint8Array([1, 0, 1]), 104 hash: 'SHA-256' 105 }, 106 result: 'CryptoKeyPair', 107 usages: [ 108 'encrypt', 109 'decrypt', 110 'wrapKey', 111 'unwrapKey', 112 ], 113 }, 114 'ECDSA': { 115 algorithm: { namedCurve: 'P-521' }, 116 result: 'CryptoKeyPair', 117 usages: [ 118 'sign', 119 'verify', 120 ], 121 }, 122 'ECDH': { 123 algorithm: { namedCurve: 'P-521' }, 124 result: 'CryptoKeyPair', 125 usages: [ 126 'deriveKey', 127 'deriveBits', 128 ], 129 }, 130 'Ed25519': { 131 result: 'CryptoKeyPair', 132 usages: [ 133 'sign', 134 'verify', 135 ], 136 }, 137 'Ed448': { 138 result: 'CryptoKeyPair', 139 usages: [ 140 'sign', 141 'verify', 142 ], 143 }, 144 'X25519': { 145 result: 'CryptoKeyPair', 146 usages: [ 147 'deriveKey', 148 'deriveBits', 149 ], 150 }, 151 'X448': { 152 result: 'CryptoKeyPair', 153 usages: [ 154 'deriveKey', 155 'deriveBits', 156 ], 157 }, 158}; 159 160// Test invalid algorithms 161{ 162 async function test(algorithm) { 163 return assert.rejects( 164 // The extractable and usages values are invalid here also, 165 // but the unrecognized algorithm name should be caught first. 166 subtle.generateKey(algorithm, 7, []), { 167 message: /Unrecognized algorithm name/, 168 name: 'NotSupportedError', 169 }); 170 } 171 172 const tests = [ 173 'AES', 174 { name: 'AES' }, 175 { name: 'AES-CMAC' }, 176 { name: 'AES-CFB' }, 177 { name: 'HMAC', hash: 'MD5' }, 178 { 179 name: 'RSA', 180 hash: 'SHA-256', 181 modulusLength: 2048, 182 publicExponent: new Uint8Array([1, 0, 1]) 183 }, 184 { 185 name: 'RSA-PSS', 186 hash: 'SHA', 187 modulusLength: 2048, 188 publicExponent: new Uint8Array([1, 0, 1]) 189 }, 190 { 191 name: 'EC', 192 namedCurve: 'P521' 193 }, 194 ].map(async (algorithm) => test(algorithm)); 195 196 Promise.all(tests).then(common.mustCall()); 197} 198 199// Test bad usages 200{ 201 async function test(name) { 202 await assert.rejects( 203 subtle.generateKey( 204 { 205 name, ...vectors[name].algorithm 206 }, 207 true, 208 []), 209 { message: /Usages cannot be empty/ }); 210 211 // For CryptoKeyPair results the private key 212 // usages must not be empty. 213 // - ECDH(-like) algorithm key pairs only have private key usages 214 // - Signing algorithm key pairs may pass a non-empty array but 215 // with only a public key usage 216 if ( 217 vectors[name].result === 'CryptoKeyPair' && 218 vectors[name].usages.includes('verify') 219 ) { 220 await assert.rejects( 221 subtle.generateKey( 222 { 223 name, ...vectors[name].algorithm 224 }, 225 true, 226 ['verify']), 227 { message: /Usages cannot be empty/ }); 228 } 229 230 const invalidUsages = []; 231 allUsages.forEach((usage) => { 232 if (!vectors[name].usages.includes(usage)) 233 invalidUsages.push(usage); 234 }); 235 for (const invalidUsage of invalidUsages) { 236 await assert.rejects( 237 subtle.generateKey( 238 { 239 name, ...vectors[name].algorithm 240 }, 241 true, 242 [...vectors[name].usages, invalidUsage]), 243 { message: /Unsupported key usage/ }); 244 } 245 } 246 247 const tests = Object.keys(vectors).map(test); 248 249 Promise.all(tests).then(common.mustCall()); 250} 251 252// Test RSA key generation 253{ 254 async function test( 255 name, 256 modulusLength, 257 publicExponent, 258 hash, 259 privateUsages, 260 publicUsages = privateUsages) { 261 let usages = privateUsages; 262 if (publicUsages !== privateUsages) 263 usages = usages.concat(publicUsages); 264 const { publicKey, privateKey } = await subtle.generateKey({ 265 name, 266 modulusLength, 267 publicExponent, 268 hash 269 }, true, usages); 270 271 assert(publicKey); 272 assert(privateKey); 273 assert(isCryptoKey(publicKey)); 274 assert(isCryptoKey(privateKey)); 275 276 assert(publicKey instanceof CryptoKey); 277 assert(privateKey instanceof CryptoKey); 278 279 assert.strictEqual(publicKey.type, 'public'); 280 assert.strictEqual(privateKey.type, 'private'); 281 assert.strictEqual(publicKey.toString(), '[object CryptoKey]'); 282 assert.strictEqual(privateKey.toString(), '[object CryptoKey]'); 283 assert.strictEqual(publicKey.extractable, true); 284 assert.strictEqual(privateKey.extractable, true); 285 assert.deepStrictEqual(publicKey.usages, publicUsages); 286 assert.deepStrictEqual(privateKey.usages, privateUsages); 287 assert.strictEqual(publicKey.algorithm.name, name); 288 assert.strictEqual(publicKey.algorithm.modulusLength, modulusLength); 289 assert.deepStrictEqual(publicKey.algorithm.publicExponent, publicExponent); 290 assert.strictEqual( 291 KeyObject.from(publicKey).asymmetricKeyDetails.publicExponent, 292 bigIntArrayToUnsignedBigInt(publicExponent)); 293 assert.strictEqual(publicKey.algorithm.hash.name, hash); 294 assert.strictEqual(privateKey.algorithm.name, name); 295 assert.strictEqual(privateKey.algorithm.modulusLength, modulusLength); 296 assert.deepStrictEqual(privateKey.algorithm.publicExponent, publicExponent); 297 assert.strictEqual( 298 KeyObject.from(privateKey).asymmetricKeyDetails.publicExponent, 299 bigIntArrayToUnsignedBigInt(publicExponent)); 300 assert.strictEqual(privateKey.algorithm.hash.name, hash); 301 302 // Missing parameters 303 await assert.rejects( 304 subtle.generateKey({ name, publicExponent, hash }, true, usages), { 305 code: 'ERR_MISSING_OPTION' 306 }); 307 308 await assert.rejects( 309 subtle.generateKey({ name, modulusLength, hash }, true, usages), { 310 code: 'ERR_MISSING_OPTION' 311 }); 312 313 await assert.rejects( 314 subtle.generateKey({ name, modulusLength }, true, usages), { 315 code: 'ERR_MISSING_OPTION' 316 }); 317 318 await Promise.all([{}].map((modulusLength) => { 319 return assert.rejects(subtle.generateKey({ 320 name, 321 modulusLength, 322 publicExponent, 323 hash 324 }, true, usages), { 325 code: 'ERR_INVALID_ARG_TYPE' 326 }); 327 })); 328 329 await Promise.all( 330 [ 331 '', 332 true, 333 {}, 334 1, 335 [], 336 new Uint32Array(2), 337 ].map((publicExponent) => { 338 return assert.rejects( 339 subtle.generateKey( 340 { name, modulusLength, publicExponent, hash }, true, usages), 341 { code: 'ERR_INVALID_ARG_TYPE' }); 342 })); 343 344 await Promise.all([true, 1].map((hash) => { 345 return assert.rejects(subtle.generateKey({ 346 name, 347 modulusLength, 348 publicExponent, 349 hash 350 }, true, usages), { 351 message: /Unrecognized algorithm name/, 352 name: 'NotSupportedError', 353 }); 354 })); 355 356 await Promise.all(['', {}, 1, false].map((usages) => { 357 return assert.rejects(subtle.generateKey({ 358 name, 359 modulusLength, 360 publicExponent, 361 hash 362 }, true, usages), { 363 code: 'ERR_INVALID_ARG_TYPE' 364 }); 365 })); 366 367 await Promise.all([[1], [1, 0, 0]].map((publicExponent) => { 368 return assert.rejects(subtle.generateKey({ 369 name, 370 modulusLength, 371 publicExponent: new Uint8Array(publicExponent), 372 hash 373 }, true, usages), { 374 name: 'OperationError', 375 }); 376 })); 377 } 378 379 const kTests = [ 380 [ 381 'RSASSA-PKCS1-v1_5', 382 1024, 383 Buffer.from([1, 0, 1]), 384 'SHA-256', 385 ['sign'], 386 ['verify'], 387 ], 388 [ 389 'RSA-PSS', 390 2048, 391 Buffer.from([1, 0, 1]), 392 'SHA-512', 393 ['sign'], 394 ['verify'], 395 ], 396 [ 397 'RSA-OAEP', 398 1024, 399 Buffer.from([3]), 400 'SHA-384', 401 ['decrypt', 'unwrapKey'], 402 ['encrypt', 'wrapKey'], 403 ], 404 ]; 405 406 const tests = kTests.map((args) => test(...args)); 407 408 Promise.all(tests).then(common.mustCall()); 409} 410 411// Test EC Key Generation 412{ 413 async function test( 414 name, 415 namedCurve, 416 privateUsages, 417 publicUsages = privateUsages) { 418 419 let usages = privateUsages; 420 if (publicUsages !== privateUsages) 421 usages = usages.concat(publicUsages); 422 423 const { publicKey, privateKey } = await subtle.generateKey({ 424 name, 425 namedCurve 426 }, true, usages); 427 428 assert(publicKey); 429 assert(privateKey); 430 assert(isCryptoKey(publicKey)); 431 assert(isCryptoKey(privateKey)); 432 433 assert.strictEqual(publicKey.type, 'public'); 434 assert.strictEqual(privateKey.type, 'private'); 435 assert.strictEqual(publicKey.toString(), '[object CryptoKey]'); 436 assert.strictEqual(privateKey.toString(), '[object CryptoKey]'); 437 assert.strictEqual(publicKey.extractable, true); 438 assert.strictEqual(privateKey.extractable, true); 439 assert.deepStrictEqual(publicKey.usages, publicUsages); 440 assert.deepStrictEqual(privateKey.usages, privateUsages); 441 assert.strictEqual(publicKey.algorithm.name, name); 442 assert.strictEqual(privateKey.algorithm.name, name); 443 assert.strictEqual(publicKey.algorithm.namedCurve, namedCurve); 444 assert.strictEqual(privateKey.algorithm.namedCurve, namedCurve); 445 446 // Invalid parameters 447 [1, true, {}, [], null].forEach(async (namedCurve) => { 448 await assert.rejects( 449 subtle.generateKey({ name, namedCurve }, true, privateUsages), { 450 name: 'NotSupportedError' 451 }); 452 }); 453 await assert.rejects( 454 subtle.generateKey({ name, namedCurve: undefined }, true, privateUsages), { 455 name: 'TypeError', 456 code: 'ERR_MISSING_OPTION' 457 }); 458 } 459 460 const kTests = [ 461 [ 462 'ECDSA', 463 'P-384', 464 ['sign'], 465 ['verify'], 466 ], 467 [ 468 'ECDSA', 469 'P-521', 470 ['sign'], 471 ['verify'], 472 ], 473 [ 474 'ECDH', 475 'P-384', 476 ['deriveKey', 'deriveBits'], 477 [], 478 ], 479 [ 480 'ECDH', 481 'P-521', 482 ['deriveKey', 'deriveBits'], 483 [], 484 ], 485 ]; 486 487 const tests = kTests.map((args) => test(...args)); 488 489 // Test bad parameters 490 491 Promise.all(tests).then(common.mustCall()); 492} 493 494// Test AES Key Generation 495{ 496 async function test(name, length, usages) { 497 const key = await subtle.generateKey({ 498 name, 499 length 500 }, true, usages); 501 502 assert(key); 503 assert(isCryptoKey(key)); 504 505 assert.strictEqual(key.type, 'secret'); 506 assert.strictEqual(key.toString(), '[object CryptoKey]'); 507 assert.strictEqual(key.extractable, true); 508 assert.deepStrictEqual(key.usages, usages); 509 assert.strictEqual(key.algorithm.name, name); 510 assert.strictEqual(key.algorithm.length, length); 511 512 // Invalid parameters 513 [1, 100, 257, '', false, null].forEach(async (length) => { 514 await assert.rejects( 515 subtle.generateKey({ name, length }, true, usages), { 516 name: 'OperationError' 517 }); 518 }); 519 520 await assert.rejects( 521 subtle.generateKey({ name, length: undefined }, true, usages), { 522 name: 'TypeError', 523 code: 'ERR_MISSING_OPTION' 524 }); 525 } 526 527 const kTests = [ 528 [ 'AES-CTR', 128, ['encrypt', 'decrypt', 'wrapKey']], 529 [ 'AES-CTR', 256, ['encrypt', 'decrypt', 'unwrapKey']], 530 [ 'AES-CBC', 128, ['encrypt', 'decrypt']], 531 [ 'AES-CBC', 256, ['encrypt', 'decrypt']], 532 [ 'AES-GCM', 128, ['encrypt', 'decrypt']], 533 [ 'AES-GCM', 256, ['encrypt', 'decrypt']], 534 [ 'AES-KW', 128, ['wrapKey', 'unwrapKey']], 535 [ 'AES-KW', 256, ['wrapKey', 'unwrapKey']], 536 ]; 537 538 const tests = Promise.all(kTests.map((args) => test(...args))); 539 540 tests.then(common.mustCall()); 541} 542 543// Test HMAC Key Generation 544{ 545 async function test(length, hash, usages) { 546 const key = await subtle.generateKey({ 547 name: 'HMAC', 548 length, 549 hash 550 }, true, usages); 551 552 if (length === undefined) { 553 switch (hash) { 554 case 'SHA-1': length = 512; break; 555 case 'SHA-256': length = 512; break; 556 case 'SHA-384': length = 1024; break; 557 case 'SHA-512': length = 1024; break; 558 } 559 } 560 561 assert(key); 562 assert(isCryptoKey(key)); 563 564 assert.strictEqual(key.type, 'secret'); 565 assert.strictEqual(key.toString(), '[object CryptoKey]'); 566 assert.strictEqual(key.extractable, true); 567 assert.deepStrictEqual(key.usages, usages); 568 assert.strictEqual(key.algorithm.name, 'HMAC'); 569 assert.strictEqual(key.algorithm.length, length); 570 assert.strictEqual(key.algorithm.hash.name, hash); 571 572 [1, false, null].forEach(async (hash) => { 573 await assert.rejects( 574 subtle.generateKey({ name: 'HMAC', length, hash }, true, usages), { 575 message: /Unrecognized algorithm name/, 576 name: 'NotSupportedError', 577 }); 578 }); 579 } 580 581 const kTests = [ 582 [ undefined, 'SHA-1', ['sign', 'verify']], 583 [ undefined, 'SHA-256', ['sign', 'verify']], 584 [ undefined, 'SHA-384', ['sign', 'verify']], 585 [ undefined, 'SHA-512', ['sign', 'verify']], 586 [ 128, 'SHA-256', ['sign', 'verify']], 587 [ 1024, 'SHA-512', ['sign', 'verify']], 588 ]; 589 590 const tests = Promise.all(kTests.map((args) => test(...args))); 591 592 tests.then(common.mustCall()); 593} 594 595// End user code cannot create CryptoKey directly 596assert.throws(() => new CryptoKey(), { code: 'ERR_ILLEGAL_CONSTRUCTOR' }); 597 598{ 599 const buffer = Buffer.from('Hello World'); 600 const keyObject = createSecretKey(buffer); 601 assert(!isCryptoKey(buffer)); 602 assert(!isCryptoKey(keyObject)); 603} 604 605// Test OKP Key Generation 606{ 607 async function test( 608 name, 609 privateUsages, 610 publicUsages = privateUsages) { 611 612 let usages = privateUsages; 613 if (publicUsages !== privateUsages) 614 usages = usages.concat(publicUsages); 615 616 const { publicKey, privateKey } = await subtle.generateKey({ 617 name, 618 }, true, usages); 619 620 assert(publicKey); 621 assert(privateKey); 622 assert(isCryptoKey(publicKey)); 623 assert(isCryptoKey(privateKey)); 624 625 assert.strictEqual(publicKey.type, 'public'); 626 assert.strictEqual(privateKey.type, 'private'); 627 assert.strictEqual(publicKey.toString(), '[object CryptoKey]'); 628 assert.strictEqual(privateKey.toString(), '[object CryptoKey]'); 629 assert.strictEqual(publicKey.extractable, true); 630 assert.strictEqual(privateKey.extractable, true); 631 assert.deepStrictEqual(publicKey.usages, publicUsages); 632 assert.deepStrictEqual(privateKey.usages, privateUsages); 633 assert.strictEqual(publicKey.algorithm.name, name); 634 assert.strictEqual(privateKey.algorithm.name, name); 635 } 636 637 const kTests = [ 638 [ 639 'Ed25519', 640 ['sign'], 641 ['verify'], 642 ], 643 [ 644 'Ed448', 645 ['sign'], 646 ['verify'], 647 ], 648 [ 649 'X25519', 650 ['deriveKey', 'deriveBits'], 651 [], 652 ], 653 [ 654 'X448', 655 ['deriveKey', 'deriveBits'], 656 [], 657 ], 658 ]; 659 660 const tests = kTests.map((args) => test(...args)); 661 662 // Test bad parameters 663 664 Promise.all(tests).then(common.mustCall()); 665} 666