Documentation (14 pages)
Security Model
Tallow is built with a security-maximalist approach. Every layer is designed to protect your data, even against future quantum computers.
Threat Model
Assets Protected
| Asset | Protection |
|---|---|
| File content (plaintext) | AES-256-GCM encryption, never leaves your device unencrypted |
| Key material | Zeroized on drop, pinned in RAM via mlock, never swapped to disk |
| Metadata (filenames, sizes) | Encrypted in transit, EXIF stripped |
| Identity (who communicates) | Optional Tor anonymity layer (SOCKS5 proxy) |
Trust Boundaries
- Client to Relay: The relay is fully untrusted. It only sees ciphertext and room codes. Even a compromised relay cannot read your files.
- Client to Tor: Tor provides IP anonymity only. Tallow’s encryption is independent of Tor.
- Client to Client: Authenticated via PAKE (password-authenticated key exchange) and safety numbers.
Adversary Profiles
| Adversary | Capability | Mitigation |
|---|---|---|
| Shared WiFi attacker | Passive packet capture | E2E encryption, TLS on relay connection |
| Compromised relay | Full server control | Zero-knowledge design, E2E encryption |
| Nation-state (passive) | Backbone taps, metadata collection | Tor integration, traffic padding |
| Nation-state (active) | DNS/BGP hijack, quantum computers | Post-quantum KEM, DoH, certificate pinning |
Cryptographic Algorithms
Key Exchange: Hybrid ML-KEM-1024 + X25519
Tallow uses a hybrid key encapsulation mechanism combining:
- ML-KEM-1024 (FIPS 203) — Post-quantum lattice-based KEM at Security Level 5, protecting against future quantum computers with a 15-year threat horizon
- X25519 (RFC 7748) — Classical elliptic-curve Diffie-Hellman, well-studied with constant-time implementations
Both algorithms must be broken to compromise the key exchange. This provides security against both classical and quantum adversaries.
Symmetric Encryption: AES-256-GCM
- 256-bit keys derived via HKDF-SHA256
- Counter-based 96-bit nonces (guaranteed unique, no birthday bound)
- Additional authenticated data (AAD) binds chunk index to prevent reordering
- Hardware-accelerated via AES-NI on supported platforms
Hashing: BLAKE3
- Primary hash function for integrity verification
- Merkle tree construction for file integrity — verified on the CLI transfer lane (receiver rebuilds the tree and compares roots); the browser sender does not send a Merkle root today, so the web lane rests on the per-chunk AES-GCM tags plus the transport
- Faster than SHA-256, parallelizable
- SHA3-256 used only where NIST compliance is specifically required
Password Hashing: Argon2id
- Memory-hard function resistant to GPU/ASIC attacks
- Parameters (per profile): Interactive — 3 iterations, 256 MiB, 4 lanes; Room (room-code derivation, wasm-safe) — 3 iterations, 64 MiB, 1 lane
- Used for deriving encryption keys from passwords and room codes
Signatures: Ed25519 + ML-DSA-87
- Hybrid signature scheme for identity verification
- Ed25519 for classical security
- ML-DSA-87 for post-quantum security
- Used for key rotation records and identity authentication
Transport & Certificates
- TLS 1.2/1.3 only, AEAD cipher suites, forward secrecy; the TLS 1.3 handshake negotiates the X25519MLKEM768 post-quantum hybrid
- Certificates are Let’s Encrypt (Caddy-managed ECDSA P-256, CT-logged); certificate transparency is monitored daily
- No OCSP stapling is served (Let’s Encrypt has deprecated OCSP); revocation relies on short-lived certificates plus CT monitoring
Memory Safety
- Rust — No buffer overflows, use-after-free, or data races
- Zeroize — All key material is securely wiped on drop using the
zeroizecrate - mlock — Secrets are pinned in RAM and never swapped to disk
- Core dumps disabled —
prctl(PR_SET_DUMPABLE, 0)on Linux - Constant-time operations — All secret-dependent comparisons use the
subtlecrate - No unsafe —
#![forbid(unsafe_code)]in all crates except where explicitly required with documented SAFETY justifications
Relay Zero-Knowledge Design
The relay server is designed to know as little as possible:
- It receives a room code (a one-way, memory-hard Argon2id derivation of the passcode) — not the passcode itself
- It forwards encrypted bytes between participants — it cannot decrypt them
- It stores nothing to disk — all data is in-memory and ephemeral
- Rooms automatically expire after a configurable timeout (default 10 minutes)
- Rate limiting prevents abuse without requiring identity
Even if you self-host the relay and it is later compromised, historical transfers cannot be decrypted.
Optional: Tor / Onion Access
Tallow’s site and relay are also reachable as a Tor v3 onion service, for users who want to route around IP-level observation entirely:
- Onion address:
xmizmujadxbapmh5lhidxjux3v4e4efm45fgpqfwlgd5tcdv2zwrktad.onion - The onion serves the app and the relay from a single origin — the page routes its own relay traffic, so nothing leaves the tor circuit.
- Pages loaded from the onion automatically enter tor-mode: no WebRTC (no IP-bearing ICE candidates), no WebTransport, and scaled timeouts for circuit latency.
- Nothing is required. The clearnet site and relay are fully functional without Tor; the onion is strictly an opt-in alternative for users who choose it.
STRIDE Analysis
| Category | Threat | Mitigation |
|---|---|---|
| Spoofing | Impersonation | PAKE authentication, safety numbers |
| Tampering | Data modification | Per-chunk AEAD authentication tags (End-to-end lane) and transport protection for the rest; whole-file Merkle verification on the CLI lane |
| Repudiation | Denial of action | Not a design goal (privacy tool) |
| Information Disclosure | Data leakage | E2E encryption, zeroization, secure memory |
| Denial of Service | Resource exhaustion | Rate limiting, resource caps |
| Elevation of Privilege | Sandbox escape | OS sandbox (Landlock + Seccomp on Linux) |
Operational hardening evidence
The guarantees above name concrete operating-system controls. This section carries the artefacts behind them, so each claim has a published source or an explicit statement of where the artefact lives (audit rows A09-24…A09-30).
Core dumps disabled (row A09-25) — kernel and container layer:
kernel.core_pattern = |/bin/falsefs.suid_dumpable = 0kernel.kptr_restrict = 2# prod relay image — no core dumps, no ptrace into the processRUN echo "ulimit -c 0" >> /etc/profile.d/no-core.shThe relay additionally calls prctl(PR_SET_DUMPABLE, 0) at startup, so even a
root-invoked gcore on the running process produces nothing.
Key material never reaches swap (row A09-26):
# /etc/sysctl.d/61-tallow-swap.conf (paired with mlock in the relay)vm.swappiness = 0Secrets are pinned with mlock/mprotect in-process (see Memory Safety
above). Where a host cannot disable swap, the swap device must be encrypted —
documented as the deployment precondition rather than assumed away.
No crash reporters (row A09-27): “We run no crash reporters. Period.”
Neither the relay nor the web application links a Sentry/Bugsnag-class SDK, and
no crash path uploads memory context. This is a statement of fact about the
build, verifiable by inspecting the dependency tree (cargo tree, pnpm ls).
Structured, allowlist-only logs (row A09-28): the log schema is exactly the
field set published on Transparency —
timestamp, method, path, status, latency, bytes. The LogSafe proc-macro
that makes unsafe fields a compile error lives in the relay repository;
publishing its source is pending the source-repository release (D-03 /
crypto register), and the field allowlist is the part that is published today.
IP stripping at the edge (row A09-29): the Caddy access log deletes the
client-IP fields at the encoder, and the app never reads X-Forwarded-For.
The integration test that asserts no X-Forwarded-For header reaches the
application is a relay-repository artefact — not published here, because it
lives outside the website mandate (D-20); the invariant itself is what the
no addresses are logged row of the transparency table asserts.
Rate limiting by proof-of-work (row A09-30): the challenge spec and the target relay configuration are published at Rate Limits (Proof-of-Work); the relay-side migration off IP keying is tracked in the crypto register (pending, not silently assumed).