black-bagg 0.2.5

Ultra-secure, zero-trace, pure-Rust CLI password manager for spies, journalists, and privacy maximalists.
Documentation

black-bag

black-bag is a zero‑trace, ultra‑secure, CLI‑only password manager for high‑risk operators. It ships as a single Rust binary with strict defaults (Argon2id time=10; lanes≥4), ML‑KEM‑1024 cascaded wrapping, XChaCha20‑Poly1305 encryption, and meticulous I/O hygiene (no stdout secrets by default, no temp files, atomic 0600 writes). Cross‑platform (macOS/Linux/Windows).

Core ideas:

  • Secrets never go to stdout by default; reveals are TTY‑only unless you consciously opt into unsafe modes.
  • Strong crypto: Argon2id → ML‑KEM‑1024 → 32‑byte DEK → XChaCha20‑Poly1305 for payload and per‑record secret fields.
  • Strict limits and locking: page‑locking (best effort), input caps, constant‑time ops, sanitized errors.

Contents

  • Install and Build
  • Quick Start
  • Operations Guide (day‑to‑day flows)
  • CLI Reference (every command + flags, with examples)
  • Security Model (short)
  • Technology Deep Dive (all primitives, flows, AADs, sidecars)
  • Vault Format (types, blobs, limits)
  • Configuration (env/feature flags)
  • Troubleshooting

Install and Build

# From crates.io (recommended)
cargo install --locked black-bagg --features pq

# Local build
cargo build --release
install -m 0755 target/release/black-bag ~/.local/bin/black-bag

black-bag --version

Tips:

  • Use an encrypted disk; avoid shell history (e.g., HISTCONTROL=ignorespace).
  • For mission shells, set BLACK_BAG_HARD_MODE=1 to force TTY‑only and disable unsafe stdout/clipboard overrides.

Quick Start

# 1) Initialize vault (256 MiB Argon2 memory)
black-bag init --mem-kib 262144 --argon-lanes auto

# 2) Add a login
black-bag add login --title "Ops" --username phoenix --url https://ops.example --tags mission

# 3) List masked summaries
black-bag list --query ops

# 4) Reveal one record on a TTY
black-bag get <UUID> --reveal  # TTY required

# 5) Add a TOTP seed (prefer SHA‑256)
black-bag add totp --issuer GitHub --account you@example --secret-file ./totp.txt --algorithm sha256
black-bag totp code <UUID>

# 6) Rotate wrapping keys after missions
black-bag rotate

# 7) Split recovery material (3‑of‑7)
black-bag recovery split --threshold 3 --shares 7

Operations Guide

Create and Unlock

  • black-bag init --mem-kib 262144 --argon-lanes auto prompts for a strong passphrase (≥14 NFKC chars and zxcvbn≥3).
  • On first use of commands like list, get, add, the CLI prompts to unlock.

Add Records (selected families)

  • Login: add login --title "Portal" --username alice --url https://example --tags prod
  • Note: add note --title "Protocol" --tags red-team then paste body (Ctrl‑D to end).
  • API: add api --service intel --environment prod --access-key AKIA... --scopes read,write
  • TOTP: add totp --issuer GitHub --account you@example --secret-file ./secret.txt --algorithm sha256
    • Safer alternative: printf 'otpauth://...' | black-bag add totp --otpauth-stdin
    • Optional ASCII QR: add --qr --confirm-qr.
  • SSH/PGP/Recovery: add ssh|pgp|recovery then paste private/armored payload; stored encrypted.

Search and Inspect

  • list (masked): filter with --kind, --tag, --query, or --fuzzy.
  • get <UUID> --reveal shows secrets on TTY. Clipboard copy requires build feature and --unsafe-clipboard.

Rotation, Passphrase Changes, Migration

  • rotate [--mem-kib N] refreshes KEM and rewraps DEK (updates header MAC/epoch).
  • passwd [--mem-kib N] [--argon-lanes auto|N] [--rekey-dek] changes passphrase and optionally rekeys payload DEK.
  • migrate bumps an older vault to latest on‑disk version and recomputes header MAC.

Recovery and Backups

  • Shamir split: recovery split --threshold 3 --shares 7 outputs shares and a share‑set ID (commit). Store separately.
  • Combine: recovery combine --threshold 3 --shares 1-<b64>,2-<b64>,3-<b64> [--set-id ...] [--raw]
    • --raw emits binary to TTY; otherwise base64.
  • Backup check: backup verify --path ~/.config/black_bag/vault.cbor validates public integrity sidecar without passphrase.

CLI Reference (Complete)

Global flags (prefix any subcommand):

  • --unsafe-stdout – allow secrets to flow to stdout/JSON (default off; prefer TTY)
  • --require-mlock – require page‑locking or abort
  • --emit <tty|stdout|json> – preferred output mode for non‑reveal flows (stdout/json require --unsafe-stdout)
  • --format <text|json|ndjson> – output format for non‑secret results (defaults to text)
  • --schema-version <N> – include JSON schema version in machine‑readable output (defaults to 1)
  • --quiet – suppress warnings and non‑essential notices
  • --agent <none|keychain> – enable keychain agent (feature‑gated)
  • --unsafe-clipboard – allow copying secrets to clipboard (feature‑gated)
  • --duress – operate on a separate duress vault file
  • Env hard mode: BLACK_BAG_HARD_MODE=1 forces TTY‑only/clipboard‑off regardless of flags

init

Initialize a new vault.

black-bag init --mem-kib <KiB> [--argon-lanes auto|N]

Args:

  • --mem-kib (default 262144; min 32768)
  • --argon-lanes auto (CPU cores capped 8, min 4) or integer ≥4

add

Add records in families. Sensitive values are captured by prompt/stdin/file; never argv.

Common options: --title <str> --tags <t1,t2> --notes <str>

Families:

  • login--username <str> --url <str>
  • contact--full-name <str> --emails <e1,e2> --phones <p1,p2>
  • id--id-type <str> --name-on-doc <str> --number <str> --issuing-country <str> --expiry <YYYY-MM-DD> (secret via prompt)
  • note – paste body after prompt (Ctrl‑D to finish)
  • bank--institution <str> --account-name <str> --routing-number <str> (account number via prompt)
  • wifi--ssid <str> --security <str> --location <str> (passphrase via prompt)
  • api--service <str> --environment <str> --access-key <str> --scopes <s1,s2> (secret key via prompt)
  • wallet--asset <str> --address <str> --network <str> (secret key via prompt)
  • totp--issuer <str> --account <str> --secret-file <PATH> --secret-stdin --otpauth-stdin --qr --confirm-qr --digits 6..8 --step <secs> --skew <steps> --algorithm <sha1|sha256|sha512>
  • ssh--label <str> --comment <str> then paste private key
  • pgp--label <str> --fingerprint <str> then paste armored private key
  • recovery--description <str> then paste recovery payload

Examples:

black-bag add login --title "Ops" --username alice --url https://ops --tags prod
black-bag add note --title "Protocol" --tags red-team
black-bag add totp --issuer GitHub --account you@example --secret-file ./secret.txt --algorithm sha256

list

black-bag list [--kind <family>] [--tag <tag>] [--query <text>] [--fuzzy]

Families: login, contact, id, note, bank, wifi, api, wallet, totp, ssh, pgp, recovery

get

black-bag get <UUID> [--reveal] [--clipboard]

Notes: --reveal requires TTY; --clipboard requires feature + --unsafe-clipboard and auto‑clears.

rotate

black-bag rotate [--mem-kib <KiB>]

doctor

black-bag doctor [--json]

Prints ready status, Argon2 params, record count, timestamps. JSON/NDJSON include schema and headerMacVerified.

passwd

black-bag passwd [--mem-kib <KiB>] [--argon-lanes auto|N] [--rekey-dek]

migrate

black-bag migrate [--pq ml-kem-1024|kyber1024|next] [--aead xchacha20poly1305|aes256gcm]

Notes: flags are accepted for forward compatibility; current builds keep ML‑KEM‑1024 + XChaCha20‑Poly1305.

export csv

black-bag export csv [--kind <family>] --fields <f1,f2,...> [--include-secrets] --unsafe-stdout

Common fields: id,kind,title,tags,summary,username,url,password,secret_key,totp_secret

backup verify

black-bag backup verify --path <vault.cbor>

recovery split / combine

black-bag recovery split --threshold <n> --shares <m> [--duress]
black-bag recovery combine --threshold <n> --shares <id-b64,...> [--set-id <base32>] [--raw] [--duress]

totp code

black-bag totp code --id <UUID> [--time <unix>]

selftest

black-bag selftest

version

black-bag version

Prints version + enabled features (PQC, AEAD, Argon caps, compiled features).

completions

Generate shell completions (print to stdout):

black-bag completions <bash|zsh|fish|pwsh>

help-man

Render a manpage to stdout:

black-bag help-man | man -l -

Stdout/Stderr & Schema Contract

  • Secrets and warnings are sent to the TTY or stderr; machine‑readable data are sent to stdout.
  • When --format json|ndjson (or --json), a schema field is included (default 1). Field names remain stable across schema versions.

Exit Codes (Scriptable)

  • 0 OK
  • 2 Input/usage/validation error
  • 3 Integrity failure (e.g., header MAC, tag mismatch)
  • 4 Unlock error (failed to unlock vault)
  • 5 I/O or environment errors
  • 6 Concurrency/locking errors
  • 7 Policy violations (requires TTY, unsafe flags not set, etc.)
  • 10 Internal/unknown error

Security Model (Short)

  • KDF: Argon2id (time=10; lanes≥4, capped 8). Passphrase policy: NFKC, ≥14 chars, uniqueness, zxcvbn≥3.
  • Wrapping: ML‑KEM‑1024 public key + ciphertext. Decapsulation secret sealed with KEK.
  • Payload & secret fields: XChaCha20‑Poly1305 AEAD with distinct AADs; per‑record rDEKs for secrets at rest.
  • Header integrity: keyed BLAKE3 MAC over critical header fields; verified on unlock (constant‑time compare).
  • Public integrity sidecar: Blake3 tag keyed by KEM public to detect bit‑rot without secrets.
  • Anti‑rollback: header epoch + .epoch sidecar (warn/fail); optional keychain epoch pin (feature‑gated) to fail on rollback.
  • Process hardening: no coredumps; tracer/ptrace protections; release panic=abort; sanitized errors by default.
  • Windows ACL: refuse insecure vault directory (Everyone/Authenticated Users write).

References: docs/CRYPTO_POLICY.md, docs/THREAT_MODEL.md, docs/SECURITY_MODEL.md, docs/VAULT_FORMAT.md.

Documentation map:

  • Full CLI reference: docs/CLI_REFERENCE.md
  • Technology deep dive: docs/TECHNOLOGY.md
  • Vault format: docs/VAULT_FORMAT.md
  • Security model (narrative): docs/SECURITY_MODEL.md
  • Configuration: docs/CONFIGURATION.md
  • Crypto policy: docs/CRYPTO_POLICY.md
  • Threat model: docs/THREAT_MODEL.md

Technology Deep Dive (Essentials)

  • KDF (Argon2id)

    • Defaults: time=10; lanes=max(4, min(8, CPU cores)); mem configurable (suggest 256 MiB)
    • Salt=32B from OsRng; output KEK=32B (Zeroizing)
  • KEM (ML‑KEM‑1024) via pqcrypto-mlkem

    • Sizes: PK=1568, SK=3168, CT=1568, SS=32 (checked at runtime)
    • Decapsulation secret (SK) sealed under KEK; shared secret derived at unlock
  • AEAD (XChaCha20‑Poly1305, 24B nonces)

    • AAD labels (exact):
      • Payload: black-bag::payload
      • Sealed DEK: black-bag::sealed-dek
      • Sealed decapsulation: black-bag::sealed-dk
      • Sealed rDEK: black-bag::record-dek
      • Secret field blob: black-bag::record-secret
  • Key hierarchy

    • Passphrase → Argon2id → KEK
    • KEK decrypts decapsulation secret (ML‑KEM‑1024 SK)
    • SK + (PK, CT) → shared (decaps) → decrypt DEK
    • DEK decrypts payload; per‑record rDEKs wrap secret fields
  • Header MAC (keyed BLAKE3)

    • Context: black-bag header mac; verified constant‑time before any decryption
    • Covers: created/updated, Argon params+salt, epoch, KEM public+ciphertext, sealed blobs (nonces+ciphertexts)
  • Integrity & Anti‑rollback

    • Public integrity .int: keyed Blake3 over serialized CBOR with context black-bag public integrity
    • Epoch .epoch: last seen epoch; warn/fail if header epoch behind sidecar; optional keychain epoch pin (feature‑gated)
  • Padding & secret blobs

    • Optional payload padding: magic BBPAD1\0\0 + length (LE u64) + CBOR + random pad (block set by BLACK_BAG_PAD_BLOCK)
    • Secret field blob at rest: BBE1 | 24B nonce | ciphertext(tag) under rDEK with AAD record-secret
  • Shamir (recovery)

    • GF(2^8): constant‑time gf_mul; fixed addition‑chain gf_inv
    • Framed share: [id | 16B salt | payload | 32B HMAC‑SHA256]
    • MAC/commit contexts: normal black-bag share mac/commit; duress black-bag share mac duress/duress commit
    • Share‑set commit (base32) verified on combine; legacy (id|payload) still accepted
  • Process & memory hygiene

    • Zeroization on drop; best‑effort page‑locking; TTY‑only reveals by default; atomic 0600 writes; file/dir sync
    • Linux: O_NOATIME reads; undumpable + tracer detection; macOS: deny attach; Windows: ACL checks
  • Limits and families

    • Vault file ≤ 64 MiB; payload plaintext ≤ 32 MiB; secret field ≤ 8 KiB; notes/keys ≤ 256 KiB
    • Records ≤ 100k; tags/record ≤ 64; tag length ≤ 128; title ≤ 256; URL ≤ 4096; username ≤ 2048
    • Families: login, contact, id, note, bank, wifi, api, wallet, totp, ssh, pgp, recovery

Configuration

  • Env vars: BLACK_BAG_VAULT_PATH, BLACK_BAG_VAULT_DURESS_PATH, BLACK_BAG_UNSAFE_STDOUT, BLACK_BAG_REQUIRE_MLOCK, BLACK_BAG_EMIT, BLACK_BAG_AGENT, BLACK_BAG_UNSAFE_CLIPBOARD, BLACK_BAG_DURESS, BLACK_BAG_HARD_MODE, BLACK_BAG_STRICT_ROLLBACK, BLACK_BAG_ALLOW_ROLLBACK (see docs for details).
  • Features: default mlock, pq; optional tui, agent-keychain, clipboard, fuzzing.

Troubleshooting

  • “vault not initialized” → run init or set BLACK_BAG_VAULT_PATH.
  • “header integrity check failed” → stop; header was altered (rollback/tamper). Restore last known good.
  • Windows “insecure vault directory permissions” → restrict ACL to current user only.
  • TOTP QR warn → requires --confirm-qr.
  • Clipboard unavailable → not compiled or --unsafe-clipboard not set.

Mission‑Ready Checklist

  • Argon2id + ML‑KEM‑1024 + XChaCha20‑Poly1305 enabled by default
  • TTY‑only reveals; no stdout secrets by default
  • Strict input caps; atomic writes; zeroized buffers
  • Cross‑platform (macOS/Linux/Windows)
  • Comprehensive record families; search/tagging
  • Tests green; warnings denied
  • Operator docs + threat model

For production roll‑out, schedule an independent audit and set up long‑run fuzzing (see docs/FURTHER_HARDENING.md).

Recent Security Changes

  • Removed CLI secret parameters (prompt/stdin/file only)
  • Constant‑time Shamir GF operations; sanitized errors
  • Migrated to ML‑KEM‑1024; runtime size checks
  • Input bounds added (pre/post parse caps)
  • Argon2 defaults increased (time=10; lanes auto≥4)

Backups & Integrity

  • backup verify --path <vault.cbor> [--pub-key <ed25519.pub>] checks the public integrity sidecar (.int) and, if a public key is provided, verifies the detached signature sidecar (.int.sig). This checks bit‑rot, not authenticity unless a signature is supplied; keep vault and .int paired.
  • backup sign --path <vault.cbor> --key <ed25519.sk> [--pub-out <ed25519.pub>] signs the integrity tag with an operator‑managed Ed25519 key.

Recovery (Audit‑proof & Human‑proof)

What Changed (Recent)

  • Added record edit/delete commands for operator ergonomics.
  • Expanded export csv with per‑family schemas and --schema header‑only mode.
  • get --otpauth (text/JSON) and ASCII QR for TOTP provisioning; totp doctor for skew/TTL diagnostics.
  • scan passwords finds duplicates and weak (zxcvbn<3) offline; JSON via --format json.
  • recovery split --threshold N --shares M [--with-checksum] [--qr --confirm-qr] [--duress] prints id-<base64> tokens; optional checksum is appended as a trailing comment and QR codes can be printed for human transfer.
  • recovery verify --threshold N --shares "1-...,2-..." [--set-id ...] [--duress] prints OK, mismatch, or tamper without emitting any secret.
  • recovery doctor ... detects legacy‑only / mixed sets and prints exact remediation (what to re‑split).