mirror of
https://github.com/nodejs/node.git
synced 2025-05-10 17:57:53 +00:00

PR-URL: https://github.com/nodejs/node/pull/23541 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Tobias Nießen <tniessen@tnie.de> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com> Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
744 lines
21 KiB
JavaScript
744 lines
21 KiB
JavaScript
'use strict';
|
|
|
|
const common = require('../common');
|
|
if (!common.hasCrypto)
|
|
common.skip('missing crypto');
|
|
|
|
const assert = require('assert');
|
|
const {
|
|
createSign,
|
|
createVerify,
|
|
generateKeyPair,
|
|
generateKeyPairSync,
|
|
publicEncrypt,
|
|
privateDecrypt
|
|
} = require('crypto');
|
|
const { promisify } = require('util');
|
|
|
|
// Asserts that the size of the given key (in chars or bytes) is within 10% of
|
|
// the expected size.
|
|
function assertApproximateSize(key, expectedSize) {
|
|
const u = typeof key === 'string' ? 'chars' : 'bytes';
|
|
const min = Math.floor(0.9 * expectedSize);
|
|
const max = Math.ceil(1.1 * expectedSize);
|
|
assert(key.length >= min,
|
|
`Key (${key.length} ${u}) is shorter than expected (${min} ${u})`);
|
|
assert(key.length <= max,
|
|
`Key (${key.length} ${u}) is longer than expected (${max} ${u})`);
|
|
}
|
|
|
|
// Tests that a key pair can be used for encryption / decryption.
|
|
function testEncryptDecrypt(publicKey, privateKey) {
|
|
const message = 'Hello Node.js world!';
|
|
const plaintext = Buffer.from(message, 'utf8');
|
|
const ciphertext = publicEncrypt(publicKey, plaintext);
|
|
const received = privateDecrypt(privateKey, ciphertext);
|
|
assert.strictEqual(received.toString('utf8'), message);
|
|
}
|
|
|
|
// Tests that a key pair can be used for signing / verification.
|
|
function testSignVerify(publicKey, privateKey) {
|
|
const message = 'Hello Node.js world!';
|
|
const signature = createSign('SHA256').update(message)
|
|
.sign(privateKey, 'hex');
|
|
const okay = createVerify('SHA256').update(message)
|
|
.verify(publicKey, signature, 'hex');
|
|
assert(okay);
|
|
}
|
|
|
|
// Constructs a regular expression for a PEM-encoded key with the given label.
|
|
function getRegExpForPEM(label, cipher) {
|
|
const head = `\\-\\-\\-\\-\\-BEGIN ${label}\\-\\-\\-\\-\\-`;
|
|
const rfc1421Header = cipher == null ? '' :
|
|
`\nProc-Type: 4,ENCRYPTED\nDEK-Info: ${cipher},[^\n]+\n`;
|
|
const body = '([a-zA-Z0-9\\+/=]{64}\n)*[a-zA-Z0-9\\+/=]{1,64}';
|
|
const end = `\\-\\-\\-\\-\\-END ${label}\\-\\-\\-\\-\\-`;
|
|
return new RegExp(`^${head}${rfc1421Header}\n${body}\n${end}\n$`);
|
|
}
|
|
|
|
const pkcs1PubExp = getRegExpForPEM('RSA PUBLIC KEY');
|
|
const pkcs1PrivExp = getRegExpForPEM('RSA PRIVATE KEY');
|
|
const pkcs1EncExp = (cipher) => getRegExpForPEM('RSA PRIVATE KEY', cipher);
|
|
const spkiExp = getRegExpForPEM('PUBLIC KEY');
|
|
const pkcs8Exp = getRegExpForPEM('PRIVATE KEY');
|
|
const pkcs8EncExp = getRegExpForPEM('ENCRYPTED PRIVATE KEY');
|
|
const sec1Exp = getRegExpForPEM('EC PRIVATE KEY');
|
|
const sec1EncExp = (cipher) => getRegExpForPEM('EC PRIVATE KEY', cipher);
|
|
|
|
// Since our own APIs only accept PEM, not DER, we need to convert DER to PEM
|
|
// for testing.
|
|
function convertDERToPEM(label, der) {
|
|
const base64 = der.toString('base64');
|
|
const lines = [];
|
|
let i = 0;
|
|
while (i < base64.length) {
|
|
const n = Math.min(base64.length - i, 64);
|
|
lines.push(base64.substr(i, n));
|
|
i += n;
|
|
}
|
|
const body = lines.join('\n');
|
|
const r = `-----BEGIN ${label}-----\n${body}\n-----END ${label}-----\n`;
|
|
assert(getRegExpForPEM(label).test(r));
|
|
return r;
|
|
}
|
|
|
|
{
|
|
// To make the test faster, we will only test sync key generation once and
|
|
// with a relatively small key.
|
|
const ret = generateKeyPairSync('rsa', {
|
|
publicExponent: 0x10001,
|
|
modulusLength: 1024,
|
|
publicKeyEncoding: {
|
|
type: 'pkcs1',
|
|
format: 'pem'
|
|
},
|
|
privateKeyEncoding: {
|
|
type: 'pkcs8',
|
|
format: 'pem'
|
|
}
|
|
});
|
|
|
|
assert.strictEqual(Object.keys(ret).length, 2);
|
|
const { publicKey, privateKey } = ret;
|
|
|
|
assert.strictEqual(typeof publicKey, 'string');
|
|
assert(pkcs1PubExp.test(publicKey));
|
|
assertApproximateSize(publicKey, 272);
|
|
assert.strictEqual(typeof privateKey, 'string');
|
|
assert(pkcs8Exp.test(privateKey));
|
|
assertApproximateSize(privateKey, 912);
|
|
|
|
testEncryptDecrypt(publicKey, privateKey);
|
|
testSignVerify(publicKey, privateKey);
|
|
}
|
|
|
|
{
|
|
// Test async RSA key generation.
|
|
generateKeyPair('rsa', {
|
|
publicExponent: 0x10001,
|
|
modulusLength: 4096,
|
|
publicKeyEncoding: {
|
|
type: 'pkcs1',
|
|
format: 'der'
|
|
},
|
|
privateKeyEncoding: {
|
|
type: 'pkcs1',
|
|
format: 'pem'
|
|
}
|
|
}, common.mustCall((err, publicKeyDER, privateKey) => {
|
|
assert.ifError(err);
|
|
|
|
// The public key is encoded as DER (which is binary) instead of PEM. We
|
|
// will still need to convert it to PEM for testing.
|
|
assert(Buffer.isBuffer(publicKeyDER));
|
|
const publicKey = convertDERToPEM('RSA PUBLIC KEY', publicKeyDER);
|
|
assertApproximateSize(publicKey, 720);
|
|
|
|
assert.strictEqual(typeof privateKey, 'string');
|
|
assert(pkcs1PrivExp.test(privateKey));
|
|
assertApproximateSize(privateKey, 3272);
|
|
|
|
testEncryptDecrypt(publicKey, privateKey);
|
|
testSignVerify(publicKey, privateKey);
|
|
}));
|
|
|
|
// Now do the same with an encrypted private key.
|
|
generateKeyPair('rsa', {
|
|
publicExponent: 0x10001,
|
|
modulusLength: 4096,
|
|
publicKeyEncoding: {
|
|
type: 'pkcs1',
|
|
format: 'der'
|
|
},
|
|
privateKeyEncoding: {
|
|
type: 'pkcs1',
|
|
format: 'pem',
|
|
cipher: 'aes-256-cbc',
|
|
passphrase: 'secret'
|
|
}
|
|
}, common.mustCall((err, publicKeyDER, privateKey) => {
|
|
assert.ifError(err);
|
|
|
|
// The public key is encoded as DER (which is binary) instead of PEM. We
|
|
// will still need to convert it to PEM for testing.
|
|
assert(Buffer.isBuffer(publicKeyDER));
|
|
const publicKey = convertDERToPEM('RSA PUBLIC KEY', publicKeyDER);
|
|
assertApproximateSize(publicKey, 720);
|
|
|
|
assert.strictEqual(typeof privateKey, 'string');
|
|
assert(pkcs1EncExp('AES-256-CBC').test(privateKey));
|
|
|
|
// Since the private key is encrypted, signing shouldn't work anymore.
|
|
assert.throws(() => {
|
|
testSignVerify(publicKey, privateKey);
|
|
}, /bad decrypt|asn1 encoding routines/);
|
|
|
|
const key = { key: privateKey, passphrase: 'secret' };
|
|
testEncryptDecrypt(publicKey, key);
|
|
testSignVerify(publicKey, key);
|
|
}));
|
|
}
|
|
|
|
{
|
|
// Test async DSA key generation.
|
|
generateKeyPair('dsa', {
|
|
modulusLength: 2048,
|
|
divisorLength: 256,
|
|
publicKeyEncoding: {
|
|
type: 'spki',
|
|
format: 'pem'
|
|
},
|
|
privateKeyEncoding: {
|
|
type: 'pkcs8',
|
|
format: 'der',
|
|
cipher: 'aes-128-cbc',
|
|
passphrase: 'secret'
|
|
}
|
|
}, common.mustCall((err, publicKey, privateKeyDER) => {
|
|
assert.ifError(err);
|
|
|
|
assert.strictEqual(typeof publicKey, 'string');
|
|
assert(spkiExp.test(publicKey));
|
|
// The private key is DER-encoded.
|
|
assert(Buffer.isBuffer(privateKeyDER));
|
|
const privateKey = convertDERToPEM('ENCRYPTED PRIVATE KEY', privateKeyDER);
|
|
|
|
assertApproximateSize(publicKey, 1194);
|
|
assertApproximateSize(privateKey, 1054);
|
|
|
|
// Since the private key is encrypted, signing shouldn't work anymore.
|
|
assert.throws(() => {
|
|
testSignVerify(publicKey, privateKey);
|
|
}, /bad decrypt|asn1 encoding routines/);
|
|
|
|
// Signing should work with the correct password.
|
|
testSignVerify(publicKey, {
|
|
key: privateKey,
|
|
passphrase: 'secret'
|
|
});
|
|
}));
|
|
}
|
|
|
|
{
|
|
// Test async elliptic curve key generation, e.g. for ECDSA, with a SEC1
|
|
// private key.
|
|
generateKeyPair('ec', {
|
|
namedCurve: 'prime256v1',
|
|
paramEncoding: 'named',
|
|
publicKeyEncoding: {
|
|
type: 'spki',
|
|
format: 'pem'
|
|
},
|
|
privateKeyEncoding: {
|
|
type: 'sec1',
|
|
format: 'pem'
|
|
}
|
|
}, common.mustCall((err, publicKey, privateKey) => {
|
|
assert.ifError(err);
|
|
|
|
assert.strictEqual(typeof publicKey, 'string');
|
|
assert(spkiExp.test(publicKey));
|
|
assert.strictEqual(typeof privateKey, 'string');
|
|
assert(sec1Exp.test(privateKey));
|
|
|
|
testSignVerify(publicKey, privateKey);
|
|
}));
|
|
|
|
// Do the same with an encrypted private key.
|
|
generateKeyPair('ec', {
|
|
namedCurve: 'prime256v1',
|
|
paramEncoding: 'named',
|
|
publicKeyEncoding: {
|
|
type: 'spki',
|
|
format: 'pem'
|
|
},
|
|
privateKeyEncoding: {
|
|
type: 'sec1',
|
|
format: 'pem',
|
|
cipher: 'aes-128-cbc',
|
|
passphrase: 'secret'
|
|
}
|
|
}, common.mustCall((err, publicKey, privateKey) => {
|
|
assert.ifError(err);
|
|
|
|
assert.strictEqual(typeof publicKey, 'string');
|
|
assert(spkiExp.test(publicKey));
|
|
assert.strictEqual(typeof privateKey, 'string');
|
|
assert(sec1EncExp('AES-128-CBC').test(privateKey));
|
|
|
|
// Since the private key is encrypted, signing shouldn't work anymore.
|
|
assert.throws(() => {
|
|
testSignVerify(publicKey, privateKey);
|
|
}, /bad decrypt|asn1 encoding routines/);
|
|
|
|
testSignVerify(publicKey, { key: privateKey, passphrase: 'secret' });
|
|
}));
|
|
}
|
|
|
|
{
|
|
// Test async elliptic curve key generation, e.g. for ECDSA, with an encrypted
|
|
// private key.
|
|
generateKeyPair('ec', {
|
|
namedCurve: 'P-256',
|
|
paramEncoding: 'named',
|
|
publicKeyEncoding: {
|
|
type: 'spki',
|
|
format: 'pem'
|
|
},
|
|
privateKeyEncoding: {
|
|
type: 'pkcs8',
|
|
format: 'pem',
|
|
cipher: 'aes-128-cbc',
|
|
passphrase: 'top secret'
|
|
}
|
|
}, common.mustCall((err, publicKey, privateKey) => {
|
|
assert.ifError(err);
|
|
|
|
assert.strictEqual(typeof publicKey, 'string');
|
|
assert(spkiExp.test(publicKey));
|
|
assert.strictEqual(typeof privateKey, 'string');
|
|
assert(pkcs8EncExp.test(privateKey));
|
|
|
|
// Since the private key is encrypted, signing shouldn't work anymore.
|
|
assert.throws(() => {
|
|
testSignVerify(publicKey, privateKey);
|
|
}, /bad decrypt|asn1 encoding routines/);
|
|
|
|
testSignVerify(publicKey, {
|
|
key: privateKey,
|
|
passphrase: 'top secret'
|
|
});
|
|
}));
|
|
}
|
|
|
|
{
|
|
// Test the util.promisified API with async RSA key generation.
|
|
promisify(generateKeyPair)('rsa', {
|
|
publicExponent: 0x10001,
|
|
modulusLength: 3072,
|
|
publicKeyEncoding: {
|
|
type: 'pkcs1',
|
|
format: 'pem'
|
|
},
|
|
privateKeyEncoding: {
|
|
type: 'pkcs1',
|
|
format: 'pem'
|
|
}
|
|
}).then(common.mustCall((keys) => {
|
|
const { publicKey, privateKey } = keys;
|
|
assert.strictEqual(typeof publicKey, 'string');
|
|
assert(pkcs1PubExp.test(publicKey));
|
|
assertApproximateSize(publicKey, 600);
|
|
|
|
assert.strictEqual(typeof privateKey, 'string');
|
|
assert(pkcs1PrivExp.test(privateKey));
|
|
assertApproximateSize(privateKey, 2455);
|
|
|
|
testEncryptDecrypt(publicKey, privateKey);
|
|
testSignVerify(publicKey, privateKey);
|
|
})).catch(common.mustNotCall());
|
|
}
|
|
|
|
{
|
|
// Test invalid key types.
|
|
for (const type of [undefined, null, 0]) {
|
|
common.expectsError(() => generateKeyPairSync(type, {}), {
|
|
type: TypeError,
|
|
code: 'ERR_INVALID_ARG_TYPE',
|
|
message: 'The "type" argument must be of type string. Received type ' +
|
|
typeof type
|
|
});
|
|
}
|
|
|
|
common.expectsError(() => generateKeyPairSync('rsa2', {}), {
|
|
type: TypeError,
|
|
code: 'ERR_INVALID_ARG_VALUE',
|
|
message: "The argument 'type' must be one of " +
|
|
"'rsa', 'dsa', 'ec'. Received 'rsa2'"
|
|
});
|
|
}
|
|
|
|
{
|
|
// Test keygen without options object.
|
|
common.expectsError(() => generateKeyPair('rsa', common.mustNotCall()), {
|
|
type: TypeError,
|
|
code: 'ERR_INVALID_ARG_TYPE',
|
|
message: 'The "options" argument must be of ' +
|
|
'type object. Received type undefined'
|
|
});
|
|
}
|
|
|
|
{
|
|
// Missing / invalid publicKeyEncoding.
|
|
for (const enc of [undefined, null, 0, 'a', true]) {
|
|
common.expectsError(() => generateKeyPairSync('rsa', {
|
|
modulusLength: 4096,
|
|
publicKeyEncoding: enc,
|
|
privateKeyEncoding: {
|
|
type: 'pkcs1',
|
|
format: 'pem'
|
|
}
|
|
}), {
|
|
type: TypeError,
|
|
code: 'ERR_INVALID_OPT_VALUE',
|
|
message: `The value "${enc}" is invalid for option "publicKeyEncoding"`
|
|
});
|
|
}
|
|
|
|
// Missing publicKeyEncoding.type.
|
|
for (const type of [undefined, null, 0, true, {}]) {
|
|
common.expectsError(() => generateKeyPairSync('rsa', {
|
|
modulusLength: 4096,
|
|
publicKeyEncoding: {
|
|
type,
|
|
format: 'pem'
|
|
},
|
|
privateKeyEncoding: {
|
|
type: 'pkcs1',
|
|
format: 'pem'
|
|
}
|
|
}), {
|
|
type: TypeError,
|
|
code: 'ERR_INVALID_OPT_VALUE',
|
|
message: `The value "${type}" is invalid for option ` +
|
|
'"publicKeyEncoding.type"'
|
|
});
|
|
}
|
|
|
|
// Missing / invalid publicKeyEncoding.format.
|
|
for (const format of [undefined, null, 0, false, 'a', {}]) {
|
|
common.expectsError(() => generateKeyPairSync('rsa', {
|
|
modulusLength: 4096,
|
|
publicKeyEncoding: {
|
|
type: 'pkcs1',
|
|
format
|
|
},
|
|
privateKeyEncoding: {
|
|
type: 'pkcs1',
|
|
format: 'pem'
|
|
}
|
|
}), {
|
|
type: TypeError,
|
|
code: 'ERR_INVALID_OPT_VALUE',
|
|
message: `The value "${format}" is invalid for option ` +
|
|
'"publicKeyEncoding.format"'
|
|
});
|
|
}
|
|
|
|
// Missing / invalid privateKeyEncoding.
|
|
for (const enc of [undefined, null, 0, 'a', true]) {
|
|
common.expectsError(() => generateKeyPairSync('rsa', {
|
|
modulusLength: 4096,
|
|
publicKeyEncoding: {
|
|
type: 'pkcs1',
|
|
format: 'pem'
|
|
},
|
|
privateKeyEncoding: enc
|
|
}), {
|
|
type: TypeError,
|
|
code: 'ERR_INVALID_OPT_VALUE',
|
|
message: `The value "${enc}" is invalid for option "privateKeyEncoding"`
|
|
});
|
|
}
|
|
|
|
// Missing / invalid privateKeyEncoding.type.
|
|
for (const type of [undefined, null, 0, true, {}]) {
|
|
common.expectsError(() => generateKeyPairSync('rsa', {
|
|
modulusLength: 4096,
|
|
publicKeyEncoding: {
|
|
type: 'pkcs1',
|
|
format: 'pem'
|
|
},
|
|
privateKeyEncoding: {
|
|
type,
|
|
format: 'pem'
|
|
}
|
|
}), {
|
|
type: TypeError,
|
|
code: 'ERR_INVALID_OPT_VALUE',
|
|
message: `The value "${type}" is invalid for option ` +
|
|
'"privateKeyEncoding.type"'
|
|
});
|
|
}
|
|
|
|
// Missing / invalid privateKeyEncoding.format.
|
|
for (const format of [undefined, null, 0, false, 'a', {}]) {
|
|
common.expectsError(() => generateKeyPairSync('rsa', {
|
|
modulusLength: 4096,
|
|
publicKeyEncoding: {
|
|
type: 'pkcs1',
|
|
format: 'pem'
|
|
},
|
|
privateKeyEncoding: {
|
|
type: 'pkcs1',
|
|
format
|
|
}
|
|
}), {
|
|
type: TypeError,
|
|
code: 'ERR_INVALID_OPT_VALUE',
|
|
message: `The value "${format}" is invalid for option ` +
|
|
'"privateKeyEncoding.format"'
|
|
});
|
|
}
|
|
|
|
// cipher of invalid type.
|
|
for (const cipher of [0, true, {}]) {
|
|
common.expectsError(() => generateKeyPairSync('rsa', {
|
|
modulusLength: 4096,
|
|
publicKeyEncoding: {
|
|
type: 'pkcs1',
|
|
format: 'pem'
|
|
},
|
|
privateKeyEncoding: {
|
|
type: 'pkcs1',
|
|
format: 'pem',
|
|
cipher
|
|
}
|
|
}), {
|
|
type: TypeError,
|
|
code: 'ERR_INVALID_OPT_VALUE',
|
|
message: `The value "${cipher}" is invalid for option ` +
|
|
'"privateKeyEncoding.cipher"'
|
|
});
|
|
}
|
|
|
|
// Invalid cipher.
|
|
common.expectsError(() => generateKeyPairSync('rsa', {
|
|
modulusLength: 4096,
|
|
publicKeyEncoding: {
|
|
type: 'pkcs1',
|
|
format: 'pem'
|
|
},
|
|
privateKeyEncoding: {
|
|
type: 'pkcs8',
|
|
format: 'pem',
|
|
cipher: 'foo',
|
|
passphrase: 'secret'
|
|
}
|
|
}), {
|
|
type: Error,
|
|
message: 'Unknown cipher'
|
|
});
|
|
|
|
// cipher, but no valid passphrase.
|
|
for (const passphrase of [undefined, null, 5, false, true]) {
|
|
common.expectsError(() => generateKeyPairSync('rsa', {
|
|
modulusLength: 4096,
|
|
publicKeyEncoding: {
|
|
type: 'pkcs1',
|
|
format: 'pem'
|
|
},
|
|
privateKeyEncoding: {
|
|
type: 'pkcs8',
|
|
format: 'pem',
|
|
cipher: 'aes-128-cbc',
|
|
passphrase
|
|
}
|
|
}), {
|
|
type: TypeError,
|
|
code: 'ERR_INVALID_OPT_VALUE',
|
|
message: `The value "${passphrase}" is invalid for option ` +
|
|
'"privateKeyEncoding.passphrase"'
|
|
});
|
|
}
|
|
|
|
// Test invalid callbacks.
|
|
for (const cb of [undefined, null, 0, {}]) {
|
|
common.expectsError(() => generateKeyPair('rsa', {
|
|
modulusLength: 4096,
|
|
publicKeyEncoding: { type: 'pkcs1', format: 'pem' },
|
|
privateKeyEncoding: { type: 'pkcs1', format: 'pem' }
|
|
}, cb), {
|
|
type: TypeError,
|
|
code: 'ERR_INVALID_CALLBACK'
|
|
});
|
|
}
|
|
}
|
|
|
|
// Test RSA parameters.
|
|
{
|
|
// Test invalid modulus lengths.
|
|
for (const modulusLength of [undefined, null, 'a', true, {}, [], 512.1, -1]) {
|
|
common.expectsError(() => generateKeyPair('rsa', {
|
|
modulusLength
|
|
}), {
|
|
type: TypeError,
|
|
code: 'ERR_INVALID_OPT_VALUE',
|
|
message: `The value "${modulusLength}" is invalid for option ` +
|
|
'"modulusLength"'
|
|
});
|
|
}
|
|
|
|
// Test invalid exponents.
|
|
for (const publicExponent of ['a', true, {}, [], 3.5, -1]) {
|
|
common.expectsError(() => generateKeyPair('rsa', {
|
|
modulusLength: 4096,
|
|
publicExponent
|
|
}), {
|
|
type: TypeError,
|
|
code: 'ERR_INVALID_OPT_VALUE',
|
|
message: `The value "${publicExponent}" is invalid for option ` +
|
|
'"publicExponent"'
|
|
});
|
|
}
|
|
}
|
|
|
|
// Test DSA parameters.
|
|
{
|
|
// Test invalid modulus lengths.
|
|
for (const modulusLength of [undefined, null, 'a', true, {}, [], 4096.1]) {
|
|
common.expectsError(() => generateKeyPair('dsa', {
|
|
modulusLength
|
|
}), {
|
|
type: TypeError,
|
|
code: 'ERR_INVALID_OPT_VALUE',
|
|
message: `The value "${modulusLength}" is invalid for option ` +
|
|
'"modulusLength"'
|
|
});
|
|
}
|
|
|
|
// Test invalid divisor lengths.
|
|
for (const divisorLength of ['a', true, {}, [], 4096.1]) {
|
|
common.expectsError(() => generateKeyPair('dsa', {
|
|
modulusLength: 2048,
|
|
divisorLength
|
|
}), {
|
|
type: TypeError,
|
|
code: 'ERR_INVALID_OPT_VALUE',
|
|
message: `The value "${divisorLength}" is invalid for option ` +
|
|
'"divisorLength"'
|
|
});
|
|
}
|
|
}
|
|
|
|
// Test EC parameters.
|
|
{
|
|
// Test invalid curves.
|
|
common.expectsError(() => {
|
|
generateKeyPairSync('ec', {
|
|
namedCurve: 'abcdef',
|
|
publicKeyEncoding: { type: 'spki', format: 'pem' },
|
|
privateKeyEncoding: { type: 'sec1', format: 'pem' }
|
|
});
|
|
}, {
|
|
type: TypeError,
|
|
message: 'Invalid ECDH curve name'
|
|
});
|
|
|
|
// It should recognize both NIST and standard curve names.
|
|
generateKeyPair('ec', {
|
|
namedCurve: 'P-256',
|
|
publicKeyEncoding: { type: 'spki', format: 'pem' },
|
|
privateKeyEncoding: { type: 'pkcs8', format: 'pem' }
|
|
}, common.mustCall((err, publicKey, privateKey) => {
|
|
assert.ifError(err);
|
|
}));
|
|
|
|
generateKeyPair('ec', {
|
|
namedCurve: 'secp192k1',
|
|
publicKeyEncoding: { type: 'spki', format: 'pem' },
|
|
privateKeyEncoding: { type: 'pkcs8', format: 'pem' }
|
|
}, common.mustCall((err, publicKey, privateKey) => {
|
|
assert.ifError(err);
|
|
}));
|
|
}
|
|
|
|
// Test invalid key encoding types.
|
|
{
|
|
// Invalid public key type.
|
|
for (const type of ['foo', 'pkcs8', 'sec1']) {
|
|
common.expectsError(() => {
|
|
generateKeyPairSync('rsa', {
|
|
modulusLength: 4096,
|
|
publicKeyEncoding: { type, format: 'pem' },
|
|
privateKeyEncoding: { type: 'pkcs8', format: 'pem' }
|
|
});
|
|
}, {
|
|
type: TypeError,
|
|
code: 'ERR_INVALID_OPT_VALUE',
|
|
message: `The value "${type}" is invalid for option ` +
|
|
'"publicKeyEncoding.type"'
|
|
});
|
|
}
|
|
|
|
// Invalid private key type.
|
|
for (const type of ['foo', 'spki']) {
|
|
common.expectsError(() => {
|
|
generateKeyPairSync('rsa', {
|
|
modulusLength: 4096,
|
|
publicKeyEncoding: { type: 'spki', format: 'pem' },
|
|
privateKeyEncoding: { type, format: 'pem' }
|
|
});
|
|
}, {
|
|
type: TypeError,
|
|
code: 'ERR_INVALID_OPT_VALUE',
|
|
message: `The value "${type}" is invalid for option ` +
|
|
'"privateKeyEncoding.type"'
|
|
});
|
|
}
|
|
|
|
// Key encoding doesn't match key type.
|
|
for (const type of ['dsa', 'ec']) {
|
|
common.expectsError(() => {
|
|
generateKeyPairSync(type, {
|
|
modulusLength: 4096,
|
|
namedCurve: 'P-256',
|
|
publicKeyEncoding: { type: 'pkcs1', format: 'pem' },
|
|
privateKeyEncoding: { type: 'pkcs8', format: 'pem' }
|
|
});
|
|
}, {
|
|
type: Error,
|
|
code: 'ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS',
|
|
message: 'The selected key encoding pkcs1 can only be used for RSA keys.'
|
|
});
|
|
|
|
common.expectsError(() => {
|
|
generateKeyPairSync(type, {
|
|
modulusLength: 4096,
|
|
namedCurve: 'P-256',
|
|
publicKeyEncoding: { type: 'spki', format: 'pem' },
|
|
privateKeyEncoding: { type: 'pkcs1', format: 'pem' }
|
|
});
|
|
}, {
|
|
type: Error,
|
|
code: 'ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS',
|
|
message: 'The selected key encoding pkcs1 can only be used for RSA keys.'
|
|
});
|
|
}
|
|
|
|
for (const type of ['rsa', 'dsa']) {
|
|
common.expectsError(() => {
|
|
generateKeyPairSync(type, {
|
|
modulusLength: 4096,
|
|
publicKeyEncoding: { type: 'spki', format: 'pem' },
|
|
privateKeyEncoding: { type: 'sec1', format: 'pem' }
|
|
});
|
|
}, {
|
|
type: Error,
|
|
code: 'ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS',
|
|
message: 'The selected key encoding sec1 can only be used for EC keys.'
|
|
});
|
|
}
|
|
|
|
// Attempting to encrypt a DER-encoded, non-PKCS#8 key.
|
|
for (const type of ['pkcs1', 'sec1']) {
|
|
common.expectsError(() => {
|
|
generateKeyPairSync(type === 'pkcs1' ? 'rsa' : 'ec', {
|
|
modulusLength: 4096,
|
|
namedCurve: 'P-256',
|
|
publicKeyEncoding: { type: 'spki', format: 'pem' },
|
|
privateKeyEncoding: {
|
|
type,
|
|
format: 'der',
|
|
cipher: 'aes-128-cbc',
|
|
passphrase: 'hello'
|
|
}
|
|
});
|
|
}, {
|
|
type: Error,
|
|
code: 'ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS',
|
|
message: `The selected key encoding ${type} does not support encryption.`
|
|
});
|
|
}
|
|
}
|