Post-quantum secure secrets manager with v7 TC-HKEM vaults (ML-KEM-768 + X25519 with ciphertext binding and passphrase commitment), plus a terminal UI.
Defense-in-depth cryptography: v7 vaults protect secrets with both classical security (X25519) and post-quantum security (ML-KEM-768), combined via the TC-HKEM (Triple-Committed Hybrid KEM) construction. Security holds if either algorithm is secure. Legacy v1–v6 vaults are migrated in place to v7 on unlock.
# Install
cargo install --path .
# Initialize vault (stored at ~/.dota/vault.json by default)
dota init
# Launch TUI (default command)
dota
# Or use CLI commands
dota set API_KEY "secret-value"
dota get API_KEY
dota listflowchart LR
A[Passphrase] -->|Argon2id| B[Master Key mk]
B --> C["ML-KEM-768\nencapsulate → ss_kem, ct_kem"]
B --> D["X25519 ephemeral DH\n→ ss_dh, eph_pk"]
B -->|"τ = HMAC(mk, ct_kem ‖ eph_pk)"| E
C --> E["TC-HKEM combiner\nHKDF(ss_kem‖ss_dh‖ct_kem‖eph_pk‖τ)"]
D --> E
E --> F["AES-256-GCM"]
F --> G[Encrypted Secret]
TC-HKEM: Each secret is encrypted with AES-256-GCM. The per-secret AES key is derived via the Triple-Committed Hybrid KEM combiner:
- ML-KEM-768 encapsulation → 32-byte shared secret (
ss_kem) + ciphertext (ct_kem) - X25519 ephemeral DH → 32-byte shared secret (
ss_dh) + ephemeral public key (eph_pk) - Passphrase commitment: τ =
HMAC-SHA256(mk, ct_kem ‖ eph_pk) - Ciphertext binding:
ct_kemandeph_pkare included directly in the HKDF input HKDF-SHA256(ss_kem ‖ ss_dh ‖ ct_kem ‖ eph_pk ‖ τ, "dota-v7-tchkem-salt", "dota-v7-secret-key")→ 32-byte AES key
The vault stores ML-KEM ciphertexts, X25519 ephemeral public keys, and AES-GCM ciphertexts. A canonical authenticated header is protected by HMAC-SHA256 under the passphrase-derived master key before any private-key decryption occurs. The v7 TC-HKEM combiner achieves best-of-both-worlds IND-CCA security and binds the passphrase into every per-secret key derivation.
- Post-quantum security
- Real ML-KEM-768 (NIST FIPS 203 final standard) — resists quantum computer attacks.
- Classical security
- X25519 elliptic curve Diffie-Hellman — protects against classical adversaries.
- Best-of-both-worlds IND-CCA
- TC-HKEM ciphertext binding ensures security if either algorithm holds (GHP18 reduction).
- Passphrase commitment
- Master key
mkis bound into every per-secret key derivation via τ = HMAC(mk,ct_kem ‖ eph_pk). Knowledge of the KEM private keys alone is insufficient. - Memory safety
- Rust with
ZeroizeOnDropfor all sensitive types — passphrases, shared secrets, and AES keys are wiped from memory on drop. - Authenticated metadata
version,min_version, algorithm IDs, public keys, andsuiteare covered by thev7HMAC-SHA256 key commitment before any private-key decryption.- Automatic migration
- Legacy
v1–v6vaults upgrade tov7on unlock; originals are backed up automatically. - Key rotation
- dota rotate-keys generates fresh ML-KEM-768 and X25519 keypairs and re-encrypts all secrets.
- Export to environment
- dota export-env VAR1 VAR2 outputs shell-compatible variable assignments for CI/CD pipelines.
- TUI and CLI
- Interactive ratatui terminal interface or scriptable command-line operations.
- Trust boundary
- The vault file must be protected at rest. Use full-disk encryption (LUKS, FileVault, BitLocker) or store on an encrypted volume.
- Passphrase strength
- Argon2id with 64 MiB memory, 3 iterations, 4 threads (OWASP 2024 recommended parameters). Adjust only if you understand the security tradeoffs.
- No network
- All operations are fully local. There is no cloud sync, remote key escrow, or telemetry.
- Threat model
- Protects against passive adversaries with quantum computers (harvest-now-decrypt-later attacks). Does not protect against active quantum adversaries or compromised endpoints.
- Algorithm choices
v7uses ML-KEM-768 (post-quantum), X25519 (classical), AES-256-GCM (authenticated encryption), HKDF-SHA256 (TC-HKEM combiner), and HMAC-SHA256 (header commitment + passphrase binding).- Side channels
- No explicit protection against timing or cache attacks beyond what the underlying cryptography libraries provide.
Cryptographic details
- Passphrase → Argon2id (64 MiB, 3 iterations, 4 threads, 32-byte master key
mk) mk→ purpose-labeled wrapping keys for encrypting the ML-KEM-768 and X25519 private keys, HMAC-SHA256 header commitment, and TC-HKEM passphrase commitment τ- ML-KEM-768 and X25519 keypairs are generated from
OsRngand stored encrypted in the vault (no deterministic derivation frommk)
1. ML-KEM-768 encapsulate(pk_kem) → (ss_kem, ct_kem)
2. X25519 ephemeral DH(pk_x25519) → (ss_dh, eph_pk)
3. τ = HMAC-SHA256(mk, ct_kem ‖ eph_pk)
4. IKM = ss_kem ‖ ss_dh ‖ ct_kem ‖ eph_pk ‖ τ (≈ 1216 bytes for ML-KEM-768)
5. aes_key = HKDF-SHA256(IKM, "dota-v7-tchkem-salt", "dota-v7-secret-key")
6. (ciphertext, tag) = AES-256-GCM(plaintext, aes_key, random_nonce)
Security properties:
- Theorem 1 — Best-of-both-worlds IND-CCA:
Adv ≤ Adv_ML-KEM^{ind-cca}(B₁) + Adv_X25519^{gap-cdh}(B₂) + q_H/2^256. Ciphertext binding enables the B₁ reduction. - Theorem 2 — Passphrase binding:
Adv^{mk-bind} ≤ Adv_HMAC^{prf}(B₃) + q_H/2^256. Knowledge of(dk, sk_dh)alone is insufficient withoutmk.
JSON structure with versioning (current: v7, suite: dota-v7-tchkem-mlkem768-x25519-aes256gcm):
version- Protocol version for forward compatibility. Current:
7. min_version- Anti-rollback floor — vault is rejected by implementations older than this version.
kdf- Argon2id parameters: algorithm, salt, time_cost, memory_cost, parallelism.
key_commitment- HMAC-SHA256 over the canonical header (version, min_version, KDF params, algorithm IDs, public keys, suite). Verified before any private-key decryption.
kem- ML-KEM-768 public key and AES-256-GCM-wrapped private key.
x25519- X25519 public key, algorithm label, and AES-256-GCM-wrapped private key.
suite- Active cipher-suite identifier:
dota-v7-tchkem-mlkem768-x25519-aes256gcm. secrets- Map of name →
{algorithm, kem_ciphertext, x25519_ephemeral_public, ciphertext, nonce, created, modified}. migrated_from- Original version and migration path for vaults upgraded from older formats.
- dota init
- Initialize a new vault at
~/.dota/vault.json(or--vault PATH). - dota / dota unlock
- Launch the interactive TUI (default command when no subcommand is given).
- dota set NAME VALUE
- Store or update a secret. Omit VALUE to read from stdin or an interactive prompt.
- dota get NAME
- Print a secret value to stdout.
- dota list
- List all secret names (values are never printed).
- dota rm NAME
- Permanently remove a secret.
- dota export-env [NAMES…]
- Print
export KEY=VALUElines for the named secrets (or all secrets if no names given). Safe toevalin shell scripts. - dota change-passphrase
- Re-derive the master key and re-wrap all private key material under a new passphrase.
- dota rotate-keys
- Generate fresh ML-KEM-768 and X25519 keypairs and re-encrypt all secrets.
- dota upgrade
- Explicitly migrate the vault to the current format version (
v7). Migration also happens automatically on any unlock. - dota info
- Show vault metadata: version, suite, KDF parameters, key commitment status, and secret count.
All commands accept --vault PATH to override the default vault location.
TUI keyboard shortcuts
- j / k or ↑ / ↓
- Navigate the secrets list.
- Enter
- Copy the selected secret value to the clipboard.
- n
- Create a new secret (prompts for name and value).
- e
- Edit the selected secret’s value.
- d
- Delete the selected secret (requires confirmation).
- r
- Rotate all encryption keys.
- q
- Quit.
- Failed to decrypt vault: Incorrect passphrase or corrupted vault file. Check
~/.dota/vault.json. - Slow unlock: Argon2id intentionally uses 64 MiB RAM and 3 iterations with 4 threads. This takes roughly 1–3 seconds on modern hardware and is by design.
# Run all tests
cargo test
# Run with debug logging
RUST_LOG=debug cargo run
# Check formatting and lints
cargo fmt --check && cargo clippy
# Build optimized release binary
cargo build --releaseMIT
If you use this in research or security audits:
@software{dota2026,
author = {Fitch, Zack},
title = {Defense of the Artifacts: Post-quantum secure secrets manager},
year = {2026},
url = {https://github.com/johnzfitch/dota}
}