black-bagg 0.2.6

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

[![crates.io](https://img.shields.io/crates/v/black-bagg.svg?style=flat-square)](https://crates.io/crates/black-bagg) [![docs.rs](https://docs.rs/black-bagg/badge.svg)](https://docs.rs/black-bagg) [![license: MIT OR Apache-2.0](https://img.shields.io/badge/license-MIT%20OR%20Apache--2.0-blue.svg?style=flat-square)](LICENSE-MIT)

Install: `cargo install black-bagg` (binary name: `black-bag`)
Docs: https://docs.rs/black-bagg · Crate: https://crates.io/crates/black-bagg


`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

```bash
# 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

```bash
# 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
- [x] Argon2id + ML‑KEM‑1024 + XChaCha20‑Poly1305 enabled by default
- [x] TTY‑only reveals; no stdout secrets by default
- [x] Strict input caps; atomic writes; zeroized buffers
- [x] Cross‑platform (macOS/Linux/Windows)
- [x] Comprehensive record families; search/tagging
- [x] Tests green; warnings denied
- [x] 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).