Update all documentation for post-consolidation feature set
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>
This commit is contained in:
parent
fef552b9c1
commit
428750e971
24
CLAUDE.md
24
CLAUDE.md
@ -34,6 +34,9 @@ src/soosef/ Core library
|
||||
paths.py All ~/.soosef/* path constants (single source of truth)
|
||||
config.py Unified config loader
|
||||
exceptions.py SoosefError base exception
|
||||
metadata.py Extract-then-strip EXIF pipeline with field classification
|
||||
evidence.py Self-contained evidence package export (ZIP with verify.py)
|
||||
archive.py Cold archive export for long-term preservation (OAIS-aligned)
|
||||
|
||||
stegasoo/ Steganography engine (inlined from stegasoo)
|
||||
encode.py / decode.py Core encode/decode API
|
||||
@ -54,6 +57,7 @@ src/soosef/ Core library
|
||||
constants.py Magic bytes, version constants
|
||||
cli.py Stegasoo-specific CLI commands
|
||||
api.py / api_auth.py Stegasoo REST API + auth
|
||||
carrier_tracker.py Carrier image reuse tracking (warns on reuse)
|
||||
image_utils.py / audio_utils.py / video_utils.py
|
||||
keygen.py / qr_utils.py / recovery.py / debug.py / utils.py
|
||||
platform_presets.py Social-media-aware encoding presets
|
||||
@ -75,10 +79,12 @@ src/soosef/ Core library
|
||||
api.py Verisoo REST API
|
||||
|
||||
federation/ Federated attestation chain system
|
||||
chain.py Hash chain construction
|
||||
chain.py Hash chain construction + key rotation/recovery/delivery-ack records
|
||||
entropy.py Entropy source for chain seeds
|
||||
models.py ChainEntry, ChainState dataclasses
|
||||
serialization.py CBOR chain serialization + export
|
||||
anchors.py RFC 3161 timestamps + manual chain anchors
|
||||
exchange.py Cross-org attestation bundle export/import
|
||||
|
||||
keystore/ Unified key management
|
||||
manager.py Owns all key material (channel keys + Ed25519 identity)
|
||||
@ -106,6 +112,9 @@ frontends/web/ Unified Flask web UI
|
||||
fieldkit.py /fieldkit/* (killswitch, deadman, status)
|
||||
keys.py /keys/* (unified key management)
|
||||
admin.py /admin/* (user management)
|
||||
dropbox.py /dropbox/* (token-gated anonymous source upload)
|
||||
templates/dropbox/
|
||||
admin.html Drop box admin panel
|
||||
|
||||
frontends/cli/ CLI package init (main entry point is src/soosef/cli.py)
|
||||
```
|
||||
@ -123,14 +132,23 @@ Stegasoo and Verisoo are inlined subpackages, not separate pip packages:
|
||||
|
||||
- **Two key domains, never merged**: Stegasoo AES-256-GCM (derived from factors) and
|
||||
Verisoo Ed25519 (signing identity) are separate security concerns
|
||||
- **Extract-then-strip model**: Stego strips all EXIF (carrier is vessel); attestation
|
||||
extracts evidentiary EXIF then strips dangerous fields (device serial, etc.)
|
||||
- **subprocess_stego.py copies verbatim** from stegasoo — it's a crash-safety boundary
|
||||
- **All state under ~/.soosef/** — one directory to back up, one to destroy
|
||||
- **Offline-first**: All static assets vendored, no CDN. pip wheels bundled for airgap install
|
||||
- **Flask blueprints**: stego, attest, fieldkit, keys, admin — clean route separation
|
||||
- **Flask-WTF**: CSRF protection on all form endpoints
|
||||
- **Flask blueprints**: stego, attest, fieldkit, keys, admin, dropbox — clean route separation
|
||||
- **Flask-WTF**: CSRF protection on all form endpoints; drop box is CSRF-exempt (sources
|
||||
don't have sessions)
|
||||
- **Client-side SHA-256**: Drop box upload page uses SubtleCrypto for pre-upload hashing
|
||||
- **Waitress**: Production WSGI server (replaces dev-only Flask server)
|
||||
- **FastAPI option**: `soosef.api` provides a REST API alternative to the Flask web UI
|
||||
- **Pluggable backends**: Stego backends (LSB, DCT) registered via `backends/registry.py`
|
||||
- **ImageHashes generalized**: phash/dhash now optional, enabling non-image attestation
|
||||
- **Lazy path resolution**: Modules use `import soosef.paths as _paths` for --data-dir support
|
||||
- **Two-way federation**: Delivery acknowledgment records enable handshake proof
|
||||
- **Chain record types**: CONTENT_TYPE_KEY_ROTATION, CONTENT_TYPE_KEY_RECOVERY,
|
||||
CONTENT_TYPE_DELIVERY_ACK (in federation/chain.py)
|
||||
|
||||
## Code conventions
|
||||
|
||||
|
||||
203
README.md
203
README.md
@ -14,10 +14,13 @@
|
||||
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
|
||||
- **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
|
||||
@ -47,32 +50,111 @@ channel key, writes a default config, and starts an HTTPS web UI on
|
||||
- **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 files
|
||||
- Perceptual hashing (ImageHash) for tamper-evident photo attestation
|
||||
- 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/`
|
||||
- **Dead man's switch** -- automated purge if check-in is missed
|
||||
- **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
|
||||
- **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)
|
||||
|
||||
### Key Management
|
||||
### Cover / Duress Mode
|
||||
|
||||
- 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
|
||||
- **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
|
||||
|
||||
---
|
||||
|
||||
@ -100,8 +182,8 @@ pip install "soosef[dev]" # All + pytest, black, ruff, mypy
|
||||
| `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 |
|
||||
| `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) |
|
||||
@ -174,7 +256,14 @@ soosef stego encode cover.png -r shared_photo.jpg -m "Secret" -o stego_output.pn
|
||||
# Encode a file instead of text
|
||||
soosef stego encode cover.png -r shared_photo.jpg -f document.pdf
|
||||
|
||||
# Dry run — check capacity without encoding
|
||||
# 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)
|
||||
@ -242,7 +331,7 @@ soosef fieldkit usb check # Check for unauthorized USB devices
|
||||
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-identity # Generate new Ed25519 identity (records rotation in chain)
|
||||
soosef keys rotate-channel # Generate new channel key
|
||||
```
|
||||
|
||||
@ -250,11 +339,20 @@ soosef keys rotate-channel # Generate new channel key
|
||||
|
||||
```bash
|
||||
soosef chain status # Show chain head, length, integrity
|
||||
soosef chain verify # Verify entire chain 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
|
||||
soosef chain export --start 0 --end 100 -o chain.cbor
|
||||
|
||||
# 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
|
||||
```
|
||||
|
||||
---
|
||||
@ -262,7 +360,7 @@ 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.
|
||||
through Flask blueprints. Served by **Waitress** (production WSGI server) by default.
|
||||
|
||||
### Routes
|
||||
|
||||
@ -273,6 +371,7 @@ through Flask blueprints.
|
||||
| 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 -->
|
||||
@ -307,6 +406,7 @@ SooSeF loads configuration from `~/.soosef/config.json`. All fields have sensibl
|
||||
| `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 |
|
||||
|
||||
@ -314,7 +414,7 @@ SooSeF loads configuration from `~/.soosef/config.json`. All fields have sensibl
|
||||
|
||||
| Variable | Description |
|
||||
|---|---|
|
||||
| `SOOSEF_DATA_DIR` | Override the data directory (default: `~/.soosef`) |
|
||||
| `SOOSEF_DATA_DIR` | Override the data directory (default: `~/.soosef`). Enables portable USB mode and cover/duress directory naming |
|
||||
|
||||
---
|
||||
|
||||
@ -326,21 +426,31 @@ SooSeF loads configuration from `~/.soosef/config.json`. All fields have sensibl
|
||||
src/soosef/
|
||||
__init__.py Package init, __version__
|
||||
cli.py Click CLI (entry point: soosef)
|
||||
paths.py All path constants (single source of truth)
|
||||
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)
|
||||
manager.py Key material management (channel + identity + trust store + backup)
|
||||
models.py KeyBundle, IdentityBundle dataclasses
|
||||
export.py Encrypted key bundle export/import
|
||||
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
|
||||
deadman.py Dead man's switch
|
||||
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
|
||||
geofence.py GPS boundary enforcement (gpsd integration)
|
||||
|
||||
frontends/web/
|
||||
app.py Flask app factory (create_app())
|
||||
@ -354,6 +464,7 @@ frontends/web/
|
||||
fieldkit.py /fieldkit/*
|
||||
keys.py /keys/*
|
||||
admin.py /admin/*
|
||||
dropbox.py /dropbox/* (source drop box)
|
||||
```
|
||||
|
||||
### Data directory (`~/.soosef/`)
|
||||
@ -362,15 +473,17 @@ frontends/web/
|
||||
~/.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)
|
||||
auth/ Web UI auth database (soosef.db)
|
||||
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
|
||||
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
|
||||
@ -386,11 +499,14 @@ 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.
|
||||
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.
|
||||
functionality. RFC 3161 timestamping and webhook warnings are optional features that
|
||||
gracefully degrade when offline.
|
||||
|
||||
**Web UI hardening:**
|
||||
- CSRF protection via Flask-WTF
|
||||
@ -398,11 +514,32 @@ functionality.
|
||||
- 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
|
||||
|
||||
@ -207,10 +207,12 @@ This creates the `~/.soosef/` directory structure:
|
||||
channel.key AES-256-GCM channel key
|
||||
attestations/ Verisoo attestation log and index
|
||||
chain/ Hash chain data
|
||||
anchors/ External timestamp anchors
|
||||
auth/ Web UI user database (SQLite)
|
||||
certs/ Self-signed TLS certificates
|
||||
fieldkit/ Killswitch, deadman, tamper, USB, geofence state
|
||||
temp/ Ephemeral upload/processing files
|
||||
dropbox/ Source drop box submissions
|
||||
instance/ Flask session data
|
||||
audit.jsonl Append-only audit trail
|
||||
```
|
||||
@ -319,7 +321,8 @@ panel. All fields have sensible defaults -- you only need to set what you want t
|
||||
| `max_upload_mb` | `50` | Maximum file upload size in MB. |
|
||||
| `session_timeout_minutes` | `15` | Idle session expiry. Lower is safer. |
|
||||
| `login_lockout_attempts` | `5` | Failed logins before lockout. |
|
||||
| `login_lockout_minutes` | `15` | Lockout duration. |
|
||||
| `login_lockout_minutes` | `15` | Lockout duration after exceeding failed login attempts. |
|
||||
| `default_embed_mode` | `auto` | Default steganographic embedding mode for Stegasoo. |
|
||||
| `killswitch_enabled` | `false` | Enable software killswitch. |
|
||||
| `deadman_enabled` | `false` | Enable dead man's switch. |
|
||||
| `deadman_interval_hours` | `24` | Hours between required check-ins. |
|
||||
@ -329,7 +332,8 @@ panel. All fields have sensible defaults -- you only need to set what you want t
|
||||
| `tamper_monitoring_enabled` | `false` | File integrity monitoring. |
|
||||
| `chain_enabled` | `true` | Wrap attestations in a hash chain. |
|
||||
| `chain_auto_wrap` | `true` | Automatically chain verisoo attestations. |
|
||||
| `backup_reminder_days` | `7` | Warn if no backup in this many days. |
|
||||
| `backup_reminder_days` | `7` | Warn if no backup in this many days. `soosef status` reports overdue backups. |
|
||||
| `cover_name` | `""` | If set, used as the CN in the self-signed SSL certificate instead of "localhost". See Section 14 (Cover/Duress Mode). |
|
||||
| `gpio_killswitch_pin` | `17` | GPIO pin for hardware killswitch button. |
|
||||
| `gpio_killswitch_hold_seconds` | `5.0` | Required hold time to trigger hardware killswitch. |
|
||||
|
||||
@ -344,8 +348,10 @@ Example minimal config for a field deployment:
|
||||
"deadman_enabled": true,
|
||||
"deadman_interval_hours": 12,
|
||||
"deadman_grace_hours": 1,
|
||||
"deadman_warning_webhook": "https://hooks.example.com/alert",
|
||||
"killswitch_enabled": true,
|
||||
"backup_reminder_days": 3
|
||||
"backup_reminder_days": 3,
|
||||
"cover_name": "Local Inventory Manager"
|
||||
}
|
||||
```
|
||||
|
||||
@ -382,8 +388,18 @@ soosef status
|
||||
```
|
||||
|
||||
The dead man's switch enforcement loop runs as a background thread inside `soosef serve`,
|
||||
checking every 60 seconds. It will send a webhook warning (if configured) during the grace
|
||||
period, then execute a full purge if the grace period expires.
|
||||
checking every 60 seconds. It will send a webhook warning (if configured via
|
||||
`deadman_warning_webhook`) during the grace period, then execute a full purge if the grace
|
||||
period expires. The webhook must be a public URL -- SSRF protection blocks private/internal
|
||||
IP ranges.
|
||||
|
||||
For cron-based enforcement outside the web server (e.g., on a headless node), use:
|
||||
|
||||
```bash
|
||||
soosef fieldkit check-deadman
|
||||
```
|
||||
|
||||
Exit codes: 0 = not armed or not overdue, 1 = unexpected error, 2 = killswitch fired.
|
||||
|
||||
Disarm:
|
||||
|
||||
@ -407,7 +423,7 @@ Coordinates are in decimal degrees, radius in meters.
|
||||
Record currently connected USB devices as the trusted baseline:
|
||||
|
||||
```bash
|
||||
soosef fieldkit usb learn
|
||||
soosef fieldkit usb snapshot
|
||||
```
|
||||
|
||||
When monitoring is enabled, SooSeF will alert (or trigger killswitch, depending on config)
|
||||
@ -423,6 +439,48 @@ soosef fieldkit tamper baseline
|
||||
|
||||
SooSeF monitors for unexpected changes to tracked files when tamper monitoring is enabled.
|
||||
|
||||
### 8.5 Killswitch
|
||||
|
||||
The killswitch destroys all key material and data in a deliberate order designed to
|
||||
maximize what is gone before any interruption. The destruction sequence is:
|
||||
|
||||
1. **Ed25519 identity keys** (most critical -- without these, signed attestations cannot
|
||||
be forged and encrypted data is unrecoverable)
|
||||
2. **AES-256-GCM channel key**
|
||||
3. **Flask session secret**
|
||||
4. **Auth database** (user accounts)
|
||||
5. **Attestation log and chain data**
|
||||
6. **Temp files and audit log**
|
||||
7. **Configuration**
|
||||
8. **System journal entries** for the soosef unit
|
||||
9. **Deep forensic scrub** (see below)
|
||||
10. **Self-uninstall** of the soosef pip package
|
||||
|
||||
Trigger manually:
|
||||
|
||||
```bash
|
||||
soosef fieldkit purge --confirm CONFIRM-PURGE
|
||||
```
|
||||
|
||||
**Deep forensic scrub.** When the killswitch fires with `ALL` scope, it performs a deep
|
||||
forensic scrub that removes traces of SooSeF beyond the `~/.soosef/` directory:
|
||||
|
||||
- **Python bytecache**: removes all `__pycache__` directories and `.pyc` files for
|
||||
soosef, stegasoo, and verisoo from site-packages
|
||||
- **pip dist-info**: removes package metadata directories that would reveal what was
|
||||
installed
|
||||
- **pip download cache**: removes cached wheels and source distributions under
|
||||
`~/.cache/pip/` matching soosef/stegasoo/verisoo
|
||||
- **Shell history**: rewrites `~/.bash_history`, `~/.zsh_history`, and fish history to
|
||||
remove all lines containing "soosef"
|
||||
- **Self-uninstall**: runs `pip uninstall -y soosef` to remove the package from the
|
||||
virtual environment
|
||||
|
||||
After a full purge, the system will show minimal evidence that SooSeF was ever installed.
|
||||
Note that this is best-effort -- filesystem journal entries, inode metadata, and flash
|
||||
wear-leveling remnants may still exist. For complete deniability, use full-disk encryption
|
||||
(LUKS) and physically destroy the storage media.
|
||||
|
||||
---
|
||||
|
||||
## 9. Key Management
|
||||
@ -443,7 +501,7 @@ Back up keys regularly. SooSeF warns if no backup has been taken within the
|
||||
`backup_reminder_days` window (default: 7 days).
|
||||
|
||||
```bash
|
||||
soosef keys backup --output /media/usb/soosef-backup.enc
|
||||
soosef keys export /media/usb/soosef-backup.enc
|
||||
```
|
||||
|
||||
This creates an encrypted bundle. You will be prompted for a passphrase. Store the USB
|
||||
@ -452,25 +510,25 @@ drive physically separate from the Pi.
|
||||
### 9.2 Restore
|
||||
|
||||
```bash
|
||||
soosef keys restore --input /media/usb/soosef-backup.enc
|
||||
soosef keys import /media/usb/soosef-backup.enc
|
||||
```
|
||||
|
||||
### 9.3 Key rotation
|
||||
|
||||
Generate a new channel key (the old one is overwritten):
|
||||
Rotate the identity keypair (old key is archived, not destroyed):
|
||||
|
||||
```bash
|
||||
soosef init --no-identity
|
||||
soosef keys rotate-identity
|
||||
```
|
||||
|
||||
Generate a new identity (the old one is overwritten -- all previous attestations will
|
||||
reference the old fingerprint):
|
||||
Rotate the channel key:
|
||||
|
||||
```bash
|
||||
soosef init --no-channel
|
||||
soosef keys rotate-channel
|
||||
```
|
||||
|
||||
After rotating keys, take a fresh backup immediately.
|
||||
After rotating keys, take a fresh backup immediately. Notify all collaborators of the
|
||||
new identity fingerprint so they can update their trusted-key lists.
|
||||
|
||||
### 9.4 Trusting collaborator keys
|
||||
|
||||
@ -484,7 +542,401 @@ Verify the fingerprint out-of-band (in person, over a secure channel) before tru
|
||||
|
||||
---
|
||||
|
||||
## 10. Operational Security Notes
|
||||
## 10. Source Drop Box
|
||||
|
||||
The source drop box provides a SecureDrop-like anonymous file intake that runs inside
|
||||
SooSeF. Sources do not need a SooSeF account -- they receive a one-time upload URL and
|
||||
submit files through their browser.
|
||||
|
||||
### 10.1 Creating tokens
|
||||
|
||||
An admin creates a time-limited upload token through the web UI at `/dropbox/admin` or
|
||||
through the admin panel. Each token has:
|
||||
|
||||
- **Label**: a human-readable name for the source (stored server-side only)
|
||||
- **Expiry**: how many hours the link is valid (default: 24)
|
||||
- **Max files**: maximum number of uploads allowed on this link (default: 10)
|
||||
|
||||
Tokens are 32-byte cryptographically random URL-safe strings. Once created, the admin
|
||||
receives a URL of the form `https://<host>:<port>/dropbox/upload/<token>`.
|
||||
|
||||
### 10.2 Sharing URLs securely
|
||||
|
||||
Share the upload URL with the source over an already-secure channel:
|
||||
|
||||
- In-person on paper (best for high-risk sources)
|
||||
- Encrypted messaging (Signal, Wire)
|
||||
- Verbal dictation over a secure voice call
|
||||
|
||||
Never send drop box URLs over unencrypted email or SMS.
|
||||
|
||||
### 10.3 What happens on upload
|
||||
|
||||
When a source uploads files:
|
||||
|
||||
1. The browser computes SHA-256 fingerprints client-side (via SubtleCrypto) before upload
|
||||
so the source has a verifiable record of what they submitted
|
||||
2. EXIF metadata is extracted for evidentiary fields (GPS, timestamp) and then stripped
|
||||
from the stored copy to protect source device information
|
||||
3. The original bytes are attested (signed) before stripping, so the attestation hash
|
||||
matches what the source actually submitted
|
||||
4. The source receives a receipt code (HMAC-derived from the file hash and token) for
|
||||
each file
|
||||
5. The token usage counter increments; once max files is reached, the link stops accepting
|
||||
|
||||
### 10.4 Receipt verification
|
||||
|
||||
Sources can verify their submission was received by posting their receipt code to
|
||||
`/dropbox/verify-receipt`. This returns the filename, SHA-256, and reception timestamp if
|
||||
the receipt is valid.
|
||||
|
||||
### 10.5 Operational security for the drop box
|
||||
|
||||
- **No SooSeF branding**: the upload page is a minimal HTML form with no identifying
|
||||
marks, styled generically
|
||||
- **No authentication required**: the source never creates an account or reveals
|
||||
identity information
|
||||
- **Token self-destruction**: tokens are deleted from the SQLite database after expiry;
|
||||
expired tokens are cleaned up on every admin page load
|
||||
- **Revocation**: admins can revoke tokens immediately from `/dropbox/admin`
|
||||
- **Tor compatibility**: the upload page is a self-contained HTML page with inline
|
||||
JavaScript (SubtleCrypto only) and no external resources. It works over Tor Browser
|
||||
with JavaScript enabled. No CDN, no fonts, no analytics
|
||||
- **No IP logging**: SooSeF does not log source IP addresses. Ensure your reverse proxy
|
||||
(if any) also does not log access. If running behind Tor, the source's real IP is never
|
||||
visible to the server
|
||||
- **Receipt codes are deterministic**: the receipt is an HMAC of the file's SHA-256 keyed
|
||||
by the token, so the source can independently verify it corresponds to their file
|
||||
|
||||
If operating in a high-risk environment, consider running SooSeF as a Tor hidden service
|
||||
(`.onion` address). Configure a torrc hidden service pointing to `127.0.0.1:5000` and
|
||||
share the `.onion` URL instead of a LAN address.
|
||||
|
||||
### 10.6 Drop box file storage
|
||||
|
||||
Uploaded files are stored in `~/.soosef/temp/dropbox/` with filenames derived from the
|
||||
SHA-256 prefix. This directory has mode 0700. Token metadata and receipts are stored in a
|
||||
SQLite database at `~/.soosef/auth/dropbox.db`.
|
||||
|
||||
---
|
||||
|
||||
## 11. Chain Anchoring
|
||||
|
||||
Chain anchoring externally proves that your attestation chain existed before a given time.
|
||||
A single anchor for the chain head implicitly timestamps every record that preceded it,
|
||||
because the chain is append-only with hash linkage.
|
||||
|
||||
### 11.1 When to anchor
|
||||
|
||||
Anchor your chain:
|
||||
|
||||
- Before sharing evidence with a third party (proves the chain existed before disclosure)
|
||||
- At regular intervals (daily or weekly) to establish a timeline
|
||||
- Before and after major investigations or events
|
||||
- Before key rotation (locks the existing chain state)
|
||||
|
||||
### 11.2 Automated anchoring (RFC 3161 TSA)
|
||||
|
||||
If the device has internet access (even temporarily), submit the chain head to a
|
||||
Timestamping Authority:
|
||||
|
||||
```bash
|
||||
soosef chain anchor --tsa https://freetsa.org/tsr
|
||||
```
|
||||
|
||||
This sends the chain head digest to the TSA, receives a signed timestamp token, and saves
|
||||
both the anchor and the TSA response as a JSON file under `~/.soosef/chain/anchors/`.
|
||||
|
||||
The TSA token is a cryptographically signed proof from a third party that the hash existed
|
||||
at the timestamp. This is legally stronger than a self-asserted timestamp.
|
||||
|
||||
### 11.3 Manual anchoring
|
||||
|
||||
Without `--tsa`, the command exports the anchor hash for manual external submission:
|
||||
|
||||
```bash
|
||||
soosef chain anchor
|
||||
```
|
||||
|
||||
This prints a compact text block containing the chain ID, head index, record count, and
|
||||
digest hash. Publish this text to any external witness:
|
||||
|
||||
- Tweet or public social media post (timestamped by the platform)
|
||||
- Email to a trusted third party (timestamped by the mail server)
|
||||
- Newspaper classified advertisement
|
||||
- Blockchain transaction (e.g., Bitcoin OP_RETURN)
|
||||
- Notarized document
|
||||
|
||||
The anchor file is saved locally regardless of whether a TSA was used.
|
||||
|
||||
### 11.4 Airgapped anchoring procedure
|
||||
|
||||
For fully airgapped deployments:
|
||||
|
||||
1. Run `soosef chain anchor` on the airgapped device
|
||||
2. Copy the printed anchor text to a USB drive (text file, photograph of screen, or
|
||||
paper transcription)
|
||||
3. On an internet-connected device, publish the anchor text to one or more external
|
||||
witnesses
|
||||
4. Document the publication (URL, screenshot, transaction ID) and store it alongside
|
||||
the USB key backup
|
||||
|
||||
### 11.5 Verifying anchors
|
||||
|
||||
To verify that the current chain state matches a previously created anchor:
|
||||
|
||||
```bash
|
||||
soosef chain verify
|
||||
```
|
||||
|
||||
This checks all hash linkage and signatures in the chain. If the chain has been tampered
|
||||
with since the anchor was created, verification will fail.
|
||||
|
||||
---
|
||||
|
||||
## 12. Cross-Organization Federation
|
||||
|
||||
Federation allows multiple SooSeF instances to exchange attestation records for
|
||||
collaborative investigations. Bundles are self-authenticating: each record carries the
|
||||
signer's public key, so the importer can verify signatures against their trust store.
|
||||
|
||||
### 12.1 Exchanging trust keys
|
||||
|
||||
Before two organizations can exchange attestation bundles, they must trust each other's
|
||||
identity keys.
|
||||
|
||||
On Organization A:
|
||||
|
||||
```bash
|
||||
# Export public key
|
||||
cp ~/.soosef/identity/public.pem /media/usb/org-a-pubkey.pem
|
||||
```
|
||||
|
||||
On Organization B:
|
||||
|
||||
```bash
|
||||
# Import Org A's key and verify fingerprint
|
||||
soosef keys trust --import /media/usb/org-a-pubkey.pem
|
||||
```
|
||||
|
||||
Always verify fingerprints out-of-band (in person, over a known-secure voice channel).
|
||||
Repeat in both directions so each organization trusts the other.
|
||||
|
||||
### 12.2 Exporting attestation bundles
|
||||
|
||||
Export a JSON bundle containing attestation records and chain data:
|
||||
|
||||
```bash
|
||||
soosef chain export --output /media/usb/investigation-bundle.zip
|
||||
```
|
||||
|
||||
To export only records from a specific index range:
|
||||
|
||||
```bash
|
||||
soosef chain export --start 100 --end 200 --output /media/usb/partial-bundle.zip
|
||||
```
|
||||
|
||||
The export includes:
|
||||
|
||||
- Attestation records with full signatures
|
||||
- Chain records with hash linkage
|
||||
- The signer's public key
|
||||
- A standalone verification script (requires only Python + `cryptography`)
|
||||
- A human-readable README
|
||||
|
||||
### 12.3 Importing attestation bundles
|
||||
|
||||
On the receiving organization's SooSeF instance:
|
||||
|
||||
- Records are imported into the local attestation log with a `federated_from` metadata tag
|
||||
- Records signed by untrusted fingerprints are rejected (unless trust-on-first-use is used)
|
||||
- Duplicate records (matching SHA-256) are skipped automatically
|
||||
|
||||
### 12.4 Delivery acknowledgments
|
||||
|
||||
When a bundle is imported and the receiving instance has a chain store and private key,
|
||||
SooSeF automatically creates a delivery acknowledgment record in the local chain. This
|
||||
records the bundle hash, sender fingerprint, and count of records received. The
|
||||
acknowledgment provides a cryptographic receipt that the bundle was delivered and ingested.
|
||||
|
||||
### 12.5 Selective disclosure for legal proceedings
|
||||
|
||||
To produce evidence for a court order or legal discovery request without revealing the
|
||||
entire chain:
|
||||
|
||||
```bash
|
||||
soosef chain disclose --indices 42,43,44 --output disclosure.json
|
||||
```
|
||||
|
||||
This exports a proof bundle where the selected records are shown in full and all other
|
||||
records appear only as hashes. A third party can verify that the selected records are part
|
||||
of an unbroken hash chain without seeing the contents of other records.
|
||||
|
||||
### 12.6 Federation on airgapped networks
|
||||
|
||||
All federation is designed for offline/sneakernet operation:
|
||||
|
||||
1. Export the bundle to a USB drive on the sending instance
|
||||
2. Physically carry the USB to the receiving instance
|
||||
3. Import the bundle
|
||||
4. Export the delivery acknowledgment back on USB if needed
|
||||
|
||||
No network connectivity is required at any point.
|
||||
|
||||
---
|
||||
|
||||
## 13. Evidence Packages and Cold Archives
|
||||
|
||||
SooSeF provides two export formats for preserving evidence outside the running instance.
|
||||
|
||||
### 13.1 Evidence packages
|
||||
|
||||
An evidence package is a self-contained ZIP that bundles everything needed for independent
|
||||
verification of specific attested images. Use evidence packages when you need to hand off
|
||||
a subset of evidence to a third party (lawyer, court, partner organization).
|
||||
|
||||
Contents of an evidence package:
|
||||
|
||||
- `images/` -- original image files
|
||||
- `manifest.json` -- attestation records and chain data for those images
|
||||
- `public_key.pem` -- signer's Ed25519 public key
|
||||
- `verify.py` -- standalone verification script (requires only Python 3.11+ and
|
||||
`cryptography`)
|
||||
- `README.txt` -- human-readable instructions
|
||||
|
||||
The package is self-contained. No SooSeF installation is required to verify the evidence.
|
||||
The standalone `verify.py` script checks image SHA-256 hashes against attestation records
|
||||
and verifies chain hash linkage.
|
||||
|
||||
**When to create evidence packages:**
|
||||
|
||||
- Before handing evidence to a legal team
|
||||
- When sharing with a partner organization that does not run SooSeF
|
||||
- For court submission (the self-contained verifier is the key feature)
|
||||
- Before any action that might destroy the running instance (travel through hostile
|
||||
checkpoints, anticipated raids)
|
||||
|
||||
### 13.2 Cold archives
|
||||
|
||||
A cold archive is a full snapshot of the entire SooSeF evidence store, designed for
|
||||
long-term preservation. It follows OAIS (ISO 14721) alignment: the archive is
|
||||
self-describing, includes its own verification code, and documents the cryptographic
|
||||
algorithms used.
|
||||
|
||||
Contents of a cold archive:
|
||||
|
||||
- `chain/` -- raw append-only hash chain binary, state checkpoint, and anchor files
|
||||
- `attestations/` -- full verisoo attestation log and LMDB index
|
||||
- `keys/public.pem` -- signer's public key
|
||||
- `keys/bundle.enc` -- encrypted key bundle (optional, password-protected)
|
||||
- `keys/trusted/` -- trusted collaborator public keys
|
||||
- `manifest.json` -- archive metadata and integrity hashes
|
||||
- `verify.py` -- standalone verification script
|
||||
- `ALGORITHMS.txt` -- documents all cryptographic algorithms and formats used (Ed25519,
|
||||
SHA-256, AES-256-GCM, Argon2id, CBOR, etc.) so the archive remains verifiable even if
|
||||
SooSeF no longer exists
|
||||
- `README.txt` -- human-readable description
|
||||
|
||||
To restore a cold archive on a fresh SooSeF instance:
|
||||
|
||||
```bash
|
||||
soosef archive import <archive.zip>
|
||||
```
|
||||
|
||||
**When to create cold archives:**
|
||||
|
||||
- At regular intervals (weekly or monthly) as part of your backup strategy
|
||||
- Before key rotation
|
||||
- Before traveling with the device
|
||||
- Before anticipated risk events
|
||||
- When archiving a completed investigation
|
||||
|
||||
### 13.3 Legal discovery workflow
|
||||
|
||||
For legal discovery and court proceedings:
|
||||
|
||||
1. Use `soosef chain disclose` for selective disclosure (Section 12.5) when you must
|
||||
respond to a specific request without revealing the full chain
|
||||
2. Use evidence packages for handing specific images and their attestations to counsel
|
||||
3. Use cold archives when full preservation is required
|
||||
|
||||
All three formats include standalone verification scripts so that the receiving party does
|
||||
not need to install SooSeF.
|
||||
|
||||
### 13.4 Long-term archival best practices
|
||||
|
||||
- Store cold archives on at least two separate physical media (USB drives, optical discs)
|
||||
- Keep one copy offsite (safe deposit box, trusted third party)
|
||||
- Include the encrypted key bundle in the archive (set a strong passphrase and store it
|
||||
separately from the archive media)
|
||||
- Write the passphrase on paper and store it in a different physical location from the
|
||||
archive media
|
||||
- Periodically verify archive integrity: unzip and run `python verify.py`
|
||||
- The `ALGORITHMS.txt` file documents everything needed to write a verifier from scratch,
|
||||
even decades from now
|
||||
|
||||
---
|
||||
|
||||
## 14. Cover/Duress Mode
|
||||
|
||||
Cover mode disguises a SooSeF installation so that casual inspection of the device does
|
||||
not immediately reveal it as a security toolkit.
|
||||
|
||||
### 14.1 Renaming the data directory
|
||||
|
||||
By default, SooSeF stores everything under `~/.soosef/`. To use an inconspicuous name,
|
||||
set the `SOOSEF_DATA_DIR` environment variable:
|
||||
|
||||
```bash
|
||||
export SOOSEF_DATA_DIR=~/.local/share/inventory
|
||||
soosef init
|
||||
```
|
||||
|
||||
All SooSeF commands respect this variable. Add it to the soosef user's shell profile or
|
||||
the systemd service file:
|
||||
|
||||
```ini
|
||||
Environment="SOOSEF_DATA_DIR=/home/soosef/.local/share/inventory"
|
||||
```
|
||||
|
||||
You can also pass `--data-dir` to any command:
|
||||
|
||||
```bash
|
||||
soosef --data-dir ~/.local/share/inventory serve --host 0.0.0.0
|
||||
```
|
||||
|
||||
### 14.2 Cover name for SSL certificates
|
||||
|
||||
Set `cover_name` in config to change the Common Name (CN) in the self-signed SSL
|
||||
certificate. Without this, the certificate CN defaults to "localhost". With a cover name,
|
||||
a browser inspector sees a plausible-looking certificate:
|
||||
|
||||
```json
|
||||
{
|
||||
"cover_name": "Local Inventory Manager"
|
||||
}
|
||||
```
|
||||
|
||||
Delete `~/.soosef/certs/cert.pem` and restart the server to regenerate the certificate
|
||||
with the new CN.
|
||||
|
||||
### 14.3 Portable USB operation
|
||||
|
||||
SooSeF can run entirely from a USB drive:
|
||||
|
||||
1. Install SooSeF into a virtualenv on the USB drive
|
||||
2. Set `SOOSEF_DATA_DIR` to a directory on the USB
|
||||
3. Run `soosef serve` from the USB
|
||||
|
||||
When you remove the USB, no trace of SooSeF remains on the host machine (assuming no
|
||||
swap, no core dumps, and the host filesystem was not used for temp files).
|
||||
|
||||
Combine with the `cover_name` setting and a renamed data directory for maximum
|
||||
deniability.
|
||||
|
||||
---
|
||||
|
||||
## 15. Operational Security Notes
|
||||
|
||||
SooSeF is a tool, not a shield. Understand what it cannot do.
|
||||
|
||||
@ -541,7 +993,7 @@ This is a fundamental limitation of Python-based security tools.
|
||||
|
||||
---
|
||||
|
||||
## 11. Troubleshooting
|
||||
## 16. Troubleshooting
|
||||
|
||||
### Health check
|
||||
|
||||
@ -561,7 +1013,7 @@ soosef status
|
||||
```
|
||||
|
||||
This checks identity key, channel key, trusted keys, dead man's switch state, geofence,
|
||||
chain status, and backup status.
|
||||
chain status, and backup status. Use `--json` for machine-readable output.
|
||||
|
||||
### Common issues
|
||||
|
||||
@ -636,3 +1088,52 @@ Always run SooSeF as the same user.
|
||||
```bash
|
||||
ls -la ~/.soosef/
|
||||
```
|
||||
|
||||
**Drop box tokens expire immediately**
|
||||
|
||||
Token expiry is checked against UTC. If the Pi's system clock is wrong, tokens may appear
|
||||
expired as soon as they are created. Verify the clock:
|
||||
|
||||
```bash
|
||||
date -u
|
||||
```
|
||||
|
||||
On airgapped systems without NTP, set the clock manually before creating tokens:
|
||||
|
||||
```bash
|
||||
sudo date -s "2026-04-01 12:00:00"
|
||||
```
|
||||
|
||||
**Chain anchor TSA submission fails**
|
||||
|
||||
TSA submission requires network access. On an airgapped device, use manual anchoring
|
||||
instead (`soosef chain anchor` without `--tsa`). If the TSA URL is unreachable, the anchor
|
||||
is still saved locally -- only the external timestamp token is missing.
|
||||
|
||||
**SSL certificate shows wrong name**
|
||||
|
||||
If you set `cover_name` after the certificate was already generated, delete the old
|
||||
certificate and restart:
|
||||
|
||||
```bash
|
||||
rm ~/.soosef/certs/cert.pem ~/.soosef/certs/key.pem
|
||||
sudo systemctl restart soosef
|
||||
```
|
||||
|
||||
**Account lockout after repeated failed logins**
|
||||
|
||||
After `login_lockout_attempts` (default: 5) failed login attempts, the account is locked
|
||||
for `login_lockout_minutes` (default: 15) minutes. Wait for the lockout to expire, or
|
||||
restart the server to clear lockout state.
|
||||
|
||||
**Evidence package verify.py fails**
|
||||
|
||||
The standalone verification script requires Python 3.11+ and the `cryptography` package.
|
||||
Install it in a fresh virtualenv on the verifying machine:
|
||||
|
||||
```bash
|
||||
python3 -m venv verify-env
|
||||
source verify-env/bin/activate
|
||||
pip install cryptography
|
||||
python verify.py
|
||||
```
|
||||
|
||||
Loading…
Reference in New Issue
Block a user