1'use strict'; 2 3const { 4 FunctionPrototypeCall, 5 ObjectDefineProperty, 6 SafeArrayIterator, 7} = primordials; 8 9const { 10 DhKeyPairGenJob, 11 DsaKeyPairGenJob, 12 EcKeyPairGenJob, 13 NidKeyPairGenJob, 14 RsaKeyPairGenJob, 15 SecretKeyGenJob, 16 kCryptoJobAsync, 17 kCryptoJobSync, 18 kKeyVariantRSA_PSS, 19 kKeyVariantRSA_SSA_PKCS1_v1_5, 20 EVP_PKEY_ED25519, 21 EVP_PKEY_ED448, 22 EVP_PKEY_X25519, 23 EVP_PKEY_X448, 24 OPENSSL_EC_NAMED_CURVE, 25 OPENSSL_EC_EXPLICIT_CURVE, 26} = internalBinding('crypto'); 27 28const { 29 PublicKeyObject, 30 PrivateKeyObject, 31 SecretKeyObject, 32 parsePublicKeyEncoding, 33 parsePrivateKeyEncoding, 34} = require('internal/crypto/keys'); 35 36const { 37 kAesKeyLengths, 38} = require('internal/crypto/util'); 39 40const { 41 customPromisifyArgs, 42 kEmptyObject, 43} = require('internal/util'); 44 45const { 46 validateFunction, 47 validateBuffer, 48 validateString, 49 validateInteger, 50 validateObject, 51 validateOneOf, 52 validateInt32, 53 validateUint32, 54} = require('internal/validators'); 55 56const { 57 codes: { 58 ERR_INCOMPATIBLE_OPTION_PAIR, 59 ERR_INVALID_ARG_VALUE, 60 ERR_MISSING_OPTION, 61 }, 62} = require('internal/errors'); 63 64const { isArrayBufferView } = require('internal/util/types'); 65 66const { getOptionValue } = require('internal/options'); 67 68function isJwk(obj) { 69 return obj != null && obj.kty !== undefined; 70} 71 72function wrapKey(key, ctor) { 73 if (typeof key === 'string' || 74 isArrayBufferView(key) || 75 isJwk(key)) 76 return key; 77 return new ctor(key); 78} 79 80function generateKeyPair(type, options, callback) { 81 if (typeof options === 'function') { 82 callback = options; 83 options = undefined; 84 } 85 validateFunction(callback, 'callback'); 86 87 const job = createJob(kCryptoJobAsync, type, options); 88 89 job.ondone = (error, result) => { 90 if (error) return FunctionPrototypeCall(callback, job, error); 91 // If no encoding was chosen, return key objects instead. 92 let { 0: pubkey, 1: privkey } = result; 93 pubkey = wrapKey(pubkey, PublicKeyObject); 94 privkey = wrapKey(privkey, PrivateKeyObject); 95 FunctionPrototypeCall(callback, job, null, pubkey, privkey); 96 }; 97 98 job.run(); 99} 100 101ObjectDefineProperty(generateKeyPair, customPromisifyArgs, { 102 __proto__: null, 103 value: ['publicKey', 'privateKey'], 104 enumerable: false, 105}); 106 107function generateKeyPairSync(type, options) { 108 return handleError(createJob(kCryptoJobSync, type, options).run()); 109} 110 111function handleError(ret) { 112 if (ret == null) 113 return; // async 114 115 const { 0: err, 1: keys } = ret; 116 if (err !== undefined) 117 throw err; 118 119 const { 0: publicKey, 1: privateKey } = keys; 120 121 // If no encoding was chosen, return key objects instead. 122 return { 123 publicKey: wrapKey(publicKey, PublicKeyObject), 124 privateKey: wrapKey(privateKey, PrivateKeyObject), 125 }; 126} 127 128function parseKeyEncoding(keyType, options = kEmptyObject) { 129 const { publicKeyEncoding, privateKeyEncoding } = options; 130 131 let publicFormat, publicType; 132 if (publicKeyEncoding == null) { 133 publicFormat = publicType = undefined; 134 } else if (typeof publicKeyEncoding === 'object') { 135 ({ 136 format: publicFormat, 137 type: publicType, 138 } = parsePublicKeyEncoding(publicKeyEncoding, keyType, 139 'publicKeyEncoding')); 140 } else { 141 throw new ERR_INVALID_ARG_VALUE('options.publicKeyEncoding', 142 publicKeyEncoding); 143 } 144 145 let privateFormat, privateType, cipher, passphrase; 146 if (privateKeyEncoding == null) { 147 privateFormat = privateType = undefined; 148 } else if (typeof privateKeyEncoding === 'object') { 149 ({ 150 format: privateFormat, 151 type: privateType, 152 cipher, 153 passphrase, 154 } = parsePrivateKeyEncoding(privateKeyEncoding, keyType, 155 'privateKeyEncoding')); 156 } else { 157 throw new ERR_INVALID_ARG_VALUE('options.privateKeyEncoding', 158 privateKeyEncoding); 159 } 160 161 return [ 162 publicFormat, 163 publicType, 164 privateFormat, 165 privateType, 166 cipher, 167 passphrase, 168 ]; 169} 170 171function createJob(mode, type, options) { 172 validateString(type, 'type'); 173 174 const encoding = new SafeArrayIterator(parseKeyEncoding(type, options)); 175 176 if (options !== undefined) 177 validateObject(options, 'options'); 178 179 switch (type) { 180 case 'rsa': 181 case 'rsa-pss': 182 { 183 validateObject(options, 'options'); 184 const { modulusLength } = options; 185 validateUint32(modulusLength, 'options.modulusLength'); 186 187 let { publicExponent } = options; 188 if (publicExponent == null) { 189 publicExponent = 0x10001; 190 } else { 191 validateUint32(publicExponent, 'options.publicExponent'); 192 } 193 194 if (type === 'rsa') { 195 return new RsaKeyPairGenJob( 196 mode, 197 kKeyVariantRSA_SSA_PKCS1_v1_5, // Used also for RSA-OAEP 198 modulusLength, 199 publicExponent, 200 ...encoding); 201 } 202 203 const { 204 hash, mgf1Hash, hashAlgorithm, mgf1HashAlgorithm, saltLength, 205 } = options; 206 207 const pendingDeprecation = getOptionValue('--pending-deprecation'); 208 209 if (saltLength !== undefined) 210 validateInt32(saltLength, 'options.saltLength', 0); 211 if (hashAlgorithm !== undefined) 212 validateString(hashAlgorithm, 'options.hashAlgorithm'); 213 if (mgf1HashAlgorithm !== undefined) 214 validateString(mgf1HashAlgorithm, 'options.mgf1HashAlgorithm'); 215 if (hash !== undefined) { 216 pendingDeprecation && process.emitWarning( 217 '"options.hash" is deprecated, ' + 218 'use "options.hashAlgorithm" instead.', 219 'DeprecationWarning', 220 'DEP0154'); 221 validateString(hash, 'options.hash'); 222 if (hashAlgorithm && hash !== hashAlgorithm) { 223 throw new ERR_INVALID_ARG_VALUE('options.hash', hash); 224 } 225 } 226 if (mgf1Hash !== undefined) { 227 pendingDeprecation && process.emitWarning( 228 '"options.mgf1Hash" is deprecated, ' + 229 'use "options.mgf1HashAlgorithm" instead.', 230 'DeprecationWarning', 231 'DEP0154'); 232 validateString(mgf1Hash, 'options.mgf1Hash'); 233 if (mgf1HashAlgorithm && mgf1Hash !== mgf1HashAlgorithm) { 234 throw new ERR_INVALID_ARG_VALUE('options.mgf1Hash', mgf1Hash); 235 } 236 } 237 238 return new RsaKeyPairGenJob( 239 mode, 240 kKeyVariantRSA_PSS, 241 modulusLength, 242 publicExponent, 243 hashAlgorithm || hash, 244 mgf1HashAlgorithm || mgf1Hash, 245 saltLength, 246 ...encoding); 247 } 248 case 'dsa': 249 { 250 validateObject(options, 'options'); 251 const { modulusLength } = options; 252 validateUint32(modulusLength, 'options.modulusLength'); 253 254 let { divisorLength } = options; 255 if (divisorLength == null) { 256 divisorLength = -1; 257 } else 258 validateInt32(divisorLength, 'options.divisorLength', 0); 259 260 return new DsaKeyPairGenJob( 261 mode, 262 modulusLength, 263 divisorLength, 264 ...encoding); 265 } 266 case 'ec': 267 { 268 validateObject(options, 'options'); 269 const { namedCurve } = options; 270 validateString(namedCurve, 'options.namedCurve'); 271 let { paramEncoding } = options; 272 if (paramEncoding == null || paramEncoding === 'named') 273 paramEncoding = OPENSSL_EC_NAMED_CURVE; 274 else if (paramEncoding === 'explicit') 275 paramEncoding = OPENSSL_EC_EXPLICIT_CURVE; 276 else 277 throw new ERR_INVALID_ARG_VALUE('options.paramEncoding', paramEncoding); 278 279 return new EcKeyPairGenJob( 280 mode, 281 namedCurve, 282 paramEncoding, 283 ...encoding); 284 } 285 case 'ed25519': 286 case 'ed448': 287 case 'x25519': 288 case 'x448': 289 { 290 let id; 291 switch (type) { 292 case 'ed25519': 293 id = EVP_PKEY_ED25519; 294 break; 295 case 'ed448': 296 id = EVP_PKEY_ED448; 297 break; 298 case 'x25519': 299 id = EVP_PKEY_X25519; 300 break; 301 case 'x448': 302 id = EVP_PKEY_X448; 303 break; 304 } 305 return new NidKeyPairGenJob(mode, id, ...encoding); 306 } 307 case 'dh': 308 { 309 validateObject(options, 'options'); 310 const { group, primeLength, prime, generator } = options; 311 if (group != null) { 312 if (prime != null) 313 throw new ERR_INCOMPATIBLE_OPTION_PAIR('group', 'prime'); 314 if (primeLength != null) 315 throw new ERR_INCOMPATIBLE_OPTION_PAIR('group', 'primeLength'); 316 if (generator != null) 317 throw new ERR_INCOMPATIBLE_OPTION_PAIR('group', 'generator'); 318 319 validateString(group, 'options.group'); 320 321 return new DhKeyPairGenJob(mode, group, ...encoding); 322 } 323 324 if (prime != null) { 325 if (primeLength != null) 326 throw new ERR_INCOMPATIBLE_OPTION_PAIR('prime', 'primeLength'); 327 328 validateBuffer(prime, 'options.prime'); 329 } else if (primeLength != null) { 330 validateInt32(primeLength, 'options.primeLength', 0); 331 } else { 332 throw new ERR_MISSING_OPTION( 333 'At least one of the group, prime, or primeLength options'); 334 } 335 336 if (generator != null) { 337 validateInt32(generator, 'options.generator', 0); 338 } 339 return new DhKeyPairGenJob( 340 mode, 341 prime != null ? prime : primeLength, 342 generator == null ? 2 : generator, 343 ...encoding); 344 } 345 default: 346 // Fall through 347 } 348 throw new ERR_INVALID_ARG_VALUE('type', type, 'must be a supported key type'); 349} 350 351// Symmetric Key Generation 352 353function generateKeyJob(mode, keyType, options) { 354 validateString(keyType, 'type'); 355 validateObject(options, 'options'); 356 const { length } = options; 357 switch (keyType) { 358 case 'hmac': 359 validateInteger(length, 'options.length', 8, 2 ** 31 - 1); 360 break; 361 case 'aes': 362 validateOneOf(length, 'options.length', kAesKeyLengths); 363 break; 364 default: 365 throw new ERR_INVALID_ARG_VALUE( 366 'type', 367 keyType, 368 'must be a supported key type'); 369 } 370 371 return new SecretKeyGenJob(mode, length); 372} 373 374function handleGenerateKeyError(ret) { 375 if (ret === undefined) 376 return; // async 377 378 const { 0: err, 1: key } = ret; 379 if (err !== undefined) 380 throw err; 381 382 return wrapKey(key, SecretKeyObject); 383} 384 385function generateKey(type, options, callback) { 386 if (typeof options === 'function') { 387 callback = options; 388 options = undefined; 389 } 390 391 validateFunction(callback, 'callback'); 392 393 const job = generateKeyJob(kCryptoJobAsync, type, options); 394 395 job.ondone = (error, key) => { 396 if (error) return FunctionPrototypeCall(callback, job, error); 397 FunctionPrototypeCall(callback, job, null, wrapKey(key, SecretKeyObject)); 398 }; 399 400 handleGenerateKeyError(job.run()); 401} 402 403function generateKeySync(type, options) { 404 return handleGenerateKeyError( 405 generateKeyJob(kCryptoJobSync, type, options).run()); 406} 407 408module.exports = { 409 generateKeyPair, 410 generateKeyPairSync, 411 generateKey, 412 generateKeySync, 413}; 414