0
0
mirror of https://github.com/nodejs/node.git synced 2024-12-01 16:10:02 +01:00
nodejs/lib/internal/crypto/keygen.js
Michaël Zasso 0646eda4fc
lib: flatten access to primordials
Store all primordials as properties of the primordials object.
Static functions are prefixed by the constructor's name and prototype
methods are prefixed by the constructor's name followed by "Prototype".
For example: primordials.Object.keys becomes primordials.ObjectKeys.

PR-URL: https://github.com/nodejs/node/pull/30610
Refs: https://github.com/nodejs/node/issues/29766
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
2019-11-25 10:28:15 +01:00

268 lines
8.1 KiB
JavaScript

'use strict';
const {
ObjectDefineProperty,
} = primordials;
const { AsyncWrap, Providers } = internalBinding('async_wrap');
const {
generateKeyPairRSA,
generateKeyPairRSAPSS,
generateKeyPairDSA,
generateKeyPairEC,
generateKeyPairNid,
EVP_PKEY_ED25519,
EVP_PKEY_ED448,
EVP_PKEY_X25519,
EVP_PKEY_X448,
OPENSSL_EC_NAMED_CURVE,
OPENSSL_EC_EXPLICIT_CURVE
} = internalBinding('crypto');
const {
parsePublicKeyEncoding,
parsePrivateKeyEncoding,
PublicKeyObject,
PrivateKeyObject
} = require('internal/crypto/keys');
const { customPromisifyArgs } = require('internal/util');
const { isUint32, validateString } = require('internal/validators');
const {
ERR_INVALID_ARG_TYPE,
ERR_INVALID_ARG_VALUE,
ERR_INVALID_CALLBACK,
ERR_INVALID_OPT_VALUE
} = require('internal/errors').codes;
const { isArrayBufferView } = require('internal/util/types');
function wrapKey(key, ctor) {
if (typeof key === 'string' || isArrayBufferView(key))
return key;
return new ctor(key);
}
function generateKeyPair(type, options, callback) {
if (typeof options === 'function') {
callback = options;
options = undefined;
}
const impl = check(type, options);
if (typeof callback !== 'function')
throw new ERR_INVALID_CALLBACK(callback);
const wrap = new AsyncWrap(Providers.KEYPAIRGENREQUEST);
wrap.ondone = (ex, pubkey, privkey) => {
if (ex) return callback.call(wrap, ex);
// If no encoding was chosen, return key objects instead.
pubkey = wrapKey(pubkey, PublicKeyObject);
privkey = wrapKey(privkey, PrivateKeyObject);
callback.call(wrap, null, pubkey, privkey);
};
handleError(impl(wrap));
}
ObjectDefineProperty(generateKeyPair, customPromisifyArgs, {
value: ['publicKey', 'privateKey'],
enumerable: false
});
function generateKeyPairSync(type, options) {
const impl = check(type, options);
return handleError(impl());
}
function handleError(ret) {
if (ret === undefined)
return; // async
const [err, publicKey, privateKey] = ret;
if (err !== undefined)
throw err;
// If no encoding was chosen, return key objects instead.
return {
publicKey: wrapKey(publicKey, PublicKeyObject),
privateKey: wrapKey(privateKey, PrivateKeyObject)
};
}
function parseKeyEncoding(keyType, options) {
const { publicKeyEncoding, privateKeyEncoding } = options;
let publicFormat, publicType;
if (publicKeyEncoding == null) {
publicFormat = publicType = undefined;
} else if (typeof publicKeyEncoding === 'object') {
({
format: publicFormat,
type: publicType
} = parsePublicKeyEncoding(publicKeyEncoding, keyType,
'publicKeyEncoding'));
} else {
throw new ERR_INVALID_OPT_VALUE('publicKeyEncoding', publicKeyEncoding);
}
let privateFormat, privateType, cipher, passphrase;
if (privateKeyEncoding == null) {
privateFormat = privateType = undefined;
} else if (typeof privateKeyEncoding === 'object') {
({
format: privateFormat,
type: privateType,
cipher,
passphrase
} = parsePrivateKeyEncoding(privateKeyEncoding, keyType,
'privateKeyEncoding'));
} else {
throw new ERR_INVALID_OPT_VALUE('privateKeyEncoding', privateKeyEncoding);
}
return {
cipher, passphrase, publicType, publicFormat, privateType, privateFormat
};
}
function check(type, options, callback) {
validateString(type, 'type');
// These will be set after parsing the type and type-specific options to make
// the order a bit more intuitive.
let cipher, passphrase, publicType, publicFormat, privateType, privateFormat;
if (options !== undefined && typeof options !== 'object')
throw new ERR_INVALID_ARG_TYPE('options', 'object', options);
function needOptions() {
if (options == null)
throw new ERR_INVALID_ARG_TYPE('options', 'object', options);
return options;
}
let impl;
switch (type) {
case 'rsa':
case 'rsa-pss':
{
const { modulusLength } = needOptions();
if (!isUint32(modulusLength))
throw new ERR_INVALID_OPT_VALUE('modulusLength', modulusLength);
let { publicExponent } = options;
if (publicExponent == null) {
publicExponent = 0x10001;
} else if (!isUint32(publicExponent)) {
throw new ERR_INVALID_OPT_VALUE('publicExponent', publicExponent);
}
if (type === 'rsa') {
impl = (wrap) => generateKeyPairRSA(modulusLength, publicExponent,
publicFormat, publicType,
privateFormat, privateType,
cipher, passphrase, wrap);
break;
}
const { hash, mgf1Hash, saltLength } = options;
if (hash !== undefined && typeof hash !== 'string')
throw new ERR_INVALID_OPT_VALUE('hash', hash);
if (mgf1Hash !== undefined && typeof mgf1Hash !== 'string')
throw new ERR_INVALID_OPT_VALUE('mgf1Hash', mgf1Hash);
if (saltLength !== undefined && !isUint32(saltLength))
throw new ERR_INVALID_OPT_VALUE('saltLength', saltLength);
impl = (wrap) => generateKeyPairRSAPSS(modulusLength, publicExponent,
hash, mgf1Hash, saltLength,
publicFormat, publicType,
privateFormat, privateType,
cipher, passphrase, wrap);
}
break;
case 'dsa':
{
const { modulusLength } = needOptions();
if (!isUint32(modulusLength))
throw new ERR_INVALID_OPT_VALUE('modulusLength', modulusLength);
let { divisorLength } = options;
if (divisorLength == null) {
divisorLength = -1;
} else if (!isUint32(divisorLength)) {
throw new ERR_INVALID_OPT_VALUE('divisorLength', divisorLength);
}
impl = (wrap) => generateKeyPairDSA(modulusLength, divisorLength,
publicFormat, publicType,
privateFormat, privateType,
cipher, passphrase, wrap);
}
break;
case 'ec':
{
const { namedCurve } = needOptions();
if (typeof namedCurve !== 'string')
throw new ERR_INVALID_OPT_VALUE('namedCurve', namedCurve);
let { paramEncoding } = options;
if (paramEncoding == null || paramEncoding === 'named')
paramEncoding = OPENSSL_EC_NAMED_CURVE;
else if (paramEncoding === 'explicit')
paramEncoding = OPENSSL_EC_EXPLICIT_CURVE;
else
throw new ERR_INVALID_OPT_VALUE('paramEncoding', paramEncoding);
impl = (wrap) => generateKeyPairEC(namedCurve, paramEncoding,
publicFormat, publicType,
privateFormat, privateType,
cipher, passphrase, wrap);
}
break;
case 'ed25519':
case 'ed448':
case 'x25519':
case 'x448':
{
let id;
switch (type) {
case 'ed25519':
id = EVP_PKEY_ED25519;
break;
case 'ed448':
id = EVP_PKEY_ED448;
break;
case 'x25519':
id = EVP_PKEY_X25519;
break;
case 'x448':
id = EVP_PKEY_X448;
break;
}
impl = (wrap) => generateKeyPairNid(id,
publicFormat, publicType,
privateFormat, privateType,
cipher, passphrase, wrap);
}
break;
default:
throw new ERR_INVALID_ARG_VALUE('type', type,
'must be a supported key type');
}
if (options) {
({
cipher,
passphrase,
publicType,
publicFormat,
privateType,
privateFormat
} = parseKeyEncoding(type, options));
}
return impl;
}
module.exports = { generateKeyPair, generateKeyPairSync };