UNPKG

react-native-quick-crypto

Version:

A fast implementation of Node's `crypto` module written in C/C++ JSI

497 lines (473 loc) 18.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.createPrivateKey = exports.WebCryptoKeyExportStatus = exports.SecretKeyObject = exports.PublicKeyObject = exports.PrivateKeyObject = exports.KeyType = exports.KeyEncoding = exports.KWebCryptoKeyFormat = exports.KFormatType = exports.CryptoKey = exports.CipherOrWrapMode = void 0; exports.createPublicKey = createPublicKey; exports.createSecretKey = createSecretKey; exports.kNamedCurveAliases = exports.isCryptoKey = void 0; exports.parsePrivateKeyEncoding = parsePrivateKeyEncoding; exports.parsePublicKeyEncoding = parsePublicKeyEncoding; exports.preparePrivateKey = preparePrivateKey; exports.preparePublicOrPrivateKey = preparePublicOrPrivateKey; var _Utils = require("./Utils"); var _NativeQuickCrypto = require("./NativeQuickCrypto/NativeQuickCrypto"); const kNamedCurveAliases = exports.kNamedCurveAliases = { 'P-256': 'prime256v1', 'P-384': 'secp384r1', 'P-521': 'secp521r1' }; // On node this value is defined on the native side, for now I'm just creating it here in JS // TODO(osp) move this into native side to make sure they always match let KFormatType = exports.KFormatType = /*#__PURE__*/function (KFormatType) { KFormatType[KFormatType["kKeyFormatDER"] = 0] = "kKeyFormatDER"; KFormatType[KFormatType["kKeyFormatPEM"] = 1] = "kKeyFormatPEM"; KFormatType[KFormatType["kKeyFormatJWK"] = 2] = "kKeyFormatJWK"; return KFormatType; }({}); // Same as KFormatType, this enum needs to be defined on the native side let KeyType = exports.KeyType = /*#__PURE__*/function (KeyType) { KeyType[KeyType["Secret"] = 0] = "Secret"; KeyType[KeyType["Public"] = 1] = "Public"; KeyType[KeyType["Private"] = 2] = "Private"; return KeyType; }({}); // Same as KFormatType, this enum needs to be defined on the native side let KWebCryptoKeyFormat = exports.KWebCryptoKeyFormat = /*#__PURE__*/function (KWebCryptoKeyFormat) { KWebCryptoKeyFormat[KWebCryptoKeyFormat["kWebCryptoKeyFormatRaw"] = 0] = "kWebCryptoKeyFormatRaw"; KWebCryptoKeyFormat[KWebCryptoKeyFormat["kWebCryptoKeyFormatPKCS8"] = 1] = "kWebCryptoKeyFormatPKCS8"; KWebCryptoKeyFormat[KWebCryptoKeyFormat["kWebCryptoKeyFormatSPKI"] = 2] = "kWebCryptoKeyFormatSPKI"; KWebCryptoKeyFormat[KWebCryptoKeyFormat["kWebCryptoKeyFormatJWK"] = 3] = "kWebCryptoKeyFormatJWK"; return KWebCryptoKeyFormat; }({}); let WebCryptoKeyExportStatus = exports.WebCryptoKeyExportStatus = /*#__PURE__*/function (WebCryptoKeyExportStatus) { WebCryptoKeyExportStatus[WebCryptoKeyExportStatus["OK"] = 0] = "OK"; WebCryptoKeyExportStatus[WebCryptoKeyExportStatus["INVALID_KEY_TYPE"] = 1] = "INVALID_KEY_TYPE"; WebCryptoKeyExportStatus[WebCryptoKeyExportStatus["FAILED"] = 2] = "FAILED"; return WebCryptoKeyExportStatus; }({}); var KeyInputContext = /*#__PURE__*/function (KeyInputContext) { KeyInputContext[KeyInputContext["kConsumePublic"] = 0] = "kConsumePublic"; KeyInputContext[KeyInputContext["kConsumePrivate"] = 1] = "kConsumePrivate"; KeyInputContext[KeyInputContext["kCreatePublic"] = 2] = "kCreatePublic"; KeyInputContext[KeyInputContext["kCreatePrivate"] = 3] = "kCreatePrivate"; return KeyInputContext; }(KeyInputContext || {}); let KeyEncoding = exports.KeyEncoding = /*#__PURE__*/function (KeyEncoding) { KeyEncoding[KeyEncoding["kKeyEncodingPKCS1"] = 0] = "kKeyEncodingPKCS1"; KeyEncoding[KeyEncoding["kKeyEncodingPKCS8"] = 1] = "kKeyEncodingPKCS8"; KeyEncoding[KeyEncoding["kKeyEncodingSPKI"] = 2] = "kKeyEncodingSPKI"; KeyEncoding[KeyEncoding["kKeyEncodingSEC1"] = 3] = "kKeyEncodingSEC1"; return KeyEncoding; }({}); const encodingNames = { [KeyEncoding.kKeyEncodingPKCS1]: 'pkcs1', [KeyEncoding.kKeyEncodingPKCS8]: 'pkcs8', [KeyEncoding.kKeyEncodingSPKI]: 'spki', [KeyEncoding.kKeyEncodingSEC1]: 'sec1' }; let CipherOrWrapMode = exports.CipherOrWrapMode = /*#__PURE__*/function (CipherOrWrapMode) { CipherOrWrapMode[CipherOrWrapMode["kWebCryptoCipherEncrypt"] = 0] = "kWebCryptoCipherEncrypt"; CipherOrWrapMode[CipherOrWrapMode["kWebCryptoCipherDecrypt"] = 1] = "kWebCryptoCipherDecrypt"; return CipherOrWrapMode; }({}); // kWebCryptoWrapKey, // kWebCryptoUnwrapKey, function option(name, objName) { return objName === undefined ? `options.${name}` : `options.${objName}.${name}`; } function parseKeyFormat(formatStr, defaultFormat, optionName) { if (formatStr === undefined && defaultFormat !== undefined) return defaultFormat;else if (formatStr === 'pem') return KFormatType.kKeyFormatPEM;else if (formatStr === 'der') return KFormatType.kKeyFormatDER;else if (formatStr === 'jwk') return KFormatType.kKeyFormatJWK; throw new Error(`Invalid key format str: ${optionName}`); // throw new ERR_INVALID_ARG_VALUE(optionName, formatStr); } function parseKeyType(typeStr, required, keyType, isPublic, optionName) { if (typeStr === undefined && !required) { return undefined; } else if (typeStr === 'pkcs1') { if (keyType !== undefined && keyType !== 'rsa') { throw new Error(`Crypto incompatible key options: ${typeStr} can only be used for RSA keys`); } return KeyEncoding.kKeyEncodingPKCS1; } else if (typeStr === 'spki' && isPublic !== false) { return KeyEncoding.kKeyEncodingSPKI; } else if (typeStr === 'pkcs8' && isPublic !== true) { return KeyEncoding.kKeyEncodingPKCS8; } else if (typeStr === 'sec1' && isPublic !== true) { if (keyType !== undefined && keyType !== 'ec') { throw new Error(`Incompatible key options ${typeStr} can only be used for EC keys`); } return KeyEncoding.kKeyEncodingSEC1; } throw new Error(`Invalid option ${optionName} - ${typeStr}`); } function parseKeyFormatAndType(enc, keyType, isPublic, objName) { const { format: formatStr, type: typeStr } = enc; const isInput = keyType === undefined; const format = parseKeyFormat(formatStr, isInput ? KFormatType.kKeyFormatPEM : undefined, option('format', objName)); const isRequired = (!isInput || format === KFormatType.kKeyFormatDER) && format !== KFormatType.kKeyFormatJWK; const type = parseKeyType(typeStr, isRequired, keyType, isPublic, option('type', objName)); return { format, type }; } function parseKeyEncoding(enc, keyType, isPublic, objName) { // validateObject(enc, 'options'); const isInput = keyType === undefined; const { format, type } = parseKeyFormatAndType(enc, keyType, isPublic, objName); let cipher, passphrase, encoding; if (isPublic !== true) { ({ cipher, passphrase, encoding } = enc); if (!isInput) { if (cipher != null) { if (typeof cipher !== 'string') throw new Error(`Invalid argument ${option('cipher', objName)}: ${cipher}`); if (format === KFormatType.kKeyFormatDER && (type === KeyEncoding.kKeyEncodingPKCS1 || type === KeyEncoding.kKeyEncodingSEC1)) { throw new Error(`Incompatible key options ${encodingNames[type]} does not support encryption`); } } else if (passphrase !== undefined) { throw new Error(`invalid argument ${option('cipher', objName)}: ${cipher}`); } } if (isInput && passphrase !== undefined && !(0, _Utils.isStringOrBuffer)(passphrase) || !isInput && cipher != null && !(0, _Utils.isStringOrBuffer)(passphrase)) { throw new Error(`Invalid argument value ${option('passphrase', objName)}: ${passphrase}`); } } if (passphrase !== undefined) passphrase = (0, _Utils.binaryLikeToArrayBuffer)(passphrase, encoding); return { format, type, cipher, passphrase }; } function prepareAsymmetricKey(key, ctx) { // TODO(osp) check, KeyObject some node object // if (isKeyObject(key)) { // // Best case: A key object, as simple as that. // return { data: getKeyObjectHandle(key, ctx) }; // } else // if (isCryptoKey(key)) { // return { data: getKeyObjectHandle(key[kKeyObject], ctx) }; // } else if ((0, _Utils.isStringOrBuffer)(key)) { // Expect PEM by default, mostly for backward compatibility. return { format: KFormatType.kKeyFormatPEM, data: (0, _Utils.binaryLikeToArrayBuffer)(key) }; } else if (typeof key === 'object') { const { key: data, encoding } = key; // // The 'key' property can be a KeyObject as well to allow specifying // // additional options such as padding along with the key. // if (isKeyObject(data)) { // return { data: getKeyObjectHandle(data, ctx) }; // } // else if (isCryptoKey(data)) // return { data: getKeyObjectHandle(data[kKeyObject], ctx) }; // else if (isJwk(data) && format === 'jwk') // return { data: getKeyObjectHandleFromJwk(data, ctx), format: 'jwk' }; // Either PEM or DER using PKCS#1 or SPKI. if (!(0, _Utils.isStringOrBuffer)(data)) { throw new Error('prepareAsymmetricKey: key is not a string or ArrayBuffer'); } const isPublic = ctx === KeyInputContext.kConsumePrivate || ctx === KeyInputContext.kCreatePrivate ? false : undefined; return { data: (0, _Utils.binaryLikeToArrayBuffer)(data, encoding), ...parseKeyEncoding(key, undefined, isPublic) }; } throw new Error('[prepareAsymetricKey] Invalid argument key: ${key}'); } // TODO(osp) any here is a node KeyObject function preparePrivateKey(key) { return prepareAsymmetricKey(key, KeyInputContext.kConsumePrivate); } // TODO(osp) any here is a node KeyObject function preparePublicOrPrivateKey(key) { return prepareAsymmetricKey(key, KeyInputContext.kConsumePublic); } // Parses the public key encoding based on an object. keyType must be undefined // when this is used to parse an input encoding and must be a valid key type if // used to parse an output encoding. function parsePublicKeyEncoding(enc, keyType, objName) { return parseKeyEncoding(enc, keyType, keyType ? true : undefined, objName); } // Parses the private key encoding based on an object. keyType must be undefined // when this is used to parse an input encoding and must be a valid key type if // used to parse an output encoding. function parsePrivateKeyEncoding(enc, keyType, objName) { return parseKeyEncoding(enc, keyType, false, objName); } // function getKeyObjectHandle(key: any, ctx: KeyInputContext) { // if (ctx === KeyInputContext.kConsumePublic) { // throw new Error( // 'Invalid argument type for "key". Need ArrayBuffer, TypeArray, KeyObject, CryptoKey, string' // ); // } // if (key.type !== 'private') { // if ( // ctx === KeyInputContext.kConsumePrivate || // ctx === KeyInputContext.kCreatePublic // ) // throw new Error(`Invalid KeyObject type: ${key.type}, expected 'public'`); // if (key.type !== 'public') { // throw new Error( // `Invalid KeyObject type: ${key.type}, expected 'private' or 'public'` // ); // } // } // return key.handle; // } function prepareSecretKey(key, encoding, bufferOnly = false) { try { if (!bufferOnly) { // TODO: maybe use `key.constructor.name === 'KeyObject'` ? if (key instanceof KeyObject) { if (key.type !== 'secret') throw new Error(`invalid KeyObject type: ${key.type}, expected 'secret'`); return key.handle.export(); } // TODO: maybe use `key.constructor.name === 'CryptoKey'` ? else if (key instanceof CryptoKey) { if (key.type !== 'secret') throw new Error(`invalid CryptoKey type: ${key.type}, expected 'secret'`); return key.keyObject.handle.export(); } } if (key instanceof ArrayBuffer) { return key; } return (0, _Utils.binaryLikeToArrayBuffer)(key, encoding); } catch (error) { throw new Error('Invalid argument type for "key". Need ArrayBuffer, TypedArray, KeyObject, CryptoKey, string', { cause: error }); } } function createSecretKey(key, encoding) { const k = prepareSecretKey(key, encoding, true); const handle = _NativeQuickCrypto.NativeQuickCrypto.webcrypto.createKeyObjectHandle(); handle.init(KeyType.Secret, k); return new SecretKeyObject(handle); } function createPublicKey(key) { const { format, type, data, passphrase } = prepareAsymmetricKey(key, KeyInputContext.kCreatePublic); const handle = _NativeQuickCrypto.NativeQuickCrypto.webcrypto.createKeyObjectHandle(); if (format === KFormatType.kKeyFormatJWK) { handle.init(KeyType.Public, data); } else { handle.init(KeyType.Public, data, format, type, passphrase); } return new PublicKeyObject(handle); } const createPrivateKey = key => { const { format, type, data, passphrase } = prepareAsymmetricKey(key, KeyInputContext.kCreatePrivate); const handle = _NativeQuickCrypto.NativeQuickCrypto.webcrypto.createKeyObjectHandle(); if (format === KFormatType.kKeyFormatJWK) { handle.init(KeyType.Private, data); } else { handle.init(KeyType.Private, data, format, type, passphrase); } return new PrivateKeyObject(handle); }; // const isKeyObject = (obj: any): obj is KeyObject => { // return obj != null && obj.keyType !== undefined; // }; exports.createPrivateKey = createPrivateKey; class CryptoKey { constructor(keyObject, keyAlgorithm, keyUsages, keyExtractable) { this.keyObject = keyObject; this.keyAlgorithm = keyAlgorithm; this.keyUsages = keyUsages; this.keyExtractable = keyExtractable; } // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any inspect(_depth, _options) { throw new Error('CryptoKey.inspect is not implemented'); // if (depth < 0) return this; // const opts = { // ...options, // depth: options.depth == null ? null : options.depth - 1, // }; // return `CryptoKey ${inspect( // { // type: this.type, // extractable: this.extractable, // algorithm: this.algorithm, // usages: this.usages, // }, // opts // )}`; } get type() { // if (!(this instanceof CryptoKey)) throw new Error('Invalid CryptoKey'); return this.keyObject.type; } get extractable() { return this.keyExtractable; } get algorithm() { return this.keyAlgorithm; } get usages() { return this.keyUsages; } } exports.CryptoKey = CryptoKey; class KeyObject { type = 'unknown'; // eslint-disable-next-line @typescript-eslint/no-unused-vars export(_options) { return new ArrayBuffer(0); } constructor(type, handle) { if (type !== 'secret' && type !== 'public' && type !== 'private') throw new Error(`invalid KeyObject type: ${type}`); this.handle = handle; this.type = type; } // get type(): string { // return this.type; // } // static from(key) { // if (!isCryptoKey(key)) // throw new ERR_INVALID_ARG_TYPE('key', 'CryptoKey', key); // return key[kKeyObject]; // } // equals(otherKeyObject) { // if (!isKeyObject(otherKeyObject)) { // throw new ERR_INVALID_ARG_TYPE( // 'otherKeyObject', // 'KeyObject', // otherKeyObject // ); // } // return ( // otherKeyObject.type === this.type && // this[kHandle].equals(otherKeyObject[kHandle]) // ); // } } class SecretKeyObject extends KeyObject { constructor(handle) { super('secret', handle); } // get symmetricKeySize() { // return this[kHandle].getSymmetricKeySize(); // } export(options) { if (options !== undefined) { if (options.format === 'jwk') { throw new Error('SecretKey export for jwk is not implemented'); // return this.handle.exportJwk({}, false); } } return this.handle.export(); } } // const kAsymmetricKeyType = Symbol('kAsymmetricKeyType'); // const kAsymmetricKeyDetails = Symbol('kAsymmetricKeyDetails'); // function normalizeKeyDetails(details = {}) { // if (details.publicExponent !== undefined) { // return { // ...details, // publicExponent: bigIntArrayToUnsignedBigInt( // new Uint8Array(details.publicExponent) // ), // }; // } // return details; // } exports.SecretKeyObject = SecretKeyObject; class AsymmetricKeyObject extends KeyObject { constructor(type, handle) { super(type, handle); } get asymmetricKeyType() { if (!this._asymmetricKeyType) { this._asymmetricKeyType = this.handle.getAsymmetricKeyType(); } return this._asymmetricKeyType; } // get asymmetricKeyDetails() { // switch (this._asymmetricKeyType) { // case 'rsa': // case 'rsa-pss': // case 'dsa': // case 'ec': // return ( // this[kAsymmetricKeyDetails] || // (this[kAsymmetricKeyDetails] = normalizeKeyDetails( // this[kHandle].keyDetail({}) // )) // ); // default: // return {}; // } // } } class PublicKeyObject extends AsymmetricKeyObject { constructor(handle) { super('public', handle); } export(options) { if (options?.format === 'jwk') { throw new Error('PublicKey export for jwk is not implemented'); // return this.handle.exportJwk({}, false); } const { format, type } = parsePublicKeyEncoding(options, this.asymmetricKeyType); return this.handle.export(format, type); } } exports.PublicKeyObject = PublicKeyObject; class PrivateKeyObject extends AsymmetricKeyObject { constructor(handle) { super('private', handle); } export(options) { if (options?.format === 'jwk') { if (options.passphrase !== undefined) { throw new Error('jwk does not support encryption'); } throw new Error('PrivateKey export for jwk is not implemented'); // return this.handle.exportJwk({}, false); } const { format, type, cipher, passphrase } = parsePrivateKeyEncoding(options, this.asymmetricKeyType); return this.handle.export(format, type, cipher, passphrase); } } // eslint-disable-next-line @typescript-eslint/no-explicit-any exports.PrivateKeyObject = PrivateKeyObject; const isCryptoKey = obj => { return obj !== null && obj?.keyObject !== undefined; }; exports.isCryptoKey = isCryptoKey; //# sourceMappingURL=keys.js.map