README.md (608 lines): - Added 11 new feature sections: extract-then-strip EXIF, federation, timestamp anchoring, selective disclosure, evidence packages, cold archives, source drop box, key rotation/recovery, cover mode - Expanded steganography (transport-aware, carrier tracking), attestation (non-image files, investigation namespaces, derivation lineage), fieldkit (forensic scrub, webhook, self-uninstall) - Added Cross-Domain Applications section (human rights, research, elections, supply chain, art, whistleblowing, environment) - Updated CLI reference with chain anchor/disclose/export commands - Updated architecture with all new modules and data directory layout CLAUDE.md (155 lines): - Added metadata.py, evidence.py, archive.py, carrier_tracker.py, anchors.py, exchange.py, dropbox blueprint to architecture tree - Added 7 new design decisions (extract-then-strip, CSRF exemption, client-side hashing, ImageHashes generalization, lazy paths, two-way federation, chain record types) docs/deployment.md (1139 lines): - Added 5 new operational sections: source drop box setup, chain anchoring procedures, cross-org federation, evidence/archive workflows, cover/duress mode - Updated killswitch section with full 10-step destruction sequence - Updated config table with all new fields - Added 5 new troubleshooting entries Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
609 lines
27 KiB
Markdown
609 lines
27 KiB
Markdown
# SooSeF -- Soo Security Fieldkit
|
|
|
|
**Offline-first security toolkit for journalists, NGOs, and at-risk organizations.**
|
|
|
|
<!-- badges -->
|
|

|
|

|
|

|
|
|
|
---
|
|
|
|
## 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, with transport-aware encoding for lossy channels (WhatsApp, Signal, Telegram)
|
|
- **Prove authenticity** of photos, documents, and arbitrary files with Ed25519 signatures, Merkle-style hash chains, and RFC 3161 trusted timestamps
|
|
- **Protect data in the field** with a killswitch (including deep forensic scrub and self-uninstall), dead man's switch with webhook warnings, tamper detection, USB device whitelisting, and GPS geofencing
|
|
- **Manage cryptographic keys** with identity rotation, channel key generation, encrypted key bundle export/import, QR code sharing, trust store management, and identity recovery from chain
|
|
- **Federate attestations** across organizations with signed exchange bundles, delivery acknowledgments, selective disclosure for legal discovery, and investigation namespaces
|
|
- **Accept anonymous submissions** through a SecureDrop-style source drop box with token-gated uploads, client-side SHA-256 hashing, and automatic EXIF extraction/stripping
|
|
- **Preserve evidence long-term** with self-contained evidence packages and OAIS-aligned cold archives that include standalone verification scripts and algorithm documentation
|
|
|
|
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
|
|
- **Transport-aware encoding** -- `--transport whatsapp|signal|telegram|discord|email|direct` auto-selects the right encoding mode and carrier resolution for lossy messaging platforms. WhatsApp/Signal/Telegram force DCT/JPEG mode and pre-resize the carrier to survive recompression
|
|
- **Carrier reuse tracking** -- warns when a carrier image has been used before, since comparing two versions of the same carrier trivially reveals steganographic modification
|
|
- 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 arbitrary files (CSV, documents, sensor data)
|
|
- Perceptual hashing (pHash, dHash) for tamper-evident photo attestation; SHA-256-only mode for non-image files
|
|
- LMDB-backed attestation storage
|
|
- Append-only hash chain (CBOR-encoded) with Merkle tree verification
|
|
- Batch attestation for directories
|
|
- **Investigation namespaces** -- tag and filter attestations by case or project
|
|
- **Derived work lineage** -- parent-child attestation tracking for editorial workflows
|
|
- **Chain position proof** -- verification receipts include the record's position in the hash chain
|
|
|
|
### Extract-Then-Strip EXIF Pipeline
|
|
|
|
Resolves the tension between steganography (strip everything to protect sources) and
|
|
attestation (preserve everything to prove provenance):
|
|
|
|
1. Extract all EXIF metadata from the original image bytes
|
|
2. Classify fields as **evidentiary** (GPS coordinates, timestamp -- valuable for provenance) or **dangerous** (device serial number, firmware version -- could identify the source)
|
|
3. Preserve evidentiary fields in the attestation record
|
|
4. Strip all metadata from the stored/display copy
|
|
|
|
### Cross-Organization Federation
|
|
|
|
- **Attestation exchange** -- export signed bundles of attestation records and chain data for offline transfer to partner organizations
|
|
- **Delivery acknowledgments** -- when an organization imports a bundle, a `soosef/delivery-ack-v1` chain record is signed and can be shared back, creating a two-way federation handshake
|
|
- **Trust store** -- import collaborator Ed25519 public keys; only records signed by trusted keys are imported during federation
|
|
- **Investigation filtering** -- export/import only records tagged with a specific investigation
|
|
|
|
### External Timestamp Anchoring
|
|
|
|
Two mechanisms to externally prove that the chain head existed before a given time:
|
|
|
|
- **RFC 3161 TSA** -- automated submission to any RFC 3161 Timestamping Authority (e.g., FreeTSA). The signed timestamp token is saved alongside the chain
|
|
- **Manual anchors** -- export the chain head hash as a compact string for manual submission to any external witness (blockchain transaction, newspaper classified, tweet, email to a TSA)
|
|
|
|
A single anchor for the chain head implicitly timestamps every record that preceded it, because the chain is append-only with hash linkage.
|
|
|
|
### Selective Disclosure
|
|
|
|
Produce verifiable proofs for specific chain records while keeping others redacted. Selected records are included in full; non-selected records appear only as hashes. A third party can verify that the disclosed records are part of an unbroken chain without seeing the contents of other records. Designed for legal discovery, court orders, and FOIA responses.
|
|
|
|
### Evidence Packages
|
|
|
|
Self-contained ZIP bundles for handing evidence to lawyers, courts, or archives:
|
|
|
|
- Original images
|
|
- Attestation records with full Ed25519 signatures
|
|
- Chain segment with hash linkage
|
|
- Signer's public key
|
|
- `verify.py` -- standalone verification script that requires only Python 3.11+ and the `cryptography` pip package (no SooSeF installation needed)
|
|
- Human-readable README
|
|
|
|
### Cold Archive
|
|
|
|
Full-state export for long-term evidence preservation (10+ year horizon), aligned with OAIS (ISO 14721):
|
|
|
|
- Raw chain binary and state checkpoint
|
|
- Attestation log and LMDB index
|
|
- External timestamp anchors
|
|
- Public key and trusted collaborator keys
|
|
- Encrypted key bundle (optional, password-protected)
|
|
- `ALGORITHMS.txt` documenting every cryptographic algorithm, parameter, and format used
|
|
- `verify.py` standalone verifier
|
|
- `manifest.json` with SHA-256 integrity hashes of key files
|
|
|
|
### Source Drop Box
|
|
|
|
SecureDrop-style anonymous intake built into the SooSeF web UI:
|
|
|
|
- Admin creates a time-limited upload token with a configurable file limit
|
|
- Source opens the token URL (no account or SooSeF branding -- source safety)
|
|
- **Client-side SHA-256** via SubtleCrypto runs in the browser before upload, so the source can independently verify what they submitted
|
|
- Files are run through the extract-then-strip EXIF pipeline and auto-attested on receipt
|
|
- Source receives HMAC-derived receipt codes that prove delivery
|
|
- Tokens and receipts are stored in SQLite; tokens auto-expire
|
|
|
|
### Key Rotation and Recovery
|
|
|
|
- **Key rotation** -- both identity (Ed25519) and channel (AES) keys can be rotated. The chain records the rotation as a `soosef/key-rotation-v1` record signed by the OLD key, creating a cryptographic trust chain
|
|
- **Identity recovery** -- after device loss, a new key can be introduced via a `soosef/key-recovery-v1` chain record. The record carries the old fingerprint and optional cosigner fingerprints for audit
|
|
- **Channel key only export** -- share just the channel key (not identity keys) with collaborators via encrypted file or QR code (`soosef-channel:` URI scheme)
|
|
- **Backup tracking** -- records when the last backup was taken and warns when overdue
|
|
|
|
### Fieldkit
|
|
|
|
- **Killswitch** -- emergency destruction of all data under `~/.soosef/`, ordered by sensitivity (keys first, then data, then logs). Includes:
|
|
- **Deep forensic scrub** -- removes `__pycache__`, `.pyc`, pip `dist-info`, pip download cache, and scrubs shell history entries containing "soosef"
|
|
- **Self-uninstall** -- runs `pip uninstall -y soosef` as the final step
|
|
- **System log clearing** -- best-effort journald vacuum on Linux
|
|
- **Dead man's switch** -- automated purge if check-in is missed, with a configurable grace period. During the grace period, a **webhook warning** is sent (POST to a configured URL) and a local warning file is written before the killswitch fires
|
|
- **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. Supports live GPS via **gpsd** (`get_current_location()` connects to `127.0.0.1:2947`)
|
|
- **Hardware killswitch** -- GPIO pin monitoring for Raspberry Pi physical button (configurable pin and hold duration)
|
|
|
|
### Cover / Duress Mode
|
|
|
|
- **Configurable certificate CN** -- set `cover_name` in config to replace "SooSeF Local" in the self-signed TLS certificate
|
|
- **Portable data directory** -- set `SOOSEF_DATA_DIR` to relocate all state to an arbitrary path (e.g., an innocuously named directory on a USB stick). All paths resolve lazily from `BASE_DIR`, so runtime overrides propagate correctly
|
|
|
|
---
|
|
|
|
## 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, QR code support, piexif |
|
|
| `web` | Flask web UI with Waitress/Gunicorn, pyzbar QR scanning, 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
|
|
|
|
# Transport-aware encoding (auto-selects DCT/JPEG and resizes for the platform)
|
|
soosef stego encode cover.jpg -r shared.jpg -m "Secret" --transport whatsapp
|
|
soosef stego encode cover.jpg -r shared.jpg -m "Secret" --transport signal
|
|
soosef stego encode cover.jpg -r shared.jpg -m "Secret" --transport telegram
|
|
soosef stego encode cover.jpg -r shared.jpg -m "Secret" --transport email
|
|
soosef stego encode cover.jpg -r shared.jpg -m "Secret" --transport direct
|
|
|
|
# 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 (records rotation in chain)
|
|
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 (hashes + signatures)
|
|
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
|
|
|
|
# Evidence export
|
|
soosef chain export --start 0 --end 100 -o chain.zip
|
|
|
|
# Selective disclosure (for legal discovery / court orders)
|
|
soosef chain disclose -i 5,12,47 -o disclosure.json
|
|
|
|
# External timestamp anchoring
|
|
soosef chain anchor # Manual anchor (prints hash for tweet/email/blockchain)
|
|
soosef chain anchor --tsa https://freetsa.org/tsr # RFC 3161 automated anchor
|
|
```
|
|
|
|
---
|
|
|
|
## Web UI
|
|
|
|
Start with `soosef serve`. The web UI provides authenticated access to all features
|
|
through Flask blueprints. Served by **Waitress** (production WSGI server) by default.
|
|
|
|
### 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) |
|
|
| dropbox | `/dropbox/admin`, `/dropbox/upload/<token>` | Source drop box: token creation (admin), anonymous upload (source), receipt verification |
|
|
| 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 |
|
|
| `cover_name` | string | `""` | If set, used for SSL cert CN instead of "SooSeF Local" (cover/duress mode) |
|
|
| `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`). Enables portable USB mode and cover/duress directory naming |
|
|
|
|
---
|
|
|
|
## Architecture
|
|
|
|
### Source layout
|
|
|
|
```
|
|
src/soosef/
|
|
__init__.py Package init, __version__
|
|
cli.py Click CLI (entry point: soosef)
|
|
paths.py All path constants (lazy resolution from BASE_DIR)
|
|
config.py Unified config loader (dataclass + JSON)
|
|
exceptions.py SoosefError base exception
|
|
metadata.py Extract-then-strip EXIF pipeline
|
|
evidence.py Self-contained evidence package export
|
|
archive.py Cold archive for long-term preservation (OAIS-aligned)
|
|
stegasoo/ Steganography engine (subpackage)
|
|
encode.py Transport-aware encoding (--transport flag)
|
|
carrier_tracker.py Carrier reuse tracking and warnings
|
|
verisoo/ Attestation engine (subpackage)
|
|
models.py ImageHashes (images + arbitrary files), AttestationRecord
|
|
keystore/
|
|
manager.py Key material management (channel + identity + trust store + backup)
|
|
models.py KeyBundle, IdentityBundle dataclasses
|
|
export.py Full bundle export, channel-key-only export, QR code sharing
|
|
federation/
|
|
chain.py Append-only hash chain (key rotation, recovery, delivery ack, selective disclosure)
|
|
anchors.py RFC 3161 timestamps + manual chain anchors
|
|
exchange.py Cross-org attestation bundle export/import
|
|
fieldkit/
|
|
killswitch.py Emergency data destruction (deep forensic scrub, self-uninstall)
|
|
deadman.py Dead man's switch (webhook warnings)
|
|
tamper.py File integrity monitoring
|
|
usb_monitor.py USB device whitelist (Linux/pyudev)
|
|
geofence.py GPS boundary enforcement (gpsd integration)
|
|
|
|
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/*
|
|
dropbox.py /dropbox/* (source drop box)
|
|
```
|
|
|
|
### Data directory (`~/.soosef/`)
|
|
|
|
```
|
|
~/.soosef/
|
|
config.json Unified configuration
|
|
audit.jsonl Append-only audit trail
|
|
carrier_history.json Carrier reuse tracking database
|
|
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, anchors/)
|
|
auth/ Web UI auth database (soosef.db, dropbox.db)
|
|
certs/ Self-signed TLS certificates
|
|
fieldkit/ Fieldkit state (deadman.json, tamper/, usb/, geofence.json)
|
|
temp/ Ephemeral file storage (dropbox uploads)
|
|
instance/ Flask instance (sessions, secret key)
|
|
trusted_keys/ Collaborator Ed25519 public keys (trust store)
|
|
```
|
|
|
|
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. The deep forensic scrub extends beyond
|
|
the data directory to remove Python bytecache, pip metadata, pip download cache, shell
|
|
history entries, and the soosef package itself.
|
|
|
|
**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. RFC 3161 timestamping and webhook warnings are optional features that
|
|
gracefully degrade when offline.
|
|
|
|
**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
|
|
- Dead man's switch webhook SSRF protection (blocks private/internal targets)
|
|
|
|
**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.
|
|
|
|
**Chain integrity.** The append-only hash chain uses Ed25519 signatures and SHA-256
|
|
linkage. Key rotation records create a verifiable trust chain; identity recovery records
|
|
are auditable. Selective disclosure uses Merkle-style proofs so third parties can verify
|
|
specific records without accessing the full chain.
|
|
|
|
---
|
|
|
|
## Cross-Domain Applications
|
|
|
|
While SooSeF was designed for journalist and NGO field security, the attestation chain,
|
|
federation, and evidence packaging capabilities apply to a range of domains:
|
|
|
|
- **Human rights documentation** -- field workers attest photos and videos of incidents with GPS and timestamps, federate evidence to international partners, and produce court-ready evidence packages
|
|
- **Research integrity** -- researchers attest datasets (CSV, sensor readings) at collection time, creating a tamper-evident chain of custody. `ImageHashes.from_file()` supports arbitrary file types via SHA-256
|
|
- **Election monitoring** -- observers attest ballot images and tally sheets with location metadata, anchor the chain to an RFC 3161 TSA for independent time proof, and use selective disclosure for audit requests
|
|
- **Supply chain verification** -- attest inspection photos, sensor data, and certificates of origin at each stage. Federation enables multi-party chains across organizations
|
|
- **Art authentication** -- attest high-resolution photographs of artworks with device and location metadata, creating provenance records that survive format conversion via perceptual hashing
|
|
- **Corporate whistleblowing** -- the source drop box accepts anonymous uploads with client-side hashing. Cover mode (`cover_name`, `SOOSEF_DATA_DIR`) disguises the installation. The killswitch provides emergency destruction if the instance is compromised
|
|
- **Environmental monitoring** -- attest sensor data, satellite imagery, and field photographs. Cold archives with `ALGORITHMS.txt` ensure evidence remains verifiable decades later
|
|
|
|
---
|
|
|
|
## 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.
|