UNPKG

react-native-quick-crypto

Version:

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

532 lines (501 loc) 20.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ECCurve = void 0; exports.createCipher = createCipher; exports.createCipheriv = createCipheriv; exports.createDecipher = createDecipher; exports.createDecipheriv = createDecipheriv; exports.generateKeyPairPromise = exports.generateKeyPair = void 0; exports.generateKeyPairSync = generateKeyPairSync; exports.publicEncrypt = exports.publicDecrypt = exports.privateDecrypt = void 0; var _NativeQuickCrypto = require("./NativeQuickCrypto/NativeQuickCrypto"); var _readableStream = _interopRequireDefault(require("readable-stream")); var _Utils = require("./Utils"); var _Cipher = require("./NativeQuickCrypto/Cipher"); var _string_decoder = require("string_decoder"); var _reactNativeBuffer = require("@craftzdog/react-native-buffer"); var _safeBuffer = require("safe-buffer"); var _constants = require("./constants"); var _keys = require("./keys"); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } // @types/node let ECCurve = exports.ECCurve = /*#__PURE__*/function (ECCurve) { ECCurve[ECCurve["OPENSSL_EC_EXPLICIT_CURVE"] = 0] = "OPENSSL_EC_EXPLICIT_CURVE"; ECCurve[ECCurve["OPENSSL_EC_NAMED_CURVE"] = 1] = "OPENSSL_EC_NAMED_CURVE"; return ECCurve; }({}); // make sure that nextTick is there global.process.nextTick = setImmediate; const createInternalCipher = _NativeQuickCrypto.NativeQuickCrypto.createCipher; const createInternalDecipher = _NativeQuickCrypto.NativeQuickCrypto.createDecipher; const _publicEncrypt = _NativeQuickCrypto.NativeQuickCrypto.publicEncrypt; const _publicDecrypt = _NativeQuickCrypto.NativeQuickCrypto.publicDecrypt; const _privateDecrypt = _NativeQuickCrypto.NativeQuickCrypto.privateDecrypt; // eslint-disable-next-line @typescript-eslint/no-explicit-any function getUIntOption(options, key) { let value; if (options && (value = options[key]) != null) { // >>> Turns any type into a positive integer (also sets the sign bit to 0) if (value >>> 0 !== value) throw new Error(`options.${key}: ${value}`); return value; } return -1; } function normalizeEncoding(enc) { if (!enc) return 'utf8'; let retried; while (true) { switch (enc) { case 'utf8': case 'utf-8': return 'utf8'; case 'ucs2': case 'ucs-2': case 'utf16le': case 'utf-16le': return 'utf16le'; case 'latin1': case 'binary': return 'latin1'; case 'base64': case 'ascii': case 'hex': return enc; default: if (retried) return; // undefined enc = ('' + enc).toLowerCase(); retried = true; } } } function validateEncoding(data, encoding) { const normalizedEncoding = normalizeEncoding(encoding); const length = data.length; if (normalizedEncoding === 'hex' && length % 2 !== 0) { throw new Error(`Encoding ${encoding} not valid for data length ${length}`); } } function getDecoder(decoder, encoding) { return decoder ?? new _string_decoder.StringDecoder(encoding); } class CipherCommon extends _readableStream.default.Transform { constructor(cipherType, cipherKey, isCipher, options = {}, iv) { super(options); const cipherKeyBuffer = (0, _Utils.binaryLikeToArrayBuffer)(cipherKey); // defaults to 16 bytes const authTagLength = getUIntOption(options, 'authTagLength') !== -1 ? getUIntOption(options, 'authTagLength') : 16; const args = { cipher_type: cipherType, cipher_key: cipherKeyBuffer, iv, ...options, auth_tag_len: authTagLength }; this.internal = isCipher ? createInternalCipher(args) : createInternalDecipher(args); } update(data, inputEncoding, outputEncoding) { const defaultEncoding = (0, _Utils.getDefaultEncoding)(); inputEncoding = inputEncoding ?? defaultEncoding; outputEncoding = outputEncoding ?? defaultEncoding; if (typeof data === 'string') { validateEncoding(data, inputEncoding); } else if (!ArrayBuffer.isView(data)) { throw new Error('Invalid data argument'); } if (typeof data === 'string') { // On node this is handled on the native side // on our case we need to correctly send the arraybuffer to the jsi side inputEncoding = inputEncoding === 'buffer' ? 'utf8' : inputEncoding; data = (0, _Utils.binaryLikeToArrayBuffer)(data, inputEncoding); } else { data = (0, _Utils.binaryLikeToArrayBuffer)(data, inputEncoding); } const ret = this.internal.update(data); if (outputEncoding && outputEncoding !== 'buffer') { this.decoder = getDecoder(this.decoder, outputEncoding); // eslint-disable-next-line @typescript-eslint/no-explicit-any return this.decoder.write(_safeBuffer.Buffer.from(ret)); } return ret; } final(outputEncoding) { const ret = this.internal.final(); if (outputEncoding && outputEncoding !== 'buffer') { this.decoder = getDecoder(this.decoder, outputEncoding); // eslint-disable-next-line @typescript-eslint/no-explicit-any return this.decoder.end(_safeBuffer.Buffer.from(ret)); } return ret; } _transform(chunk, encoding, callback) { this.push(this.update(chunk, encoding)); callback(); } _flush(callback) { this.push(this.final()); callback(); } setAutoPadding(autoPadding) { this.internal.setAutoPadding(!!autoPadding); return this; } setAAD(buffer, options) { this.internal.setAAD({ data: buffer.buffer, plaintextLength: options?.plaintextLength }); return this; } getAuthTag() { return _reactNativeBuffer.Buffer.from(this.internal.getAuthTag()); } setAuthTag(tag) { this.internal.setAuthTag((0, _Utils.binaryLikeToArrayBuffer)(tag)); return this; } } class Cipher extends CipherCommon { constructor(cipherType, cipherKey, options = {}, iv) { if (iv != null) { iv = (0, _Utils.binaryLikeToArrayBuffer)(iv); } super(cipherType, cipherKey, true, options, iv); } } class Decipher extends CipherCommon { constructor(cipherType, cipherKey, options = {}, iv) { if (iv != null) { iv = (0, _Utils.binaryLikeToArrayBuffer)(iv); } super(cipherType, cipherKey, false, options, iv); } } /** * @deprecated Use createDecipheriv instead. This function will be removed in 1.0+ */ /** * @deprecated Use createDecipheriv instead. This function will be removed in 1.0+ */ /** * @deprecated Use createDecipheriv instead. This function will be removed in 1.0+ */ /** * @deprecated Use createDecipheriv instead. This function will be removed in 1.0+ */ function createDecipher(algorithm, password, options) { if (options === undefined) options = {}; return new Decipher(algorithm, password, options); } function createDecipheriv(algorithm, key, iv, options) { return new Decipher(algorithm, key, options, iv); } /** * @deprecated Use createCipheriv instead. This function will be removed in 1.0+ */ /** * @deprecated Use createCipheriv instead. This function will be removed in 1.0+ */ /** * @deprecated Use createCipheriv instead. This function will be removed in 1.0+ */ /** * @deprecated Use createCipheriv instead. This function will be removed in 1.0+ */ function createCipher(algorithm, password, options) { return new Cipher(algorithm, password, options); } function createCipheriv(algorithm, key, iv, options) { return new Cipher(algorithm, key, options, iv); } // RSA Functions // Follows closely the model implemented in node function rsaFunctionFor(method, defaultPadding, keyType) { return (options, buffer) => { const { format, type, data, passphrase } = keyType === 'private' ? (0, _keys.preparePrivateKey)(options) : (0, _keys.preparePublicOrPrivateKey)(options); const padding = options && typeof options === 'object' && 'padding' in options && typeof options.padding === 'number' ? options.padding : defaultPadding; const oaepHash = options && typeof options === 'object' && 'oaepHash' in options ? options.oaepHash : undefined; const encoding = options && typeof options === 'object' && 'encoding' in options ? options.encoding : undefined; let oaepLabel = options && typeof options === 'object' && 'oaepLabel' in options ? options.oaepLabel : undefined; if (oaepHash !== undefined) (0, _Utils.validateString)(oaepHash, 'key.oaepHash'); if (oaepLabel !== undefined) oaepLabel = (0, _Utils.binaryLikeToArrayBuffer)(oaepLabel, encoding); buffer = (0, _Utils.binaryLikeToArrayBuffer)(buffer, encoding); const rawRes = method(data, format, type, passphrase, buffer, padding, oaepHash, oaepLabel); return _reactNativeBuffer.Buffer.from(rawRes); }; } const publicEncrypt = exports.publicEncrypt = rsaFunctionFor(_publicEncrypt, _constants.constants.RSA_PKCS1_OAEP_PADDING, 'public'); const publicDecrypt = exports.publicDecrypt = rsaFunctionFor(_publicDecrypt, _constants.constants.RSA_PKCS1_PADDING, 'public'); // const privateEncrypt = rsaFunctionFor(_privateEncrypt, constants.RSA_PKCS1_PADDING, // 'private'); const privateDecrypt = exports.privateDecrypt = rsaFunctionFor(_privateDecrypt, _constants.constants.RSA_PKCS1_OAEP_PADDING, 'private'); // _ _ __ _____ _ // | | | |/ / | __ \ (_) // __ _ ___ _ __ ___ _ __ __ _| |_ ___| ' / ___ _ _| |__) |_ _ _ _ __ // / _` |/ _ \ '_ \ / _ \ '__/ _` | __/ _ \ < / _ \ | | | ___/ _` | | '__| // | (_| | __/ | | | __/ | | (_| | || __/ . \ __/ |_| | | | (_| | | | // \__, |\___|_| |_|\___|_| \__,_|\__\___|_|\_\___|\__, |_| \__,_|_|_| // __/ | __/ | // |___/ |___/ function parseKeyEncoding(keyType, options = _Utils.kEmptyObject) { const { publicKeyEncoding, privateKeyEncoding } = options; let publicFormat, publicType; if (publicKeyEncoding == null) { publicFormat = publicType = undefined; } else if (typeof publicKeyEncoding === 'object') { ({ format: publicFormat, type: publicType } = (0, _keys.parsePublicKeyEncoding)(publicKeyEncoding, keyType, 'publicKeyEncoding')); } else { throw new Error('Invalid argument options.publicKeyEncoding', publicKeyEncoding); } let privateFormat, privateType, cipher, passphrase; if (privateKeyEncoding == null) { privateFormat = privateType = undefined; } else if (typeof privateKeyEncoding === 'object') { ({ format: privateFormat, type: privateType, cipher, passphrase } = (0, _keys.parsePrivateKeyEncoding)(privateKeyEncoding, keyType, 'privateKeyEncoding')); } else { throw new Error('Invalid argument options.privateKeyEncoding', publicKeyEncoding); } return [publicFormat, publicType, privateFormat, privateType, cipher, passphrase]; } /** On node a very complex "job" chain is created, we are going for a far simpler approach and calling * an internal function that basically executes the same byte shuffling on the native side */ function internalGenerateKeyPair(isAsync, type, options, callback) { const encoding = parseKeyEncoding(type, options); // if (options !== undefined) // validateObject(options, 'options'); switch (type) { case 'rsa-pss': // fallthrough case 'rsa': return internalRsaGenerateKeyPair(isAsync, type, options, callback, encoding); // case 'dsa': { // validateObject(options, 'options'); // const { modulusLength } = options!; // validateUint32(modulusLength, 'options.modulusLength'); // let { divisorLength } = options!; // if (divisorLength == null) { // divisorLength = -1; // } else validateInt32(divisorLength, 'options.divisorLength', 0); // // return new DsaKeyPairGenJob( // // mode, // // modulusLength, // // divisorLength, // // ...encoding); // } case 'ec': return internalEcGenerateKeyPair(isAsync, type, options, callback, encoding); // case 'ed25519': // case 'ed448': // case 'x25519': // case 'x448': { // let id; // switch (type) { // case 'ed25519': // id = EVP_PKEY_ED25519; // break; // case 'ed448': // id = EVP_PKEY_ED448; // break; // case 'x25519': // id = EVP_PKEY_X25519; // break; // case 'x448': // id = EVP_PKEY_X448; // break; // } // return new NidKeyPairGenJob(mode, id, ...encoding); // } // case 'dh': { // validateObject(options, 'options'); // const { group, primeLength, prime, generator } = options; // if (group != null) { // if (prime != null) // throw new ERR_INCOMPATIBLE_OPTION_PAIR('group', 'prime'); // if (primeLength != null) // throw new ERR_INCOMPATIBLE_OPTION_PAIR('group', 'primeLength'); // if (generator != null) // throw new ERR_INCOMPATIBLE_OPTION_PAIR('group', 'generator'); // validateString(group, 'options.group'); // return new DhKeyPairGenJob(mode, group, ...encoding); // } // if (prime != null) { // if (primeLength != null) // throw new ERR_INCOMPATIBLE_OPTION_PAIR('prime', 'primeLength'); // validateBuffer(prime, 'options.prime'); // } else if (primeLength != null) { // validateInt32(primeLength, 'options.primeLength', 0); // } else { // throw new ERR_MISSING_OPTION( // 'At least one of the group, prime, or primeLength options' // ); // } // if (generator != null) { // validateInt32(generator, 'options.generator', 0); // } // return new DhKeyPairGenJob( // mode, // prime != null ? prime : primeLength, // generator == null ? 2 : generator, // ...encoding // ); // } default: // Fall through } const err = new Error(` Invalid Argument options: '${type}' scheme not supported for generateKey(). Currently not all encryption methods are supported in quick-crypto. Check implementation_coverage.md for status. `); return [err, undefined, undefined]; } const internalRsaGenerateKeyPair = (isAsync, type, options, callback, encoding) => { (0, _Utils.validateObject)(options, 'options'); const { modulusLength } = options; (0, _Utils.validateUint32)(modulusLength, 'options.modulusLength'); let { publicExponent } = options; if (publicExponent == null) { publicExponent = 0x10001; } else { (0, _Utils.validateUint32)(publicExponent, 'options.publicExponent'); } if (type === 'rsa') { if (isAsync) { _NativeQuickCrypto.NativeQuickCrypto.generateKeyPair(_Cipher.KeyVariant.RSA_SSA_PKCS1_v1_5, // Used also for RSA-OAEP modulusLength, publicExponent, ...encoding).then(([err, publicKey, privateKey]) => { if (publicKey instanceof _reactNativeBuffer.Buffer) { publicKey = _reactNativeBuffer.Buffer.from(publicKey); } if (privateKey instanceof _reactNativeBuffer.Buffer) { privateKey = _reactNativeBuffer.Buffer.from(privateKey); } callback(err, publicKey, privateKey); }).catch(err => { callback(err, undefined, undefined); }); } else { const [err, publicKey, privateKey] = _NativeQuickCrypto.NativeQuickCrypto.generateKeyPairSync(_Cipher.KeyVariant.RSA_SSA_PKCS1_v1_5, modulusLength, publicExponent, ...encoding); const pub = publicKey instanceof _reactNativeBuffer.Buffer ? _reactNativeBuffer.Buffer.from(publicKey) : publicKey; const priv = privateKey instanceof _reactNativeBuffer.Buffer ? _reactNativeBuffer.Buffer.from(privateKey) : privateKey; return [err, pub, priv]; } } const { hash, mgf1Hash, hashAlgorithm, mgf1HashAlgorithm, saltLength } = options; // // We don't have a process object on RN // // const pendingDeprecation = getOptionValue('--pending-deprecation'); if (saltLength !== undefined) (0, _Utils.validateInt32)(saltLength, 'options.saltLength', 0); if (hashAlgorithm !== undefined) (0, _Utils.validateString)(hashAlgorithm, 'options.hashAlgorithm'); if (mgf1HashAlgorithm !== undefined) (0, _Utils.validateString)(mgf1HashAlgorithm, 'options.mgf1HashAlgorithm'); if (hash !== undefined) { // pendingDeprecation && process.emitWarning( // '"options.hash" is deprecated, ' + // 'use "options.hashAlgorithm" instead.', // 'DeprecationWarning', // 'DEP0154'); (0, _Utils.validateString)(hash, 'options.hash'); if (hashAlgorithm && hash !== hashAlgorithm) { throw new Error(`Invalid Argument options.hash ${hash}`); } } if (mgf1Hash !== undefined) { // pendingDeprecation && process.emitWarning( // '"options.mgf1Hash" is deprecated, ' + // 'use "options.mgf1HashAlgorithm" instead.', // 'DeprecationWarning', // 'DEP0154'); (0, _Utils.validateString)(mgf1Hash, 'options.mgf1Hash'); if (mgf1HashAlgorithm && mgf1Hash !== mgf1HashAlgorithm) { throw new Error(`Invalid Argument options.mgf1Hash ${mgf1Hash}`); } } return _NativeQuickCrypto.NativeQuickCrypto.generateKeyPairSync(_Cipher.KeyVariant.RSA_PSS, modulusLength, publicExponent, hashAlgorithm || hash, mgf1HashAlgorithm || mgf1Hash, saltLength, ...encoding); }; const internalEcGenerateKeyPair = (isAsync, _type, options, callback, encoding) => { (0, _Utils.validateObject)(options, 'options'); const { namedCurve } = options; (0, _Utils.validateString)(namedCurve, 'options.namedCurve'); let paramEncodingFlag = ECCurve.OPENSSL_EC_NAMED_CURVE; const { paramEncoding } = options; if (paramEncoding == null || paramEncoding === 'named') paramEncodingFlag = ECCurve.OPENSSL_EC_NAMED_CURVE;else if (paramEncoding === 'explicit') paramEncodingFlag = ECCurve.OPENSSL_EC_EXPLICIT_CURVE;else throw new Error(`Invalid Argument options.paramEncoding ${paramEncoding}`); if (isAsync) { _NativeQuickCrypto.NativeQuickCrypto.generateKeyPair(_Cipher.KeyVariant.EC, namedCurve, paramEncodingFlag, ...encoding).then(([err, publicKey, privateKey]) => { if (publicKey instanceof _reactNativeBuffer.Buffer) { publicKey = _reactNativeBuffer.Buffer.from(publicKey); } if (privateKey instanceof _reactNativeBuffer.Buffer) { privateKey = _reactNativeBuffer.Buffer.from(privateKey); } callback?.(err, publicKey, privateKey); }).catch(err => { callback?.(err, undefined, undefined); }); } const [err, publicKey, privateKey] = _NativeQuickCrypto.NativeQuickCrypto.generateKeyPairSync(_Cipher.KeyVariant.EC, namedCurve, paramEncodingFlag, ...encoding); const pub = publicKey instanceof _reactNativeBuffer.Buffer ? _reactNativeBuffer.Buffer.from(publicKey) : publicKey; const priv = privateKey instanceof _reactNativeBuffer.Buffer ? _reactNativeBuffer.Buffer.from(privateKey) : privateKey; return [err, pub, priv]; }; const generateKeyPair = (type, options, callback) => { (0, _Utils.validateFunction)(callback); internalGenerateKeyPair(true, type, options, callback); }; // Promisify generateKeyPair // (attempted to use util.promisify, to no avail) exports.generateKeyPair = generateKeyPair; const generateKeyPairPromise = (type, options) => { return new Promise((resolve, reject) => { generateKeyPair(type, options, (err, publicKey, privateKey) => { if (err) { reject([err, undefined]); } else { resolve([undefined, { publicKey, privateKey }]); } }); }); }; // generateKeyPairSync exports.generateKeyPairPromise = generateKeyPairPromise; function generateKeyPairSync(type, options) { // eslint-disable-next-line @typescript-eslint/no-unused-vars const [_, publicKey, privateKey] = internalGenerateKeyPair(false, type, options, undefined); return { publicKey, privateKey }; } //# sourceMappingURL=Cipher.js.map