black-bag
black-bag is a zero-trace, no-compromise command-line vault for high-risk operators. It ships as a single Rust binary with the strongest defaults we can provide: Argon2id hardening, ML-KEM-1024 cascaded wrapping (Kyber), XChaCha20-Poly1305 payload encryption, zeroization of every secret buffer, and page-locked memory on Unix. There is no optional telemetry, no cloud, no GUI—just a laser-focused CLI that keeps secrets safe even under hostile conditions.
Install
Install the CLI from crates.io (recommended):
Upgrade to the latest release:
Note: The installed binary is named black-bag.
Alternatively, build from source (see “Building and testing” below).
Post-Quantum Signatures: ML-DSA-87 (at a glance)
ML-DSA-87 is the NIST-standardized module-lattice digital signature scheme (derived from CRYSTALS-Dilithium, highest-security parameter set). In black-bag it provides authenticity for offline vault backups.
- What it is: A post-quantum digital signature (Dilithium Level-5). Public key ≈ 2.6 KiB; signatures ≈ 4.6 KiB.
- What it protects: Ensures a
.cborbackup and its keyed integrity tag (.int) came from an authorized signer (no silent tampering). - How we use it:
- We compute a BLAKE3 keyed integrity tag
.intover the vault bytes, keyed by the embedded ML‑KEM public key (binds tag to the specific vault state). - We sign that tag with ML‑DSA‑87, producing
.int.sig. - Verification requires both
.intand.int.sigwith the corresponding ML‑DSA public key.
- We compute a BLAKE3 keyed integrity tag
- Commands (preferred PQ flow):
- Generate keys (one‑time):
black-bag backup keygen --pub-out backup.mldsa87.pub --sk-out backup.mldsa87.sk
- Sign a backup:
black-bag backup sign --path /path/to/vault.cbor --key backup.mldsa87.sk --pub-out backup.mldsa87.pub
- Verify before restore/use:
black-bag backup verify --path /path/to/vault.cbor --pub-key backup.mldsa87.pub
- Generate keys (one‑time):
- Best practices:
- Keep
backup.mldsa87.skoffline/air‑gapped. Distribute onlybackup.mldsa87.pub. - Always verify both integrity and signature;
.intalone only detects bit‑rot. - Treat
.cbor,.int, and.int.sigas a set; transport them together.
- Keep
Security Profile (summary)
- KDF: Argon2id (tunable; default 256 MiB, 3 passes, 1 lane)
- Key Encapsulation: ML‑KEM‑1024 (Kyber Level‑5)
- Payload AEAD: XChaCha20‑Poly1305 with domain‑separated AAD
- PQ Signatures: ML‑DSA‑87 (Dilithium Level‑5) for backup authenticity
- Memory Hygiene: zeroization everywhere +
mlockon Unix (passphrase/DEK/secret buffers) - Recovery: Shamir Secret Sharing via vetted
sharks(GF(2^8)) - Zero‑trace UX: no secrets in argv/stdout/logs/tmp; atomic writes with strict perms
Highlights
- Zero-trace posture – secrets never touch stdout, logs, temp files, or the clipboard. All input happens via hidden TTY prompts and is stored only after AEAD encryption.
- Modern crypto pipeline – Argon2id → ML-KEM-1024 (Kyber) → random 32-byte DEKs sealed with XChaCha20-Poly1305. Writes are atomic/fdatasync’d with strict permissions and zeroized in memory on drop.
- Rich record catalogue – logins, contacts, identity docs, secure notes, payment cards, SSH keys, PGP keys, TOTP seeds, recovery kits, bank accounts, Wi-Fi profiles, API credentials, and crypto wallets. Every record supports tagging and full-text queries.
- Cross-platform parity – builds cleanly on macOS, Linux, and Windows.
mlockis enabled automatically on supported Unix platforms and degrades gracefully elsewhere. - Security by default – all protective features are enabled in every binary; there are no configuration flags that weaken the threat posture.
Quick Start
# initialize a vault (recommended memory cost: 256 MiB)
# add and list API credentials (secrets collected via hidden prompts)
# inspect a record by UUID (safe default: masked; use --reveal on a TTY)
Quick start
# prerequisites: Rust toolchain 1.81+ (via rustup) and a standard build environment
Recommended hygiene:
- Run from an encrypted disk.
- Disable shell history or use
HISTCONTROL=ignorespacewith leading spaces. - Set
RUST_BACKTRACE=0in operational shells.
Creating your vault
You’ll be prompted for the master passphrase twice. The vault stores under the platform data directory (e.g., ~/.config/black_bag/vault.cbor).
Adding records
# login
# contact
# identity document
# secure note
# bank account
# Wi-Fi profile
# API credential
# crypto wallet
# totp secret
# totp codes
Sensitive fields (passwords, passphrases, API secrets, private keys) are collected via hidden prompts after the command issues—nothing sensitive ever appears in argv or shell history.
Listing, filtering, and querying
Inspect a specific record:
Rotation, health, and recovery
black-bag rotate– rewraps the master DEK with fresh randomness.black-bag doctor– prints health info (Argon2 params, feature flags, item counts).black-bag recovery split/combine– manage Shamir shares for catastrophic recovery.black-bag version– display binary version, target, and compiled features (mlock, pq, fuzzing, etc.).black-bag backup keygen/sign/verify– manage ML-DSA-87 (or legacy Ed25519) authenticity for offline backups.
CLI Command Reference
| Command | Purpose | Notes |
|---|---|---|
black-bag init [--mem-kib <KiB>] |
Create a new vault with Argon2id + ML-KEM-1024 wrapping | Default memory cost: 262 144 KiB (256 MiB) |
black-bag add <record-type> [...] |
Add login/contact/id/note/bank/wifi/api/wallet/totp/ssh/pgp/recovery records | Sensitive fields captured via hidden prompts |
black-bag list [--kind KIND] [--tag TAG] [--query Q] |
Show masked summaries | Combine filters for scoped audits |
black-bag get <UUID> [--reveal] |
Inspect records; optionally reveal secrets on a TTY | --reveal denied on non-interactive stdout |
black-bag totp code <UUID> [--time <UNIX>] |
Generate TOTP codes | Supports historic/time-skew analysis |
black-bag rotate [--mem-kib <KiB>] |
Rewrap master DEK with fresh randomness | Optional Argon2 memory override |
black-bag doctor [--json] |
Print health diagnostics | JSON output ready for monitoring pipelines |
black-bag recovery split/combine |
Manage Shamir shares for disaster recovery | Configurable thresholds and share counts |
black-bag backup keygen/sign/verify |
Manage ML-DSA-87 (or Ed25519) backup signatures | Produces .int + .int.sig companions |
black-bag selftest |
Run embedded round-trip checks | Useful after upgrades or migrations |
black-bag version |
Display build version, target, and enabled features (mlock, pq, fuzzing) |
Validate release binaries |
Backup workflow (PQ-safe)
# one-time: generate ML-DSA-87 keys for signing sidecars
# copy the ciphertext and immediately mint/update integrity + signature sidecars
# later, verify both bit-rot and authenticity before restore
The signing command produces two companions next to the ciphertext: vault-*.cbor.int (BLAKE3 tag keyed with the ML-KEM public key) and vault-*.cbor.int.sig (ML-DSA-87 or Ed25519 signature). Ship all three files together and treat any verification failure as tampering until proven otherwise.
Rotation, health, and recovery
black-bag rotate– rewraps the master DEK with fresh randomness.black-bag doctor– prints health info (Argon2 params, feature flags, item counts).black-bag recovery split/combine– manage Shamir shares for catastrophic recovery.black-bag selftest– quick sanity check of encryption/decryption paths.black-bag version– confirm binary target/profile and compiled features (mlock,pq, etc.).
Threat model (summary)
See docs/THREAT_MODEL.md for assumptions, adversary capabilities, and residual risks. Treat the vault ciphertext as sensitive and keep backups offline.
Building and testing
Build from source (alternative install)
Prereqs: Rust toolchain 1.81+ (via rustup) and a standard build environment.
CI should run the same three commands on every commit. Tests cover cryptographic round-trips, helper utilities, and zero-trace guarantees.
Memory Locking (mlock)
On Unix platforms, when built with the default mlock feature, black-bag locks sensitive memory (passphrases, DEKs, and secret buffers) using mlock(2) for the lifetime of those values and unlocks it on drop. This reduces the risk of secrets being paged to disk.
- Status check:
black-bag doctorreports whethermlockis enabled and functional on the host. - macOS:
mlockgenerally works for unprivileged processes within system limits. If you run into failures, check per-process limits withulimit -land consider adjusting them in your launch environment. - Linux:
mlockrequires sufficientRLIMIT_MEMLOCKorCAP_IPC_LOCK.- Temporary session limit (KB):
ulimit -l 65536(example: 64 MiB) - Or grant the binary capability (use with care):
sudo setcap cap_ipc_lock=+ep $(command -v black-bag)
- Temporary session limit (KB):
Notes:
- Locking is best‑effort; when the OS refuses the lock, black-bag continues to function but warns in
doctoroutput. You should raise limits untildoctorreportsmlock: enabled and working. - On non‑Unix targets, the
mlockfeature is ignored and no attempts are made to pin memory.
Doctor JSON fields
|
Emits:
ready(bool): overall health flagrecordCount(number): number of recordsargonMemKib,argonTimeCost,argonLanes: Argon2id paramscreatedAt,updatedAt: RFC3339 timestampsmlock.enabled(bool): build/OS supportmlock.ok(bool): lock/unlock probe succeededmlock.error(string|null): OS error if locking failed
Shamir Secret Sharing
The recovery split / combine commands now use the vetted sharks crate (GF(2^8)) under the hood, keeping the same CLI and share format (id-base64). See docs/SHAMIR.md for details and security notes.
Configuration
BLACK_BAG_VAULT_PATH: override the default vault path. Useful for testing, multiple vaults, or automation.- Recommended shell hygiene:
HISTCONTROL=ignorespaceand avoid pasting secrets into terminals; keepRUST_BACKTRACE=0in operational shells.
Troubleshooting
- “mlock: enabled but failed (…)” → Raise OS limits (macOS: check
ulimit -l; Linux:ulimit -l 65536or grantCAP_IPC_LOCK). - “Verify failed” on a backup → Stop; do not restore. Recompute
.intand re‑verify with the correct.int.sigand public key. - “record … not found” → Use
black-bag list --query <needle>to locate likely matches, then rerunget.
License
Dual‑licensed under Apache‑2.0 and MIT (your choice). See LICENSE-APACHE and LICENSE-MIT.
Mission-ready checklist
- Argon2id + ML-KEM-1024 + XChaCha20-Poly1305 enabled by default
- No GUI, clipboard, or plaintext log exposure
- Cross-platform parity (Windows/macOS/Linux)
- Comprehensive record catalogue with search & tagging
- Lint/tests clean with zero warnings
- Operator docs and threat model committed
For production roll-out, schedule an independent cryptography/code audit and set up fuzzing pipelines (see docs/FURTHER_HARDENING.md).