Compare commits
No commits in common. "22e3ebed4303cad1b1ffa9f22e7fa6f0f1dfc509" and "3e2f608e27063a7fca255ef27c228a4c75b1df9f" have entirely different histories.
22e3ebed43
...
3e2f608e27
2 changed files with 3 additions and 136 deletions
112
README.md
112
README.md
|
@ -1,112 +0,0 @@
|
||||||
# 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 std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
use argon2::{Argon2, ParamsBuilder};
|
use argon2::Argon2;
|
||||||
use chacha20poly1305::aead::generic_array::sequence::GenericSequence;
|
use chacha20poly1305::aead::generic_array::sequence::GenericSequence;
|
||||||
use chacha20poly1305::aead::generic_array::GenericArray;
|
use chacha20poly1305::aead::generic_array::GenericArray;
|
||||||
use chacha20poly1305::aead::{AeadInPlace, NewAead};
|
use chacha20poly1305::aead::{AeadInPlace, NewAead};
|
||||||
|
@ -152,7 +152,7 @@ pub fn open_in_place(
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let pw_key = if let Some(password) = password {
|
let pw_key = if let Some(password) = password {
|
||||||
let salt_buf = data.split_off(data.len() - Salt::SIZE);
|
let salt_buf = data.split_off(data.len() - Salt::SIZE);
|
||||||
let argon = get_argon2();
|
let argon = Argon2::default();
|
||||||
let mut pw_key = Key::default();
|
let mut pw_key = Key::default();
|
||||||
argon
|
argon
|
||||||
.hash_password_into(password.expose_secret(), &salt_buf, &mut pw_key)
|
.hash_password_into(password.expose_secret(), &salt_buf, &mut pw_key)
|
||||||
|
@ -255,34 +255,13 @@ impl AsRef<[u8]> for Salt {
|
||||||
/// Hashes an input to output a usable key.
|
/// Hashes an input to output a usable key.
|
||||||
fn kdf(password: &SecretVec<u8>) -> Result<(Secret<Key>, Salt), argon2::Error> {
|
fn kdf(password: &SecretVec<u8>) -> Result<(Secret<Key>, Salt), argon2::Error> {
|
||||||
let salt = Salt::random();
|
let salt = Salt::random();
|
||||||
let hasher = get_argon2();
|
let hasher = Argon2::default();
|
||||||
let mut key = Key::default();
|
let mut key = Key::default();
|
||||||
hasher.hash_password_into(password.expose_secret().as_ref(), salt.as_ref(), &mut key)?;
|
hasher.hash_password_into(password.expose_secret().as_ref(), salt.as_ref(), &mut key)?;
|
||||||
|
|
||||||
Ok((Secret::new(key), salt))
|
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
|
/// Fetches a cryptographically secure random number generator. This indirection
|
||||||
/// is used for better auditing the quality of rng. Notably, this function
|
/// is used for better auditing the quality of rng. Notably, this function
|
||||||
/// returns a `Rng` with the `CryptoRng` marker trait, preventing
|
/// returns a `Rng` with the `CryptoRng` marker trait, preventing
|
||||||
|
|
Loading…
Reference in a new issue