UNPKG

react-native-quick-crypto

Version:

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

173 lines (167 loc) 5.5 kB
"use strict"; import { NitroModules } from 'react-native-nitro-modules'; import Stream from 'readable-stream'; import { Buffer } from '@craftzdog/react-native-buffer'; // @types/node import { ab2str, binaryLikeToArrayBuffer } from './utils'; import { getDefaultEncoding, getUIntOption, normalizeEncoding, validateEncoding } from './utils/cipher'; class CipherUtils { static native = NitroModules.createHybridObject('Cipher'); static getSupportedCiphers() { return this.native.getSupportedCiphers(); } } export function getCiphers() { return CipherUtils.getSupportedCiphers(); } class CipherCommon extends Stream.Transform { constructor({ isCipher, cipherType, cipherKey, iv, options }) { // Explicitly create TransformOptions for super() const streamOptions = {}; if (options) { // List known TransformOptions keys (adjust if needed) const transformKeys = ['readableHighWaterMark', 'writableHighWaterMark', 'decodeStrings', 'defaultEncoding', 'objectMode', 'destroy', 'read', 'write', 'writev', 'final', 'transform', 'flush' // Add any other relevant keys from readable-stream's TransformOptions ]; for (const key of transformKeys) { if (key in options) { // eslint-disable-next-line @typescript-eslint/no-explicit-any streamOptions[key] = options[key]; } } } super(streamOptions); // Pass filtered options const authTagLen = getUIntOption(options ?? {}, 'authTagLength') !== -1 ? getUIntOption(options ?? {}, 'authTagLength') : 16; // defaults to 16 bytes const factory = NitroModules.createHybridObject('CipherFactory'); this.native = factory.createCipher({ isCipher, cipherType, cipherKey: binaryLikeToArrayBuffer(cipherKey), iv: binaryLikeToArrayBuffer(iv), authTagLen }); } update(data, inputEncoding, outputEncoding) { const defaultEncoding = 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'); } const ret = this.native.update(binaryLikeToArrayBuffer(data, inputEncoding)); if (outputEncoding && outputEncoding !== 'buffer') { return ab2str(ret, outputEncoding); } return Buffer.from(ret); } final(outputEncoding) { const ret = this.native.final(); if (outputEncoding && outputEncoding !== 'buffer') { return ab2str(ret, outputEncoding); } return Buffer.from(ret); } _transform(chunk, encoding, callback) { this.push(this.update(chunk, normalizeEncoding(encoding))); callback(); } _flush(callback) { this.push(this.final()); callback(); } setAutoPadding(autoPadding) { const res = this.native.setAutoPadding(!!autoPadding); if (!res) { throw new Error('setAutoPadding failed'); } return this; } setAAD(buffer, options) { // Check if native parts are initialized if (!this.native || typeof this.native.setAAD !== 'function') { throw new Error('Cipher native object or setAAD method not initialized.'); } const res = this.native.setAAD(buffer.buffer, options?.plaintextLength); if (!res) { throw new Error('setAAD failed (native call returned false)'); } return this; } getAuthTag() { return Buffer.from(this.native.getAuthTag()); } setAuthTag(tag) { const res = this.native.setAuthTag(binaryLikeToArrayBuffer(tag)); if (!res) { throw new Error('setAuthTag failed'); } return this; } getSupportedCiphers() { return this.native.getSupportedCiphers(); } } class Cipheriv extends CipherCommon { constructor(cipherType, cipherKey, iv, options) { super({ isCipher: true, cipherType, cipherKey: binaryLikeToArrayBuffer(cipherKey), iv: binaryLikeToArrayBuffer(iv), options }); } } class Decipheriv extends CipherCommon { constructor(cipherType, cipherKey, iv, options) { super({ isCipher: false, cipherType, cipherKey: binaryLikeToArrayBuffer(cipherKey), iv: binaryLikeToArrayBuffer(iv), options }); } } export function createDecipheriv(algorithm, key, iv, options) { return new Decipheriv(algorithm, key, iv, options); } export function createCipheriv(algorithm, key, iv, options) { return new Cipheriv(algorithm, key, iv, options); } /** * xsalsa20 stream encryption with @noble/ciphers compatible API * * @param key - 32 bytes * @param nonce - 24 bytes * @param data - data to encrypt * @param output - unused * @param counter - unused * @returns encrypted data */ export function xsalsa20(key, nonce, data, // @ts-expect-error haven't implemented this part of @noble/ciphers API // eslint-disable-next-line @typescript-eslint/no-unused-vars output, // @ts-expect-error haven't implemented this part of @noble/ciphers API // eslint-disable-next-line @typescript-eslint/no-unused-vars counter) { const factory = NitroModules.createHybridObject('CipherFactory'); const native = factory.createCipher({ isCipher: true, cipherType: 'xsalsa20', cipherKey: binaryLikeToArrayBuffer(key), iv: binaryLikeToArrayBuffer(nonce) }); const result = native.update(binaryLikeToArrayBuffer(data)); return new Uint8Array(result); } //# sourceMappingURL=cipher.js.map