• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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