Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/kudelskisecurity/oramfs

Resizable ORAM, remote storage agnostic, written in Rust
https://github.com/kudelskisecurity/oramfs

Last synced: about 9 hours ago
JSON representation

Resizable ORAM, remote storage agnostic, written in Rust

Awesome Lists containing this project

README

        

# oramfs - ORAM filesystem written in Rust

[![Rust 1.49+](https://img.shields.io/badge/Rust-1.49+-green.svg)](https://blog.rust-lang.org/2020/12/31/Rust-1.49.0.html) [![crates.io](https://img.shields.io/crates/v/oramfs.svg)](https://crates.io/crates/oramfs) [![License: GPL v3](https://img.shields.io/badge/License-GPL%20v3-blue.svg)](http://www.gnu.org/licenses/gpl-3.0)

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.

`oramfs` features:

* 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](https://eprint.iacr.org/2013/280.pdf), etc.
* Written in **Rust** - Avoids memory safety issues, great performance

**[DISCLAIMER]** `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 `oramfs`
. For example, [Rclone](https://github.com/rclone/rclone) supports mounting a variety of cloud storage providers as
local directories.

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.

# Requirements

* Rust
* FUSE (`libfuse-dev` package on Debian-based systems, `fuse` package on Arch)

# Getting started

Install Rust using [rustup](https://rustup.rs/) if it is not installed yet.

In `/etc/fuse.conf`, make sure to enable the `user_allow_other` option.

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.

Then, build `oramfs` using `cargo`:

```
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
```

The `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 `~/.cargo/bin` by
default.

```
export PATH=$PATH:target/release
```

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 `myoram`:

```
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 `~/.config/oramfs/oramfs.yml`.

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,
using `rclone config`. The `rclone` config file should have an entry for that remote, similar to:

```
[myftp]
type = ftp
host = 1.2.3.4
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 `myoram`:

```
$ 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.

# Configuration

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 `pathoram` to `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.

# Advanced Usage

Show help with `cargo run -- -h`

## Foreground mode

By default, `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
```

## Manual mode

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 `/tmp/oramfs_{ORAM_NAME}/oram`.

```
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 `oram` file
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
the `oram` file:

```
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.

## ORAM initialization

**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 `--init`.

`oramfs` looks at the `init` property in the global oramfs config file to determine whether the ORAM was already
initialized.

## Using other ORAM schemes than Path ORAM

This prototype currently only implements [Path ORAM](https://eprint.iacr.org/2013/280.pdf), 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 `oramfs`.

To use another scheme, such as `fakeoram`, update the configuration file and change the `algorithm` entry to `fakeoram`.
Then simply mount and initialize the oram.

# Privacy

The `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.

# Performance

When native CPU instructions can be used, AES may be faster than ChaCha8. Changing the cipher can be achieved by
passing the `--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 `cargo`'s `--release` flag and to pass
the `RUSTFLAGS="-Ctarget-cpu=native"` environment variable.

## Benchmarks

`oramfs` was compared to [UtahFS](https://github.com/cloudflare/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 `n`, `z` and `b` parameters in `oramfs`.
In this benchmark, the defaults were used for 10MB. Then the ORAM was enlarged (`n` was doubled) for 25 MB.

### Read performance

| File size | oramfs | UtahFS | Speedup |
| --- | --- | --- | --- |
| 10 MB | 1 sec | 9 sec | 9x |
| 25 MB | 3 sec | 26.5 sec | 8.8x |

### Write performance

| File size | oramfs | UtahFS | Speedup |
| --- | --- | --- | --- |
| 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

Note that `oramfs` is still a prototype and has the following known limitations.

## Memory zeroization

Memory is currently not zeroized on exit/crash. An attacker may be able to extract private keys or passphrases from
non-zeroized memory.

## Read caching

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
```

# Testing

Run tests with `cargo test --release`

# Contributing

Feel free to open an issue or pull request.

Code should be formatted with rustfmt. To automatically format the whole project:

```
cargo fmt
```

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/.