- JavaScript 45.2%
- HTML 33.7%
- Python 19.7%
- CSS 1.3%
- Shell 0.1%
| deploy | ||
| docs | ||
| gemale | ||
| lexicon | ||
| tests | ||
| tools | ||
| webapp | ||
| .gitignore | ||
| config.example.json | ||
| README.md | ||
Gemale — CLI-Referenz (Python)
Verschlüsselte Kurzbotschaften, versteckt in einer harmlosen Handzeichnung, gelesen über einen reinen Text-Dialog. Ohne Netz, ohne Strom, ohne vorausgesetztes Gerät. Dieses Repo ist der Kommandozeilen-Prototyp aus Phase 2 des Projekts (siehe Phase-0-Spezifikation und Papiertest, Phase 1).
Der Mensch ist der Codec: Er setzt eine Textanleitung in eine Skizze um und liest eine Skizze über einen Fragebogen zurück. Die App macht nur Krypto, Codebuch und Fehlerkorrektur — keine Bildverarbeitung, keine Kamera.
Es gibt zwei Umsetzungen, die byte-genau kompatibel sind (Cross-Language-Interop
geprüft): dieses CLI (Python) und die PWA unter webapp/ (JavaScript). Eine auf dem
Handy gezeichnete Nachricht liest das CLI und umgekehrt.
Die PWA läuft live auf gemale.de — offline-fähig, installierbar, ganz ohne externe Abhängigkeiten. Sie kann über das CLI hinaus: passwortgeschützter Schlüssel-Tresor (PBKDF2 → AES-GCM), QR-Schlüsseltausch mit In-App-Kamera für den Austausch im Funkloch, sowie eine Mal-Fassade als Tarnung (ohne Anmeldung zeigt die App nur eine funktionierende Zeichenfläche; die echten Reiter erscheinen erst nach einer vereinbarten Geste + Passphrase). Ein Panik-Knopf und ein Inaktivitäts-Timeout sperren den Tresor sofort bzw. automatisch.
Schnellstart
cd gemale
python3 -m gemale demo --scene zug # Vorführung mit einer Szene
python3 -m gemale info --scene unterwasser
Kompaktformat (1 Bild) mit Schlüssel:
python3 -m gemale genkey --out key.hex
# Senden: Botschaft -> Zeichenanleitung (die man dann abzeichnet)
python3 -m gemale encode --scene haus --typ treffen --stunde 18 \
--ort 3 --ttl 7 --key key.hex
# Lesen: Fragebogen zur fremden Skizze -> Botschaft
python3 -m gemale decode --scene haus --key key.hex
Die strukturierte Botschaft hat vier Felder:
| Feld | Werte |
|---|---|
--typ |
eine von 16 Arten: treffen, status_ok, hilfe, warnung, ressource, komme, bleib_weg, freitext, entwarnung, sammeln, evakuieren, angriff, verletzte, nachschub, position, abbruch |
--stunde |
0–23 |
--ort |
0–7 (acht vereinbarte Orte pro Gruppe) |
--ttl |
Gültigkeit in Tagen: 1, 2, 3, 5, 7, 14, 30 oder 0 = unbegrenzt (Hinterlassenschaft) |
Sechs Szenen: haus, unterwasser, zug, familie, bauernhof, geburtstag. decode
ohne --answers fragt interaktiv; encode --json gibt die Kanalwerte aus.
Freitext über mehrere Bilder (Hybrid-Container)
Bis zu 3 Bilder als ein Paket: strukturierter Kopf plus Freitext aus dem geteilten Codebuch (Wörter, Phrasen, Emojis), Huffman-komprimiert.
# 3 Bilder tragen die Botschaft + mehrere Wörter
python3 -m gemale text-encode --scenes haus,unterwasser,zug \
--typ treffen --stunde 18 --ort 3 --ttl 7 --words "bringt wasser mit" --key key.hex
# Lesen: je Bild ein Fragebogen
python3 -m gemale text-decode --scenes haus,unterwasser,zug --key key.hex
Alles ist ein Parameter
Der Kern des Designs: Was der Papiertest (Phase 1) verändern könnte, ist eine
Stellschraube in der Konfiguration — ohne Code anzufassen. Voreinstellung in
gemale/config.py (DEFAULT), Überschreiben per JSON:
python3 -m gemale info --config config.example.json
python3 -m gemale encode --config config.example.json --key key.hex --typ warnung
Eine Override-Datei muss nicht vollständig sein: Jeder Top-Level-Block, den sie
setzt (codebook, channels, ecc), ersetzt genau diesen Block; der Rest bleibt
Voreinstellung. config.example.json zeigt typische Anpassungen nach einem
Papiertest (wackelige Zählkanäle verkleinert, ein Kanal abgeschaltet, Kapazität am
Codebuch ausgeglichen).
Wichtigste Stellschrauben:
| Parameter | Wirkung |
|---|---|
channels[].range / .states |
Wertebereich eines Kanals — verkleinern, wenn er im Papiertest Fehler macht |
channels[].enabled |
Kanal ganz abschalten, ohne ihn zu löschen |
codebook.mac_bits |
Fälschungsschutz gegen Kapazität — der wichtigste Kompromiss |
codebook.fields_secret |
Felder der Botschaft (Bitbreite oder benannte Werteliste) |
codebook.fields_cleartext[counter].bits |
Wie viele Botschaften vor Nonce-Wiederholung (PSK) |
ecc.scheme |
none (nur Erkennung) oder rs (Reed-Solomon, kostet Kapazität) |
info rechnet nach jeder Änderung sofort vor, ob Nutzlast in die Kanäle passt
(Reserve … [OK] / [ZU KLEIN!]), und encode bricht mit klarer Meldung ab,
wenn nicht.
Architektur (Schichten der Spezifikation)
Botschaft ─► codebook ─► aead (Verschlüsseln + MAC) ─► [gf_rs] ─► channels ─► scene
dict int Kopf + Ciphertext + Tag optional Mixed-Radix Text
| Datei | Schicht |
|---|---|
config.py |
alle Parameter + Kapazitätsrechnung |
codebook.py |
Botschaft ↔ Integer (Klartext-Kopf / geheime Nutzlast getrennt) |
aead.py |
Verschlüsselung + Echtheitsprüfung (Encrypt-then-MAC) |
gf_rs.py |
Reed-Solomon über GF(256), optional |
pipeline.py |
Mixed-Radix Kanalwerte + encode / decode / selfcheck |
container.py |
Mehrbild-Hybrid: strukturierter Kopf + Huffman-Freitext |
huffman.py |
deterministische Huffman-Codes fürs Freitext-Codebuch |
vocab.py |
geteiltes Codebuch (Wörter/Phrasen/Emojis) — generiert aus lexicon/lexicon.json |
scenes/*.py |
die austauschbaren Motive: Kanalwerte ↔ Anleitung/Fragebogen |
cli.py |
Kommandozeile |
config.py + codebook.py + aead.py + pipeline.py sind der szenenunabhängige
Kern; scenes/*.py sind die austauschbaren Häute. Eine weitere Szene ist eine
Parallel-Datei unter scenes/ mit eigenem Kanalsatz. Das Freitext-Codebuch entsteht
aus lexicon/lexicon.json über node tools/gen_lexicon.mjs (erzeugt vocab.py +
webapp/js/vocab.js byte-gleich).
Zwei ehrliche Befunde aus dem Bauen
-
Kapazität ist knapp. Eine einzelne unverdächtige Zeichnung trägt real nur ~50 Bit (nach der Merkmals-Erweiterung; die schlanken Ur-Szenen hatten ~30–36). Davon nutzt das Kompaktformat 33 Bit Nutzlast mit einer 12-Bit-MAC (1/4096 Fälschungschance pro handgemaltem Versuch — jeder wird zusätzlich von einem Menschen geprüft). Der Mehrbild-Container hat einen eigenen 16-Bit-MAC und trägt im Freitext ~12 Wörter. Mehr MAC/Felder brauchen mehr Kanäle oder mehr Bilder.
-
Byte-Reed-Solomon ist hier teuer. Jedes Paritäts-Byte kostet 8 Bit Kapazität — für ein einzelnes Bild schnell zu viel. Voreinstellung ist deshalb
ecc: none: Fehler werden über die MAC erkannt und über den Selbstcheck (Absender liest die eigene Skizze zurück, bevor er sie hinterlässt) abgefangen, statt sie vorwärts zu korrigieren. RS ist voll implementiert und getestet und lohnt bei kapazitätsstarken Profilen oder Mehrbild-Botschaften.
Krypto-Hinweis
aead.py setzt bekannte Standard-Bausteine der Python-Standardbibliothek zusammen
(SHA-256-Schlüsselstrom + HMAC-SHA-256, Encrypt-then-MAC) — abhängigkeitsfrei und
korrekt für den Prototyp, aber kein eigenes Krypto-Design. Für den echten
Einsatz aead.py durch libsodium (PyNaCl, XChaCha20-Poly1305) ersetzen; die
Schnittstelle seal_bits/open_bits bleibt gleich. Rein symmetrisch, Schlüssel
persönlich getauscht — quantenresistent, kein angreifbarer Public-Key-Handshake.
Dokumentation
Die eingefrorenen Konzept- und Protokoll-Dokumente liegen unter docs/.
Die Dateien in docs/artifacts/ sind die Repo-Sicherung früherer
Online-Konzeptseiten — nichts davon liegt noch außerhalb dieses Repos oder gemale.de.
| Dokument | Inhalt |
|---|---|
| Phase-0-Spezifikation | Konzept eingefroren: Prinzipien, Schichtarchitektur, Codebuch v1, Sicherheitsmodell, Kanäle, Phasenplan |
| Papiertest (Phase 1) | Analoges Testkit: Würfeltabelle, Testkarten, Fragebogen, Go/No-Go |
| Protokoll-Spezifikation v0.1 | Gruppen, Schlüssel & Zeit: Blom-Mesh, Rundruf-Fail-Safe, Epochen/TTL, App-Verteilung |
| Kritzel-16 — Glyphen-Alphabet | Frühes Formatkonzept: 16-Glyphen-Alphabet + Motiv-Modus, Kanal-Katalog |
| Kurzanleitung für Eingeweihte | Bedien-Kurzanleitung (identisch zum in-App-Hilfe-Reiter) |
| Krypto-Härtung (v2-Plan) | Ehrliche Schwächen-Bewertung + Härtungs-Tracks (A ✅ AEAD, B ✅ Tresor, C offen) |
Tests
python3 -m unittest discover -s tests -v
Deckt ab: Rundlauf über 400 Zufallsbotschaften, Makro-Variation (gleiche Botschaft → verschiedene Bilder), Ablehnung bei falschem Schlüssel und bei Verfälschung, Reed-Solomon-Korrektur, und dass der Kapazitäts-Wächter anschlägt.