1'use strict'; 2 3const common = require('../common'); 4if (!common.hasCrypto) 5 common.skip('missing crypto'); 6 7const assert = require('assert'); 8const { 9 constants, 10 createSign, 11 createVerify, 12 generateKeyPair, 13 generateKeyPairSync, 14 publicEncrypt, 15 privateDecrypt, 16 sign, 17 verify 18} = require('crypto'); 19const { inspect, promisify } = require('util'); 20 21// Asserts that the size of the given key (in chars or bytes) is within 10% of 22// the expected size. 23function assertApproximateSize(key, expectedSize) { 24 const u = typeof key === 'string' ? 'chars' : 'bytes'; 25 const min = Math.floor(0.9 * expectedSize); 26 const max = Math.ceil(1.1 * expectedSize); 27 assert(key.length >= min, 28 `Key (${key.length} ${u}) is shorter than expected (${min} ${u})`); 29 assert(key.length <= max, 30 `Key (${key.length} ${u}) is longer than expected (${max} ${u})`); 31} 32 33// Tests that a key pair can be used for encryption / decryption. 34function testEncryptDecrypt(publicKey, privateKey) { 35 const message = 'Hello Node.js world!'; 36 const plaintext = Buffer.from(message, 'utf8'); 37 for (const key of [publicKey, privateKey]) { 38 const ciphertext = publicEncrypt(key, plaintext); 39 const received = privateDecrypt(privateKey, ciphertext); 40 assert.strictEqual(received.toString('utf8'), message); 41 } 42} 43 44// Tests that a key pair can be used for signing / verification. 45function testSignVerify(publicKey, privateKey) { 46 const message = Buffer.from('Hello Node.js world!'); 47 48 function oldSign(algo, data, key) { 49 return createSign(algo).update(data).sign(key); 50 } 51 52 function oldVerify(algo, data, key, signature) { 53 return createVerify(algo).update(data).verify(key, signature); 54 } 55 56 for (const signFn of [sign, oldSign]) { 57 const signature = signFn('SHA256', message, privateKey); 58 for (const verifyFn of [verify, oldVerify]) { 59 for (const key of [publicKey, privateKey]) { 60 const okay = verifyFn('SHA256', message, key, signature); 61 assert(okay); 62 } 63 } 64 } 65} 66 67// Constructs a regular expression for a PEM-encoded key with the given label. 68function getRegExpForPEM(label, cipher) { 69 const head = `\\-\\-\\-\\-\\-BEGIN ${label}\\-\\-\\-\\-\\-`; 70 const rfc1421Header = cipher == null ? '' : 71 `\nProc-Type: 4,ENCRYPTED\nDEK-Info: ${cipher},[^\n]+\n`; 72 const body = '([a-zA-Z0-9\\+/=]{64}\n)*[a-zA-Z0-9\\+/=]{1,64}'; 73 const end = `\\-\\-\\-\\-\\-END ${label}\\-\\-\\-\\-\\-`; 74 return new RegExp(`^${head}${rfc1421Header}\n${body}\n${end}\n$`); 75} 76 77const pkcs1PubExp = getRegExpForPEM('RSA PUBLIC KEY'); 78const pkcs1PrivExp = getRegExpForPEM('RSA PRIVATE KEY'); 79const pkcs1EncExp = (cipher) => getRegExpForPEM('RSA PRIVATE KEY', cipher); 80const spkiExp = getRegExpForPEM('PUBLIC KEY'); 81const pkcs8Exp = getRegExpForPEM('PRIVATE KEY'); 82const pkcs8EncExp = getRegExpForPEM('ENCRYPTED PRIVATE KEY'); 83const sec1Exp = getRegExpForPEM('EC PRIVATE KEY'); 84const sec1EncExp = (cipher) => getRegExpForPEM('EC PRIVATE KEY', cipher); 85 86{ 87 // To make the test faster, we will only test sync key generation once and 88 // with a relatively small key. 89 const ret = generateKeyPairSync('rsa', { 90 publicExponent: 3, 91 modulusLength: 512, 92 publicKeyEncoding: { 93 type: 'pkcs1', 94 format: 'pem' 95 }, 96 privateKeyEncoding: { 97 type: 'pkcs8', 98 format: 'pem' 99 } 100 }); 101 102 assert.strictEqual(Object.keys(ret).length, 2); 103 const { publicKey, privateKey } = ret; 104 105 assert.strictEqual(typeof publicKey, 'string'); 106 assert(pkcs1PubExp.test(publicKey)); 107 assertApproximateSize(publicKey, 162); 108 assert.strictEqual(typeof privateKey, 'string'); 109 assert(pkcs8Exp.test(privateKey)); 110 assertApproximateSize(privateKey, 512); 111 112 testEncryptDecrypt(publicKey, privateKey); 113 testSignVerify(publicKey, privateKey); 114} 115 116{ 117 // Test sync key generation with key objects. 118 const { publicKey, privateKey } = generateKeyPairSync('rsa', { 119 modulusLength: 512 120 }); 121 122 assert.strictEqual(typeof publicKey, 'object'); 123 assert.strictEqual(publicKey.type, 'public'); 124 assert.strictEqual(publicKey.asymmetricKeyType, 'rsa'); 125 126 assert.strictEqual(typeof privateKey, 'object'); 127 assert.strictEqual(privateKey.type, 'private'); 128 assert.strictEqual(privateKey.asymmetricKeyType, 'rsa'); 129} 130 131{ 132 const publicKeyEncoding = { 133 type: 'pkcs1', 134 format: 'der' 135 }; 136 137 // Test async RSA key generation. 138 generateKeyPair('rsa', { 139 publicExponent: 0x10001, 140 modulusLength: 512, 141 publicKeyEncoding, 142 privateKeyEncoding: { 143 type: 'pkcs1', 144 format: 'pem' 145 } 146 }, common.mustCall((err, publicKeyDER, privateKey) => { 147 assert.ifError(err); 148 149 assert(Buffer.isBuffer(publicKeyDER)); 150 assertApproximateSize(publicKeyDER, 74); 151 152 assert.strictEqual(typeof privateKey, 'string'); 153 assert(pkcs1PrivExp.test(privateKey)); 154 assertApproximateSize(privateKey, 512); 155 156 const publicKey = { key: publicKeyDER, ...publicKeyEncoding }; 157 testEncryptDecrypt(publicKey, privateKey); 158 testSignVerify(publicKey, privateKey); 159 })); 160 161 // Now do the same with an encrypted private key. 162 generateKeyPair('rsa', { 163 publicExponent: 0x1001, 164 modulusLength: 512, 165 publicKeyEncoding, 166 privateKeyEncoding: { 167 type: 'pkcs1', 168 format: 'pem', 169 cipher: 'aes-256-cbc', 170 passphrase: 'secret' 171 } 172 }, common.mustCall((err, publicKeyDER, privateKey) => { 173 assert.ifError(err); 174 175 assert(Buffer.isBuffer(publicKeyDER)); 176 assertApproximateSize(publicKeyDER, 74); 177 178 assert.strictEqual(typeof privateKey, 'string'); 179 assert(pkcs1EncExp('AES-256-CBC').test(privateKey)); 180 181 // Since the private key is encrypted, signing shouldn't work anymore. 182 const publicKey = { key: publicKeyDER, ...publicKeyEncoding }; 183 assert.throws(() => testSignVerify(publicKey, privateKey), { 184 name: 'TypeError', 185 code: 'ERR_MISSING_PASSPHRASE', 186 message: 'Passphrase required for encrypted key' 187 }); 188 189 const key = { key: privateKey, passphrase: 'secret' }; 190 testEncryptDecrypt(publicKey, key); 191 testSignVerify(publicKey, key); 192 })); 193 194 // Now do the same with an encrypted private key, but encoded as DER. 195 generateKeyPair('rsa', { 196 publicExponent: 0x10001, 197 modulusLength: 512, 198 publicKeyEncoding, 199 privateKeyEncoding: { 200 type: 'pkcs8', 201 format: 'der', 202 cipher: 'aes-256-cbc', 203 passphrase: 'secret' 204 } 205 }, common.mustCall((err, publicKeyDER, privateKeyDER) => { 206 assert.ifError(err); 207 208 assert(Buffer.isBuffer(publicKeyDER)); 209 assertApproximateSize(publicKeyDER, 74); 210 211 assert(Buffer.isBuffer(privateKeyDER)); 212 213 // Since the private key is encrypted, signing shouldn't work anymore. 214 const publicKey = { key: publicKeyDER, ...publicKeyEncoding }; 215 assert.throws(() => { 216 testSignVerify(publicKey, { 217 key: privateKeyDER, 218 format: 'der', 219 type: 'pkcs8' 220 }); 221 }, { 222 name: 'TypeError', 223 code: 'ERR_MISSING_PASSPHRASE', 224 message: 'Passphrase required for encrypted key' 225 }); 226 227 // Signing should work with the correct password. 228 229 const privateKey = { 230 key: privateKeyDER, 231 format: 'der', 232 type: 'pkcs8', 233 passphrase: 'secret' 234 }; 235 testEncryptDecrypt(publicKey, privateKey); 236 testSignVerify(publicKey, privateKey); 237 })); 238 239 // Now do the same with an encrypted private key, but encoded as DER. 240 generateKeyPair('rsa', { 241 publicExponent: 0x10001, 242 modulusLength: 512, 243 publicKeyEncoding, 244 privateKeyEncoding: { 245 type: 'pkcs8', 246 format: 'der' 247 } 248 }, common.mustCall((err, publicKeyDER, privateKeyDER) => { 249 assert.ifError(err); 250 251 assert(Buffer.isBuffer(publicKeyDER)); 252 assertApproximateSize(publicKeyDER, 74); 253 254 assert(Buffer.isBuffer(privateKeyDER)); 255 256 const publicKey = { key: publicKeyDER, ...publicKeyEncoding }; 257 const privateKey = { 258 key: privateKeyDER, 259 format: 'der', 260 type: 'pkcs8', 261 passphrase: 'secret' 262 }; 263 testEncryptDecrypt(publicKey, privateKey); 264 testSignVerify(publicKey, privateKey); 265 })); 266} 267 268{ 269 // Test RSA-PSS. 270 generateKeyPair('rsa-pss', { 271 modulusLength: 512, 272 saltLength: 16, 273 hash: 'sha256', 274 mgf1Hash: 'sha256' 275 }, common.mustCall((err, publicKey, privateKey) => { 276 assert.ifError(err); 277 278 assert.strictEqual(publicKey.type, 'public'); 279 assert.strictEqual(publicKey.asymmetricKeyType, 'rsa-pss'); 280 281 assert.strictEqual(privateKey.type, 'private'); 282 assert.strictEqual(privateKey.asymmetricKeyType, 'rsa-pss'); 283 284 // Unlike RSA, RSA-PSS does not allow encryption. 285 assert.throws(() => { 286 testEncryptDecrypt(publicKey, privateKey); 287 }, /operation not supported for this keytype/); 288 289 // RSA-PSS also does not permit signing with PKCS1 padding. 290 assert.throws(() => { 291 testSignVerify({ 292 key: publicKey, 293 padding: constants.RSA_PKCS1_PADDING 294 }, { 295 key: privateKey, 296 padding: constants.RSA_PKCS1_PADDING 297 }); 298 }, /illegal or unsupported padding mode/); 299 300 // The padding should correctly default to RSA_PKCS1_PSS_PADDING now. 301 testSignVerify(publicKey, privateKey); 302 })); 303} 304 305{ 306 const privateKeyEncoding = { 307 type: 'pkcs8', 308 format: 'der' 309 }; 310 311 // Test async DSA key generation. 312 generateKeyPair('dsa', { 313 modulusLength: 512, 314 divisorLength: 256, 315 publicKeyEncoding: { 316 type: 'spki', 317 format: 'pem' 318 }, 319 privateKeyEncoding: { 320 cipher: 'aes-128-cbc', 321 passphrase: 'secret', 322 ...privateKeyEncoding 323 } 324 }, common.mustCall((err, publicKey, privateKeyDER) => { 325 assert.ifError(err); 326 327 assert.strictEqual(typeof publicKey, 'string'); 328 assert(spkiExp.test(publicKey)); 329 // The private key is DER-encoded. 330 assert(Buffer.isBuffer(privateKeyDER)); 331 332 assertApproximateSize(publicKey, 440); 333 assertApproximateSize(privateKeyDER, 336); 334 335 // Since the private key is encrypted, signing shouldn't work anymore. 336 assert.throws(() => { 337 return testSignVerify(publicKey, { 338 key: privateKeyDER, 339 ...privateKeyEncoding 340 }); 341 }, { 342 name: 'TypeError', 343 code: 'ERR_MISSING_PASSPHRASE', 344 message: 'Passphrase required for encrypted key' 345 }); 346 347 // Signing should work with the correct password. 348 testSignVerify(publicKey, { 349 key: privateKeyDER, 350 ...privateKeyEncoding, 351 passphrase: 'secret' 352 }); 353 })); 354} 355 356{ 357 // Test async elliptic curve key generation, e.g. for ECDSA, with a SEC1 358 // private key. 359 generateKeyPair('ec', { 360 namedCurve: 'prime256v1', 361 paramEncoding: 'named', 362 publicKeyEncoding: { 363 type: 'spki', 364 format: 'pem' 365 }, 366 privateKeyEncoding: { 367 type: 'sec1', 368 format: 'pem' 369 } 370 }, common.mustCall((err, publicKey, privateKey) => { 371 assert.ifError(err); 372 373 assert.strictEqual(typeof publicKey, 'string'); 374 assert(spkiExp.test(publicKey)); 375 assert.strictEqual(typeof privateKey, 'string'); 376 assert(sec1Exp.test(privateKey)); 377 378 testSignVerify(publicKey, privateKey); 379 })); 380 381 // Test async elliptic curve key generation, e.g. for ECDSA, with a SEC1 382 // private key with paramEncoding explicit. 383 generateKeyPair('ec', { 384 namedCurve: 'prime256v1', 385 paramEncoding: 'explicit', 386 publicKeyEncoding: { 387 type: 'spki', 388 format: 'pem' 389 }, 390 privateKeyEncoding: { 391 type: 'sec1', 392 format: 'pem' 393 } 394 }, common.mustCall((err, publicKey, privateKey) => { 395 assert.ifError(err); 396 397 assert.strictEqual(typeof publicKey, 'string'); 398 assert(spkiExp.test(publicKey)); 399 assert.strictEqual(typeof privateKey, 'string'); 400 assert(sec1Exp.test(privateKey)); 401 402 testSignVerify(publicKey, privateKey); 403 })); 404 405 // Do the same with an encrypted private key. 406 generateKeyPair('ec', { 407 namedCurve: 'prime256v1', 408 paramEncoding: 'named', 409 publicKeyEncoding: { 410 type: 'spki', 411 format: 'pem' 412 }, 413 privateKeyEncoding: { 414 type: 'sec1', 415 format: 'pem', 416 cipher: 'aes-128-cbc', 417 passphrase: 'secret' 418 } 419 }, common.mustCall((err, publicKey, privateKey) => { 420 assert.ifError(err); 421 422 assert.strictEqual(typeof publicKey, 'string'); 423 assert(spkiExp.test(publicKey)); 424 assert.strictEqual(typeof privateKey, 'string'); 425 assert(sec1EncExp('AES-128-CBC').test(privateKey)); 426 427 // Since the private key is encrypted, signing shouldn't work anymore. 428 assert.throws(() => testSignVerify(publicKey, privateKey), { 429 name: 'TypeError', 430 code: 'ERR_MISSING_PASSPHRASE', 431 message: 'Passphrase required for encrypted key' 432 }); 433 434 testSignVerify(publicKey, { key: privateKey, passphrase: 'secret' }); 435 })); 436 437 // Do the same with an encrypted private key with paramEncoding explicit. 438 generateKeyPair('ec', { 439 namedCurve: 'prime256v1', 440 paramEncoding: 'explicit', 441 publicKeyEncoding: { 442 type: 'spki', 443 format: 'pem' 444 }, 445 privateKeyEncoding: { 446 type: 'sec1', 447 format: 'pem', 448 cipher: 'aes-128-cbc', 449 passphrase: 'secret' 450 } 451 }, common.mustCall((err, publicKey, privateKey) => { 452 assert.ifError(err); 453 454 assert.strictEqual(typeof publicKey, 'string'); 455 assert(spkiExp.test(publicKey)); 456 assert.strictEqual(typeof privateKey, 'string'); 457 assert(sec1EncExp('AES-128-CBC').test(privateKey)); 458 459 // Since the private key is encrypted, signing shouldn't work anymore. 460 assert.throws(() => testSignVerify(publicKey, privateKey), { 461 name: 'TypeError', 462 code: 'ERR_MISSING_PASSPHRASE', 463 message: 'Passphrase required for encrypted key' 464 }); 465 466 testSignVerify(publicKey, { key: privateKey, passphrase: 'secret' }); 467 })); 468} 469 470{ 471 // Test async elliptic curve key generation, e.g. for ECDSA, with an encrypted 472 // private key. 473 generateKeyPair('ec', { 474 namedCurve: 'P-256', 475 paramEncoding: 'named', 476 publicKeyEncoding: { 477 type: 'spki', 478 format: 'pem' 479 }, 480 privateKeyEncoding: { 481 type: 'pkcs8', 482 format: 'pem', 483 cipher: 'aes-128-cbc', 484 passphrase: 'top secret' 485 } 486 }, common.mustCall((err, publicKey, privateKey) => { 487 assert.ifError(err); 488 489 assert.strictEqual(typeof publicKey, 'string'); 490 assert(spkiExp.test(publicKey)); 491 assert.strictEqual(typeof privateKey, 'string'); 492 assert(pkcs8EncExp.test(privateKey)); 493 494 // Since the private key is encrypted, signing shouldn't work anymore. 495 assert.throws(() => testSignVerify(publicKey, privateKey), { 496 name: 'TypeError', 497 code: 'ERR_MISSING_PASSPHRASE', 498 message: 'Passphrase required for encrypted key' 499 }); 500 501 testSignVerify(publicKey, { 502 key: privateKey, 503 passphrase: 'top secret' 504 }); 505 })); 506 507 // Test async elliptic curve key generation, e.g. for ECDSA, with an encrypted 508 // private key with paramEncoding explicit. 509 generateKeyPair('ec', { 510 namedCurve: 'P-256', 511 paramEncoding: 'explicit', 512 publicKeyEncoding: { 513 type: 'spki', 514 format: 'pem' 515 }, 516 privateKeyEncoding: { 517 type: 'pkcs8', 518 format: 'pem', 519 cipher: 'aes-128-cbc', 520 passphrase: 'top secret' 521 } 522 }, common.mustCall((err, publicKey, privateKey) => { 523 assert.ifError(err); 524 525 assert.strictEqual(typeof publicKey, 'string'); 526 assert(spkiExp.test(publicKey)); 527 assert.strictEqual(typeof privateKey, 'string'); 528 assert(pkcs8EncExp.test(privateKey)); 529 530 // Since the private key is encrypted, signing shouldn't work anymore. 531 assert.throws(() => testSignVerify(publicKey, privateKey), { 532 name: 'TypeError', 533 code: 'ERR_MISSING_PASSPHRASE', 534 message: 'Passphrase required for encrypted key' 535 }); 536 537 testSignVerify(publicKey, { 538 key: privateKey, 539 passphrase: 'top secret' 540 }); 541 })); 542} 543 544// Test invalid parameter encoding. 545{ 546 assert.throws(() => generateKeyPairSync('ec', { 547 namedCurve: 'P-256', 548 paramEncoding: 'otherEncoding', 549 publicKeyEncoding: { 550 type: 'spki', 551 format: 'pem' 552 }, 553 privateKeyEncoding: { 554 type: 'pkcs8', 555 format: 'pem', 556 cipher: 'aes-128-cbc', 557 passphrase: 'top secret' 558 } 559 }), { 560 name: 'TypeError', 561 code: 'ERR_INVALID_OPT_VALUE', 562 message: 'The value "otherEncoding" is invalid for ' + 563 'option "paramEncoding"' 564 }); 565} 566 567{ 568 // Test the util.promisified API with async RSA key generation. 569 promisify(generateKeyPair)('rsa', { 570 publicExponent: 0x10001, 571 modulusLength: 512, 572 publicKeyEncoding: { 573 type: 'pkcs1', 574 format: 'pem' 575 }, 576 privateKeyEncoding: { 577 type: 'pkcs1', 578 format: 'pem' 579 } 580 }).then(common.mustCall((keys) => { 581 const { publicKey, privateKey } = keys; 582 assert.strictEqual(typeof publicKey, 'string'); 583 assert(pkcs1PubExp.test(publicKey)); 584 assertApproximateSize(publicKey, 180); 585 586 assert.strictEqual(typeof privateKey, 'string'); 587 assert(pkcs1PrivExp.test(privateKey)); 588 assertApproximateSize(privateKey, 512); 589 590 testEncryptDecrypt(publicKey, privateKey); 591 testSignVerify(publicKey, privateKey); 592 })); 593} 594 595{ 596 // Test invalid key types. 597 for (const type of [undefined, null, 0]) { 598 assert.throws(() => generateKeyPairSync(type, {}), { 599 name: 'TypeError', 600 code: 'ERR_INVALID_ARG_TYPE', 601 message: 'The "type" argument must be of type string.' + 602 common.invalidArgTypeHelper(type) 603 }); 604 } 605 606 assert.throws(() => generateKeyPairSync('rsa2', {}), { 607 name: 'TypeError', 608 code: 'ERR_INVALID_ARG_VALUE', 609 message: "The argument 'type' must be a supported key type. Received 'rsa2'" 610 }); 611} 612 613{ 614 // Test keygen without options object. 615 assert.throws(() => generateKeyPair('rsa', common.mustNotCall()), { 616 name: 'TypeError', 617 code: 'ERR_INVALID_ARG_TYPE', 618 message: 'The "options" argument must be of type object. ' + 619 'Received undefined' 620 }); 621 622 // Even if no options are required, it should be impossible to pass anything 623 // but an object (or undefined). 624 assert.throws(() => generateKeyPair('ed448', 0, common.mustNotCall()), { 625 name: 'TypeError', 626 code: 'ERR_INVALID_ARG_TYPE', 627 message: 'The "options" argument must be of type object. ' + 628 'Received type number (0)' 629 }); 630} 631 632{ 633 // If no publicKeyEncoding is specified, a key object should be returned. 634 generateKeyPair('rsa', { 635 modulusLength: 1024, 636 privateKeyEncoding: { 637 type: 'pkcs1', 638 format: 'pem' 639 } 640 }, common.mustCall((err, publicKey, privateKey) => { 641 assert.ifError(err); 642 643 assert.strictEqual(typeof publicKey, 'object'); 644 assert.strictEqual(publicKey.type, 'public'); 645 assert.strictEqual(publicKey.asymmetricKeyType, 'rsa'); 646 647 // The private key should still be a string. 648 assert.strictEqual(typeof privateKey, 'string'); 649 650 testEncryptDecrypt(publicKey, privateKey); 651 testSignVerify(publicKey, privateKey); 652 })); 653 654 // If no privateKeyEncoding is specified, a key object should be returned. 655 generateKeyPair('rsa', { 656 modulusLength: 1024, 657 publicKeyEncoding: { 658 type: 'pkcs1', 659 format: 'pem' 660 } 661 }, common.mustCall((err, publicKey, privateKey) => { 662 assert.ifError(err); 663 664 // The public key should still be a string. 665 assert.strictEqual(typeof publicKey, 'string'); 666 667 assert.strictEqual(typeof privateKey, 'object'); 668 assert.strictEqual(privateKey.type, 'private'); 669 assert.strictEqual(privateKey.asymmetricKeyType, 'rsa'); 670 671 testEncryptDecrypt(publicKey, privateKey); 672 testSignVerify(publicKey, privateKey); 673 })); 674} 675 676{ 677 // Invalid publicKeyEncoding. 678 for (const enc of [0, 'a', true]) { 679 assert.throws(() => generateKeyPairSync('rsa', { 680 modulusLength: 4096, 681 publicKeyEncoding: enc, 682 privateKeyEncoding: { 683 type: 'pkcs1', 684 format: 'pem' 685 } 686 }), { 687 name: 'TypeError', 688 code: 'ERR_INVALID_OPT_VALUE', 689 message: `The value "${enc}" is invalid for option "publicKeyEncoding"` 690 }); 691 } 692 693 // Missing publicKeyEncoding.type. 694 for (const type of [undefined, null, 0, true, {}]) { 695 assert.throws(() => generateKeyPairSync('rsa', { 696 modulusLength: 4096, 697 publicKeyEncoding: { 698 type, 699 format: 'pem' 700 }, 701 privateKeyEncoding: { 702 type: 'pkcs1', 703 format: 'pem' 704 } 705 }), { 706 name: 'TypeError', 707 code: 'ERR_INVALID_OPT_VALUE', 708 message: `The value "${inspect(type)}" is invalid for option ` + 709 '"publicKeyEncoding.type"' 710 }); 711 } 712 713 // Missing / invalid publicKeyEncoding.format. 714 for (const format of [undefined, null, 0, false, 'a', {}]) { 715 const expected = typeof format === 'string' ? format : inspect(format); 716 assert.throws(() => generateKeyPairSync('rsa', { 717 modulusLength: 4096, 718 publicKeyEncoding: { 719 type: 'pkcs1', 720 format 721 }, 722 privateKeyEncoding: { 723 type: 'pkcs1', 724 format: 'pem' 725 } 726 }), { 727 name: 'TypeError', 728 code: 'ERR_INVALID_OPT_VALUE', 729 message: `The value "${expected}" is invalid for option ` + 730 '"publicKeyEncoding.format"' 731 }); 732 } 733 734 // Invalid privateKeyEncoding. 735 for (const enc of [0, 'a', true]) { 736 assert.throws(() => generateKeyPairSync('rsa', { 737 modulusLength: 4096, 738 publicKeyEncoding: { 739 type: 'pkcs1', 740 format: 'pem' 741 }, 742 privateKeyEncoding: enc 743 }), { 744 name: 'TypeError', 745 code: 'ERR_INVALID_OPT_VALUE', 746 message: `The value "${enc}" is invalid for option "privateKeyEncoding"` 747 }); 748 } 749 750 // Missing / invalid privateKeyEncoding.type. 751 for (const type of [undefined, null, 0, true, {}]) { 752 assert.throws(() => generateKeyPairSync('rsa', { 753 modulusLength: 4096, 754 publicKeyEncoding: { 755 type: 'pkcs1', 756 format: 'pem' 757 }, 758 privateKeyEncoding: { 759 type, 760 format: 'pem' 761 } 762 }), { 763 name: 'TypeError', 764 code: 'ERR_INVALID_OPT_VALUE', 765 message: `The value "${inspect(type)}" is invalid for option ` + 766 '"privateKeyEncoding.type"' 767 }); 768 } 769 770 // Missing / invalid privateKeyEncoding.format. 771 for (const format of [undefined, null, 0, false, 'a', {}]) { 772 const expected = typeof format === 'string' ? format : inspect(format); 773 assert.throws(() => generateKeyPairSync('rsa', { 774 modulusLength: 4096, 775 publicKeyEncoding: { 776 type: 'pkcs1', 777 format: 'pem' 778 }, 779 privateKeyEncoding: { 780 type: 'pkcs1', 781 format 782 } 783 }), { 784 name: 'TypeError', 785 code: 'ERR_INVALID_OPT_VALUE', 786 message: `The value "${expected}" is invalid for option ` + 787 '"privateKeyEncoding.format"' 788 }); 789 } 790 791 // Cipher of invalid type. 792 for (const cipher of [0, true, {}]) { 793 assert.throws(() => generateKeyPairSync('rsa', { 794 modulusLength: 4096, 795 publicKeyEncoding: { 796 type: 'pkcs1', 797 format: 'pem' 798 }, 799 privateKeyEncoding: { 800 type: 'pkcs1', 801 format: 'pem', 802 cipher 803 } 804 }), { 805 name: 'TypeError', 806 code: 'ERR_INVALID_OPT_VALUE', 807 message: `The value "${inspect(cipher)}" is invalid for option ` + 808 '"privateKeyEncoding.cipher"' 809 }); 810 } 811 812 // Invalid cipher. 813 assert.throws(() => generateKeyPairSync('rsa', { 814 modulusLength: 4096, 815 publicKeyEncoding: { 816 type: 'pkcs1', 817 format: 'pem' 818 }, 819 privateKeyEncoding: { 820 type: 'pkcs8', 821 format: 'pem', 822 cipher: 'foo', 823 passphrase: 'secret' 824 } 825 }), { 826 name: 'Error', 827 code: 'ERR_CRYPTO_UNKNOWN_CIPHER', 828 message: 'Unknown cipher' 829 }); 830 831 // Cipher, but no valid passphrase. 832 for (const passphrase of [undefined, null, 5, false, true]) { 833 assert.throws(() => generateKeyPairSync('rsa', { 834 modulusLength: 4096, 835 publicKeyEncoding: { 836 type: 'pkcs1', 837 format: 'pem' 838 }, 839 privateKeyEncoding: { 840 type: 'pkcs8', 841 format: 'pem', 842 cipher: 'aes-128-cbc', 843 passphrase 844 } 845 }), { 846 name: 'TypeError', 847 code: 'ERR_INVALID_OPT_VALUE', 848 message: `The value "${passphrase}" is invalid for option ` + 849 '"privateKeyEncoding.passphrase"' 850 }); 851 } 852 853 // Test invalid callbacks. 854 for (const cb of [undefined, null, 0, {}]) { 855 assert.throws(() => generateKeyPair('rsa', { 856 modulusLength: 512, 857 publicKeyEncoding: { type: 'pkcs1', format: 'pem' }, 858 privateKeyEncoding: { type: 'pkcs1', format: 'pem' } 859 }, cb), { 860 name: 'TypeError', 861 code: 'ERR_INVALID_CALLBACK' 862 }); 863 } 864} 865 866// Test RSA parameters. 867{ 868 // Test invalid modulus lengths. 869 for (const modulusLength of [undefined, null, 'a', true, {}, [], 512.1, -1]) { 870 const expected = typeof modulusLength === 'string' ? 871 modulusLength : inspect(modulusLength); 872 assert.throws(() => generateKeyPair('rsa', { 873 modulusLength 874 }), { 875 name: 'TypeError', 876 code: 'ERR_INVALID_OPT_VALUE', 877 message: `The value "${expected}" is invalid for option ` + 878 '"modulusLength"' 879 }); 880 } 881 882 // Test invalid exponents. 883 for (const publicExponent of ['a', true, {}, [], 3.5, -1]) { 884 const expected = typeof publicExponent === 'string' ? 885 publicExponent : inspect(publicExponent); 886 assert.throws(() => generateKeyPair('rsa', { 887 modulusLength: 4096, 888 publicExponent 889 }), { 890 name: 'TypeError', 891 code: 'ERR_INVALID_OPT_VALUE', 892 message: `The value "${expected}" is invalid for option ` + 893 '"publicExponent"' 894 }); 895 } 896} 897 898// Test DSA parameters. 899{ 900 // Test invalid modulus lengths. 901 for (const modulusLength of [undefined, null, 'a', true, {}, [], 4096.1]) { 902 const expected = typeof modulusLength === 'string' ? 903 modulusLength : inspect(modulusLength); 904 assert.throws(() => generateKeyPair('dsa', { 905 modulusLength 906 }), { 907 name: 'TypeError', 908 code: 'ERR_INVALID_OPT_VALUE', 909 message: `The value "${expected}" is invalid for option ` + 910 '"modulusLength"' 911 }); 912 } 913 914 // Test invalid divisor lengths. 915 for (const divisorLength of ['a', true, {}, [], 4096.1]) { 916 const expected = typeof divisorLength === 'string' ? 917 divisorLength : inspect(divisorLength); 918 assert.throws(() => generateKeyPair('dsa', { 919 modulusLength: 2048, 920 divisorLength 921 }), { 922 name: 'TypeError', 923 code: 'ERR_INVALID_OPT_VALUE', 924 message: `The value "${expected}" is invalid for option ` + 925 '"divisorLength"' 926 }); 927 } 928} 929 930// Test EC parameters. 931{ 932 // Test invalid curves. 933 assert.throws(() => { 934 generateKeyPairSync('ec', { 935 namedCurve: 'abcdef', 936 publicKeyEncoding: { type: 'spki', format: 'pem' }, 937 privateKeyEncoding: { type: 'sec1', format: 'pem' } 938 }); 939 }, { 940 name: 'TypeError', 941 message: 'Invalid ECDH curve name' 942 }); 943 944 // Test error type when curve is not a string 945 for (const namedCurve of [true, {}, [], 123]) { 946 assert.throws(() => { 947 generateKeyPairSync('ec', { 948 namedCurve, 949 publicKeyEncoding: { type: 'spki', format: 'pem' }, 950 privateKeyEncoding: { type: 'sec1', format: 'pem' } 951 }); 952 }, { 953 name: 'TypeError', 954 code: 'ERR_INVALID_OPT_VALUE', 955 message: `The value "${inspect(namedCurve)}" is invalid for option ` + 956 '"namedCurve"' 957 }); 958 } 959 960 // It should recognize both NIST and standard curve names. 961 generateKeyPair('ec', { 962 namedCurve: 'P-256', 963 publicKeyEncoding: { type: 'spki', format: 'pem' }, 964 privateKeyEncoding: { type: 'pkcs8', format: 'pem' } 965 }, common.mustCall((err, publicKey, privateKey) => { 966 assert.ifError(err); 967 })); 968 969 generateKeyPair('ec', { 970 namedCurve: 'secp256k1', 971 publicKeyEncoding: { type: 'spki', format: 'pem' }, 972 privateKeyEncoding: { type: 'pkcs8', format: 'pem' } 973 }, common.mustCall((err, publicKey, privateKey) => { 974 assert.ifError(err); 975 })); 976} 977 978// Test EdDSA key generation. 979{ 980 if (!/^1\.1\.0/.test(process.versions.openssl)) { 981 ['ed25519', 'ed448', 'x25519', 'x448'].forEach((keyType) => { 982 generateKeyPair(keyType, common.mustCall((err, publicKey, privateKey) => { 983 assert.ifError(err); 984 985 assert.strictEqual(publicKey.type, 'public'); 986 assert.strictEqual(publicKey.asymmetricKeyType, keyType); 987 988 assert.strictEqual(privateKey.type, 'private'); 989 assert.strictEqual(privateKey.asymmetricKeyType, keyType); 990 })); 991 }); 992 } 993} 994 995// Test classic Diffie-Hellman key generation. 996{ 997 generateKeyPair('dh', { 998 primeLength: 1024 999 }, common.mustCall((err, publicKey, privateKey) => { 1000 assert.ifError(err); 1001 1002 assert.strictEqual(publicKey.type, 'public'); 1003 assert.strictEqual(publicKey.asymmetricKeyType, 'dh'); 1004 1005 assert.strictEqual(privateKey.type, 'private'); 1006 assert.strictEqual(privateKey.asymmetricKeyType, 'dh'); 1007 })); 1008 1009 assert.throws(() => { 1010 generateKeyPair('dh', common.mustNotCall()); 1011 }, { 1012 name: 'TypeError', 1013 code: 'ERR_INVALID_ARG_TYPE', 1014 message: 'The "options" argument must be of type object. Received undefined' 1015 }); 1016 1017 assert.throws(() => { 1018 generateKeyPair('dh', {}, common.mustNotCall()); 1019 }, { 1020 name: 'TypeError', 1021 code: 'ERR_MISSING_OPTION', 1022 message: 'At least one of the group, prime, or primeLength options is ' + 1023 'required' 1024 }); 1025 1026 assert.throws(() => { 1027 generateKeyPair('dh', { 1028 group: 'modp0' 1029 }, common.mustNotCall()); 1030 }, { 1031 name: 'Error', 1032 code: 'ERR_CRYPTO_UNKNOWN_DH_GROUP', 1033 message: 'Unknown DH group' 1034 }); 1035 1036 // Test incompatible options. 1037 const allOpts = { 1038 group: 'modp5', 1039 prime: Buffer.alloc(0), 1040 primeLength: 1024, 1041 generator: 2 1042 }; 1043 const incompatible = [ 1044 ['group', 'prime'], 1045 ['group', 'primeLength'], 1046 ['group', 'generator'], 1047 ['prime', 'primeLength'] 1048 ]; 1049 for (const [opt1, opt2] of incompatible) { 1050 assert.throws(() => { 1051 generateKeyPairSync('dh', { 1052 [opt1]: allOpts[opt1], 1053 [opt2]: allOpts[opt2] 1054 }); 1055 }, { 1056 name: 'TypeError', 1057 code: 'ERR_INCOMPATIBLE_OPTION_PAIR', 1058 message: `Option "${opt1}" cannot be used in combination with option ` + 1059 `"${opt2}"` 1060 }); 1061 } 1062} 1063 1064// Test invalid key encoding types. 1065{ 1066 // Invalid public key type. 1067 for (const type of ['foo', 'pkcs8', 'sec1']) { 1068 assert.throws(() => { 1069 generateKeyPairSync('rsa', { 1070 modulusLength: 4096, 1071 publicKeyEncoding: { type, format: 'pem' }, 1072 privateKeyEncoding: { type: 'pkcs8', format: 'pem' } 1073 }); 1074 }, { 1075 name: 'TypeError', 1076 code: 'ERR_INVALID_OPT_VALUE', 1077 message: `The value "${type}" is invalid for option ` + 1078 '"publicKeyEncoding.type"' 1079 }); 1080 } 1081 1082 // Invalid hash value. 1083 for (const hashValue of [123, true, {}, []]) { 1084 assert.throws(() => { 1085 generateKeyPairSync('rsa-pss', { 1086 modulusLength: 4096, 1087 hash: hashValue 1088 }); 1089 }, { 1090 name: 'TypeError', 1091 code: 'ERR_INVALID_OPT_VALUE', 1092 message: `The value "${inspect(hashValue)}" is invalid for option "hash"` 1093 }); 1094 } 1095 1096 // Invalid private key type. 1097 for (const type of ['foo', 'spki']) { 1098 assert.throws(() => { 1099 generateKeyPairSync('rsa', { 1100 modulusLength: 4096, 1101 publicKeyEncoding: { type: 'spki', format: 'pem' }, 1102 privateKeyEncoding: { type, format: 'pem' } 1103 }); 1104 }, { 1105 name: 'TypeError', 1106 code: 'ERR_INVALID_OPT_VALUE', 1107 message: `The value "${type}" is invalid for option ` + 1108 '"privateKeyEncoding.type"' 1109 }); 1110 } 1111 1112 // Key encoding doesn't match key type. 1113 for (const type of ['dsa', 'ec']) { 1114 assert.throws(() => { 1115 generateKeyPairSync(type, { 1116 modulusLength: 4096, 1117 namedCurve: 'P-256', 1118 publicKeyEncoding: { type: 'pkcs1', format: 'pem' }, 1119 privateKeyEncoding: { type: 'pkcs8', format: 'pem' } 1120 }); 1121 }, { 1122 name: 'Error', 1123 code: 'ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS', 1124 message: 'The selected key encoding pkcs1 can only be used for RSA keys.' 1125 }); 1126 1127 assert.throws(() => { 1128 generateKeyPairSync(type, { 1129 modulusLength: 4096, 1130 namedCurve: 'P-256', 1131 publicKeyEncoding: { type: 'spki', format: 'pem' }, 1132 privateKeyEncoding: { type: 'pkcs1', format: 'pem' } 1133 }); 1134 }, { 1135 name: 'Error', 1136 code: 'ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS', 1137 message: 'The selected key encoding pkcs1 can only be used for RSA keys.' 1138 }); 1139 } 1140 1141 for (const type of ['rsa', 'dsa']) { 1142 assert.throws(() => { 1143 generateKeyPairSync(type, { 1144 modulusLength: 4096, 1145 publicKeyEncoding: { type: 'spki', format: 'pem' }, 1146 privateKeyEncoding: { type: 'sec1', format: 'pem' } 1147 }); 1148 }, { 1149 name: 'Error', 1150 code: 'ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS', 1151 message: 'The selected key encoding sec1 can only be used for EC keys.' 1152 }); 1153 } 1154 1155 // Attempting to encrypt a DER-encoded, non-PKCS#8 key. 1156 for (const type of ['pkcs1', 'sec1']) { 1157 assert.throws(() => { 1158 generateKeyPairSync(type === 'pkcs1' ? 'rsa' : 'ec', { 1159 modulusLength: 4096, 1160 namedCurve: 'P-256', 1161 publicKeyEncoding: { type: 'spki', format: 'pem' }, 1162 privateKeyEncoding: { 1163 type, 1164 format: 'der', 1165 cipher: 'aes-128-cbc', 1166 passphrase: 'hello' 1167 } 1168 }); 1169 }, { 1170 name: 'Error', 1171 code: 'ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS', 1172 message: `The selected key encoding ${type} does not support encryption.` 1173 }); 1174 } 1175} 1176{ 1177 // Test RSA-PSS. 1178 assert.throws( 1179 () => { 1180 generateKeyPair('rsa-pss', { 1181 modulusLength: 512, 1182 saltLength: 16, 1183 hash: 'sha256', 1184 mgf1Hash: undefined 1185 }); 1186 }, 1187 { 1188 name: 'TypeError', 1189 code: 'ERR_INVALID_CALLBACK', 1190 message: 'Callback must be a function. Received undefined' 1191 } 1192 ); 1193 1194 for (const mgf1Hash of [null, 0, false, {}, []]) { 1195 const expected = inspect(mgf1Hash); 1196 assert.throws( 1197 () => { 1198 generateKeyPair('rsa-pss', { 1199 modulusLength: 512, 1200 saltLength: 16, 1201 hash: 'sha256', 1202 mgf1Hash 1203 }); 1204 }, 1205 { 1206 name: 'TypeError', 1207 code: 'ERR_INVALID_OPT_VALUE', 1208 message: `The value "${expected}" is invalid for option "mgf1Hash"` 1209 } 1210 ); 1211 } 1212} 1213