• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict';
2
3const {
4  ArrayFrom,
5  ArrayPrototypeSlice,
6  ObjectDefineProperty,
7  ObjectDefineProperties,
8  ObjectSetPrototypeOf,
9  Symbol,
10  SymbolToStringTag,
11  Uint8Array,
12} = primordials;
13
14const {
15  KeyObjectHandle,
16  createNativeKeyObjectClass,
17  kKeyTypeSecret,
18  kKeyTypePublic,
19  kKeyTypePrivate,
20  kKeyFormatPEM,
21  kKeyFormatDER,
22  kKeyFormatJWK,
23  kKeyEncodingPKCS1,
24  kKeyEncodingPKCS8,
25  kKeyEncodingSPKI,
26  kKeyEncodingSEC1,
27} = internalBinding('crypto');
28
29const {
30  validateObject,
31  validateOneOf,
32  validateString,
33} = require('internal/validators');
34
35const {
36  codes: {
37    ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS,
38    ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE,
39    ERR_CRYPTO_INVALID_JWK,
40    ERR_ILLEGAL_CONSTRUCTOR,
41    ERR_INVALID_ARG_TYPE,
42    ERR_INVALID_ARG_VALUE,
43    ERR_INVALID_THIS,
44  },
45} = require('internal/errors');
46
47const {
48  kHandle,
49  kKeyObject,
50  getArrayBufferOrView,
51  bigIntArrayToUnsignedBigInt,
52} = require('internal/crypto/util');
53
54const {
55  isAnyArrayBuffer,
56  isArrayBufferView,
57} = require('internal/util/types');
58
59const {
60  makeTransferable,
61  kClone,
62  kDeserialize,
63} = require('internal/worker/js_transferable');
64
65const {
66  customInspectSymbol: kInspect,
67  kEnumerableProperty,
68} = require('internal/util');
69
70const { inspect } = require('internal/util/inspect');
71
72const { Buffer } = require('buffer');
73
74const kAlgorithm = Symbol('kAlgorithm');
75const kExtractable = Symbol('kExtractable');
76const kKeyType = Symbol('kKeyType');
77const kKeyUsages = Symbol('kKeyUsages');
78
79// Key input contexts.
80const kConsumePublic = 0;
81const kConsumePrivate = 1;
82const kCreatePublic = 2;
83const kCreatePrivate = 3;
84
85const encodingNames = [];
86for (const m of [[kKeyEncodingPKCS1, 'pkcs1'], [kKeyEncodingPKCS8, 'pkcs8'],
87                 [kKeyEncodingSPKI, 'spki'], [kKeyEncodingSEC1, 'sec1']])
88  encodingNames[m[0]] = m[1];
89
90// Creating the KeyObject class is a little complicated due to inheritance
91// and the fact that KeyObjects should be transferrable between threads,
92// which requires the KeyObject base class to be implemented in C++.
93// The creation requires a callback to make sure that the NativeKeyObject
94// base class cannot exist without the other KeyObject implementations.
95const {
96  0: KeyObject,
97  1: SecretKeyObject,
98  2: PublicKeyObject,
99  3: PrivateKeyObject,
100} = createNativeKeyObjectClass((NativeKeyObject) => {
101  // Publicly visible KeyObject class.
102  class KeyObject extends NativeKeyObject {
103    constructor(type, handle) {
104      if (type !== 'secret' && type !== 'public' && type !== 'private')
105        throw new ERR_INVALID_ARG_VALUE('type', type);
106      if (typeof handle !== 'object' || !(handle instanceof KeyObjectHandle))
107        throw new ERR_INVALID_ARG_TYPE('handle', 'object', handle);
108
109      super(handle);
110
111      this[kKeyType] = type;
112
113      ObjectDefineProperty(this, kHandle, {
114        __proto__: null,
115        value: handle,
116        enumerable: false,
117        configurable: false,
118        writable: false,
119      });
120    }
121
122    get type() {
123      return this[kKeyType];
124    }
125
126    static from(key) {
127      if (!isCryptoKey(key))
128        throw new ERR_INVALID_ARG_TYPE('key', 'CryptoKey', key);
129      return key[kKeyObject];
130    }
131
132    equals(otherKeyObject) {
133      if (!isKeyObject(otherKeyObject)) {
134        throw new ERR_INVALID_ARG_TYPE(
135          'otherKeyObject', 'KeyObject', otherKeyObject);
136      }
137
138      return otherKeyObject.type === this.type &&
139        this[kHandle].equals(otherKeyObject[kHandle]);
140    }
141  }
142
143  ObjectDefineProperties(KeyObject.prototype, {
144    [SymbolToStringTag]: {
145      __proto__: null,
146      configurable: true,
147      value: 'KeyObject',
148    },
149  });
150
151  class SecretKeyObject extends KeyObject {
152    constructor(handle) {
153      super('secret', handle);
154    }
155
156    get symmetricKeySize() {
157      return this[kHandle].getSymmetricKeySize();
158    }
159
160    export(options) {
161      if (options !== undefined) {
162        validateObject(options, 'options');
163        validateOneOf(
164          options.format, 'options.format', [undefined, 'buffer', 'jwk']);
165        if (options.format === 'jwk') {
166          return this[kHandle].exportJwk({}, false);
167        }
168      }
169      return this[kHandle].export();
170    }
171  }
172
173  const kAsymmetricKeyType = Symbol('kAsymmetricKeyType');
174  const kAsymmetricKeyDetails = Symbol('kAsymmetricKeyDetails');
175
176  function normalizeKeyDetails(details = {}) {
177    if (details.publicExponent !== undefined) {
178      return {
179        ...details,
180        publicExponent:
181          bigIntArrayToUnsignedBigInt(new Uint8Array(details.publicExponent)),
182      };
183    }
184    return details;
185  }
186
187  class AsymmetricKeyObject extends KeyObject {
188    // eslint-disable-next-line no-useless-constructor
189    constructor(type, handle) {
190      super(type, handle);
191    }
192
193    get asymmetricKeyType() {
194      return this[kAsymmetricKeyType] ||
195             (this[kAsymmetricKeyType] = this[kHandle].getAsymmetricKeyType());
196    }
197
198    get asymmetricKeyDetails() {
199      switch (this.asymmetricKeyType) {
200        case 'rsa':
201        case 'rsa-pss':
202        case 'dsa':
203        case 'ec':
204          return this[kAsymmetricKeyDetails] ||
205             (this[kAsymmetricKeyDetails] = normalizeKeyDetails(
206               this[kHandle].keyDetail({}),
207             ));
208        default:
209          return {};
210      }
211    }
212  }
213
214  class PublicKeyObject extends AsymmetricKeyObject {
215    constructor(handle) {
216      super('public', handle);
217    }
218
219    export(options) {
220      if (options && options.format === 'jwk') {
221        return this[kHandle].exportJwk({}, false);
222      }
223      const {
224        format,
225        type,
226      } = parsePublicKeyEncoding(options, this.asymmetricKeyType);
227      return this[kHandle].export(format, type);
228    }
229  }
230
231  class PrivateKeyObject extends AsymmetricKeyObject {
232    constructor(handle) {
233      super('private', handle);
234    }
235
236    export(options) {
237      if (options && options.format === 'jwk') {
238        if (options.passphrase !== undefined) {
239          throw new ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS(
240            'jwk', 'does not support encryption');
241        }
242        return this[kHandle].exportJwk({}, false);
243      }
244      const {
245        format,
246        type,
247        cipher,
248        passphrase,
249      } = parsePrivateKeyEncoding(options, this.asymmetricKeyType);
250      return this[kHandle].export(format, type, cipher, passphrase);
251    }
252  }
253
254  return [KeyObject, SecretKeyObject, PublicKeyObject, PrivateKeyObject];
255});
256
257function parseKeyFormat(formatStr, defaultFormat, optionName) {
258  if (formatStr === undefined && defaultFormat !== undefined)
259    return defaultFormat;
260  else if (formatStr === 'pem')
261    return kKeyFormatPEM;
262  else if (formatStr === 'der')
263    return kKeyFormatDER;
264  else if (formatStr === 'jwk')
265    return kKeyFormatJWK;
266  throw new ERR_INVALID_ARG_VALUE(optionName, formatStr);
267}
268
269function parseKeyType(typeStr, required, keyType, isPublic, optionName) {
270  if (typeStr === undefined && !required) {
271    return undefined;
272  } else if (typeStr === 'pkcs1') {
273    if (keyType !== undefined && keyType !== 'rsa') {
274      throw new ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS(
275        typeStr, 'can only be used for RSA keys');
276    }
277    return kKeyEncodingPKCS1;
278  } else if (typeStr === 'spki' && isPublic !== false) {
279    return kKeyEncodingSPKI;
280  } else if (typeStr === 'pkcs8' && isPublic !== true) {
281    return kKeyEncodingPKCS8;
282  } else if (typeStr === 'sec1' && isPublic !== true) {
283    if (keyType !== undefined && keyType !== 'ec') {
284      throw new ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS(
285        typeStr, 'can only be used for EC keys');
286    }
287    return kKeyEncodingSEC1;
288  }
289
290  throw new ERR_INVALID_ARG_VALUE(optionName, typeStr);
291}
292
293function option(name, objName) {
294  return objName === undefined ?
295    `options.${name}` : `options.${objName}.${name}`;
296}
297
298function parseKeyFormatAndType(enc, keyType, isPublic, objName) {
299  const { format: formatStr, type: typeStr } = enc;
300
301  const isInput = keyType === undefined;
302  const format = parseKeyFormat(formatStr,
303                                isInput ? kKeyFormatPEM : undefined,
304                                option('format', objName));
305
306  const isRequired = (!isInput ||
307                      format === kKeyFormatDER) &&
308                      format !== kKeyFormatJWK;
309  const type = parseKeyType(typeStr,
310                            isRequired,
311                            keyType,
312                            isPublic,
313                            option('type', objName));
314  return { format, type };
315}
316
317function isStringOrBuffer(val) {
318  return typeof val === 'string' ||
319         isArrayBufferView(val) ||
320         isAnyArrayBuffer(val);
321}
322
323function parseKeyEncoding(enc, keyType, isPublic, objName) {
324  validateObject(enc, 'options');
325
326  const isInput = keyType === undefined;
327
328  const {
329    format,
330    type,
331  } = parseKeyFormatAndType(enc, keyType, isPublic, objName);
332
333  let cipher, passphrase, encoding;
334  if (isPublic !== true) {
335    ({ cipher, passphrase, encoding } = enc);
336
337    if (!isInput) {
338      if (cipher != null) {
339        if (typeof cipher !== 'string')
340          throw new ERR_INVALID_ARG_VALUE(option('cipher', objName), cipher);
341        if (format === kKeyFormatDER &&
342            (type === kKeyEncodingPKCS1 ||
343             type === kKeyEncodingSEC1)) {
344          throw new ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS(
345            encodingNames[type], 'does not support encryption');
346        }
347      } else if (passphrase !== undefined) {
348        throw new ERR_INVALID_ARG_VALUE(option('cipher', objName), cipher);
349      }
350    }
351
352    if ((isInput && passphrase !== undefined &&
353         !isStringOrBuffer(passphrase)) ||
354        (!isInput && cipher != null && !isStringOrBuffer(passphrase))) {
355      throw new ERR_INVALID_ARG_VALUE(option('passphrase', objName),
356                                      passphrase);
357    }
358  }
359
360  if (passphrase !== undefined)
361    passphrase = getArrayBufferOrView(passphrase, 'key.passphrase', encoding);
362
363  return { format, type, cipher, passphrase };
364}
365
366// Parses the public key encoding based on an object. keyType must be undefined
367// when this is used to parse an input encoding and must be a valid key type if
368// used to parse an output encoding.
369function parsePublicKeyEncoding(enc, keyType, objName) {
370  return parseKeyEncoding(enc, keyType, keyType ? true : undefined, objName);
371}
372
373// Parses the private key encoding based on an object. keyType must be undefined
374// when this is used to parse an input encoding and must be a valid key type if
375// used to parse an output encoding.
376function parsePrivateKeyEncoding(enc, keyType, objName) {
377  return parseKeyEncoding(enc, keyType, false, objName);
378}
379
380function getKeyObjectHandle(key, ctx) {
381  if (ctx === kCreatePrivate) {
382    throw new ERR_INVALID_ARG_TYPE(
383      'key',
384      ['string', 'ArrayBuffer', 'Buffer', 'TypedArray', 'DataView'],
385      key,
386    );
387  }
388
389  if (key.type !== 'private') {
390    if (ctx === kConsumePrivate || ctx === kCreatePublic)
391      throw new ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE(key.type, 'private');
392    if (key.type !== 'public') {
393      throw new ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE(key.type,
394                                                   'private or public');
395    }
396  }
397
398  return key[kHandle];
399}
400
401function getKeyTypes(allowKeyObject, bufferOnly = false) {
402  const types = [
403    'ArrayBuffer',
404    'Buffer',
405    'TypedArray',
406    'DataView',
407    'string', // Only if bufferOnly == false
408    'KeyObject', // Only if allowKeyObject == true && bufferOnly == false
409    'CryptoKey', // Only if allowKeyObject == true && bufferOnly == false
410  ];
411  if (bufferOnly) {
412    return ArrayPrototypeSlice(types, 0, 4);
413  } else if (!allowKeyObject) {
414    return ArrayPrototypeSlice(types, 0, 5);
415  }
416  return types;
417}
418
419function getKeyObjectHandleFromJwk(key, ctx) {
420  validateObject(key, 'key');
421  validateOneOf(
422    key.kty, 'key.kty', ['RSA', 'EC', 'OKP']);
423  const isPublic = ctx === kConsumePublic || ctx === kCreatePublic;
424
425  if (key.kty === 'OKP') {
426    validateString(key.crv, 'key.crv');
427    validateOneOf(
428      key.crv, 'key.crv', ['Ed25519', 'Ed448', 'X25519', 'X448']);
429    validateString(key.x, 'key.x');
430
431    if (!isPublic)
432      validateString(key.d, 'key.d');
433
434    let keyData;
435    if (isPublic)
436      keyData = Buffer.from(key.x, 'base64');
437    else
438      keyData = Buffer.from(key.d, 'base64');
439
440    switch (key.crv) {
441      case 'Ed25519':
442      case 'X25519':
443        if (keyData.byteLength !== 32) {
444          throw new ERR_CRYPTO_INVALID_JWK();
445        }
446        break;
447      case 'Ed448':
448        if (keyData.byteLength !== 57) {
449          throw new ERR_CRYPTO_INVALID_JWK();
450        }
451        break;
452      case 'X448':
453        if (keyData.byteLength !== 56) {
454          throw new ERR_CRYPTO_INVALID_JWK();
455        }
456        break;
457    }
458
459    const handle = new KeyObjectHandle();
460
461    const keyType = isPublic ? kKeyTypePublic : kKeyTypePrivate;
462    if (!handle.initEDRaw(key.crv, keyData, keyType)) {
463      throw new ERR_CRYPTO_INVALID_JWK();
464    }
465
466    return handle;
467  }
468
469  if (key.kty === 'EC') {
470    validateString(key.crv, 'key.crv');
471    validateOneOf(
472      key.crv, 'key.crv', ['P-256', 'secp256k1', 'P-384', 'P-521']);
473    validateString(key.x, 'key.x');
474    validateString(key.y, 'key.y');
475
476    const jwk = {
477      kty: key.kty,
478      crv: key.crv,
479      x: key.x,
480      y: key.y,
481    };
482
483    if (!isPublic) {
484      validateString(key.d, 'key.d');
485      jwk.d = key.d;
486    }
487
488    const handle = new KeyObjectHandle();
489    const type = handle.initJwk(jwk, jwk.crv);
490    if (type === undefined)
491      throw new ERR_CRYPTO_INVALID_JWK();
492
493    return handle;
494  }
495
496  // RSA
497  validateString(key.n, 'key.n');
498  validateString(key.e, 'key.e');
499
500  const jwk = {
501    kty: key.kty,
502    n: key.n,
503    e: key.e,
504  };
505
506  if (!isPublic) {
507    validateString(key.d, 'key.d');
508    validateString(key.p, 'key.p');
509    validateString(key.q, 'key.q');
510    validateString(key.dp, 'key.dp');
511    validateString(key.dq, 'key.dq');
512    validateString(key.qi, 'key.qi');
513    jwk.d = key.d;
514    jwk.p = key.p;
515    jwk.q = key.q;
516    jwk.dp = key.dp;
517    jwk.dq = key.dq;
518    jwk.qi = key.qi;
519  }
520
521  const handle = new KeyObjectHandle();
522  const type = handle.initJwk(jwk);
523  if (type === undefined)
524    throw new ERR_CRYPTO_INVALID_JWK();
525
526  return handle;
527}
528
529function prepareAsymmetricKey(key, ctx) {
530  if (isKeyObject(key)) {
531    // Best case: A key object, as simple as that.
532    return { data: getKeyObjectHandle(key, ctx) };
533  } else if (isCryptoKey(key)) {
534    return { data: getKeyObjectHandle(key[kKeyObject], ctx) };
535  } else if (isStringOrBuffer(key)) {
536    // Expect PEM by default, mostly for backward compatibility.
537    return { format: kKeyFormatPEM, data: getArrayBufferOrView(key, 'key') };
538  } else if (typeof key === 'object') {
539    const { key: data, encoding, format } = key;
540
541    // The 'key' property can be a KeyObject as well to allow specifying
542    // additional options such as padding along with the key.
543    if (isKeyObject(data))
544      return { data: getKeyObjectHandle(data, ctx) };
545    else if (isCryptoKey(data))
546      return { data: getKeyObjectHandle(data[kKeyObject], ctx) };
547    else if (format === 'jwk') {
548      validateObject(data, 'key.key');
549      return { data: getKeyObjectHandleFromJwk(data, ctx), format: 'jwk' };
550    }
551
552    // Either PEM or DER using PKCS#1 or SPKI.
553    if (!isStringOrBuffer(data)) {
554      throw new ERR_INVALID_ARG_TYPE(
555        'key.key',
556        getKeyTypes(ctx !== kCreatePrivate),
557        data);
558    }
559
560    const isPublic =
561      (ctx === kConsumePrivate || ctx === kCreatePrivate) ? false : undefined;
562    return {
563      data: getArrayBufferOrView(data, 'key', encoding),
564      ...parseKeyEncoding(key, undefined, isPublic),
565    };
566  }
567  throw new ERR_INVALID_ARG_TYPE(
568    'key',
569    getKeyTypes(ctx !== kCreatePrivate),
570    key);
571}
572
573function preparePrivateKey(key) {
574  return prepareAsymmetricKey(key, kConsumePrivate);
575}
576
577function preparePublicOrPrivateKey(key) {
578  return prepareAsymmetricKey(key, kConsumePublic);
579}
580
581function prepareSecretKey(key, encoding, bufferOnly = false) {
582  if (!bufferOnly) {
583    if (isKeyObject(key)) {
584      if (key.type !== 'secret')
585        throw new ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE(key.type, 'secret');
586      return key[kHandle];
587    } else if (isCryptoKey(key)) {
588      if (key.type !== 'secret')
589        throw new ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE(key.type, 'secret');
590      return key[kKeyObject][kHandle];
591    }
592  }
593  if (typeof key !== 'string' &&
594      !isArrayBufferView(key) &&
595      !isAnyArrayBuffer(key)) {
596    throw new ERR_INVALID_ARG_TYPE(
597      'key',
598      getKeyTypes(!bufferOnly, bufferOnly),
599      key);
600  }
601  return getArrayBufferOrView(key, 'key', encoding);
602}
603
604function createSecretKey(key, encoding) {
605  key = prepareSecretKey(key, encoding, true);
606  const handle = new KeyObjectHandle();
607  handle.init(kKeyTypeSecret, key);
608  return new SecretKeyObject(handle);
609}
610
611function createPublicKey(key) {
612  const { format, type, data, passphrase } =
613    prepareAsymmetricKey(key, kCreatePublic);
614  let handle;
615  if (format === 'jwk') {
616    handle = data;
617  } else {
618    handle = new KeyObjectHandle();
619    handle.init(kKeyTypePublic, data, format, type, passphrase);
620  }
621  return new PublicKeyObject(handle);
622}
623
624function createPrivateKey(key) {
625  const { format, type, data, passphrase } =
626    prepareAsymmetricKey(key, kCreatePrivate);
627  let handle;
628  if (format === 'jwk') {
629    handle = data;
630  } else {
631    handle = new KeyObjectHandle();
632    handle.init(kKeyTypePrivate, data, format, type, passphrase);
633  }
634  return new PrivateKeyObject(handle);
635}
636
637function isKeyObject(obj) {
638  return obj != null && obj[kKeyType] !== undefined;
639}
640
641// Our implementation of CryptoKey is a simple wrapper around a KeyObject
642// that adapts it to the standard interface.
643// TODO(@jasnell): Embedder environments like electron may have issues
644// here similar to other things like URL. A chromium provided CryptoKey
645// will not be recognized as a Node.js CryptoKey, and vice versa. It
646// would be fantastic if we could find a way of making those interop.
647class CryptoKey {
648  constructor() {
649    throw new ERR_ILLEGAL_CONSTRUCTOR();
650  }
651
652  [kInspect](depth, options) {
653    if (depth < 0)
654      return this;
655
656    const opts = {
657      ...options,
658      depth: options.depth == null ? null : options.depth - 1,
659    };
660
661    return `CryptoKey ${inspect({
662      type: this.type,
663      extractable: this.extractable,
664      algorithm: this.algorithm,
665      usages: this.usages,
666    }, opts)}`;
667  }
668
669  get type() {
670    if (!(this instanceof CryptoKey))
671      throw new ERR_INVALID_THIS('CryptoKey');
672    return this[kKeyObject].type;
673  }
674
675  get extractable() {
676    if (!(this instanceof CryptoKey))
677      throw new ERR_INVALID_THIS('CryptoKey');
678    return this[kExtractable];
679  }
680
681  get algorithm() {
682    if (!(this instanceof CryptoKey))
683      throw new ERR_INVALID_THIS('CryptoKey');
684    return this[kAlgorithm];
685  }
686
687  get usages() {
688    if (!(this instanceof CryptoKey))
689      throw new ERR_INVALID_THIS('CryptoKey');
690    return ArrayFrom(this[kKeyUsages]);
691  }
692}
693
694ObjectDefineProperties(CryptoKey.prototype, {
695  type: kEnumerableProperty,
696  extractable: kEnumerableProperty,
697  algorithm: kEnumerableProperty,
698  usages: kEnumerableProperty,
699  [SymbolToStringTag]: {
700    __proto__: null,
701    configurable: true,
702    value: 'CryptoKey',
703  },
704});
705
706// All internal code must use new InternalCryptoKey to create
707// CryptoKey instances. The CryptoKey class is exposed to end
708// user code but is not permitted to be constructed directly.
709// Using makeTransferable also allows the CryptoKey to be
710// cloned to Workers.
711class InternalCryptoKey {
712  constructor(
713    keyObject,
714    algorithm,
715    keyUsages,
716    extractable) {
717    // Using symbol properties here currently instead of private
718    // properties because (for now) the performance penalty of
719    // private fields is still too high.
720    this[kKeyObject] = keyObject;
721    this[kAlgorithm] = algorithm;
722    this[kExtractable] = extractable;
723    this[kKeyUsages] = keyUsages;
724
725    // eslint-disable-next-line no-constructor-return
726    return makeTransferable(this);
727  }
728
729  [kClone]() {
730    const keyObject = this[kKeyObject];
731    const algorithm = this.algorithm;
732    const extractable = this.extractable;
733    const usages = this.usages;
734
735    return {
736      data: {
737        keyObject,
738        algorithm,
739        usages,
740        extractable,
741      },
742      deserializeInfo: 'internal/crypto/keys:InternalCryptoKey',
743    };
744  }
745
746  [kDeserialize]({ keyObject, algorithm, usages, extractable }) {
747    this[kKeyObject] = keyObject;
748    this[kAlgorithm] = algorithm;
749    this[kKeyUsages] = usages;
750    this[kExtractable] = extractable;
751  }
752}
753InternalCryptoKey.prototype.constructor = CryptoKey;
754ObjectSetPrototypeOf(InternalCryptoKey.prototype, CryptoKey.prototype);
755
756function isCryptoKey(obj) {
757  return obj != null && obj[kKeyObject] !== undefined;
758}
759
760module.exports = {
761  // Public API.
762  createSecretKey,
763  createPublicKey,
764  createPrivateKey,
765  KeyObject,
766  CryptoKey,
767  InternalCryptoKey,
768
769  // These are designed for internal use only and should not be exposed.
770  parsePublicKeyEncoding,
771  parsePrivateKeyEncoding,
772  parseKeyEncoding,
773  preparePrivateKey,
774  preparePublicOrPrivateKey,
775  prepareSecretKey,
776  SecretKeyObject,
777  PublicKeyObject,
778  PrivateKeyObject,
779  isKeyObject,
780  isCryptoKey,
781};
782