# Relicario — Crypto Pipeline

> **Audience:** anyone evaluating or auditing the crypto. This doc owns Argon2id parameters and rationale, XChaCha20-Poly1305 rationale, vault creation/unlock flow diagrams, DCT-steganography embed and extract flows, and the high-level encrypted-file-format diagram. **Does NOT own:** byte-level schemas or JSON shapes (see [FORMATS.md](FORMATS.md)), attacker scenarios (see [SECURITY.md](SECURITY.md)), or per-module crypto implementation (see [../crates/relicario-core/ARCHITECTURE.md](../crates/relicario-core/ARCHITECTURE.md)).

## System Overview

```
┌──────────────────────────────────────────────────────────────────┐
│                        CLIENT DEVICE (trusted)                    │
│                                                                   │
│  ┌─────────────┐    ┌──────────────┐    ┌──────────────────────┐ │
│  │  Reference   │    │  Passphrase  │    │   relicario-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)                        │
│                                                                   │
│  relicario-vault.git/                                             │
│  ├── manifest.enc        ← opaque ciphertext                      │
│  ├── settings.enc        ← opaque ciphertext                      │
│  ├── items/                                                       │
│  │   ├── a1b2c3d4e5f6a7b8.enc   ← opaque ciphertext               │
│  │   └── …                                                        │
│  ├── attachments/                                                 │
│  │   └── <item-id>/<aid>.enc    ← opaque ciphertext               │
│  └── .relicario/                                                  │
│      ├── salt            ← 32 bytes (not secret)                  │
│      ├── params.json     ← KDF params (not secret)                │
│      ├── devices.json    ← device public keys (not secret)        │
│      └── revoked.json    ← revoked device records (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        │        settings.enc
default settings ──►│  encrypt (×2)    │        (parallel artifacts;
                    └──────────────────┘         independent nonces)

                    ┌──────────────────┐
                    │  git init        │──────► vault repo
                    │  git commit      │        (ready to push)
                    └──────────────────┘
```

Item creation, the typed-item envelope (`Item` + per-type `ItemCore`),
attachment encryption, and field-history tracking are not shown above —
they are described in [`crates/relicario-core/ARCHITECTURE.md`](../crates/relicario-core/ARCHITECTURE.md).
The flow above covers only the crypto-pipeline shape that vault init
establishes; the per-item lifecycle reuses the same `master_key` +
XChaCha20-Poly1305 primitives against `items/<id>.enc` and
`attachments/<item-id>/<aid>.enc`.

## 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 zig-zag      │
                    │  positions 6-17  │
                    │  (mid-frequency) │
                    │                  │
                    │  Repeat secret   │
                    │  MIN_COPIES (5)  │
                    │  to 50 times,    │
                    │  by capacity     │
                    └────────┬─────────┘
                             │
                             ▼
                    ┌──────────────────┐
                    │  Inverse DCT     │
                    │  Reconstruct RGB │
                    │  Save JPEG (Q92) │
                    └──────────────────┘
                             │
                             ▼
                    Output: reference.jpg
                    (visually identical,
                     carries 256-bit secret)
```

The redundancy count is chosen at embed time based on available DCT capacity: `num_copies = (total_blocks / BLOCKS_PER_COPY).min(50)`, with `BLOCKS_PER_COPY = 22` and a floor of `MIN_COPIES = 5` (`crates/relicario-core/src/imgsecret.rs:78,530-537`). Images that cannot fit at least 5 copies are rejected before embed. Majority voting across these copies at extract time requires ≥ 60 % confidence per bit.

## 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      │
│  0x02   │ random per write       │ XChaCha20 stream  │ Poly1305 MAC     │
└─────────┴────────────────────────┴──────────────────┴──────────────────┘
```

`VERSION_BYTE = 0x02` (`crates/relicario-core/src/crypto.rs:59`). Blobs starting with any other byte are rejected with `UnsupportedFormatVersion { found, expected: 0x02 }`. The legacy `0x01` format from the pre-typed-items era is no longer supported.

## Crate Architecture

```
┌────────────────────────────────────────────────────────────┐
│                    relicario-cli                               │
│  Filesystem, git (shelling out), terminal I/O, clipboard    │
│                                                             │
│  Depends on: relicario-core, clap, anyhow, rpassword, arboard │
└──────────────────────┬─────────────────────────────────────┘
                       │ uses
                       ▼
┌────────────────────────────────────────────────────────────┐
│                    relicario-core                           │
│  Platform-agnostic: bytes in, bytes out                     │
│  No filesystem, no network, no git                          │
│                                                             │
│  ┌──────────┐  ┌──────────┐  ┌─────────┐  ┌────────────┐   │
│  │ crypto   │  │ imgsecret│  │ item +  │  │ vault      │   │
│  │          │  │          │  │ types   │  │            │   │
│  │ KDF      │  │ DCT      │  │ Item    │  │ encrypt_   │   │
│  │ encrypt  │  │ embed    │  │ Manifest│  │ item()     │   │
│  │ decrypt  │  │ extract  │  │ Settings│  │ decrypt_   │   │
│  │          │  │ QIM      │  │ Backup  │  │ manifest() │   │
│  │          │  │          │  │ Device  │  │ ...        │   │
│  └──────────┘  └──────────┘  └─────────┘  └────────────┘   │
│                                                             │
│  Consumed by: relicario-cli, relicario-wasm (extension),    │
│               relicario-server (pre-receive hook).          │
│  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)
```

---

**Next:** [FORMATS.md](FORMATS.md) — the byte-level wire formats.
