oramfs - ORAM filesystem written in Rust
Oramfs provides a fully encrypted and optionally authenticated Oblivious RAM filesystem. Not only does it preserve data confidentiality, but it also prevents remote attackers from observing data access patterns.
- ORAM - encrypt files and hide read/write access patterns from remote storage. Enhanced privacy!
- Resizable ORAM - extend your ORAM when more space is required!
- Cloud storage agnostic - synchronize your files to any remote server that can be mounted as a local directory
- Filesystem agnostic - ext4 is used by default. Manual mode lets you use the filesystem you like.
- Supports multiple encryption ciphers - ChaCha8, AES-CTR, AES-GCM
- Supports multiple ORAM schemes - Path ORAM, etc.
- Written in Rust - Avoids memory safety issues, great performance
oramfs is a prototype and may not be ready for production. It may erase some of your data. Make sure to
backup important data before using this software.
Why use oramfs?
On an encrypted filesystem, an ORAM prevents an attacker from knowing whether read or write operations are performed and which parts of the filesystem are accessed. This enhanced privacy comes with a loss of performance.
To setup the ORAM, two inputs are required. A public directory and a private directory. The public directory can be seen as the server and the private directory as the client.
The public directory can be any local directory, including remote data mounted as a local directory. Hence, hiding
access patterns to a remote SSH directory or to a remote cloud storage is possible. Indeed, these remote storages simply
need to be mounted as a local directory first, and then, that directory can be used as the public directory for
. For example, Rclone supports mounting a variety of cloud storage providers as
The private directory is the one that should be used to access files stored in the ORAM. Any operation performed on the private directory has an impact on the public directory. And if that public directory is a mounted remote storage, then it is safely and transparently synchronized to the remote server whenever an operation is performed on the private directory.
- FUSE (
libfuse-devpackage on Debian-based systems,
fusepackage on Arch)
Install Rust using rustup if it is not installed yet.
/etc/fuse.conf, make sure to enable the
If not already installed, install the
libfuse-dev (Debian-based systems) package using your package manager.
For other systems, the package may be named differently.
cargo build --release
To get maximum performance and take advantage of native CPU instructions (AES-NI), build in release mode and target the native CPU:
RUSTFLAGS="-Ctarget-cpu=native" cargo build --release
oramfs binary will be created in the
target/release directory. For convenience, add it to your
PATH. Note that
installing the binary can also be performed with
cargo install --path .. It will be installed in
Then, create or mount a public directory to be protected by the ORAM, and a private directory:
mkdir public mkdir private
Finally, run the executable and create an ORAM configuration called
oramfs add myoram public/ private/
Follow the interactive instructions and complete the ORAM setup.
Once the ORAM configuration is setup, the details are saved to
Now the ORAM can be mounted and unmounted at any time using this configuration.
oramfs mount myoram
To unmount the ORAM:
oramfs umount myoram
To enlarge the ORAM, make sure it is unmounted first, then double its size:
oramfs enlarge myoram
Then it can be mounted as usual and its size will be larger than before.
How does it work?
Instead of implementing a full filesystem,
oramfs only provides a mounted file. Therefore, the user is expected to
create a filesystem on top of that file using a loop device, the filesystem of their choice and finally mounting that
filesystem. Note that such operations usually require root privileges and therefore
oramfs requires those privileges
for mounting. Sudo is called to achieve this, and therefore,
oramfs can simply be run as a regular user. It will
prompt for your password when sudo is called for mount-related operations.
oramfs takes a public directory as input and exposes a single private file, which is a proxy for read and write
operations so that they are privacy-preserving thanks to an ORAM scheme.
The mounted private file can be used to setup a loop device using
losetup. Then, any filesystem, such as ext4 can be
created on top of that loop device.
oramfs automates this process, but also lets users do it manually if they want to.
+---------------------------------------------------+ | | | ext4 filesystem | <---+ or any other FS or your choice | | +---------------------------------------------------+ | | | Loop device (/dev/loop0) | <---+ created with losetup | | +---------------------------------------------------+ | | | ORAMFS (FUSE) | <---+ Input : *public* local directory | | Output : *private* single file, +-------------------+-----------------+-------------+ for use with loop device | | | | | Local directory | Cloud storage | SSHFS | <---+ Input directory can be anything | | | | that appears as a local directory, +-------------------+-----------------+-------------+ including mounted remote directories. Examples: SSH, FTP, anything supported by rclone or similar tools, any mounted FUSE filesystem, etc.
Before using ORAMFS:
$ tree . ├── private <---+ empty directory └── public <---+ this is the directory that the attacker can see ("public" directory)
When ORAMFS is in use, every operation done in the "private" directory - or directly on the "oram" private file in the mountpoint directory - appears ORAMified in the "public" directory. In the standard use case, the user does not directly modify the "oram" private file, but instead uses a higher level abstraction (the filesystem in the "private" directory). The user typically mounts their public cloud storage to the "public" directory before running ORAMFS, so that the public files are transparently synchronized to the cloud in a privacy-preserving way.
When ORAMFS is in use:
$ tree . ├── private │ └── lost+found │ └── very_private_document.txt └── public └── node_0.oram └── node_1.oram └── node_2.oram └── ...
$ tree /tmp/oramfs_myoram/ /tmp/oramfs_myoram/ └── oram
Example with remote storage
In this example, we go through the setup of an ORAM that transparently synchronizes data to a remote FTP server. Of course, one could use any other remote storage (SSH server, Google Drive, etc.). Anything that can be mounted as a local directory.
We assume that an
rclone remote has already been configured for an FTP server you have access to,
rclone config. The
rclone config file should have an entry for that remote, similar to:
[myftp] type = ftp host = 22.214.171.124 user = myusername pass = mypassword
Let's mount the remote FTP server directory as local directory. We will use this directory as public directory of our ORAM:
rclone mount --daemon --allow-other --dir-cache-time 1s --poll-interval 1s --vfs-cache-mode writes --vfs-write-back 200ms myftp:somedirectory/ public
Create an ORAM called
$ oramfs add myoram public/ private/ Please enter desired ORAM total size in bytes, or press enter to use default [default: 16000000 (16 MB)]: Adjusting ORAM size to closest valid value: 16711680 bytes Please enter path to client data directory to use, or press enter to use default [default: /home/foobar/.config/oramfs/myoram]: Please enter path to mointpoint directory to use, or press enter to use default [default: /tmp/oramfs_myoram]: Successfully added ORAM myoram.
Mount the ORAM, write a file to it:
$ oramfs mount myoram $ echo hello world > private/somefile
When finished, unmount it:
$ oramfs unmount myoram
That's it! Files written/read to/from the private directory are encrypted and access patterns are hidden to the FTP server. For more details, make sure to read the Privacy section below.
The main configuration file is located at
~/.config/oramfs/oramfs.yml. Existing ORAM profiles can be modified by
simply editing that file. For example, the ORAM scheme could be changed from
fakeoram. For a description
of all the options, see
oramfs add --help. Note that changing these options probably require re-initializing the ORAM,
and therefore, it's not possible to change those options without losing the data in an existing ORAM.
Show help with
cargo run -- -h
oramfs runs in the background. Use
--foreground to avoid that.
Note that when
oramfs runs in the foreground, it implies that manual mode is used.
oramfs mount myoram --foreground
For maximum control, manual mode can be used (
--manual). Mount ORAMFS using Path ORAM (with explicit parameters). The
mounted ORAMFS appears as a file under the specified mountpoint directory. By default, it is in
mkdir private mkdir public oramfs add myoram public/ private/ oramfs mount myoram --manual
Since manual mode does not automatically mount a file system for you, you must do it yourself. To do so, create an ext4
filesystem on top of the ORAM. Note that
mount automatically creates a loop device for us:
mkfs.ext4 /tmp/oramfs_myoram/oram mount -o sync /tmp/oramfs_myoram/oram private/ echo "hello oram" > private/hello.txt
Using another filesystem than ext4
oramfs supports any filesystem. To use something different from the default ext4, do the following.
During initialization, pass the
--manual flag, then manually create the filesystem of your choice on the
in the mountpoint directory. Here is an example with ext3:
oramfs add myoram public/ private/ oramfs mount myoram --manual mkfs.ext3 /tmp/oramfs_myoram/oram oramfs umount myoram oramfs mount myoram
When enlarging an ORAM using a different filesystem, pass the
--manual flag. Then, manually resize and unmount
oramfs umount myoram oramfs enlarge myoram --manual resize2fs -f /tmp/oramfs_myoram/oram # or equivalent for your filesystem oramfs umount myoram oramfs mount myoram
Mounting multiple ORAMs at the same time
When mounting multiple ORAMs at the same time, make sure that the ORAMs use different, public directories, private directories, mountpoints and client data directories.
Important: the first time that an ORAM is mounted, the
--init option is passed automatically.
--init is a destructive operation and it will permanently destroy any data in an existing ORAM. In practice, there
should be little need to manually pass
oramfs looks at the
init property in the global oramfs config file to determine whether the ORAM was already
Using other ORAM schemes than Path ORAM
This prototype currently only implements Path ORAM, but it is built so that more
schemes can be added in the future. To prove this, there is a second scheme named
fakeoram built-in, but it should not
be used in production because it is not a true ORAM. FakeORAM is a "Hello World" example ORAM scheme that could be
useful for developers who want to add new ORAM schemes to
To use another scheme, such as
fakeoram, update the configuration file and change the
algorithm entry to
Then simply mount and initialize the oram.
public directory can be safely mirrored to the cloud, without the cloud provider knowing which file is being
accessed and whether read or write operations were performed. One scenario would be to mount a remote Google Drive
directory as the
public directory, and use that
public directory as the public directory for the ORAMFS.
When native CPU instructions can be used, AES may be faster than ChaCha8. Changing the cipher can be achieved by
--cipher aes-ctr or
--cipher aes-gcm flag on adding an ORAM, for example. One can also directly edit
the configuration file and reinitialize the ORAM with the updated cipher. Note that this will destroy any data in the
ORAM, so proceed with caution when initializing an ORAM.
To achieve the best performance, make sure to build or run using
--release flag and to pass
RUSTFLAGS="-Ctarget-cpu=native" environment variable.
oramfs was compared to UtahFS.
Oramfs was used with default values (AES-GCM, 16MB oram size).
UtahFS was used with a local disk and the
oram option was set to true.
Note that performance is highly sensitive to the choice of
b parameters in
In this benchmark, the defaults were used for 10MB. Then the ORAM was enlarged (
n was doubled) for 25 MB.
|10 MB||1 sec||9 sec||9x|
|25 MB||3 sec||26.5 sec||8.8x|
|10 MB||15 sec||30 sec||2x|
|25 MB||50 sec||95 sec||1.9x|
Usage on SSD or flash storage
For each read or write operation, the ORAM scheme actually performs multiple operations under the scenes. Even for read operations, underlying write operations are performed. This can significantly reduce the lifespan of SSDs.
Limitations and future work
oramfs is still a prototype and has the following known limitations.
Memory is currently not zeroized on exit/crash. An attacker may be able to extract private keys or passphrases from non-zeroized memory.
If reads are cached, then the ORAM won't perform any work on cached reads. This is a privacy issue because it would mean that if all reads are cached, then we can be sure that any modification to the public directory must be a write operation.
To avoid read caching, on Linux, always clear the kernel cache before reading a file from the ORAM:
# sync; echo 1 > /proc/sys/vm/drop_caches
Run tests with
cargo test --release
Feel free to open an issue or pull request.
Code should be formatted with rustfmt. To automatically format the whole project:
No warnings should appear when running
cargo build and
cargo clippy. Additionally, all tests should pass (See Testing section).
License and Copyright
Copyright(c) 2021 Nagravision SA.
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License version 3 as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.