CLI genkey: flache, beschreibende Schlüsseldateinamen (einheitlich zum Web)
Statt Ordnern <base>_meine_seite/ mit send.key/recv.key erzeugt genkey jetzt
vier flache Dateien <base>_{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 <noreply@anthropic.com>
This commit is contained in:
15
README.md
15
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
|
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**
|
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
|
— 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
|
## Voraussetzungen
|
||||||
|
|
||||||
@@ -115,17 +115,20 @@ pip install Pillow
|
|||||||
python3 schattenpost.py genkey -o kontakt_bob -s 1000000 # 1 MB je Richtung
|
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_meine_seite__send.key` + `kontakt_bob_meine_seite__recv.key`
|
||||||
* `kontakt_bob_partner_seite/` → **dem Partner** geben (Stick, persönlich).
|
→ **du** behältst sie.
|
||||||
|
* `kontakt_bob_partner_seite__send.key` + `kontakt_bob_partner_seite__recv.key`
|
||||||
|
→ **dem Partner** geben (Stick, persönlich).
|
||||||
|
|
||||||
### 2. Senden
|
### 2. Senden
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python3 schattenpost.py hide \
|
python3 schattenpost.py hide \
|
||||||
-t "Treffpunkt morgen 18 Uhr." \
|
-t "Treffpunkt morgen 18 Uhr." \
|
||||||
-k kontakt_bob_meine_seite/send.key \
|
-k kontakt_bob_meine_seite__send.key \
|
||||||
-c urlaubsfoto.jpg \
|
-c urlaubsfoto.jpg \
|
||||||
-o Screenshot_20260714_220801.png
|
-o Screenshot_20260714_220801.png
|
||||||
```
|
```
|
||||||
@@ -136,7 +139,7 @@ Alternativ `-i datei` statt `-t`, oder Text über stdin.
|
|||||||
### 3. Empfangen
|
### 3. Empfangen
|
||||||
|
|
||||||
```bash
|
```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.
|
Mit `-o klartext.txt` in eine Datei statt auf stdout.
|
||||||
|
|||||||
@@ -258,28 +258,39 @@ def extract(stego_path: str) -> dict:
|
|||||||
# --------------------------------------------------------------------------- #
|
# --------------------------------------------------------------------------- #
|
||||||
def cmd_genkey(args):
|
def cmd_genkey(args):
|
||||||
size = args.size
|
size = args.size
|
||||||
mine = args.output + "_meine_seite"
|
base = args.output
|
||||||
theirs = args.output + "_partner_seite"
|
# Flache, beschreibende Dateinamen (einheitlich zum Web-Frontend): der
|
||||||
for d in (mine, theirs):
|
# Name trägt Seite + Rolle -> im Browser hoch- und wieder runtergeladen
|
||||||
if os.path.exists(d) and not args.force:
|
# überschreibt er sauber die richtige Datei.
|
||||||
sys.exit(f"[!] {d} existiert schon. --force zum Überschreiben.")
|
mine_send = f"{base}_meine_seite__send.key"
|
||||||
os.makedirs(d, exist_ok=True)
|
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
|
# Zwei unabhängige Ströme: sid_out = ich->Partner, sid_in = Partner->ich
|
||||||
sid_out, sid_in = os.urandom(8), os.urandom(8)
|
sid_out, sid_in = os.urandom(8), os.urandom(8)
|
||||||
mat_out, mat_in = os.urandom(size), os.urandom(size)
|
mat_out, mat_in = os.urandom(size), os.urandom(size)
|
||||||
|
|
||||||
# Meine Seite: sende mit Strom OUT, empfange Strom IN
|
# 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(mine_send, 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_recv, ROLE_RECV, sid_in, 0, mat_in)
|
||||||
# Partnerseite (spiegelverkehrt): sendet mit Strom IN, empfängt Strom OUT
|
# 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(their_send, 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_recv, ROLE_RECV, sid_out, 0, mat_out)
|
||||||
|
|
||||||
print(f"[+] Schlüsselpaar erzeugt ({size} Bytes je Richtung, echter Zufall).")
|
print(f"[+] Schlüsselpaar erzeugt ({size} Bytes je Richtung, echter Zufall).")
|
||||||
print(f" {mine}/ <- DU behältst diesen Ordner")
|
print(" DU behältst:")
|
||||||
print(f" {theirs}/ <- gib DIESEN Ordner dem Partner (Stick, persönlich)")
|
print(f" {mine_send} (zum Senden)")
|
||||||
print("[i] Jede Seite hat send.key (zum Senden) + recv.key (zum Empfangen).")
|
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.")
|
print("[i] send.key nur an EINEM Gerät gleichzeitig benutzen.")
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ $PY web/_interop_rawrgb.py mkcarrier web/_carrier.png 200 200 >/dev/null
|
|||||||
|
|
||||||
echo "== A) CLI verschlüsselt+authentifiziert -> Bild =="
|
echo "== A) CLI verschlüsselt+authentifiziert -> Bild =="
|
||||||
$PY schattenpost.py hide -t "MsgA vom CLI 🕵️" \
|
$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
|
$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 =="
|
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 =="
|
echo "== B) Web-Ausgabe zurück nach PNG, vom CLI aufdecken+prüfen =="
|
||||||
read -r W H < web/_b_dims.txt
|
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
|
$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"
|
echo " CLI reveal: $OUT"
|
||||||
if [ "$OUT" = "MsgB aus dem Browser-Core 🌐" ]; then
|
if [ "$OUT" = "MsgB aus dem Browser-Core 🌐" ]; then
|
||||||
echo " ✓ B round-trip (Web→CLI) inkl. MAC-Prüfung ok"
|
echo " ✓ B round-trip (Web→CLI) inkl. MAC-Prüfung ok"
|
||||||
|
|||||||
@@ -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
|
// A: CLI (send.key, SPS3) -> Web-Core (recv.key): extrahieren, MAC prüfen, entschlüsseln
|
||||||
{
|
{
|
||||||
const rgb = U('./_a_rgb.bin');
|
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);
|
const info = SP.extract(rgb);
|
||||||
console.log('A) CLI → Web');
|
console.log('A) CLI → Web');
|
||||||
ok(info.version === SP.STEGO_VERSION, 'Container ist SPS3');
|
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)
|
// B: Web-Core (send.key, SPS3) -> CLI (recv.key)
|
||||||
{
|
{
|
||||||
const rgb = U('./_b_rgb.bin');
|
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 🌐';
|
const msg = 'MsgB aus dem Browser-Core 🌐';
|
||||||
console.log('B) Web → CLI');
|
console.log('B) Web → CLI');
|
||||||
ok(send.role === SP.ROLE_SEND, 'partner send.key hat Rolle SEND');
|
ok(send.role === SP.ROLE_SEND, 'partner send.key hat Rolle SEND');
|
||||||
|
|||||||
Reference in New Issue
Block a user