This article also has a Chinese version.
This article will introduce an unobtrusive method of encrypting cloud storage (using OneDrive as an example), and the code has been open sourced after the project was essentially complete.
The idea to create something started when I recently got my hands on a 5TB OneDrive account, which I figured would be great for backups. I tried OneDrive for Business and it seems to connect to nodes in Hong Kong for users in China, with around 10Mbps upload bandwidth from Shanghai Unicom. But when it comes to personal data, we must consider the issue of privacy: Could my data be accessed by others?
After a bit of Googling, I realized that whether it’s OneDrive or GDrive, if it’s an account under a domain, administrators have the authority to view personal files. Even though the account is under my own domain, I still wasn’t too comfortable handing over my data in plain text to Microsoft.
There are several cloud storage encryption solutions available, such as Boxcryptor, Cryptomator, and Duplicati. They can be summarized into a few different implementation methods:
- The simplest implementation: Keep two sets of data, one encrypted and one decrypted. The encrypted version is simply dropped into OneDrive’s sync folder.
- A slightly better implementation: Only keep the encrypted version and start another software for real-time decryption. This software allows the user to choose a folder (Cryptomator), or create a virtual drive (Boxcryptor), and directly use the directory to access decrypted files.
In my view, neither of these implementations are particularly smart. My requirement is to encrypt for OneDrive or GDrive specifically - plaintext should be stored locally, otherwise heavy IO would create a performance bottleneck. After all, the IO performance of cloud storage is definitely less than local IO, even much less than my upload bandwidth. Moreover, software malfunctions could directly impact the safety of my data, which is very concerning.
I attempted to create a seamless cloud encryption tool, tentatively named OneEncrypt. It consists of an executable file and a dynamic link library file.
The basic principle is to start my executable through image hijacking, which in fact is just a launcher. This launcher is responsible for initiating OneDrive (in debug mode to bypass IFEO and pause execution), and injects a dynamic link library file. Once the dynamic library is loaded, it attempts to inject into IO-related system calls using IAT Hook. Afterwards, it resumes running the original program, and the launcher exits.
During normal operation of OneDrive, there are no extra processes running, and the Hook only affects the OneDrive process. This allows an additional layer of encryption during its ReadFile operations and decryption during WriteFile operations.
Compared to other encryption solutions, this method has clear advantages:
- Better performance. As mentioned earlier, this doesn’t need further explanation.
- Better compatibility. The local program directly reads and writes unencrypted files, rather than my virtual file system. For example, the VHD files stored in Boxcryptor cannot be properly mounted.
- Data safety does not rely on the encryption software itself. Software errors or lost keys do not affect local data.
- Zero user-perceived overhead, no processes, and there is no need to modify the OneDrive executable itself, allowing for free updates.
- It allows for some additional tweaks, such as modifying the API return results in the Hook if OneDrive doesn’t support syncing symbolic links.
As for the encryption algorithm, the requirement is to support random access and high performance, such as
xchacha20. For example, with
xchacha20, the encryption process will get the corresponding
nonce for the file, and each block of the file will have a sequential
blockId, which is used for encrypting the block. Blocks are independent of each other, so when the block size is set to a small value, it is considered to support random read and write requirements.
Following Boxcryptor’s method, the
nonce is not placed at the beginning of the file but stored in a separate
key.storage file. This approach mainly avoids complications. Further changes may be needed down the line.
The current progress is based on
libsodium libraries, and the launcher with basic
WriteFile Hooks has been completed, which can pass simple POC tests; however, there is not enough support for
SetFilePointer, et cetera.
Once the basic development is completed, it will be open sourced at https://github.com/ihciah/OneEncrypt.
As I no longer primarily use Windows, this project has been discontinued.