fieldwitness/README.md
Aaron D. Lee e50122e8e6 Fix stego CLI examples in README with accurate syntax
The stego examples used nonexistent -i flags. Replace with actual
CLI syntax: positional CARRIER argument, -r/--reference for shared
photo, -m/--message for text, -f/--file for file payload. Add
comprehensive examples covering encode, decode, dry-run, DCT mode,
audio stego, credential generation, and channel key management.
Also fix attest examples to match actual CLI commands.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 20:03:24 -04:00

472 lines
16 KiB
Markdown

# SooSeF -- Soo Security Fieldkit
**Offline-first security toolkit for journalists, NGOs, and at-risk organizations.**
<!-- badges -->
![Version](https://img.shields.io/badge/version-0.2.0-blue)
![Python](https://img.shields.io/badge/python-%3E%3D3.11-blue)
![License](https://img.shields.io/badge/license-MIT-green)
---
## What is SooSeF?
SooSeF combines steganography, provenance attestation, and field security tools into a
single package designed for airgapped and resource-constrained environments. It lets you:
- **Hide messages** in images, audio, and video using multiple steganographic techniques
- **Prove authenticity** of photos and documents with Ed25519 signatures and Merkle trees
- **Protect data in the field** with a killswitch, dead man's switch, tamper detection, USB device whitelisting, and GPS geofencing
- **Manage cryptographic keys** with identity rotation, channel key generation, and encrypted key bundle export/import
Stegasoo (steganography, v4.3.0) and Verisoo (attestation, v0.1.0) are included as
subpackages (`import soosef.stegasoo`, `import soosef.verisoo`). Everything ships as one
install: `pip install soosef`.
---
## Quick Start
```bash
pip install "soosef[web,cli]"
soosef init
soosef serve
```
This creates the `~/.soosef/` directory structure, generates an Ed25519 identity and
channel key, writes a default config, and starts an HTTPS web UI on
`https://127.0.0.1:5000`.
---
## Features
### Steganography (Stegasoo)
- **LSB encoding** -- bit-level message hiding in PNG images
- **DCT encoding** -- frequency-domain hiding in JPEG images (requires `stego-dct` extra)
- **Audio steganography** -- hide data in WAV/FLAC audio (requires `stego-audio` extra)
- **Video steganography** -- frame-level encoding
- AES-256-GCM encryption with Argon2id key derivation
- EXIF stripping on encode to prevent metadata leakage
- Compression support (zstandard, optional LZ4)
### Attestation (Verisoo)
- Ed25519 digital signatures for images and files
- Perceptual hashing (ImageHash) for tamper-evident photo attestation
- LMDB-backed attestation storage
- Append-only hash chain (CBOR-encoded) with Merkle tree verification
- Batch attestation for directories
### Fieldkit
- **Killswitch** -- emergency destruction of all data under `~/.soosef/`
- **Dead man's switch** -- automated purge if check-in is missed
- **Tamper detection** -- file integrity monitoring with baseline snapshots
- **USB whitelist** -- block or alert on unauthorized USB devices (Linux/pyudev)
- **Geofence** -- GPS boundary enforcement with configurable radius
### Key Management
- Two separate key domains: AES-256-GCM channel keys (stego) and Ed25519 identity keys (attestation)
- Key rotation for both identity and channel keys
- Encrypted key bundle export/import for secure backup and transfer
- QR code generation for key sharing
---
## Installation
### Basic install (core library only)
```bash
pip install soosef
```
### With extras
```bash
pip install "soosef[web,cli]" # Web UI + CLI (most common)
pip install "soosef[all]" # Everything except dev tools
pip install "soosef[dev]" # All + pytest, black, ruff, mypy
```
### Available extras
| Extra | What it adds |
|---|---|
| `stego-dct` | DCT steganography (numpy, scipy, jpeglib, reedsolo) |
| `stego-audio` | Audio steganography (pydub, soundfile, reedsolo) |
| `stego-compression` | LZ4 compression support |
| `attest` | Attestation features (imagehash, lmdb, exifread) |
| `cli` | Click CLI with rich output and QR code support |
| `web` | Flask web UI with Waitress/Gunicorn, includes attest + stego-dct |
| `api` | FastAPI REST API with uvicorn, includes stego-dct |
| `fieldkit` | Tamper monitoring (watchdog) and USB whitelist (pyudev) |
| `federation` | Peer-to-peer attestation federation (aiohttp) |
| `rpi` | Raspberry Pi deployment (web + cli + fieldkit + gpiozero) |
| `all` | All of the above |
| `dev` | All + pytest, pytest-cov, black, ruff, mypy |
### Airgapped / Raspberry Pi install
Bundle wheels on a networked machine, then install offline:
```bash
# On networked machine
pip download "soosef[rpi]" -d ./wheels
# Transfer ./wheels to target via USB
# On airgapped machine
pip install --no-index --find-links=./wheels "soosef[rpi]"
soosef init
soosef serve --host 0.0.0.0
```
---
## CLI Reference
All commands accept `--data-dir PATH` to override the default `~/.soosef` directory,
and `--json` for machine-readable output.
```
soosef [--data-dir PATH] [--json] COMMAND
```
### Core commands
| Command | Description |
|---|---|
| `soosef init` | Create directory structure, generate identity + channel key, write default config |
| `soosef serve` | Start the web UI (default: `https://127.0.0.1:5000`) |
| `soosef status` | Show instance status: identity, keys, chain, fieldkit, config |
### `soosef serve` options
| Option | Default | Description |
|---|---|---|
| `--host` | `127.0.0.1` | Bind address |
| `--port` | `5000` | Bind port |
| `--no-https` | off | Disable HTTPS (use HTTP) |
| `--debug` | off | Use Flask dev server instead of Waitress |
| `--workers` | `4` | Number of Waitress/Gunicorn worker threads |
### Steganography commands (`soosef stego`)
Stegasoo uses multi-factor authentication: a **reference photo** (shared image both
parties have), a **passphrase** (4+ words), and a **PIN** (6-9 digits). All three are
required to encode or decode. The passphrase and PIN are prompted interactively
(hidden input) if not provided via options.
```bash
# Encode a text message into an image
# CARRIER is the image to hide data in, -r is the shared reference photo
soosef stego encode cover.png -r shared_photo.jpg -m "Secret message"
# Passphrase: **** (prompted, hidden)
# PIN: **** (prompted, hidden)
# -> writes encoded PNG to current directory
# Encode with explicit output path
soosef stego encode cover.png -r shared_photo.jpg -m "Secret" -o stego_output.png
# Encode a file instead of text
soosef stego encode cover.png -r shared_photo.jpg -f document.pdf
# Dry run — check capacity without encoding
soosef stego encode cover.png -r shared_photo.jpg -m "Secret" --dry-run
# Decode a message from a stego image (same reference + passphrase + PIN)
soosef stego decode stego_output.png -r shared_photo.jpg
# Passphrase: ****
# PIN: ****
# -> prints decoded message or saves decoded file
# Decode and save file payload to specific path
soosef stego decode stego_output.png -r shared_photo.jpg -o recovered.pdf
# DCT mode for JPEG (survives social media compression)
soosef stego encode cover.jpg -r shared_photo.jpg -m "Secret" --platform telegram
# Audio steganography
soosef stego audio-encode audio.wav -r shared_photo.jpg -m "Hidden in audio"
soosef stego audio-decode stego.wav -r shared_photo.jpg
# Generate credentials
soosef stego generate # Generate passphrase + PIN
soosef stego generate --pin-length 8 # Longer PIN
# Channel key management
soosef stego channel status # Show current channel key
soosef stego channel generate # Generate new channel key
# Image info and capacity
soosef stego info cover.png # Image details + LSB/DCT capacity
```
### Attestation commands (`soosef attest`)
```bash
# Attest an image (sign with Ed25519 identity)
soosef attest IMAGE photo.jpg
soosef attest IMAGE photo.jpg --caption "Field report" --location "Istanbul"
# Batch attest a directory
soosef attest batch ./photos/ --caption "Field report"
# Verify an image against the attestation log
soosef attest verify photo.jpg
# View attestation log
soosef attest log --limit 20
```
### Fieldkit commands (`soosef fieldkit`)
```bash
soosef fieldkit status # Show fieldkit state
soosef fieldkit checkin # Reset dead man's switch timer
soosef fieldkit check-deadman # Check if deadman timer has expired
soosef fieldkit purge --confirm # Activate killswitch (destroys all data)
soosef fieldkit geofence set --lat 48.8566 --lon 2.3522 --radius 1000
soosef fieldkit geofence check --lat 48.8600 --lon 2.3500
soosef fieldkit geofence clear
soosef fieldkit usb snapshot # Snapshot current USB devices as whitelist
soosef fieldkit usb check # Check for unauthorized USB devices
```
### Key management commands (`soosef keys`)
```bash
soosef keys show # Display current key info
soosef keys export -o backup.enc # Export encrypted key bundle
soosef keys import -b backup.enc # Import key bundle
soosef keys rotate-identity # Generate new Ed25519 identity
soosef keys rotate-channel # Generate new channel key
```
### Chain commands (`soosef chain`)
```bash
soosef chain status # Show chain head, length, integrity
soosef chain verify # Verify entire chain integrity
soosef chain show INDEX # Show a specific chain record
soosef chain log --count 20 # Show recent chain entries
soosef chain backfill # Backfill existing attestations into chain
soosef chain export --start 0 --end 100 -o chain.cbor
```
---
## Web UI
Start with `soosef serve`. The web UI provides authenticated access to all features
through Flask blueprints.
### Routes
| Blueprint | Routes | Description |
|---|---|---|
| stego | `/encode`, `/decode`, `/generate` | Steganography operations |
| attest | `/attest`, `/verify` | Attestation signing and verification |
| fieldkit | `/fieldkit/*` | Killswitch, dead man's switch, status dashboard |
| keys | `/keys/*` | Key management, rotation, export/import |
| admin | `/admin/*` | User management (multi-user auth via SQLite) |
| health | `/health` | Capability reporting endpoint (see API section) |
<!-- TODO: screenshots -->
---
## Configuration
SooSeF loads configuration from `~/.soosef/config.json`. All fields have sensible defaults.
`soosef init` writes the default config file.
### Config fields
| Field | Type | Default | Description |
|---|---|---|---|
| `host` | string | `127.0.0.1` | Web UI bind address |
| `port` | int | `5000` | Web UI bind port |
| `https_enabled` | bool | `true` | Enable HTTPS with self-signed cert |
| `auth_enabled` | bool | `true` | Require login for web UI |
| `max_upload_mb` | int | `50` | Maximum upload size in MB |
| `session_timeout_minutes` | int | `15` | Session expiry |
| `login_lockout_attempts` | int | `5` | Failed logins before lockout |
| `login_lockout_minutes` | int | `15` | Lockout duration |
| `default_embed_mode` | string | `auto` | Stegasoo encoding mode |
| `killswitch_enabled` | bool | `false` | Enable killswitch functionality |
| `deadman_enabled` | bool | `false` | Enable dead man's switch |
| `deadman_interval_hours` | int | `24` | Check-in interval |
| `deadman_grace_hours` | int | `2` | Grace period after missed check-in |
| `deadman_warning_webhook` | string | `""` | URL to POST warning before auto-purge |
| `usb_monitoring_enabled` | bool | `false` | Enable USB device whitelist enforcement |
| `tamper_monitoring_enabled` | bool | `false` | Enable file integrity monitoring |
| `chain_enabled` | bool | `true` | Enable attestation hash chain |
| `chain_auto_wrap` | bool | `true` | Auto-wrap attestations in chain records |
| `backup_reminder_days` | int | `7` | Days before backup reminder |
| `gpio_killswitch_pin` | int | `17` | Raspberry Pi GPIO pin for hardware killswitch |
| `gpio_killswitch_hold_seconds` | float | `5.0` | Hold duration to trigger hardware killswitch |
### Environment variables
| Variable | Description |
|---|---|
| `SOOSEF_DATA_DIR` | Override the data directory (default: `~/.soosef`) |
---
## Architecture
### Source layout
```
src/soosef/
__init__.py Package init, __version__
cli.py Click CLI (entry point: soosef)
paths.py All path constants (single source of truth)
config.py Unified config loader (dataclass + JSON)
exceptions.py SoosefError base exception
stegasoo/ Steganography engine (subpackage)
verisoo/ Attestation engine (subpackage)
keystore/
manager.py Key material management (channel + identity)
models.py KeyBundle, IdentityBundle dataclasses
export.py Encrypted key bundle export/import
fieldkit/
killswitch.py Emergency data destruction
deadman.py Dead man's switch
tamper.py File integrity monitoring
usb_monitor.py USB device whitelist (Linux/pyudev)
geofence.py GPS boundary enforcement
frontends/web/
app.py Flask app factory (create_app())
auth.py SQLite3 multi-user auth
temp_storage.py File-based temp storage with expiry
subprocess_stego.py Crash-safe subprocess isolation
ssl_utils.py Self-signed HTTPS cert generation
blueprints/
stego.py /encode, /decode, /generate
attest.py /attest, /verify
fieldkit.py /fieldkit/*
keys.py /keys/*
admin.py /admin/*
```
### Data directory (`~/.soosef/`)
```
~/.soosef/
config.json Unified configuration
audit.jsonl Append-only audit trail
identity/ Ed25519 keypair (private.pem, public.pem, identity.meta.json)
stegasoo/ Channel key (channel.key)
attestations/ Verisoo attestation store (log.bin, index/, peers.json)
chain/ Hash chain (chain.bin, state.cbor)
auth/ Web UI auth database (soosef.db)
certs/ Self-signed TLS certificates
fieldkit/ Fieldkit state (deadman.json, tamper/, usb/, geofence.json)
temp/ Ephemeral file storage
instance/ Flask instance (sessions, secret key)
```
Sensitive directories (`identity/`, `auth/`, `certs/`, and the root) are created with
`0700` permissions.
---
## Security Model
**Two key domains, never merged.** Stegasoo uses AES-256-GCM with keys derived via
Argon2id from user-supplied factors. Verisoo uses Ed25519 for signing. These serve
different security purposes and are kept strictly separate.
**Killswitch priority.** The killswitch destroys all data under `~/.soosef/`, including
the audit log. This is intentional -- in a field compromise scenario, data destruction
takes precedence over audit trail preservation.
**Offline-first.** All static assets are vendored (no CDN calls). Pip wheels can be
bundled for fully airgapped installation. No network access is required for any core
functionality.
**Web UI hardening:**
- CSRF protection via Flask-WTF
- Session timeout (default: 15 minutes)
- Login rate limiting with lockout (5 attempts, 15-minute lockout)
- HTTPS by default with auto-generated self-signed certificates
- EXIF stripping on steganographic encode to prevent metadata leakage
**Subprocess isolation.** Steganographic operations run in a subprocess boundary
(`subprocess_stego.py`) to contain crashes and prevent memory corruption from affecting
the main web server process.
---
## API
### `/health` endpoint
The web UI exposes a `/health` endpoint that reports installed capabilities:
```json
{
"status": "ok",
"version": "0.2.0",
"capabilities": ["stego-lsb", "stego-dct", "attest", "fieldkit", "chain"]
}
```
Useful for monitoring and for clients to discover which extras are installed.
### FastAPI (optional)
Install the `api` extra for a standalone FastAPI REST interface:
```bash
pip install "soosef[api]"
```
This provides `soosef.api` with a FastAPI application served by uvicorn, suitable for
programmatic integration.
---
## Development
### Setup
```bash
git clone https://github.com/alee/soosef.git
cd soosef
pip install -e ".[dev]"
```
### Commands
```bash
pytest # Run tests with coverage
black src/ tests/ frontends/ # Format (100-char line length)
ruff check src/ tests/ frontends/ --fix # Lint
mypy src/ # Type check
```
### Code style
- Black with 100-character line length
- Ruff with E, F, I, N, W, UP rule sets
- mypy strict mode with missing imports ignored
- Imperative commit messages (e.g., "Add killswitch purge confirmation")
### Python support
Python 3.11, 3.12, 3.13, and 3.14.
---
## License
MIT License. See [LICENSE](LICENSE) for details.