docs: re-lead positioning on the two-factor-KDF thesis; document the key-file second factor

README: open with the two-independent-secrets-into-KDF thesis; reframe the
second factor as pluggable (reference image or key file); keep the stego
deep-dive as a distinctive option; update all scenario tables, recovery section,
env-var hint, and roadmap line to use "second-factor secret" language.

docs/CRYPTO.md: add "Pluggable second-factor containers" subsection explaining
that the 32-byte input to Argon2id is identical regardless of container;
reference-image / key-file / recovery-QR are interchangeable carriers.
Update org-vs-personal comparison table and entropy labels.

docs/FORMATS.md: document the .relkey armor format (relicario-keyfile-v1 +
base64 line) and the params.json second_factor field ("image"|"keyfile",
absent means "image" for back-compat). Cite crates/relicario-core/src/keyfile.rs
keyfile_encode/keyfile_decode with TODO-pin comments. Update KDF input formula
from image_secret to second_factor_secret.

DESIGN.md: split secrets-map "Reference image bytes" row to distinguish image
and key-file containers; add key-file row noting keyfileBase64 in
chrome.storage.local has the same in-the-clear posture as imageBase64.
Update vault-layout note and KDF conventions row.

docs/SECURITY.md: add "Second-factor storage posture" subsection explicitly
stating .relkey and keyfileBase64 are the second factor in the clear — same
posture as the reference JPEG / imageBase64, not weaker, protected by the
passphrase invariant. Add RELICARIO_KEYFILE to the env-var trust surface table.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014THSV6cA4Gxa7bxFfisHBB
This commit is contained in:
adlee-was-taken
2026-06-25 21:24:06 -04:00
parent a25f8e8a30
commit 1591fc7825
5 changed files with 100 additions and 39 deletions

View File

@@ -6,11 +6,24 @@
Relicario uses two-factor vault decryption:
1. **Passphrase** — user-memorized, zxcvbn score ≥3 required
2. **Reference image** — JPEG carrying 256-bit secret via DCT steganography
2. **Second factor** — a 32-byte secret carried by a pluggable container chosen at vault creation:
- **Reference image** (default): a JPEG with the secret embedded in DCT coefficients via QIM steganography
- **Key file**: the same 32-byte secret in a plain `.relkey` armored file
The second-factor input to the KDF is always 32 bytes regardless of container. The Argon2id input and master-key derivation are identical for both container types.
Key derivation: Argon2id (64 MiB memory, 3 iterations, 4 parallelism)
Encryption: XChaCha20-Poly1305 (192-bit nonce, 256-bit key)
### Second-factor storage posture
Both containers hold the second-factor secret **in the clear** — they are the "something you have", not an encrypted artifact. This is the expected and correct posture:
- **Reference image** (`reference.jpg`): the 32-byte secret is embedded in the JPEG DCT coefficients. The file itself is not encrypted. In the browser extension the image bytes are stored as `imageBase64` in `chrome.storage.local` — also in the clear.
- **Key file** (`.relkey`): the 32-byte secret is base64-encoded in a plain text file. The file itself is not encrypted. In the browser extension the key-file bytes are stored as `keyfileBase64` in `chrome.storage.local` — also in the clear.
The key file is **not weaker** than the reference image. Both containers are protected by the same invariant: the passphrase is also required, and the server holds only opaque ciphertext sealed under a key derived from both. A stolen second factor without the passphrase yields nothing actionable (256 bits of Argon2id to brute-force). A stolen passphrase without the second factor is equally unactionable.
## Manifest Integrity
The manifest (`manifest.enc`) is encrypted with AEAD, which provides:
@@ -212,7 +225,8 @@ reviewers to audit the surface in one place.
| Variable | Purpose | Trust |
|---|---|---|
| `RELICARIO_IMAGE` | Override the reference-image JPEG path used during vault unlock. | Trusted: filesystem path under the user's control. Read-only; its bytes feed `imgsecret::extract_secret`. |
| `RELICARIO_IMAGE` | Override the reference-image JPEG path used during vault unlock (for image-path vaults). | Trusted: filesystem path under the user's control. Read-only; its bytes feed `imgsecret::extract_secret`. |
| `RELICARIO_KEYFILE` | Override the `.relkey` key-file path used during vault unlock (for key-file-path vaults). | Trusted: filesystem path under the user's control. Read-only; its bytes feed `keyfile_decode()`. The file holds the second-factor secret in the clear — same trust surface as `RELICARIO_IMAGE`. |
| `RELICARIO_GITEA_URL` | Gitea API base URL for `relicario device add`. Equivalent to `--gitea-url`. | Trusted: HTTPS URL. Used only in the device-add code path. |
| `RELICARIO_GITEA_TOKEN` | Gitea personal-access token. Equivalent to `--gitea-token`. | **Secret**: anyone who can read this env var can manage the user's deploy keys via the Gitea API. The CLI never logs it. |
| `RELICARIO_GITEA_OWNER` | Gitea repository owner (e.g. `alee`). Equivalent to `--owner`. | Trusted: opaque string. |