[!WARNING]
This crate hasn't been audited; it's usingring
crate, which is a well-known audited library, so in principle, at least the primitives should offer a similar level of security.
This is still under development. Please do not use it with sensitive data for now; please wait for a stable release.
It's mostly ideal for experimental and learning projects.
An encrypted file system written in Rust that is mounted with FUSE on Linux. It can be used to create encrypted directories.
You can then safely back up the encrypted directory to an untrusted server without worrying about the data being exposed. You can also store it in a cloud storage service like Google Drive, Dropbox, etc., and have it synced across multiple devices.
You can use it as CLI or as a library to build your custom FUSE implementation or other apps that work with encrypted data.
There is a GUI too.
Create a simple,
performant,
modular
and ergonomic
yet very secure
encrypted filesystem
to protect your privacy
, which is also open source
and is correctly and safely using well-known audited
crates as cryptographic primitives.
The Hitchhiker’s Guide to Building an Encrypted Filesystem in Rust
There will be a series of articles about the evolution of this project, trying to keep it like a tutorial. This is the first one.
It was crate of the week in Aug 2024.
Some of these are still being worked on and marked with [WIP]
.
Security
using well-known audited AEAD
cryptography primitives;[WIP]
Data integrity
, data is written with WAL
to ensure integrity even on crash or power loss;[WIP]
Hide all info for enhanced privacy
, all metadata
, content
, file name
, file size
, *time
fields, files count
, and directory structure is encrypted;Safely
manage credentials
in memory with mlock(2)
, mprotect
, zeroize
, and expiry
to mitigate cold boot attacks;Memory safety
, performance
, and optimized
for concurrency
with Rust;keyring
;Change password
without re-encrypting all data;[WIP]
Generate unique nonce
in offline mode
;Fast seek
on both reads and writes;Writes in parallel
;FUSE
;concurrent
for all operations;[WIP]
Handle long file names
;[WIP]
Abstraction layer for Rust File
and fs
API to use it as lib to switch to using encrypted files
by just changing the use statements
;[WIP]
Abstraction layer to access the storage
with implementations for desktop, Wasm, Android, and iOS and the ability to write your own implementation.Some of these are still being worked on and marked with [WIP]
.
encrypted
data and master encryption key
in a dedicated directory with files structured on inodes
(with
metadata info), files for binary content, and directories with files/directories entries. All data, metadata, and filenames
are encrypted. It generates unique inodes for new files in a multi-instance run and offline mode.keyring
while the app runs. This is because, for security concerns, we
clear the password from memory on inactivity, and we derive it again from the password just when needed.re-encrypt
the master key
.encrypted
in chunks
of 256KB
, so when making a change, we just re-encrypt that chunks.Fast seek
on read and write, so if you're watching a movie, you can seek any position, and that would be instant.
This is because we can seek a particular chunk.zeroize
in the mem when disposing and idle. Also, it's mlock
ed while used to prevent being moved to swap. It's
also mprotect
ed while not in use.[WIP]
Ensure file integrity by saving each change to WAL, so for crashes or power loss, we apply the pending
changes at the next start. This makes the write operations atomic.For detailed description of the various sequence flows please look into Flows.
Asked ChatGPT if there are other solutions out there which offer all the key functionalities we do, seems like there are none :)
You can see the key features that separate us.
Get the image
docker pull xorio42/rencfs
Start a container to set up mount in it
docker run -it --device /dev/fuse --cap-add SYS_ADMIN --security-opt apparmor:unconfined xorio42/rencfs:latest /bin/sh
In the container, create mount and data directories
mkdir fsmnt && mkdir fsdata
Start rencfs
rencfs mount --mount-point fsmnt --data-dir fsdata
Enter a password for encryption.
Get the container ID
docker ps
In another terminal, attach to the running container with the above ID
docker exec -it <ID> /bin/sh
From here, you can play with it by creating files in fsmnt
directory
cd fsmnt
mkdir 1
ls
echo "test" > 1/test
cat 1/test
For the library, you can follow the documentation.
To use the encrypted file system, you need to have FUSE installed on your system. You can install it by running the following command (or based on your distribution).
Arch
sudo pacman -Syu && sudo pacman -S fuse3
Ubuntu
sudo apt-get update && sudo apt-get -y install fuse3
You can install the encrypted file system binary using the following command
yay -Syu && yay -S rencfs
You can install the encrypted file system binary using the following command
cargo install rencfs
A basic example of how to use the encrypted file system is shown below
rencfs mount --mount-point MOUNT_POINT --data-dir DATA_DIR
MOUNT_POINT
act as a client, and mount FUSE at the given pathDATA_DIR
where to store the encrypted data
with the sync provider. But it needs to be on the same filesystem as the data-dirIt will prompt you to enter a password to encrypt/decrypt the data.
The master encryption key is stored in a file and encrypted with a key derived from the password. This offers the possibility to change the password without needing to re-encrypt the whole data. This is done by decrypting the master key with the old password and re-encrypting it with the new password.
To change the password, you can run the following command
rencfs passwd --data-dir DATA_DIR
DATA_DIR
where the encrypted data is stored
It will prompt you to enter the old password and then the new password.
You can specify the encryption algorithm by adding this argument to the command line
--cipher CIPHER ...
Where CIPHER
is the encryption algorithm. You can check the available ciphers with rencfs --help
.
The default value is ChaCha20Poly1305
.
You can specify the log level by adding the --log-level
argument to the command line. Possible
values: TRACE
, DEBUG
, INFO
(default), WARN
, ERROR
.
rencfs --log-level LEVEL ...
You can see more here
If you want to give it a quick try and not setup anything locally you can
You can compile it, run it, and give it a quick try in the browser. After you start it from above
sudo apt-get update && sudo apt-get install fuse3
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
mkdir mnt && mkdir data
cargo run --release -- mount -m mnt -d data
Open another terminal
cd mnt
mkdir a && cd a
echo "test" > test.txt
cat test.txt
For now, the FUSE
(fuse3
crate) only works on Linux
, so to start the project, you will need to be on Linux.
Instead, you can Develop inside a Container, which will start a local Linux container, the IDE will connect to it,
and you can build and start the app there and also use the terminal to test it.
On Windows, you can start it in WSL.
git clone git@github.com:radumarias/rencfs.git && cd rencfs
To build from source, you need to have Rust installed, you can see more details on how to install it here.
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
Accordingly, it is customary for Rust developers to include this directory in their PATH
environment variable.
During installation rustup
will attempt to configure the PATH
. Because of differences between platforms, command
shells,
and bugs in rustup
, the modifications to PATH
may not take effect until the console is restarted, or the user is
logged out, or it may not succeed at all.
If, after installation, running rustc --version
in the console fails, this is the most likely reason.
In that case please add it to the PATH
manually.
The project is set up to use the nightly
toolchain in rust-toolchain. tool
; on the first build, you will see it fetch the nightly.
Make sure to add this to your $PATH
too
export PATH="$PATH::$HOME/.cargo/bin"
cargo install cargo-aur
cargo install cargo-generate-rpm
Also, these dependencies are required (or based on your distribution):
sudo pacman -Syu && sudo pacman -S fuse3 base-devel act
sudo apt-get update && sudo apt-get install fuse3 build-essential act
sudo dnf update && sudo dnf install fuse3 && dnf install @development-tools act
cargo build
cargo build --release
cargo run --release -- mount --mount-point MOUNT_POINT --data-dir DATA_DIR
If you don't want to be prompted for a password, you can set this env var and run it like this:
RENCFS_PASSWORD=PASS cargo run --release -- mount --mount-point MOUNT_POINT --data-dir DATA_DIR
For dev mode it is recommended to run with DEBUG
log level:
cargo run --release -- --log-level DEBUG mount --mount-point MOUNT_POINT --data-dir DATA_DIR
This is using cargo-generate-rpm
cargo install cargo-generate-rpm
cargo build --release
cargo generate-rpm
The generated RPM will be located here: target/generate-rpm
.
cd target/generate-rpm/
sudo dnf localinstall rencfs-xxx.x86_64.rpm
See here how to configure for RustRover and for VsCode.
You can use the .devcontainer
directory from the project to start a container with all the necessary tools to build
and run the app.
The minimum supported version is 1.75
.
The plan is to implement it also on macOS and Windows
Aes256Gcm
is slightly faster than ChaCha20Poly1305
by a factor of 1.28 on average. This is because of the hardware acceleration of AES
on most CPUs via AES-NI. However, where hardware acceleration is not available, ChaCha20Poly1305
is faster. Also ChaChaPoly1305
is better at SIMD
.
AES-NI
), then AES-GCM
provides better performance. On my benchmarks, it was
faster by a factor of 1.28 on average.AES-GCM
is either slower than ChaCha20-Poly1305
, or it leaks your
encryption
keys in cache timing.AES-GCM
can target multiple security levels (128-bit
, 192-bit
, 256-bit
), whereas ChaCha20-Poly1305
is only defined at
the 256-bit
security level.AES-GCM
: Varies, but the standard is 96 bits
(12 bytes
).
If you supply a longer nonce, this gets hashed down to 16 bytes
.ChaCha20-Poly1305
: The standardized version uses 96-bit
nonce (12 bytes
), but the original used 64-bit
nonce (8 bytes
).AES-GCM
: Messages must be less than 2^32 – 2
blocks (a.k.a. 2^36 – 32 bytes
, a.k.a. 2^39 – 256 bits
), that's
roughly 64GB
.
This also makes the security analysis of AES-GCM
with long nonces complicated since the hashed nonce doesn’t
start
with the lower 4 bytes
set to 00 00 00 02
.ChaCha20-Poly1305
: ChaCha
has an internal counter (32 bits
in the standardized IETF variant, 64 bits
in the
original design). Max message length is 2^39 - 256 bits
, about 256GB
ChaChaPoly1305
is better at SIMD
Both are good options. AES-GCM
can be faster with hardware support, but pure-software implementations of
ChaCha20-Poly1305
are almost always fast and constant-time.
Feel free to fork it, change and use it however you want. If you build something interesting and feel like sharing pull requests are always appreciated.
Please see CONTRIBUTING.md.