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 remain readable and are migrated in place to v7 on unlock.
# Install
cargo install --path .
# Initialize vault (stores in ~/.local/share/dota/vault.dota 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] -->|Argon2| B[Master Key]
B --> C["ML-KEM-768 KEM"]
B --> D["X25519 ECDH"]
C --> E["TC-HKEM Combiner"]
D --> E
B -->|"HMAC(mk, ct)"| E
E --> F["AES-256-GCM"]
F --> G[Encrypted Secrets]
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 (post-quantum KEM) shared secret
- X25519 (classical ECDH) shared secret
- Ciphertext binding (ct_kem and eph_pk included in HKDF input)
- Passphrase commitment (HMAC of master key over ciphertexts)
- HKDF-SHA256 with
v7domain-separated labels
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)
- Classical security: X25519 elliptic curve Diffie-Hellman
- Best-of-both-worlds: TC-HKEM ensures security if either algorithm holds
- Passphrase binding: Master key is committed into every per-secret key derivation
- Memory safety: Rust with zeroization of sensitive data
- Authenticated metadata:
version,min_version, algorithm ids, public keys, and suite are covered by thev7key commitment - Automatic migration: Legacy
v1-v6vaults upgrade tov7on unlock - Key rotation:
dota rotate-keysre-encrypts all secrets with new keypairs - Export to environment:
dota export-env VAR1 VAR2outputs shell-compatible format - TUI and CLI: Interactive ratatui interface or scriptable commands
- Trust boundary: Vault file must be protected at rest (use disk encryption)
- Passphrase strength: Argon2id with 64 MiB memory, 3 iterations, 4 threads (OWASP 2024 recommended parameters)
- No network: All operations are local; no cloud sync or remote key escrow
- Threat model: Protects against passive adversaries with quantum computers (harvest-now-decrypt-later). Does not protect against active quantum adversaries or compromised endpoints.
- Algorithm choices:
v7uses ML-KEM-768 for post-quantum resistance, X25519 for classical security, AES-256-GCM for authenticated encryption, HKDF-SHA256 for the TC-HKEM combiner, and HMAC-SHA256 for authenticated header commitment and passphrase binding. - Side channels: No explicit protection against timing or cache attacks (relies on constant-time implementations in dependencies).
Cryptographic details
- Passphrase → Argon2id (64 MiB, 3 iterations, 4 threads, 32-byte master key)
- Master key → Used to encrypt vault key material, authenticate the
v7header commitment, and bind the passphrase into per-secret key derivation via TC-HKEM - ML-KEM-768 and X25519 keypairs are generated randomly and stored encrypted in the vault (no deterministic derivation from the master key)
- Generate static ML-KEM-768 and X25519 recipient keypairs
- For each secret (TC-HKEM):
- ML-KEM 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 tag: tau = HMAC-SHA256(master_key, ct_kem || eph_pk)
- HKDF-SHA256(ss_kem || ss_dh || ct_kem || eph_pk || tau,
dota-v7-tchkem-salt,dota-v7-secret-key) → 32-byte AES key - AES-256-GCM(plaintext, aes_key, random_nonce) → ciphertext + tag
JSON structure with versioning (current: v7):
version: Protocol version for forward compatibilitykdf: Argon2id parameterskey_commitment: authenticated header commitment (v7 includes suite in commitment scope)kem: ML-KEM-768 public key and encrypted private keyx25519: X25519 public key, algorithm id, and encrypted private keysuite: active cipher-suite identifiersecrets: map of name → (algorithm,kem_ciphertext,x25519_ephemeral_public,ciphertext,nonce, timestamps)migrated_from: original version and migration path for upgraded vaultsmin_version: authenticated anti-rollback floor
dota init Initialize new vault
dota unlock Launch TUI (default command)
dota set <NAME> <VALUE> Store a secret
dota get <NAME> Retrieve a secret
dota list List all secret names
dota rm <NAME> Remove a secret
dota export-env [NAMES...] Export secrets as shell variables
dota change-passphrase Update vault passphrase
dota rotate-keys Re-encrypt all secrets with new keypairs
dota info Show vault metadata
TUI shortcuts
j/kor↑/↓: Navigate secrets listEnter: Copy secret to clipboardn: Create new secret (prompts for name and value)e: Edit selected secretd: Delete selected secret (requires confirmation)r: Rotate all encryption keysq: Quit
- "Failed to decrypt vault": Incorrect passphrase or corrupted vault file. Check
~/.local/share/dota/vault.dota. - Slow unlock: Argon2id intentionally uses 64 MiB RAM and 3 iterations with 4 threads. Adjust parameters in vault metadata only if you understand the security tradeoffs.
# Run tests
cargo test
# Run with debug logging
RUST_LOG=debug cargo run
# 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}
}