UNPKG

nstdlib-nightly

Version:

Node.js standard library converted to runtime-agnostic ES modules.

396 lines (347 loc) 10.8 kB
// Source: https://github.com/nodejs/node/blob/65eff1eb/lib/internal/crypto/keygen.js import { DhKeyPairGenJob, DsaKeyPairGenJob, EcKeyPairGenJob, NidKeyPairGenJob, RsaKeyPairGenJob, SecretKeyGenJob, kCryptoJobAsync, kCryptoJobSync, kKeyVariantRSA_PSS, kKeyVariantRSA_SSA_PKCS1_v1_5, EVP_PKEY_ED25519, EVP_PKEY_ED448, EVP_PKEY_X25519, EVP_PKEY_X448, OPENSSL_EC_NAMED_CURVE, OPENSSL_EC_EXPLICIT_CURVE, } from "nstdlib/stub/binding/crypto"; import { PublicKeyObject, PrivateKeyObject, SecretKeyObject, parsePublicKeyEncoding, parsePrivateKeyEncoding, } from "nstdlib/lib/internal/crypto/keys"; import { kAesKeyLengths } from "nstdlib/lib/internal/crypto/util"; import { customPromisifyArgs, kEmptyObject } from "nstdlib/lib/internal/util"; import { validateFunction, validateBuffer, validateString, validateInteger, validateObject, validateOneOf, validateInt32, validateUint32, } from "nstdlib/lib/internal/validators"; import { codes as __codes__ } from "nstdlib/lib/internal/errors"; import { isArrayBufferView } from "nstdlib/lib/internal/util/types"; const { ERR_INCOMPATIBLE_OPTION_PAIR, ERR_INVALID_ARG_VALUE, ERR_MISSING_OPTION, } = __codes__; function isJwk(obj) { return obj != null && obj.kty !== undefined; } function wrapKey(key, ctor) { if (typeof key === "string" || isArrayBufferView(key) || isJwk(key)) return key; return new ctor(key); } function generateKeyPair(type, options, callback) { if (typeof options === "function") { callback = options; options = undefined; } validateFunction(callback, "callback"); const job = createJob(kCryptoJobAsync, type, options); job.ondone = (error, result) => { if (error) return Function.prototype.call.call(callback, job, error); // If no encoding was chosen, return key objects instead. let { 0: pubkey, 1: privkey } = result; pubkey = wrapKey(pubkey, PublicKeyObject); privkey = wrapKey(privkey, PrivateKeyObject); Function.prototype.call.call(callback, job, null, pubkey, privkey); }; job.run(); } Object.defineProperty(generateKeyPair, customPromisifyArgs, { __proto__: null, value: ["publicKey", "privateKey"], enumerable: false, }); function generateKeyPairSync(type, options) { return handleError(createJob(kCryptoJobSync, type, options).run()); } function handleError(ret) { if (ret == null) return; // async const { 0: err, 1: keys } = ret; if (err !== undefined) throw err; const { 0: publicKey, 1: privateKey } = keys; // If no encoding was chosen, return key objects instead. return { publicKey: wrapKey(publicKey, PublicKeyObject), privateKey: wrapKey(privateKey, PrivateKeyObject), }; } function parseKeyEncoding(keyType, options = kEmptyObject) { 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_ARG_VALUE( "options.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_ARG_VALUE( "options.privateKeyEncoding", privateKeyEncoding, ); } return [ publicFormat, publicType, privateFormat, privateType, cipher, passphrase, ]; } function createJob(mode, type, options) { validateString(type, "type"); const encoding = new (Array.prototype[Symbol.iterator]())( parseKeyEncoding(type, options), ); if (options !== undefined) validateObject(options, "options"); switch (type) { case "rsa": case "rsa-pss": { validateObject(options, "options"); const { modulusLength } = options; validateUint32(modulusLength, "options.modulusLength"); let { publicExponent } = options; if (publicExponent == null) { publicExponent = 0x10001; } else { validateUint32(publicExponent, "options.publicExponent"); } if (type === "rsa") { return new RsaKeyPairGenJob( mode, kKeyVariantRSA_SSA_PKCS1_v1_5, // Used also for RSA-OAEP modulusLength, publicExponent, ...encoding, ); } const { hash, mgf1Hash, hashAlgorithm, mgf1HashAlgorithm, saltLength } = options; if (saltLength !== undefined) validateInt32(saltLength, "options.saltLength", 0); if (hashAlgorithm !== undefined) validateString(hashAlgorithm, "options.hashAlgorithm"); if (mgf1HashAlgorithm !== undefined) validateString(mgf1HashAlgorithm, "options.mgf1HashAlgorithm"); if (hash !== undefined) { process.emitWarning( '"options.hash" is deprecated, ' + 'use "options.hashAlgorithm" instead.', "DeprecationWarning", "DEP0154", ); validateString(hash, "options.hash"); if (hashAlgorithm && hash !== hashAlgorithm) { throw new ERR_INVALID_ARG_VALUE("options.hash", hash); } } if (mgf1Hash !== undefined) { process.emitWarning( '"options.mgf1Hash" is deprecated, ' + 'use "options.mgf1HashAlgorithm" instead.', "DeprecationWarning", "DEP0154", ); validateString(mgf1Hash, "options.mgf1Hash"); if (mgf1HashAlgorithm && mgf1Hash !== mgf1HashAlgorithm) { throw new ERR_INVALID_ARG_VALUE("options.mgf1Hash", mgf1Hash); } } return new RsaKeyPairGenJob( mode, kKeyVariantRSA_PSS, modulusLength, publicExponent, hashAlgorithm || hash, mgf1HashAlgorithm || mgf1Hash, saltLength, ...encoding, ); } case "dsa": { validateObject(options, "options"); const { modulusLength } = options; validateUint32(modulusLength, "options.modulusLength"); let { divisorLength } = options; if (divisorLength == null) { divisorLength = -1; } else validateInt32(divisorLength, "options.divisorLength", 0); return new DsaKeyPairGenJob( mode, modulusLength, divisorLength, ...encoding, ); } case "ec": { validateObject(options, "options"); const { namedCurve } = options; validateString(namedCurve, "options.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_ARG_VALUE("options.paramEncoding", paramEncoding); return new EcKeyPairGenJob(mode, namedCurve, paramEncoding, ...encoding); } 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; } return new NidKeyPairGenJob(mode, id, ...encoding); } case "dh": { validateObject(options, "options"); const { group, primeLength, prime, generator } = options; if (group != null) { if (prime != null) throw new ERR_INCOMPATIBLE_OPTION_PAIR("group", "prime"); if (primeLength != null) throw new ERR_INCOMPATIBLE_OPTION_PAIR("group", "primeLength"); if (generator != null) throw new ERR_INCOMPATIBLE_OPTION_PAIR("group", "generator"); validateString(group, "options.group"); return new DhKeyPairGenJob(mode, group, ...encoding); } if (prime != null) { if (primeLength != null) throw new ERR_INCOMPATIBLE_OPTION_PAIR("prime", "primeLength"); validateBuffer(prime, "options.prime"); } else if (primeLength != null) { validateInt32(primeLength, "options.primeLength", 0); } else { throw new ERR_MISSING_OPTION( "At least one of the group, prime, or primeLength options", ); } if (generator != null) { validateInt32(generator, "options.generator", 0); } return new DhKeyPairGenJob( mode, prime != null ? prime : primeLength, generator == null ? 2 : generator, ...encoding, ); } default: // Fall through } throw new ERR_INVALID_ARG_VALUE("type", type, "must be a supported key type"); } // Symmetric Key Generation function generateKeyJob(mode, keyType, options) { validateString(keyType, "type"); validateObject(options, "options"); const { length } = options; switch (keyType) { case "hmac": validateInteger(length, "options.length", 8, 2 ** 31 - 1); break; case "aes": validateOneOf(length, "options.length", kAesKeyLengths); break; default: throw new ERR_INVALID_ARG_VALUE( "type", keyType, "must be a supported key type", ); } return new SecretKeyGenJob(mode, length); } function handleGenerateKeyError(ret) { if (ret === undefined) return; // async const { 0: err, 1: key } = ret; if (err !== undefined) throw err; return wrapKey(key, SecretKeyObject); } function generateKey(type, options, callback) { if (typeof options === "function") { callback = options; options = undefined; } validateFunction(callback, "callback"); const job = generateKeyJob(kCryptoJobAsync, type, options); job.ondone = (error, key) => { if (error) return Function.prototype.call.call(callback, job, error); Function.prototype.call.call( callback, job, null, wrapKey(key, SecretKeyObject), ); }; handleGenerateKeyError(job.run()); } function generateKeySync(type, options) { return handleGenerateKeyError( generateKeyJob(kCryptoJobSync, type, options).run(), ); } export { generateKeyPair }; export { generateKeyPairSync }; export { generateKey }; export { generateKeySync };