Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
12e3421
rust/core: add auth types
prateekmedia Jan 16, 2026
d985bb8
rust/core: add auth key generation
prateekmedia Jan 16, 2026
a8d1622
rust/core: add auth login helpers
prateekmedia Jan 16, 2026
c29a4c7
rust/core: add auth crypto API
prateekmedia Jan 16, 2026
296232b
rust/core: add auth recovery helpers
prateekmedia Jan 16, 2026
96209bd
rust/core: add SRP session implementation
prateekmedia Jan 16, 2026
c7a6481
rust/core: add auth module exports
prateekmedia Jan 16, 2026
7f13828
rust/core: expose auth + crypto modules
prateekmedia Jan 16, 2026
b574696
rust/core/tests: add libsodium vectors
prateekmedia Jan 16, 2026
3115a2f
rust/core/tests: add auth integration tests
prateekmedia Jan 16, 2026
9155307
rust/core/tests: add crypto interop tests
prateekmedia Jan 16, 2026
363e62d
rust/core/tests: add comprehensive crypto tests
prateekmedia Jan 16, 2026
0fc0f51
rust/core/tests: add js interop test
prateekmedia Jan 16, 2026
1e943ac
rust/core/tests: add js test harness
prateekmedia Jan 16, 2026
1748a73
rust/core/docs: add auth documentation
prateekmedia Jan 16, 2026
216575f
rust/core/docs: add crypto documentation
prateekmedia Jan 16, 2026
2fccdb2
rust/core: expand README for auth + crypto
prateekmedia Jan 16, 2026
f658584
rust: update repository layout docs
prateekmedia Jan 16, 2026
0ba7c2f
rust/core: harden auth and align tests
prateekmedia Jan 17, 2026
4185501
rust/core: fix clippy warnings
prateekmedia Jan 17, 2026
df080b2
rust/core: add fuzz targets
prateekmedia Jan 16, 2026
ffa5296
ci: add rust/core fuzz workflow
prateekmedia Jan 16, 2026
c8e8454
rust/validation: add crate scaffold
prateekmedia Jan 16, 2026
17e0f60
rust/validation: add crypto validation tests
prateekmedia Jan 16, 2026
21ea911
rust/validation: add README
prateekmedia Jan 16, 2026
95d5767
rust/validation: add benchmark tooling
prateekmedia Jan 16, 2026
8f530f0
rust/validation: add wasm benchmark crate
prateekmedia Jan 16, 2026
b9c92c0
rust/validation: add browser wasm benchmark page
prateekmedia Jan 16, 2026
db38632
rust/validation: align stream tests with core semantics
prateekmedia Jan 17, 2026
35bfe3d
Merge branch 'rust-core-impl' into rust-fuzz-validation
prateekmedia Jan 17, 2026
eb086fd
docs(validation): note release validation run
prateekmedia Jan 17, 2026
83fb948
docs(validation): prefer release runs
prateekmedia Jan 17, 2026
136c71a
ci: run rust/core fuzz on core changes
prateekmedia Jan 17, 2026
986622b
ci: limit fuzz workflow to PRs
prateekmedia Jan 17, 2026
6e9b91b
style(rust): format SRP verification asserts
prateekmedia Jan 17, 2026
f7ddd75
crypto: gate blob final tag and default hash len
prateekmedia Jan 17, 2026
b4ff7c7
Merge branch 'rust-core-impl' into rust-fuzz-validation
prateekmedia Jan 17, 2026
43e5cfb
Merge branch 'rust-core-impl' into rust-fuzz-validation
prateekmedia Jan 17, 2026
84fdfa8
Update password change test fixtures
prateekmedia Jan 17, 2026
e5d712a
Fix validation and interop test expectations
prateekmedia Jan 17, 2026
d4e550f
Merge branch 'rust-core-impl' into rust-fuzz-validation
prateekmedia Jan 17, 2026
35562f8
Format hash size assertion
prateekmedia Jan 18, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions .github/workflows/rust-fuzz-core.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: "Fuzz (rust/core)"

on:
workflow_dispatch:
pull_request:
paths:
- "rust/core/**"

permissions:
contents: read

jobs:
fuzz:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Install nightly toolchain
uses: dtolnay/rust-toolchain@nightly

- name: Cache
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
rust/core/target
rust/core/fuzz/target
key: ${{ runner.os }}-cargo-fuzz-${{ hashFiles('rust/core/Cargo.lock') }}

- name: Install cargo-fuzz
run: cargo +nightly install cargo-fuzz

- name: Run fuzz target (secretbox)
working-directory: rust/core
run: cargo +nightly fuzz run secretbox -- -max_total_time=300
67 changes: 59 additions & 8 deletions rust/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
## Contents (this repo)

- `rust/core/` (`ente-core`) - shared, pure Rust code used by clients (crypto + auth, plus small HTTP/URL helpers).
- `rust/validation/` - crypto validation + benchmarks vs libsodium.
- `rust/cli/` - Rust CLI (work-in-progress).

## Directory Structure
Expand All @@ -53,15 +54,30 @@ rust/
│ ├── Cargo.toml
│ └── Cargo.lock
└── core/ # Pure Rust shared logic
├── core/ # Pure Rust shared logic
│ ├── src/
│ │ ├── lib.rs
│ │ ├── crypto/
│ │ └── auth/
│ ├── docs/
│ │ ├── crypto.md
│ │ └── auth.md
│ └── Cargo.toml # crate name: ente-core
└── validation/ # Crypto validation + benchmarks vs libsodium
├── src/
│ ├── lib.rs
│ ├── crypto/
│ └── auth/
├── docs/
│ ├── crypto.md
│ └── auth.md
└── Cargo.toml # crate name: ente-core
│ ├── main.rs
│ └── bin/
│ └── bench.rs
├── wasm/ # WASM bench bindings
│ ├── src/
│ │ └── lib.rs
│ └── Cargo.toml # crate name: ente-validation-wasm
├── js/ # JS + WASM benchmarks
│ ├── bench-wasm.mjs
│ ├── bench-wasm-browser.mjs
│ └── bench-wasm-browser.html
└── Cargo.toml # crate name: ente-validation

web/packages/wasm/ # WASM bindings (lives in web workspace)
├── src/
Expand Down Expand Up @@ -90,6 +106,8 @@ mobile/apps/photos/rust/ # Photos app-specific FRB bindings

- `ente-core` - shared business logic (pure Rust, no FFI)
- Docs: `rust/core/docs/crypto.md`, `rust/core/docs/auth.md`
- `ente-validation` - validation + benchmarks vs libsodium
- `ente-validation-wasm` - wasm bench bindings
- `ente-wasm` - wasm-bindgen wrappers for web
- `ente_rust` - shared FRB wrappers for mobile (Dart class: `EnteRust`)
- `ente_photos_rust` - Photos app-specific FRB (Dart class: `EntePhotosRust`)
Expand Down Expand Up @@ -136,6 +154,39 @@ cargo build # build
cargo test # test
```

**ente-validation (rust/validation/):**

```sh
cargo run -p ente-validation --bin ente-validation # validation suite
cargo run -p ente-validation --bin bench # benchmarks (debug)
cargo run -p ente-validation --bin bench --release # benchmarks (release)
```

**ente-validation wasm benchmarks (rust/validation/):**

Node (rust-core wasm vs libsodium-wrappers-sumo wasm):

```sh
wasm-pack build --target nodejs rust/validation/wasm # wasm bench build
cd rust/validation/js
npm install
node bench-wasm.mjs # wasm benchmarks
```

Browser (Chrome):

```sh
wasm-pack build --target web rust/validation/wasm
cd rust/validation
python3 -m http.server 8000
```

Open:

```text
http://localhost:8000/js/bench-wasm-browser.html
```

**ente-wasm (web/packages/wasm/):**

```sh
Expand Down
55 changes: 55 additions & 0 deletions rust/core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,58 @@ cargo clippy # lint
cargo build # build
cargo test # test
```

## Fuzzing

Fuzzing is an automated technique that feeds randomized and malformed inputs into
functions to uncover panics, edge cases, and security bugs that unit tests may miss.

Fuzz targets live in `rust/core/fuzz` (cargo-fuzz). Requires a nightly toolchain.

```bash
cargo +nightly install cargo-fuzz
cd rust/core
cargo +nightly fuzz run secretbox -- -max_total_time=60
```

Other targets: `sealed_box`, `stream`, `argon`.

## Tests

```
tests/
├── auth_integration.rs # Auth workflow tests
├── comprehensive_crypto_tests.rs # Stress tests (up to 50MB files)
├── crypto_interop.rs # Cross-platform vectors (JS/Dart)
└── libsodium_vectors.rs # Libsodium compatibility

src/
├── auth/*.rs # Auth unit tests
└── crypto/*.rs # Crypto unit tests
```

**Total: 186+ tests**

| Test Suite | What it tests |
|------------|---------------|
| Auth unit tests | Login, signup, recovery, SRP |
| Auth integration | Full auth workflows |
| Crypto unit tests | Individual crypto operations |
| Libsodium vectors | Cross-platform compatibility |
| Comprehensive | Large files, edge cases |

### Running Tests

```bash
# All tests
cargo test

# Auth tests only
cargo test auth

# Crypto tests only
cargo test crypto

# With output
cargo test -- --nocapture
```
4 changes: 4 additions & 0 deletions rust/core/fuzz/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/corpus/
/artifacts/
/target/
Cargo.lock
37 changes: 37 additions & 0 deletions rust/core/fuzz/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
[package]
name = "ente-core-fuzz"
version = "0.0.0"
publish = false
edition = "2021"

[package.metadata]
cargo-fuzz = true

[dependencies]
libfuzzer-sys = "0.4"
arbitrary = { version = "1", features = ["derive"] }
ente-core = { path = ".." }

[[bin]]
name = "secretbox"
path = "fuzz_targets/secretbox.rs"
test = false
doc = false

[[bin]]
name = "sealed_box"
path = "fuzz_targets/sealed_box.rs"
test = false
doc = false

[[bin]]
name = "stream"
path = "fuzz_targets/stream.rs"
test = false
doc = false

[[bin]]
name = "argon"
path = "fuzz_targets/argon.rs"
test = false
doc = false
21 changes: 21 additions & 0 deletions rust/core/fuzz/fuzz_targets/argon.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#![no_main]

use arbitrary::Arbitrary;
use libfuzzer_sys::fuzz_target;

#[derive(Arbitrary, Debug)]
struct ArgonInput {
password: Vec<u8>,
salt: [u8; 16],
mem_kib: u16,
ops: u8,
}

fuzz_target!(|input: ArgonInput| {
// Clamp to values that are fast enough for fuzzing.
let mem_limit = 8_192u32 + (u32::from(input.mem_kib % 64) * 1024); // 8 KiB .. 72 KiB
let ops_limit = 1u32 + (u32::from(input.ops % 5)); // 1..=5

let password = String::from_utf8_lossy(&input.password);
let _ = ente_core::crypto::argon::derive_key(password.as_ref(), &input.salt, mem_limit, ops_limit);
});
20 changes: 20 additions & 0 deletions rust/core/fuzz/fuzz_targets/sealed_box.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#![no_main]

use arbitrary::Arbitrary;
use libfuzzer_sys::fuzz_target;

#[derive(Arbitrary, Debug)]
struct SealedBoxInput {
ciphertext: Vec<u8>,
recipient_pk: [u8; 32],
recipient_sk: [u8; 32],
}

fuzz_target!(|input: SealedBoxInput| {
// Primary goal: ensure `open()` never panics on malformed inputs.
let _ = ente_core::crypto::sealed::open(
&input.ciphertext,
&input.recipient_pk,
&input.recipient_sk,
);
});
38 changes: 38 additions & 0 deletions rust/core/fuzz/fuzz_targets/secretbox.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#![no_main]

use arbitrary::Arbitrary;
use libfuzzer_sys::fuzz_target;

#[derive(Arbitrary, Debug)]
struct SecretBoxInput {
plaintext: Vec<u8>,
key: [u8; 32],
nonce: [u8; 24],
flip_bit: bool,
}

fuzz_target!(|input: SecretBoxInput| {
// Roundtrip: encrypt_with_nonce is deterministic, so fuzzing stays reproducible.
if let Ok(ciphertext) = ente_core::crypto::secretbox::encrypt_with_nonce(
&input.plaintext,
&input.nonce,
&input.key,
) {
let mut ct = ciphertext;

// Optional: corrupt a byte to exercise error paths.
if input.flip_bit && !ct.is_empty() {
ct[0] ^= 0x01;
}

let decrypted = ente_core::crypto::secretbox::decrypt(&ct, &input.nonce, &input.key);

if input.flip_bit {
// Corruption should fail (unless ciphertext was empty, which never happens: MAC is
// always present, so ct is at least 16 bytes).
assert!(decrypted.is_err());
} else {
assert_eq!(decrypted.unwrap(), input.plaintext);
}
}
});
21 changes: 21 additions & 0 deletions rust/core/fuzz/fuzz_targets/stream.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#![no_main]

use arbitrary::Arbitrary;
use libfuzzer_sys::fuzz_target;

#[derive(Arbitrary, Debug)]
struct StreamInput {
header: [u8; 24],
key: [u8; 32],
ciphertext: Vec<u8>,
ad: Vec<u8>,
}

fuzz_target!(|input: StreamInput| {
// Primary goal: ensure secretstream parsing/decryption never panics.
if let Ok(mut decryptor) = ente_core::crypto::stream::StreamDecryptor::new(&input.header, &input.key)
{
let _ = decryptor.pull_with_ad(&input.ciphertext, &input.ad);
let _ = decryptor.pull_typed_with_ad(&input.ciphertext, &input.ad);
}
});
2 changes: 2 additions & 0 deletions rust/core/tests/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Node dependencies should not be committed
node_modules/
Loading