1'use strict'; 2 3const { 4 ObjectDefineProperty, 5} = primordials; 6 7const { AsyncWrap, Providers } = internalBinding('async_wrap'); 8const { 9 generateKeyPairRSA, 10 generateKeyPairRSAPSS, 11 generateKeyPairDSA, 12 generateKeyPairEC, 13 generateKeyPairNid, 14 generateKeyPairDH, 15 EVP_PKEY_ED25519, 16 EVP_PKEY_ED448, 17 EVP_PKEY_X25519, 18 EVP_PKEY_X448, 19 OPENSSL_EC_NAMED_CURVE, 20 OPENSSL_EC_EXPLICIT_CURVE 21} = internalBinding('crypto'); 22const { 23 parsePublicKeyEncoding, 24 parsePrivateKeyEncoding, 25 26 PublicKeyObject, 27 PrivateKeyObject 28} = require('internal/crypto/keys'); 29const { customPromisifyArgs } = require('internal/util'); 30const { isUint32, validateString } = require('internal/validators'); 31const { 32 ERR_INCOMPATIBLE_OPTION_PAIR, 33 ERR_INVALID_ARG_TYPE, 34 ERR_INVALID_ARG_VALUE, 35 ERR_INVALID_CALLBACK, 36 ERR_INVALID_OPT_VALUE, 37 ERR_MISSING_OPTION 38} = require('internal/errors').codes; 39 40const { isArrayBufferView } = require('internal/util/types'); 41 42function wrapKey(key, ctor) { 43 if (typeof key === 'string' || isArrayBufferView(key)) 44 return key; 45 return new ctor(key); 46} 47 48function generateKeyPair(type, options, callback) { 49 if (typeof options === 'function') { 50 callback = options; 51 options = undefined; 52 } 53 54 const impl = check(type, options); 55 56 if (typeof callback !== 'function') 57 throw new ERR_INVALID_CALLBACK(callback); 58 59 const wrap = new AsyncWrap(Providers.KEYPAIRGENREQUEST); 60 wrap.ondone = (ex, pubkey, privkey) => { 61 if (ex) return callback.call(wrap, ex); 62 // If no encoding was chosen, return key objects instead. 63 pubkey = wrapKey(pubkey, PublicKeyObject); 64 privkey = wrapKey(privkey, PrivateKeyObject); 65 callback.call(wrap, null, pubkey, privkey); 66 }; 67 68 handleError(impl(wrap)); 69} 70 71ObjectDefineProperty(generateKeyPair, customPromisifyArgs, { 72 value: ['publicKey', 'privateKey'], 73 enumerable: false 74}); 75 76function generateKeyPairSync(type, options) { 77 const impl = check(type, options); 78 return handleError(impl()); 79} 80 81function handleError(ret) { 82 if (ret === undefined) 83 return; // async 84 85 const [err, publicKey, privateKey] = ret; 86 if (err !== undefined) 87 throw err; 88 89 // If no encoding was chosen, return key objects instead. 90 return { 91 publicKey: wrapKey(publicKey, PublicKeyObject), 92 privateKey: wrapKey(privateKey, PrivateKeyObject) 93 }; 94} 95 96function parseKeyEncoding(keyType, options) { 97 const { publicKeyEncoding, privateKeyEncoding } = options; 98 99 let publicFormat, publicType; 100 if (publicKeyEncoding == null) { 101 publicFormat = publicType = undefined; 102 } else if (typeof publicKeyEncoding === 'object') { 103 ({ 104 format: publicFormat, 105 type: publicType 106 } = parsePublicKeyEncoding(publicKeyEncoding, keyType, 107 'publicKeyEncoding')); 108 } else { 109 throw new ERR_INVALID_OPT_VALUE('publicKeyEncoding', publicKeyEncoding); 110 } 111 112 let privateFormat, privateType, cipher, passphrase; 113 if (privateKeyEncoding == null) { 114 privateFormat = privateType = undefined; 115 } else if (typeof privateKeyEncoding === 'object') { 116 ({ 117 format: privateFormat, 118 type: privateType, 119 cipher, 120 passphrase 121 } = parsePrivateKeyEncoding(privateKeyEncoding, keyType, 122 'privateKeyEncoding')); 123 } else { 124 throw new ERR_INVALID_OPT_VALUE('privateKeyEncoding', privateKeyEncoding); 125 } 126 127 return { 128 cipher, passphrase, publicType, publicFormat, privateType, privateFormat 129 }; 130} 131 132function check(type, options, callback) { 133 validateString(type, 'type'); 134 135 // These will be set after parsing the type and type-specific options to make 136 // the order a bit more intuitive. 137 let cipher, passphrase, publicType, publicFormat, privateType, privateFormat; 138 139 if (options !== undefined && typeof options !== 'object') 140 throw new ERR_INVALID_ARG_TYPE('options', 'object', options); 141 142 function needOptions() { 143 if (options == null) 144 throw new ERR_INVALID_ARG_TYPE('options', 'object', options); 145 return options; 146 } 147 148 let impl; 149 switch (type) { 150 case 'rsa': 151 case 'rsa-pss': 152 { 153 const { modulusLength } = needOptions(); 154 if (!isUint32(modulusLength)) 155 throw new ERR_INVALID_OPT_VALUE('modulusLength', modulusLength); 156 157 let { publicExponent } = options; 158 if (publicExponent == null) { 159 publicExponent = 0x10001; 160 } else if (!isUint32(publicExponent)) { 161 throw new ERR_INVALID_OPT_VALUE('publicExponent', publicExponent); 162 } 163 164 if (type === 'rsa') { 165 impl = (wrap) => generateKeyPairRSA(modulusLength, publicExponent, 166 publicFormat, publicType, 167 privateFormat, privateType, 168 cipher, passphrase, wrap); 169 break; 170 } 171 172 const { hash, mgf1Hash, saltLength } = options; 173 if (hash !== undefined && typeof hash !== 'string') 174 throw new ERR_INVALID_OPT_VALUE('hash', hash); 175 if (mgf1Hash !== undefined && typeof mgf1Hash !== 'string') 176 throw new ERR_INVALID_OPT_VALUE('mgf1Hash', mgf1Hash); 177 if (saltLength !== undefined && !isUint32(saltLength)) 178 throw new ERR_INVALID_OPT_VALUE('saltLength', saltLength); 179 180 impl = (wrap) => generateKeyPairRSAPSS(modulusLength, publicExponent, 181 hash, mgf1Hash, saltLength, 182 publicFormat, publicType, 183 privateFormat, privateType, 184 cipher, passphrase, wrap); 185 } 186 break; 187 case 'dsa': 188 { 189 const { modulusLength } = needOptions(); 190 if (!isUint32(modulusLength)) 191 throw new ERR_INVALID_OPT_VALUE('modulusLength', modulusLength); 192 193 let { divisorLength } = options; 194 if (divisorLength == null) { 195 divisorLength = -1; 196 } else if (!isUint32(divisorLength)) { 197 throw new ERR_INVALID_OPT_VALUE('divisorLength', divisorLength); 198 } 199 200 impl = (wrap) => generateKeyPairDSA(modulusLength, divisorLength, 201 publicFormat, publicType, 202 privateFormat, privateType, 203 cipher, passphrase, wrap); 204 } 205 break; 206 case 'ec': 207 { 208 const { namedCurve } = needOptions(); 209 if (typeof namedCurve !== 'string') 210 throw new ERR_INVALID_OPT_VALUE('namedCurve', namedCurve); 211 let { paramEncoding } = options; 212 if (paramEncoding == null || paramEncoding === 'named') 213 paramEncoding = OPENSSL_EC_NAMED_CURVE; 214 else if (paramEncoding === 'explicit') 215 paramEncoding = OPENSSL_EC_EXPLICIT_CURVE; 216 else 217 throw new ERR_INVALID_OPT_VALUE('paramEncoding', paramEncoding); 218 219 impl = (wrap) => generateKeyPairEC(namedCurve, paramEncoding, 220 publicFormat, publicType, 221 privateFormat, privateType, 222 cipher, passphrase, wrap); 223 } 224 break; 225 case 'ed25519': 226 case 'ed448': 227 case 'x25519': 228 case 'x448': 229 { 230 let id; 231 switch (type) { 232 case 'ed25519': 233 id = EVP_PKEY_ED25519; 234 break; 235 case 'ed448': 236 id = EVP_PKEY_ED448; 237 break; 238 case 'x25519': 239 id = EVP_PKEY_X25519; 240 break; 241 case 'x448': 242 id = EVP_PKEY_X448; 243 break; 244 } 245 impl = (wrap) => generateKeyPairNid(id, 246 publicFormat, publicType, 247 privateFormat, privateType, 248 cipher, passphrase, wrap); 249 } 250 break; 251 case 'dh': 252 { 253 const { group, primeLength, prime, generator } = needOptions(); 254 let args; 255 if (group != null) { 256 if (prime != null) 257 throw new ERR_INCOMPATIBLE_OPTION_PAIR('group', 'prime'); 258 if (primeLength != null) 259 throw new ERR_INCOMPATIBLE_OPTION_PAIR('group', 'primeLength'); 260 if (generator != null) 261 throw new ERR_INCOMPATIBLE_OPTION_PAIR('group', 'generator'); 262 if (typeof group !== 'string') 263 throw new ERR_INVALID_OPT_VALUE('group', group); 264 args = [group]; 265 } else { 266 if (prime != null) { 267 if (primeLength != null) 268 throw new ERR_INCOMPATIBLE_OPTION_PAIR('prime', 'primeLength'); 269 if (!isArrayBufferView(prime)) 270 throw new ERR_INVALID_OPT_VALUE('prime', prime); 271 } else if (primeLength != null) { 272 if (!isUint32(primeLength)) 273 throw new ERR_INVALID_OPT_VALUE('primeLength', primeLength); 274 } else { 275 throw new ERR_MISSING_OPTION( 276 'At least one of the group, prime, or primeLength options'); 277 } 278 279 if (generator != null) { 280 if (!isUint32(generator)) 281 throw new ERR_INVALID_OPT_VALUE('generator', generator); 282 } 283 284 args = [prime != null ? prime : primeLength, 285 generator == null ? 2 : generator]; 286 } 287 288 impl = (wrap) => generateKeyPairDH(...args, 289 publicFormat, publicType, 290 privateFormat, privateType, 291 cipher, passphrase, wrap); 292 } 293 break; 294 default: 295 throw new ERR_INVALID_ARG_VALUE('type', type, 296 'must be a supported key type'); 297 } 298 299 if (options) { 300 ({ 301 cipher, 302 passphrase, 303 publicType, 304 publicFormat, 305 privateType, 306 privateFormat 307 } = parseKeyEncoding(type, options)); 308 } 309 310 return impl; 311} 312 313module.exports = { generateKeyPair, generateKeyPairSync }; 314