• 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.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