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