Relicario

# Relicario > **Audience:** users + evaluators. This doc owns the pitch, security-model summary, quick-start commands, reference-image explanation, recovery-QR overview, and roadmap teaser. Goes no deeper — for the system tour see [DESIGN.md](DESIGN.md), for crypto see [docs/CRYPTO.md](docs/CRYPTO.md). A git-backed, self-hostable password manager. The security thesis: two independent secrets feed the Argon2id KDF, the vault server holds only opaque ciphertext, and every mutation is a git commit — a built-in audit log. Compromise of either factor alone is insufficient to decrypt the vault. The **second factor** (something you have) is pluggable: at vault creation you choose a **reference image** (a JPEG that carries a 32-byte secret hidden in its DCT coefficients via steganography) or a **key file** (the same 32-byte secret in a plain armored text file). Either container enters the KDF identically; the choice is how you store and transport it. The server only ever sees opaque ciphertext. There is nothing else going on. This README is the security proof. ## How it works ``` Your passphrase (something you know) + Your second factor — reference image or key file (something you have) | v [ Argon2id KDF ] --> master_key --> [ XChaCha20-Poly1305 ] --> encrypted vault ^ | | v Never leaves Stored in git your device (opaque ciphertext) ``` At vault creation you choose a **second-factor container**. The default is a **reference image**: Relicario generates a random 32-byte secret and embeds it into a carrier JPEG using DCT steganography. The resulting photo lives on your devices — and optionally as a "dead drop" on social media, since the embedding survives JPEG re-encoding and mild cropping. The alternative is a **key file**: the same 32-byte secret written to a plain armored `.relkey` file, suited to users who prefer copying a file to a USB drive rather than distributing a photo. To unlock the vault, you provide your passphrase and your second-factor container (reference image or key file). The client extracts the 32-byte secret, concatenates it with your passphrase, and runs Argon2id to derive the master key. The KDF input is byte-for-byte identical regardless of which container you chose. Everything else follows from there. ## Security model ### What the server sees A git repository containing: - `manifest.enc` — opaque binary blob - `items/*.enc` — more opaque binary blobs - `attachments//*.enc` — encrypted attachment blobs - `settings.enc` — encrypted vault settings - `.relicario/salt` — a random 32-byte value (not secret) - `.relicario/params.json` — Argon2id parameters (not secret) - `.relicario/devices.json` — authorized device public keys That's it. No plaintext. No metadata about what's inside. No keys, no passphrases, no second-factor containers (no reference images, no key files). ### What an attacker needs | Scenario | Has | Needs | Result | |---|---|---|---| | Server breach | Encrypted vault + salt | Passphrase AND second-factor secret | 256+ bits of entropy. Infeasible. | | Server breach + stolen second factor | Vault + second-factor secret | Passphrase | Passphrase entropy through Argon2id. 4 diceware words = ~7 million years. | | Shoulder-surfed passphrase | Passphrase | Second-factor secret | 256 bits. Infeasible. | | Stolen device (image or key file + vault) | Second factor + vault | Passphrase | Argon2id brute-force. Strong passphrase = safe. | No single point of failure. The two-factor design means the passphrase alone can't decrypt the vault, and the image alone can't decrypt the vault. ### Compared to | | Server breach entropy | KDF factors | |---|---|---| | LastPass | ~40-60 bits (master password only) | 1 | | Bitwarden | ~40-60 bits (master password only) | 1 | | 1Password | password + 128-bit Secret Key | 2 | | **Relicario** | **password + 256-bit second-factor secret (image or key file)** | **2** | ### What we don't protect against - A compromised device with active malware. No software password manager can. - Weak passphrases with a stolen second factor (reference image or key file). Use 4+ diceware words. - Rubber-hose cryptanalysis. ## Quick start ```bash # Build from source cargo build --release # Create a vault with a reference image (default — stego second factor) relicario init --image vacation.jpg --output reference.jpg # Or use a plain key file as the second factor instead relicario init --key-file /path/to/secret.relkey # Add a credential relicario add # Retrieve it relicario get github # List everything relicario list # Sync with your git remote relicario sync # Pack the vault into a single encrypted backup file relicario backup export -o vault.relbak # Print a recovery QR for your second-factor secret (see "Recovery" below) relicario recovery-qr generate # Generate a random password relicario generate -l 32 ``` ### Environment variable Set `RELICARIO_IMAGE=/path/to/reference.jpg` to avoid being prompted for the reference-image path on every command. If you use a key file instead, set `RELICARIO_KEYFILE=/path/to/secret.relkey`. ## The second factor: reference image and key file Relicario's second factor is a 32-byte secret. The **container** for that secret is your choice at vault creation. **Reference image (default):** The JPEG carrier is generated during `relicario init`. It looks like a normal photo — because it is one. The 32-byte secret is embedded in the DCT coefficients of the luminance channel using Quantization Index Modulation, with heavy redundancy and Reed-Solomon-style majority voting across multiple copies. The steganographic embedding survives: - JPEG recompression (tested down to quality 85) - Up to ~10-15% cropping from any edge - Social media re-encoding (Instagram, Discord, etc.) This means your reference image can live on your Instagram, your personal website, or anywhere else — a "dead drop" approach. It is useless without your passphrase; the stego embedding is its transport mechanism. **Key file (alternative):** A plain armored `.relkey` file holding the same 32-byte secret in base64 — no steganography. Suited to users who prefer copying a small file to a USB drive, a hardware security module, or an offline storage medium. Wire format: see [docs/FORMATS.md](docs/FORMATS.md). ## Recovery: what if I lose my second factor? Without your second-factor container (reference image or key file), the vault is undecryptable — that's the security model. But it also makes a lost or corrupted container a single point of failure. The mitigation is the **recovery QR**: a printable QR code that wraps the 32-byte second-factor secret behind a separate recovery passphrase you choose. It applies regardless of which container you chose at vault creation. If you ever lose access to your reference image or key file, scan or transcribe the QR, provide the recovery passphrase, and recover the second-factor secret. Combined with your normal vault passphrase, this restores access to the vault. ```bash # Print a recovery QR (after the vault is unlocked). # You'll be prompted for a separate recovery passphrase. relicario recovery-qr generate # Recover the second-factor secret from a stored QR payload. relicario recovery-qr unwrap ``` The QR payload is an XChaCha20-Poly1305 envelope keyed by Argon2id over a domain-separated input (prefixed with `b"relicario-recovery-v1\0"`), so even if you reuse your vault passphrase as your recovery passphrase, the wrap key cannot collide with a vault master key. Both salt and nonce are freshly randomized per call, so two QRs printed from the same passphrase yield different bytes — the printed copy doesn't leak whether you've printed others. Recommended practice: print the QR, store it offline (safe, deposit box), and forget about it. The recovery passphrase is what protects the printed copy from being useful to someone who finds it. ## Architecture A short tour of the four codebases and how they fit together lives in [DESIGN.md](DESIGN.md). Crypto pipeline diagrams are in [docs/CRYPTO.md](docs/CRYPTO.md); the wire format reference is [docs/FORMATS.md](docs/FORMATS.md); the threat model is [docs/SECURITY.md](docs/SECURITY.md). `relicario-core` is the platform-agnostic bytes-in/bytes-out heart — no filesystem, no network. The CLI binary and the browser-extension WASM bridge both consume it. See per-codebase deep-dives in `crates/*/ARCHITECTURE.md` and `extension/ARCHITECTURE.md`. ### Crypto primitives | Primitive | Purpose | Why this one | |---|---|---| | Argon2id (64 MiB, 3 iter, 4 parallel) | Key derivation from passphrase + second-factor secret | Memory-hard, GPU-resistant, OWASP recommended | | XChaCha20-Poly1305 | Authenticated encryption of vault entries | 192-bit nonce (no collision risk), fast in WASM/ARM without AES-NI | | ed25519 | Device key signing | Per-device commit authorization, revocable without KDF rotation | ### Encrypted file format ``` version (1 byte) | nonce (24 bytes) | ciphertext (variable) | auth tag (16 bytes) ``` Every write generates a fresh random nonce. The version byte allows future format changes. ## Vault layout ``` my-vault.git/ ├── manifest.enc # Encrypted item index (names, URLs, timestamps) ├── settings.enc # Encrypted vault settings (retention, caps, generator defaults) ├── items/ │ ├── a1b2c3d4e5f6a7b8.enc # One encrypted item per file │ └── … ├── attachments/ │ └── / │ └── .enc # Content-addressed encrypted attachment blob └── .relicario/ ├── salt # 32-byte random salt (not secret) ├── params.json # KDF parameters ├── devices.json # Authorized device public keys └── revoked.json # Revoked device records (when device auth is enabled) ``` Item IDs are random 16-char hex strings (64 bits of entropy). Git history is preserved — every add/edit/delete is a commit. "When was this password last rotated?" is answered by `git log` and by the per-item field history. ## Device management Each device generates its own ed25519 keypair. The public key is stored in `.relicario/devices.json` (committed to the repo). Device keys are used for commit signing — they do NOT participate in vault decryption. Revoking a device: remove its key from `devices.json` and commit. No passphrase or second-factor rotation needed. ```bash relicario device add --name laptop relicario device list relicario device revoke laptop ``` ## Building Requires Rust stable (1.70+). ```bash git clone ssh://git@git.adlee.work:2222/alee/relicario.git cd relicario cargo build --release cargo test ``` The binary is at `target/release/relicario`. ## Roadmap - [x] WASM build + Chrome MV3 browser extension (inline crypto, no native messaging) - [x] Firefox WebExtension build - [x] Typed items: Login, SecureNote, Identity, Card, Key, Document, TOTP - [x] Secure document storage (encrypted file attachments) - [x] Backup & restore (`.relbak` encrypted envelope) - [x] Recovery QR (paper-printable second-factor secret backup with separate passphrase) - [x] LastPass CSV import - [x] Device authentication (ed25519 commit signing + pre-receive hook) - [ ] Import from Bitwarden / 1Password - [ ] `relicario unlock` daemon (ssh-agent-style, holds master key for a TTL) - [ ] Android/iOS clients (Rust core compiles to ARM) - [ ] Safari extension ## License GPL-3.0-or-later — see [LICENSE](LICENSE). --- Built by [Aaron D. Lee](https://adlee.work). Design spec and threat model in `docs/superpowers/specs/`. --- **Next:** [DESIGN.md](DESIGN.md) — the system tour.