Some checks failed
CI / lint (push) Successful in 1m34s
CI / test-python (push) Successful in 1m46s
CI / test-zig (push) Successful in 1m37s
CI / test-wasm (push) Successful in 1m52s
CI / test (push) Successful in 14m16s
CI / miri (push) Successful in 14m9s
CI / build (push) Successful in 1m7s
CI / fuzz-regression (push) Successful in 9m20s
CI / publish-python (push) Successful in 1m47s
CI / publish (push) Successful in 1m53s
CI / publish-wasm (push) Failing after 1m53s
Signed-off-by: Kamal Tufekcic <kamal@lo.sh>
155 lines
9.2 KiB
Markdown
155 lines
9.2 KiB
Markdown
# libsoliton
|
|
|
|
Pure-Rust post-quantum cryptographic library. Provides composite identity keys (X-Wing + ML-DSA-65), hybrid signatures, KEM-based authentication, asynchronous key exchange, double-ratchet message encryption, and encrypted storage — all without a C toolchain.
|
|
|
|
**Use cases:** any application requiring two-party encrypted communication or authenticated key agreement — messaging, voice/video calls, peer-to-peer sessions, file transfer, encrypted storage, zero-knowledge authentication, password-protected key vaults. The library provides the complete primitive stack; the application layer decides the transport and session management.
|
|
|
|
## Documentation
|
|
|
|
| Document | Description |
|
|
|----------|-------------|
|
|
| [Abstract.md](Abstract.md) | Security analysis specification — adversary model, theorems, and verification targets for formal modeling |
|
|
| [Specification.md](Specification.md) | Full cryptographic specification (v1) |
|
|
| [CHEATSHEET.md](CHEATSHEET.md) | API quick reference with types, sizes, and signatures |
|
|
|
|
## Crate Layout
|
|
|
|
| Package | Path | Purpose |
|
|
|---------|------|---------|
|
|
| `libsoliton` (crates.io) | `soliton/` | Core library — all cryptographic logic |
|
|
| `libsoliton_capi` (crates.io) | `soliton_capi/` | C ABI FFI layer (cbindgen-generated header) |
|
|
| `soliton-py` (PyPI) | `soliton_py/` | Python binding (PyO3/maturin, wraps core Rust API) |
|
|
| `soliton-wasm` (npm) | `soliton_wasm/` | WASM binding (wasm-bindgen, wraps core Rust API) |
|
|
| `soliton-cli` (cargo) | `soliton_cli/` | Native CLI (`cargo install soliton-cli`) |
|
|
| `soliton_zig` | `soliton_zig/` | Zig wrapper (consumes CAPI via `@cImport`) |
|
|
|
|
## Testing
|
|
|
|
**By location** (non-overlapping — these add up to the total):
|
|
|
|
| Location | Count | Description |
|
|
|----------|-------|-------------|
|
|
| Core unit tests | 488 | `soliton/src/**/` `#[cfg(test)]` — unit tests + proptests |
|
|
| Core integration tests | 61 | `soliton/tests/` — KEX → ratchet, storage round-trips, argon2, zeroization verification, Appendix F vectors |
|
|
| CAPI tests | 287 | `soliton_capi/tests/` — null pointers, invalid lengths, round-trips, error codes |
|
|
| Python tests | 49 | `soliton_py/tests/` — full KEX → ratchet round-trip, serialize, reset, call keys, storage, streaming (_at), auth, identity, primitives |
|
|
| Zig tests | 35 | `soliton_zig/src/soliton.zig` — full KEX → ratchet round-trip, serialize, reset, call keys, storage, streaming (_at), auth, AEAD, X-Wing, argon2id |
|
|
| WASM tests | 36 | `soliton_wasm/tests/` — full KEX → ratchet lifecycle, serialize/deserialize, call keys, storage, streaming (_at), auth, identity, primitives, argon2id (Node + Chromium) |
|
|
| Fuzz targets | 36 | `*/fuzz/fuzz_targets/` — 29 core + 7 CAPI (`cargo-fuzz` / libFuzzer) |
|
|
|
|
**Cross-cutting** (subsets of the above):
|
|
|
|
| Property | Count | Subset of | Description |
|
|
|----------|-------|-----------|-------------|
|
|
| MIRI-eligible | 455 | core + CAPI | PQ-free subset safe for instruction-level interpretation (`--profile miri`) |
|
|
| KAT vectors | 27 | core unit + integration | 17 in `src/**/` `#[cfg(test)]` — SHA3-256, HMAC-SHA3-256, HKDF-SHA3-256, Ed25519, X25519, X-Wing, AEAD, KDF, KEX, streaming, call; 10 in `tests/compute_vectors.rs` — Specification.md Appendix F vectors F.25-F.37 (HKDF, AEAD, HybridSign, streaming, Argon2, HMAC long-key, first-message AAD, SPK sig, session-init sig, LO-Auth) |
|
|
|
|
```bash
|
|
# Full test suite
|
|
cargo test -p libsoliton -p libsoliton_capi
|
|
|
|
# MIRI (requires nightly)
|
|
MIRIFLAGS="-Zmiri-disable-isolation" cargo +nightly miri nextest run \
|
|
-p libsoliton -p libsoliton_capi --profile miri -j$(nproc)
|
|
|
|
# Fuzzing — corpus regression (replays all seeds, no mutations, fast)
|
|
./ci_regression.sh
|
|
|
|
# Fuzzing — overnight campaign (all active targets in parallel)
|
|
./fuzz_overnight.sh 24 2 # 24 hours, 2 workers per target
|
|
./fuzz_stats.sh # Post-run summary
|
|
```
|
|
|
|
## Fuzzing
|
|
|
|
36 fuzz targets (29 core + 7 CAPI) with two corpora (core: `soliton/fuzz/corpus/`, CAPI: `soliton_capi/fuzz/corpus/`) totaling 165,540 entries (883 MB). All corpora are checked in via Git LFS; `ci_regression.sh` replays every seed with zero mutations as a CI regression gate.
|
|
|
|
**Excluded targets** — 5 of 36 are excluded from overnight campaigns after saturation analysis. All reachable edges were discovered; additional CPU time yields no new coverage. These targets retain their corpora and run in `ci_regression.sh`.
|
|
|
|
| Target | Corpus | Edges | Reason |
|
|
|---|---|---|---|
|
|
| `fuzz_identity_from_bytes` | 84 | 107 | 3 length checks only |
|
|
| `fuzz_auth_verify` | ~15 | 88 | Single `ct_eq` call |
|
|
| `fuzz_xwing_roundtrip` | 7 | 763 | No adversarial input (keygen→encap→decap) |
|
|
| `fuzz_verification_phrase` | 29 | 88 | SHA3→wordlist index, trivial surface |
|
|
| `fuzz_auth_respond` | 4 | 1573 | Single KEM decap→HMAC, no branching on input |
|
|
|
|
**Latest campaign** — 31 targets (24 core + 7 CAPI), 1 libFuzzer worker per target, Hetzner CCX53 (32 vCPUs, AMD EPYC, 128 GB RAM), 24 hours.
|
|
|
|
| Metric | Value |
|
|
|---|---|
|
|
| **Total targets** | 36 (29 core + 7 CAPI) |
|
|
| **Active in campaign** | 31 |
|
|
| **Total executions** | 574.6 billion |
|
|
| **Corpus** | 165,540 entries (883 MB) |
|
|
| **Crashes / Timeouts / OOM** | **0 / 0 / 0** |
|
|
|
|
The state-machine fuzzer (`fuzz_ratchet_state_machine`) performs full cryptographic sessions with up to 200 protocol actions per execution (KEM, AEAD, HKDF, HMAC per message) — 12,714 coverage features, the highest of any target.
|
|
|
|
```bash
|
|
./ci_regression.sh # Replay all seeds — fast, runs in CI
|
|
./fuzz_overnight.sh 24 1 # 31 active targets, 24 hours, 1 worker each
|
|
./fuzz_stats.sh # Per-target breakdown (edges, features, throughput, RSS)
|
|
```
|
|
|
|
## Benchmarks
|
|
|
|
Measured with `cargo +nightly bench` across three platforms. ML-DSA-65, SHA3-256, and Keccak do not have SIMD implementations in the current RustCrypto crates — expect these numbers to improve as upstream adds platform-specific optimizations. All operations are pure ARX or field arithmetic; no hardware acceleration is required.
|
|
|
|
**Platform support:** Tested on x86-64, aarch64, riscv64gc, and wasm32. 32-bit native platforms are untested and unsupported — the library uses `u64` extensively and the PQ primitives have non-trivial stack requirements.
|
|
|
|
| Platform | CPU | Arch | Clock |
|
|
|----------|-----|------|-------|
|
|
| **Desktop** | AMD Ryzen 7 7840HS | x86-64 (Zen 4) | ~5.1 GHz boost |
|
|
| **RPi 5** | Cortex-A76 | aarch64 | 2.4 GHz |
|
|
| **VisionFive 2** | SiFive U74 | riscv64gc | 1.5 GHz |
|
|
|
|
**Per-message hot paths** (what users feel on every send/receive):
|
|
|
|
| Operation | Desktop | RPi 5 | VisionFive 2 | Notes |
|
|
|-----------|---------|-------|-------------|-------|
|
|
| Ratchet encrypt (same epoch) | 4.3 µs | 7.2 µs | 47.6 µs | HMAC-SHA3-256 + XChaCha20-Poly1305 |
|
|
| Ratchet decrypt (same epoch) | 6.2 µs | 10.1 µs | 67.5 µs | Includes `from_bytes` deserialization |
|
|
| KDF root (isolated) | 5.7 µs | 9.2 µs | 59.5 µs | HKDF-SHA3-256, 64-byte output |
|
|
| Ratchet serialize | 1.6 µs | 6.1 µs | 38.1 µs | `to_bytes` |
|
|
| Ratchet deserialize | 0.7 µs | 2.7 µs | 17.8 µs | `from_bytes_with_min_epoch` |
|
|
|
|
**Direction-change paths** (one KEM round-trip per direction switch):
|
|
|
|
| Operation | Desktop | RPi 5 | VisionFive 2 | Notes |
|
|
|-----------|---------|-------|-------------|-------|
|
|
| Ratchet encrypt (direction change) | 182 µs | 651 µs | 2.46 ms | X-Wing keygen + encapsulate + KDF |
|
|
| Ratchet decrypt (direction change) | 127 µs | 473 µs | 1.76 ms | X-Wing decapsulate + KDF |
|
|
|
|
**Session establishment** (one-time per conversation):
|
|
|
|
| Operation | Desktop | RPi 5 | VisionFive 2 | Notes |
|
|
|-----------|---------|-------|-------------|-------|
|
|
| Identity keygen | 417 µs | 1.05 ms | 4.90 ms | X-Wing + Ed25519 + ML-DSA-65 |
|
|
| Hybrid sign | 988 µs | 2.85 ms | 10.6 ms | Ed25519 + ML-DSA-65 (no SIMD) |
|
|
| Hybrid verify | 254 µs | 794 µs | 3.31 ms | Ed25519 + ML-DSA-65 (no SIMD) |
|
|
| X-Wing encapsulate | 104 µs | 417 µs | 1.44 ms | X25519 + ML-KEM-768 |
|
|
| X-Wing decapsulate | 112 µs | 451 µs | 1.61 ms | X25519 + ML-KEM-768 |
|
|
| Initiate session (Alice) | 1.41 ms | 3.95 ms | 17.1 ms | Ephemeral keygen + 3 encaps + HKDF + sign |
|
|
| Receive session (Bob) | 585 µs | 1.88 ms | 7.70 ms | Verify + 3 decaps + HKDF |
|
|
|
|
**Streaming AEAD** (file/media encryption):
|
|
|
|
| Operation | Desktop | RPi 5 | VisionFive 2 | Notes |
|
|
|-----------|---------|-------|-------------|-------|
|
|
| Encrypt 1 MiB | 537 µs (1.95 GB/s) | 3.96 ms (265 MB/s) | 31.0 ms (33.8 MB/s) | XChaCha20-Poly1305, single chunk |
|
|
| Decrypt 1 MiB | 749 µs (1.40 GB/s) | 4.74 ms (221 MB/s) | 34.9 ms (30.0 MB/s) | Includes init overhead |
|
|
| Encrypt 4 MiB sequential | 2.58 ms (1.55 GB/s) | 15.8 ms (253 MB/s) | 124 ms (32.3 MB/s) | 4 chunks, sequential API |
|
|
| Encrypt 4 MiB parallel | 867 µs (4.61 GB/s) | 5.35 ms (748 MB/s) | 33.0 ms (121 MB/s) | 4 chunks via `encrypt_chunk_at`, rayon (4 cores) |
|
|
| Encrypt 8 MiB parallel | 1.29 ms (6.21 GB/s) | 10.7 ms (748 MB/s) | 67.8 ms (118 MB/s) | 8 chunks, 2 rounds on 4 cores |
|
|
|
|
**Password KDF** (key vault protection):
|
|
|
|
| Operation | Desktop | RPi 5 | VisionFive 2 | Notes |
|
|
|-----------|---------|-------|-------------|-------|
|
|
| Argon2id OWASP minimum | 10.9 ms | 42.0 ms | 256 ms | 19 MiB, 2 passes, 1 lane |
|
|
| Argon2id recommended | 65.0 ms | 227 ms | 1.35 s | 64 MiB, 3 passes, 4 lanes |
|
|
|
|
## License
|
|
|
|
[AGPL-3.0-only](LICENSE.md)
|