craftzdog / react-native-aes-gcm-crypto

AES-GCM encryption/decryption for React Native
MIT License
243 stars 28 forks source link

Error when using EncryptFile or DecryptFile in Expo project #13

Closed gtonye closed 11 months ago

gtonye commented 11 months ago

Hi,

There was no template issue, so hopefully, this will be a format that will provide the right information.

First and foremost, thank you for the work on this library. It's working great for encrypting plain text content in our application.

We are now looking to extend the encryption to files, but we are running into a few issues with encryptFile (and subsequently decryptFile)

I hope to get some guidance on how to tackle this.

Context

I am getting:

When attempting to do:

import RNAesGcmCrypto from 'react-native-aes-gcm-crypto';
import * as Crypto from 'expo-crypto';
import * as FileSystem from 'expo-file-system';

// setup the target folder in which the encrypted file should be put
const encryptedFileFolderPath = `${FileSystem.documentDirectory}temporary-downloads`;
await FileSystem.makeDirectoryAsync(encryptedFileFolderPath, { intermediates: true });
const encryptedFilePath = `${encryptedFileFolderPath}/${Crypto.randomUUID()}.jpeg`;

// filepath contains a file returned by the camera roll using https://docs.expo.dev/versions/latest/sdk/imagepicker/
const fileStats = (await FileSystem.getInfoAsync(filePath, { size: true })) as FileSystem.FileInfo & { size: number };
console.log(` info JSON.stringify(fileStats) ${JSON.stringify(fileStats)}`); // confirms that the file exists

const encryptFileResult = await RNAesGcmCrypto.encryptFile(filePath, encryptedFilePath, encryptionKey);

Questions

  1. Is there any specific requirements for the library to be able to access the files from the expo FileSystem?

    (my thoughts) Expo specifies that the files are restricted to the app, I believe the library should have the same context, so I wonder if it's a permission issue.

  2. Is the file encryption done in chunks or is the entire file read in memory?

    (my thoughts) I was looking at using FileSystem.readAsStringAsync which allows to read chunks of the file and provide the chunks to the encryption function as a workaround.

I would love to get thoughts or quick comments on the questions and the errors. Thanks in advance for the time!

Info

I am using:

I was testing on:

I use the Expo dev client. I am not sure if you are familiar, but it's the client that you build using EAS build

gtonye commented 11 months ago

Following up on my previous post:

I have found a way to solve my issue. If anyone comes across this, I ended up settling on a solution that works for my use case. I was concerned about this library loading the file into memory and the Expo implementation requiring the loaded downloaded file to be in memory.

With the solution outlined below, I use a streaming approach to load the file in chunks, encrypt each chunk, and upload them. Similarly, I download each chunk, decrypt it, and rebuild the file.

Memory is not a concern, and the app can encrypt huge files while maintaining end-to-end encryption and the speed of the implementation in this library.

The API must support a way to store the information and the chunks. If you don't control the API, it is not guaranteed that MultiPart upload is available.

On upload to the API

sequenceDiagram
  loop while document not entirely read
    App->>App: ExpoFileSystem.readAsStringAsync(chunk_size) into chunk
    App->>App: AesGcmCrypto.encrypt(chunk, key)
    App->>Api: Store IV, tag, and encrypted chunk in file
  end

On completion of the process, based on the chunk size, the API will have 1..n files with the corresponding IVs and Tags

On download from the API

sequenceDiagram
  loop while remaining files
    Api->>App: Send file with IV, tag, encryptedChunk
    App->>App: AesGcmCrypto.decrypt(encryptedChunk, key, iv, tag) as decryptedChunk
    App->>App: ExpoFileSystem.writeAsStringAsync(decryptedChunk)
  end

The solution is to patch "Expo FileSystem" to make FilesSystem.writeAsStringAsync capable of appending to a file, which it doesn't at the moment. It would only write the entire content, which causes a few issues as it requires the app to load the entire file in memory, which impacts devices with small capabilities.

The patch involves updating the Java and Objective C implementations.