From cc6bbcfdaecad29832bf65bb598a861962d471fb Mon Sep 17 00:00:00 2001 From: Claude Code Date: Wed, 15 Jul 2026 11:47:59 +0000 Subject: [PATCH] =?UTF-8?q?CLI=20genkey:=20flache,=20beschreibende=20Schl?= =?UTF-8?q?=C3=BCsseldateinamen=20(einheitlich=20zum=20Web)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Statt Ordnern _meine_seite/ mit send.key/recv.key erzeugt genkey jetzt vier flache Dateien _{meine,partner}_seite__{send,recv}.key. So trägt der Name Seite+Rolle -> im Browser hoch/runtergeladen überschreibt der aktualisierte Schlüssel sauber die richtige Datei (kein generisches send.key mehr). Interop-Runner + mjs + README an das neue Schema angepasst. Co-Authored-By: Claude Opus 4.8 --- README.md | 15 +++++++++------ schattenpost.py | 37 ++++++++++++++++++++++++------------- web/_interop_run.sh | 4 ++-- web/_interop_test.mjs | 4 ++-- 4 files changed, 37 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 7fc702e..cd07ddf 100644 --- a/README.md +++ b/README.md @@ -95,7 +95,7 @@ statt stillen Kauderwelsch zu liefern. Lege die Schlüssel auf einen **USB-Stick** und häng ihn nur ans gerade benutzte Gerät. Dann existiert jeder `send.key` (mit seinem Offset) **physisch nur einmal** — zwei Zähler können nicht auseinanderdriften, und auf Handy/Laptop liegt nie ein -Schlüssel herum. Mehrere Kontakte = mehrere Schlüsselordner auf dem Stick. +Schlüssel herum. Mehrere Kontakte = mehrere Schlüsselsätze auf dem Stick. ## Voraussetzungen @@ -115,17 +115,20 @@ pip install Pillow python3 schattenpost.py genkey -o kontakt_bob -s 1000000 # 1 MB je Richtung ``` -Erzeugt zwei Ordner: +Erzeugt vier Dateien mit beschreibenden Namen (Seite + Rolle stehen im Dateinamen +— einheitlich zum Web-Frontend): -* `kontakt_bob_meine_seite/` → **du** behältst ihn (enthält `send.key` + `recv.key`). -* `kontakt_bob_partner_seite/` → **dem Partner** geben (Stick, persönlich). +* `kontakt_bob_meine_seite__send.key` + `kontakt_bob_meine_seite__recv.key` + → **du** behältst sie. +* `kontakt_bob_partner_seite__send.key` + `kontakt_bob_partner_seite__recv.key` + → **dem Partner** geben (Stick, persönlich). ### 2. Senden ```bash python3 schattenpost.py hide \ -t "Treffpunkt morgen 18 Uhr." \ - -k kontakt_bob_meine_seite/send.key \ + -k kontakt_bob_meine_seite__send.key \ -c urlaubsfoto.jpg \ -o Screenshot_20260714_220801.png ``` @@ -136,7 +139,7 @@ Alternativ `-i datei` statt `-t`, oder Text über stdin. ### 3. Empfangen ```bash -python3 schattenpost.py reveal -s Screenshot_20260714_220801.png -k kontakt_bob_meine_seite/recv.key +python3 schattenpost.py reveal -s Screenshot_20260714_220801.png -k kontakt_bob_meine_seite__recv.key ``` Mit `-o klartext.txt` in eine Datei statt auf stdout. diff --git a/schattenpost.py b/schattenpost.py index 0aeb7ab..90e68c2 100644 --- a/schattenpost.py +++ b/schattenpost.py @@ -258,28 +258,39 @@ def extract(stego_path: str) -> dict: # --------------------------------------------------------------------------- # def cmd_genkey(args): size = args.size - mine = args.output + "_meine_seite" - theirs = args.output + "_partner_seite" - for d in (mine, theirs): - if os.path.exists(d) and not args.force: - sys.exit(f"[!] {d} existiert schon. --force zum Überschreiben.") - os.makedirs(d, exist_ok=True) + base = args.output + # Flache, beschreibende Dateinamen (einheitlich zum Web-Frontend): der + # Name trägt Seite + Rolle -> im Browser hoch- und wieder runtergeladen + # überschreibt er sauber die richtige Datei. + mine_send = f"{base}_meine_seite__send.key" + mine_recv = f"{base}_meine_seite__recv.key" + their_send = f"{base}_partner_seite__send.key" + their_recv = f"{base}_partner_seite__recv.key" + for path in (mine_send, mine_recv, their_send, their_recv): + if os.path.exists(path) and not args.force: + sys.exit(f"[!] {path} existiert schon. --force zum Überschreiben.") + parent = os.path.dirname(base) + if parent: + os.makedirs(parent, exist_ok=True) # Zwei unabhängige Ströme: sid_out = ich->Partner, sid_in = Partner->ich sid_out, sid_in = os.urandom(8), os.urandom(8) mat_out, mat_in = os.urandom(size), os.urandom(size) # Meine Seite: sende mit Strom OUT, empfange Strom IN - write_key(os.path.join(mine, "send.key"), ROLE_SEND, sid_out, 0, mat_out) - write_key(os.path.join(mine, "recv.key"), ROLE_RECV, sid_in, 0, mat_in) + write_key(mine_send, ROLE_SEND, sid_out, 0, mat_out) + write_key(mine_recv, ROLE_RECV, sid_in, 0, mat_in) # Partnerseite (spiegelverkehrt): sendet mit Strom IN, empfängt Strom OUT - write_key(os.path.join(theirs, "send.key"), ROLE_SEND, sid_in, 0, mat_in) - write_key(os.path.join(theirs, "recv.key"), ROLE_RECV, sid_out, 0, mat_out) + write_key(their_send, ROLE_SEND, sid_in, 0, mat_in) + write_key(their_recv, ROLE_RECV, sid_out, 0, mat_out) print(f"[+] Schlüsselpaar erzeugt ({size} Bytes je Richtung, echter Zufall).") - print(f" {mine}/ <- DU behältst diesen Ordner") - print(f" {theirs}/ <- gib DIESEN Ordner dem Partner (Stick, persönlich)") - print("[i] Jede Seite hat send.key (zum Senden) + recv.key (zum Empfangen).") + print(" DU behältst:") + print(f" {mine_send} (zum Senden)") + print(f" {mine_recv} (zum Empfangen)") + print(" Dem PARTNER geben (Stick, persönlich):") + print(f" {their_send}") + print(f" {their_recv}") print("[i] send.key nur an EINEM Gerät gleichzeitig benutzen.") diff --git a/web/_interop_run.sh b/web/_interop_run.sh index 3316e17..164a867 100755 --- a/web/_interop_run.sh +++ b/web/_interop_run.sh @@ -12,7 +12,7 @@ $PY web/_interop_rawrgb.py mkcarrier web/_carrier.png 200 200 >/dev/null echo "== A) CLI verschlüsselt+authentifiziert -> Bild ==" $PY schattenpost.py hide -t "MsgA vom CLI 🕵️" \ - -k t2/rel_meine_seite/send.key -c web/_carrier.png -o web/_a.png >/dev/null + -k t2/rel_meine_seite__send.key -c web/_carrier.png -o web/_a.png >/dev/null $PY web/_interop_rawrgb.py png2rgb web/_a.png web/_a_rgb.bin >/dev/null echo "== B) Trägerbild als rohes RGB für den Web-Core bereitstellen ==" @@ -24,7 +24,7 @@ node web/_interop_test.mjs echo "== B) Web-Ausgabe zurück nach PNG, vom CLI aufdecken+prüfen ==" read -r W H < web/_b_dims.txt $PY web/_interop_rawrgb.py rgb2png web/_b_out_rgb.bin "$W" "$H" web/_b_stego.png >/dev/null -OUT=$($PY schattenpost.py reveal -s web/_b_stego.png -k t2/rel_meine_seite/recv.key) +OUT=$($PY schattenpost.py reveal -s web/_b_stego.png -k t2/rel_meine_seite__recv.key) echo " CLI reveal: $OUT" if [ "$OUT" = "MsgB aus dem Browser-Core 🌐" ]; then echo " ✓ B round-trip (Web→CLI) inkl. MAC-Prüfung ok" diff --git a/web/_interop_test.mjs b/web/_interop_test.mjs index 23a5ad4..54e4264 100644 --- a/web/_interop_test.mjs +++ b/web/_interop_test.mjs @@ -26,7 +26,7 @@ const hex = (s) => new Uint8Array(s.match(/../g).map((h) => parseInt(h, 16))); // A: CLI (send.key, SPS3) -> Web-Core (recv.key): extrahieren, MAC prüfen, entschlüsseln { const rgb = U('./_a_rgb.bin'); - const recv = SP.parseKey(U('../t2/rel_partner_seite/recv.key')); + const recv = SP.parseKey(U('../t2/rel_partner_seite__recv.key')); const info = SP.extract(rgb); console.log('A) CLI → Web'); ok(info.version === SP.STEGO_VERSION, 'Container ist SPS3'); @@ -48,7 +48,7 @@ const hex = (s) => new Uint8Array(s.match(/../g).map((h) => parseInt(h, 16))); // B: Web-Core (send.key, SPS3) -> CLI (recv.key) { const rgb = U('./_b_rgb.bin'); - const send = SP.parseKey(U('../t2/rel_partner_seite/send.key')); + const send = SP.parseKey(U('../t2/rel_partner_seite__send.key')); const msg = 'MsgB aus dem Browser-Core 🌐'; console.log('B) Web → CLI'); ok(send.role === SP.ROLE_SEND, 'partner send.key hat Rolle SEND');