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