• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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