docs: add architecture overview with flow diagrams
This commit is contained in:
254
docs/ARCHITECTURE.md
Normal file
254
docs/ARCHITECTURE.md
Normal file
@@ -0,0 +1,254 @@
|
||||
# idfoto — Architecture
|
||||
|
||||
## System Overview
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────────┐
|
||||
│ CLIENT DEVICE (trusted) │
|
||||
│ │
|
||||
│ ┌─────────────┐ ┌──────────────┐ ┌──────────────────────┐ │
|
||||
│ │ Reference │ │ Passphrase │ │ idfoto-cli │ │
|
||||
│ │ JPEG │ │ (typed) │ │ or browser ext │ │
|
||||
│ │ (on disk) │ │ │ │ │ │
|
||||
│ └──────┬───────┘ └──────┬───────┘ └──────────┬───────────┘ │
|
||||
│ │ │ │ │
|
||||
│ ▼ │ │ │
|
||||
│ ┌──────────────┐ │ │ │
|
||||
│ │ imgsecret │ │ │ │
|
||||
│ │ ::extract() │ │ │ │
|
||||
│ └──────┬───────┘ │ │ │
|
||||
│ │ │ │ │
|
||||
│ ▼ ▼ │ │
|
||||
│ ┌──────────────────────────────┐ │ │
|
||||
│ │ Argon2id KDF │ │ │
|
||||
│ │ password = passphrase ‖ │ │ │
|
||||
│ │ image_secret │ │ │
|
||||
│ │ salt = vault_salt │ │ │
|
||||
│ │ → master_key (32 bytes) │ │ │
|
||||
│ └──────────────┬───────────────┘ │ │
|
||||
│ │ │ │
|
||||
│ ▼ │ │
|
||||
│ ┌──────────────────────────────┐ │ │
|
||||
│ │ XChaCha20-Poly1305 │◄──────────────────┘ │
|
||||
│ │ encrypt / decrypt │ │
|
||||
│ │ (192-bit nonce, 256-bit │ │
|
||||
│ │ key, 128-bit auth tag) │ │
|
||||
│ └──────────────┬───────────────┘ │
|
||||
│ │ │
|
||||
└─────────────────┼──────────────────────────────────────────────────┘
|
||||
│
|
||||
│ git push / pull (HTTPS or SSH)
|
||||
▼
|
||||
┌──────────────────────────────────────────────────────────────────┐
|
||||
│ GIT SERVER (untrusted) │
|
||||
│ │
|
||||
│ idfoto-vault.git/ │
|
||||
│ ├── manifest.enc ← opaque ciphertext │
|
||||
│ ├── entries/ │
|
||||
│ │ ├── a1b2c3d4.enc ← opaque ciphertext │
|
||||
│ │ └── e5f6a7b8.enc ← opaque ciphertext │
|
||||
│ └── .idfoto/ │
|
||||
│ ├── salt ← 32 bytes (not secret) │
|
||||
│ ├── params.json ← KDF params (not secret) │
|
||||
│ └── devices.json ← device public keys (not secret) │
|
||||
│ │
|
||||
│ The server sees NOTHING useful. No keys, no plaintext, │
|
||||
│ no metadata about what's inside. │
|
||||
└──────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Vault Creation Flow
|
||||
|
||||
```
|
||||
User provides: System generates:
|
||||
├── carrier JPEG (any photo) ├── image_secret (256-bit random)
|
||||
└── passphrase (memorized) └── vault_salt (256-bit random)
|
||||
|
||||
┌──────────────────┐
|
||||
carrier JPEG ──────►│ imgsecret │──────► reference.jpg
|
||||
image_secret ──────►│ ::embed() │ (looks like a normal photo,
|
||||
│ DCT stego │ carries hidden secret)
|
||||
└──────────────────┘
|
||||
|
||||
┌──────────────────┐
|
||||
passphrase ────────►│ │
|
||||
│ Argon2id │──────► master_key (32 bytes)
|
||||
image_secret ──────►│ (64 MiB, 3 it) │ (held in memory only)
|
||||
vault_salt ────────►│ │
|
||||
└──────────────────┘
|
||||
|
||||
┌──────────────────┐
|
||||
master_key ────────►│ XChaCha20- │──────► manifest.enc
|
||||
empty manifest ────►│ Poly1305 │
|
||||
└──────────────────┘
|
||||
|
||||
┌──────────────────┐
|
||||
│ git init │──────► vault repo
|
||||
│ git commit │ (ready to push)
|
||||
└──────────────────┘
|
||||
```
|
||||
|
||||
## Unlock Flow (every vault operation)
|
||||
|
||||
```
|
||||
┌──────────────────┐
|
||||
reference.jpg ─────►│ imgsecret │──────► image_secret
|
||||
│ ::extract() │ (256 bits)
|
||||
└──────────────────┘
|
||||
|
||||
┌──────────────────┐
|
||||
passphrase ────────►│ │
|
||||
image_secret ──────►│ Argon2id │──────► master_key
|
||||
vault_salt ────────►│ │
|
||||
└──────────────────┘
|
||||
|
||||
┌──────────────────┐
|
||||
master_key ────────►│ XChaCha20 │──────► plaintext entries
|
||||
*.enc files ───────►│ ::decrypt() │ (in memory only)
|
||||
└──────────────────┘
|
||||
```
|
||||
|
||||
## imgsecret DCT Embedding
|
||||
|
||||
```
|
||||
Input JPEG
|
||||
│
|
||||
▼
|
||||
┌──────────────────┐
|
||||
│ Decode to RGB │
|
||||
│ Extract Y │
|
||||
│ (luminance) │
|
||||
└────────┬─────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────────────────────┐
|
||||
│ Full Image │
|
||||
│ ┌────────────────────────┐ │
|
||||
│ │ 15% ┌────────────┐ │ │
|
||||
│ │margin│ │15% │ │
|
||||
│ │ │ Central │mar-│ │
|
||||
│ │ │ 70% │gin │ │
|
||||
│ │ │ EMBEDDING │ │ │
|
||||
│ │ │ REGION │ │ │
|
||||
│ │ └────────────┘ │ │
|
||||
│ │ 15% margin │ │
|
||||
│ └────────────────────────┘ │
|
||||
└──────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────────┐
|
||||
│ Divide into │
|
||||
│ 8×8 blocks │
|
||||
│ Apply 2D DCT │
|
||||
└────────┬─────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────────┐
|
||||
│ For each │
|
||||
│ selected block: │
|
||||
│ │
|
||||
│ QIM embed bits │
|
||||
│ in positions │
|
||||
│ 4-15 (mid-freq) │
|
||||
│ │
|
||||
│ Repeat secret │
|
||||
│ 20+ times │
|
||||
└────────┬─────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────────┐
|
||||
│ Inverse DCT │
|
||||
│ Reconstruct RGB │
|
||||
│ Save JPEG (Q92) │
|
||||
└──────────────────┘
|
||||
│
|
||||
▼
|
||||
Output: reference.jpg
|
||||
(visually identical,
|
||||
carries 256-bit secret)
|
||||
```
|
||||
|
||||
## Extraction (with crop recovery)
|
||||
|
||||
```
|
||||
Input JPEG (possibly re-encoded or cropped)
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────┐
|
||||
│ Try canonical alignment │──── Success ──► image_secret
|
||||
│ (offset 0,0) │
|
||||
└─────────┬───────────────┘
|
||||
│ Fail
|
||||
▼
|
||||
┌─────────────────────────┐
|
||||
│ Search crop offsets │
|
||||
│ dx, dy: -15% to +15% │
|
||||
│ step: 8 pixels │
|
||||
│ (~16,800 candidates) │──── Success ──► image_secret
|
||||
│ │
|
||||
│ For each: extract bits, │
|
||||
│ majority vote, check │
|
||||
│ confidence ≥ 60% │
|
||||
└─────────┬───────────────┘
|
||||
│ All fail
|
||||
▼
|
||||
ExtractionFailed error
|
||||
```
|
||||
|
||||
## Encrypted File Format
|
||||
|
||||
```
|
||||
┌─────────┬────────────────────────┬──────────────────┬──────────────────┐
|
||||
│ version │ nonce │ ciphertext │ auth tag │
|
||||
│ 1 byte │ 24 bytes │ N bytes │ 16 bytes │
|
||||
│ 0x01 │ random per write │ XChaCha20 stream │ Poly1305 MAC │
|
||||
└─────────┴────────────────────────┴──────────────────┴──────────────────┘
|
||||
```
|
||||
|
||||
## Crate Architecture
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────────────────────────┐
|
||||
│ idfoto-cli │
|
||||
│ Filesystem, git (shelling out), terminal I/O, clipboard │
|
||||
│ │
|
||||
│ Depends on: idfoto-core, clap, anyhow, rpassword, arboard │
|
||||
└──────────────────────┬─────────────────────────────────────┘
|
||||
│ uses
|
||||
▼
|
||||
┌────────────────────────────────────────────────────────────┐
|
||||
│ idfoto-core │
|
||||
│ Platform-agnostic: bytes in, bytes out │
|
||||
│ No filesystem, no network, no git │
|
||||
│ │
|
||||
│ ┌──────────┐ ┌──────────┐ ┌─────────┐ ┌────────────┐ │
|
||||
│ │ crypto │ │ imgsecret│ │ entry │ │ vault │ │
|
||||
│ │ │ │ │ │ │ │ │ │
|
||||
│ │ KDF │ │ DCT │ │ Entry │ │ encrypt_ │ │
|
||||
│ │ encrypt │ │ embed │ │ Manifest│ │ entry() │ │
|
||||
│ │ decrypt │ │ extract │ │ search │ │ decrypt_ │ │
|
||||
│ │ │ │ QIM │ │ │ │ manifest() │ │
|
||||
│ └──────────┘ └──────────┘ └─────────┘ └────────────┘ │
|
||||
│ │
|
||||
│ Future: idfoto-wasm wraps this for browser extension │
|
||||
│ Future: JNI/Swift wrappers for Android/iOS │
|
||||
└────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Entropy at Each Attack Scenario
|
||||
|
||||
```
|
||||
Server breach only: ████████████████████████████ 256+ bits (infeasible)
|
||||
passphrase + image_secret
|
||||
|
||||
Server + stolen image: ████░░░░░░░░░░░░░░░░░░░░░░ ~51 bits (4 diceware words)
|
||||
passphrase through Argon2id ~7 million years
|
||||
|
||||
Shoulder-surfed passphrase: ████████████████████████████ 256 bits (infeasible)
|
||||
image_secret
|
||||
|
||||
Stolen device: ████░░░░░░░░░░░░░░░░░░░░░░ ~51 bits (4 diceware words)
|
||||
passphrase through Argon2id ~7 million years
|
||||
|
||||
Both factors compromised: game over (same as every password manager)
|
||||
```
|
||||
Reference in New Issue
Block a user