Compare commits
2 commits
3e2f608e27
...
22e3ebed43
Author | SHA1 | Date | |
---|---|---|---|
22e3ebed43 | |||
5bb3ad2d0d |
2 changed files with 136 additions and 3 deletions
112
README.md
Normal file
112
README.md
Normal file
|
@ -0,0 +1,112 @@
|
|||
# OmegaUpload
|
||||
|
||||
OmegaUpload is a zero-knowledge temporary file hosting service.
|
||||
|
||||
## Examples
|
||||
|
||||
```bash
|
||||
# Uploading a file:
|
||||
$ omegaupload-cli upload https://paste.example.com path/to/file
|
||||
https://paste.example.com/PgRG8Hfrr9rR#I1FG2oejo2gSjB3Ym1mEmRfcN4X8GXc2pZtZeiSsWFo=
|
||||
|
||||
# Uploading a file with a password:
|
||||
$ omegaupload-cli upload -p https://paste.example.com path/to/file
|
||||
Please set the password for this paste:
|
||||
https://paste.crabravers.club/862vhXVp3v9R#key:tbGxzHBNnXjS2eq89X9uvZKz_i8bvapLPEp8g0waQrc=!pw
|
||||
|
||||
# Downloading a file:
|
||||
$ omegaupload-cli download https://paste.example.com/PgRG8Hfrr9rR#I1FG2oejo2gSjB3Ym1mEmRfcN4X8GXc2pZtZeiSsWFo=
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
- Server has zero knowledge of uploaded data when uploading through a supported
|
||||
frontend (Direct, plaintext upload is possible but unsupported).
|
||||
- Only metadata stored on server is expiration time. This is a strong guarantee.
|
||||
- All cryptographic functions are performed on the client side and are done via
|
||||
a single common library, to minimize risk of programming error.
|
||||
- Modern crypto functions are used with recommended parameters:
|
||||
XChaCha20Poly1305 for encryption and Argon2id for KDF.
|
||||
- Customizable expiration times, from burn-after-read to 1 day.
|
||||
|
||||
## Getting Started
|
||||
|
||||
Prerequisites:
|
||||
- `yarn` 1.22.17 or later (Earlier versions untested but likely to work)
|
||||
- [`trunk`](https://trunkrs.dev/)
|
||||
- Cargo, with support for the latest Rust version
|
||||
- _(Optional)_ zstd, for zipping up the file for distribution
|
||||
|
||||
First, run `git submodule update --init --recursive`.
|
||||
|
||||
Then, run `./bin/build.sh` for a `dist.tar.zst` to be generated, where you can
|
||||
simply extract that folder and run the binary provided. The server will listen
|
||||
on port `8080`.
|
||||
|
||||
### Development
|
||||
|
||||
For development, building is as simple as `cargo build`. Note that you may need
|
||||
to run `trunk build` first before building the server. To run the server (even
|
||||
for testing) requires uploading `dist.tar.zst` to a remote server.
|
||||
|
||||
## Why OmegaUpload?
|
||||
|
||||
OmegaUpload's primary benefit is that the frontends use a unified common library
|
||||
utilizing XChaCha20Poly1305 to encrypt and decrypt files.
|
||||
|
||||
### Security
|
||||
|
||||
The primary goal was to provide a unified library across both a CLI tool and
|
||||
through the web frontend to minimize risk of compromise. As a result, the CLI
|
||||
tool and the web frontend both utilize a Rust library whose crypto module
|
||||
exposes two functions to encrypt and decrypt that only accept a message and
|
||||
necessarily key material or return only necessary key material. This small API
|
||||
effectively makes it impossible to have differences between the frontend, and
|
||||
ensures that the attack surface is limited to these functions.
|
||||
|
||||
#### Password KDF
|
||||
|
||||
If a password is provided at encryption time, argon2 is used as a key derivation
|
||||
function. Specifically, the library meets or exceeds OWASP recommended
|
||||
parameters:
|
||||
- Argon2id is used.
|
||||
- Algorithm version is `0x13`.
|
||||
- Parameters are `m = 15MiB`, `t = 2`, `p = 2`.
|
||||
|
||||
Additionally, a salt size of 16 bytes are used.
|
||||
|
||||
#### Blob Encryption
|
||||
|
||||
XChaCha20Poly1305 was used as the encryption method as it is becoming the
|
||||
mainstream recommended method for encrypting messages. This was chosen over AES
|
||||
primarily due to its strength in related-key attacks, as well as its widespread
|
||||
recognition and usage in WireGuard, Quic, and TLS.
|
||||
|
||||
As this crate uses `XChaCha20`, a 24 byte nonce and a 32 bytes key are used.
|
||||
|
||||
#### Secrecy
|
||||
|
||||
Encryption and decryption functions offered by the common crate only accept or
|
||||
return key material that will be properly zeroed on destruction. This is
|
||||
enforced by the `secrecy` crate, which, on top of offering type wrappers that
|
||||
zero the memory on drop, provide an easy way to audit when secrets are exposed.
|
||||
|
||||
This also means that to use these two functions necessarily requires the caller
|
||||
to enclose key material in the wrapped type first, reducing possibility for key
|
||||
material to remain in memory.
|
||||
|
||||
#### Memory Safety
|
||||
|
||||
Rust eliminates an entire class of memory-related bugs, and any `unsafe` block
|
||||
is documented with a safety comment. This allows for easy auditing of memory
|
||||
suspect code, and permits
|
||||
|
||||
## Why not OmegaUpload?
|
||||
|
||||
There are a few reasons to not use OmegaUpload:
|
||||
- Limited to 3GB uploads—this is a soft limit of RocksDB.
|
||||
- Cannot download files larger than 512 MiB through the web frontend—this
|
||||
is a technical limitation of the current web frontend not using a web worker
|
||||
in addition to the fact that browsers are not optimized for XChaCha20.
|
||||
- Right now, you must upload via the CLI tool.
|
||||
- The frontend uses WASM, which is a novel attack surface.
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use argon2::Argon2;
|
||||
use argon2::{Argon2, ParamsBuilder};
|
||||
use chacha20poly1305::aead::generic_array::sequence::GenericSequence;
|
||||
use chacha20poly1305::aead::generic_array::GenericArray;
|
||||
use chacha20poly1305::aead::{AeadInPlace, NewAead};
|
||||
|
@ -152,7 +152,7 @@ pub fn open_in_place(
|
|||
) -> Result<(), Error> {
|
||||
let pw_key = if let Some(password) = password {
|
||||
let salt_buf = data.split_off(data.len() - Salt::SIZE);
|
||||
let argon = Argon2::default();
|
||||
let argon = get_argon2();
|
||||
let mut pw_key = Key::default();
|
||||
argon
|
||||
.hash_password_into(password.expose_secret(), &salt_buf, &mut pw_key)
|
||||
|
@ -255,13 +255,34 @@ impl AsRef<[u8]> for Salt {
|
|||
/// Hashes an input to output a usable key.
|
||||
fn kdf(password: &SecretVec<u8>) -> Result<(Secret<Key>, Salt), argon2::Error> {
|
||||
let salt = Salt::random();
|
||||
let hasher = Argon2::default();
|
||||
let hasher = get_argon2();
|
||||
let mut key = Key::default();
|
||||
hasher.hash_password_into(password.expose_secret().as_ref(), salt.as_ref(), &mut key)?;
|
||||
|
||||
Ok((Secret::new(key), salt))
|
||||
}
|
||||
|
||||
/// Returns Argon2id configured as follows:
|
||||
/// - 15MiB of memory (`m`),
|
||||
/// - an iteration count of 2 (`t`),
|
||||
/// - and 2 degrees of parallelism (`p`).
|
||||
///
|
||||
/// This follows the [minimum recommended parameters suggested by OWASP][rec].
|
||||
///
|
||||
/// [rec]: https://link.eddie.sh/vaQ6a.
|
||||
fn get_argon2() -> Argon2<'static> {
|
||||
let mut params = ParamsBuilder::new();
|
||||
params
|
||||
.m_cost(15 * 1024) // 15 MiB
|
||||
.expect("Hard coded params to work")
|
||||
.t_cost(2)
|
||||
.expect("Hard coded params to work")
|
||||
.p_cost(2)
|
||||
.expect("Hard coded params to work");
|
||||
let params = params.params().expect("Hard coded params to work");
|
||||
Argon2::new(argon2::Algorithm::Argon2id, argon2::Version::V0x13, params)
|
||||
}
|
||||
|
||||
/// Fetches a cryptographically secure random number generator. This indirection
|
||||
/// is used for better auditing the quality of rng. Notably, this function
|
||||
/// returns a `Rng` with the `CryptoRng` marker trait, preventing
|
||||
|
|
Loading…
Reference in a new issue