From d9b2499a2e785dd4cbee84939fa4cf138dc3b7cc Mon Sep 17 00:00:00 2001 From: Russell Ballestrini Date: Wed, 27 May 2026 14:54:26 -0400 Subject: [PATCH] web/chat.html: production multi-person volume-modem chatroom MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit single self-contained page implementing the recovered zebra-report protocol as a peer-to-peer chatroom whose data path is PulseAudio sink-input state — chat content never enters an IP packet. architecture: page (TX) modulates audio output via Web Audio GainNode between MARK (0.80) and SPACE (0.20). silent stereo carrier 440/441 Hz published to PulseAudio. zebrad separate program (companion, not in this commit): polls PA sink-input volumes, decodes frames, forwards to ws://127.0.0.1:7777 — the page connects and renders received messages. RX path the page is TX-only without zebrad running; with it, full bidirectional chat. crypto modes (toggle in UI): passphrase PBKDF2-SHA256 (600k iter) → AES-GCM-256 group key. everyone with the same phrase joins the room. pubkey ECDH P-256 keypair per browser → AES-GCM-256 pairwise. peers added by pasting their pubkey. both modes use Web Crypto API. no external crypto deps. frame protocol (matches include/zebra.h): OFFER magic(ZB) + 0x01 + baud_le(2) + xor(1) — 6B READY magic(ZB) + 0x02 + baud_le(2) + xor(1) — 6B HELLO magic + 0x04 + sid(4) + maxBaud_le(2) + handleLen(1) + handle(n) + crc32_le(4) DATA magic + 0x03 + sid(4) + len_le(2) + payload(n) + crc32_le(4) handshake: 1. user clicks "benchmark self" — page schedules 200 gain events at 0.5ms intervals, measures avg, divides by 2 for safety, clamps to [ZEBRA_BAUD_MIN, ZEBRA_BAUD_MAX]. 2. user clicks "send OFFER" — sends OFFER frame at the fixed handshake baud (50). also broadcasts HELLO. 3. peer's introspector decodes; their page replies with READY at their max baud, plus their HELLO. 4. negotiated baud = min(my max, every peer's max). 5. data frames at negotiated baud. UI sections: identity handle + mode toggle room passphrase OR pubkey, depending on mode carrier start audio, live gain meter handshake benchmark, OFFER button, baud display receive ws://127.0.0.1:7777 status + connect chat peer list, log, message box about threat model, mitigation, whitepaper link defensive framing prominent: the protocol is documented as a research artifact demonstrating PA-IPC's open same-UID volume read access. "what this does not protect against" enumerated. mitigation panel cites foxhop.net/linux-audio-ipc-attack-surface. known v1 tradeoffs: - no CSMA: simultaneous TX from two peers will collide. - pubkey mode broadcasts one frame per peer per message. - GainNode scheduling has OS-level jitter — actual sustained baud will be lower than the optimistic benchmark. - base carrier is audible at 440/441 Hz; can be lowered or moved ultrasonic in a follow-up. reuses visual identity from web/index.html (chunkfive font, monospace, black/white, no framework, no build step). file: 1062 lines, 40 KB, JS syntax-clean (node --check), HTML balanced. --- web/chat.html | 1062 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1062 insertions(+) create mode 100644 web/chat.html diff --git a/web/chat.html b/web/chat.html new file mode 100644 index 0000000..f368240 --- /dev/null +++ b/web/chat.html @@ -0,0 +1,1062 @@ + + + + + +zebra report — chat + + + + +

zebra report

+

volume modem chatroom  ·  e2e encrypted  ·  no network  ·  + index

+ + +
+

identity

+
+ + + +
+
+ + + + + + + passphrase mode = group; pubkey mode = 1-to-1 ECDH. +
+
+ + +
+

room (passphrase mode)

+
+ +
+
+ + no room key +
+

+ derives a 256-bit AES-GCM key via PBKDF2-SHA256 (600k iterations) from the + passphrase + the literal string "zebra-report-v1" as salt. Anyone with the + same phrase joins the same room. +

+
+ + +
+

room (pubkey mode)

+

your public key (click to copy):

+
generating…
+
+ + +
+
+ + +
+

add one peer at a time. each add derives a 1-to-1 shared key + and lets you exchange messages with that peer.

+
+ + +
+

carrier

+
+
+ + + mic stays off · transmit only +
+
+ live gain: +
+ 0% +
+

+ a silent stereo carrier is published to PulseAudio. Each frame modulates the + GainNode between MARK (80%) and SPACE (20%) per bit. A local introspector + (see “receive” below) polls PA sink-input state and decodes. +

+
+ + +
+

handshake

+
+ + not yet measured +
+
+ + idle +
+
+ negotiated baud: + +
+

+ OFFER and READY frames travel at fixed ZEBRA_BAUD_HANDSHAKE=50. + Each peer measures its own scheduling jitter, broadcasts its max baud, and + the room settles on min(all peers). +

+
+ + +
+

receive (introspector bridge)

+
+
+ no introspector — running TX-only + +
+

+ expects zebrad at ws://127.0.0.1:7777. The bridge + polls PulseAudio sink-input volumes via PA IPC and pushes decoded frames here. + Without it the page transmits but cannot receive. The + recovered rx.c handles + decoding; zebrad is the WebSocket wrapper around it. +

+
+ + +
+

chat

+
+
+

peers in room:

+
+
none yet
+
+
+
+

log:

+
+
+
+
+ + +
+
+ + +
+

about

+

+ This page is the “production” zebra-report protocol: + a covert peer-to-peer chatroom whose data path is PulseAudio sink-input + state. Chat content never enters an IP packet — Wireshark sees only the + websocket between this browser tab and the local zebrad bridge, + and even that traffic is post-encryption ciphertext + frame metadata. +

+
+ threat model · what this protects against · what it doesn't +

+ Protects against: a network-level observer (Wireshark, IDS, + DPI middlebox, ISP) seeing chat content. None of it crosses the network. +

+

+ Does not protect against: + (1) other same-UID processes that can poll PulseAudio — this is the + entire attack surface zebra-report demonstrates; + (2) an endpoint compromise of either peer's machine; + (3) traffic analysis against the encrypted ciphertext over many sessions; + (4) anyone able to run a microphone in earshot of the speakers (n/a here — + this protocol's bytes don't ride on speaker audio energy). +

+

+ Mitigation for defenders: require PulseAudio clients to + hold a capability before reading peer sink-input volumes. The current + design grants any same-UID process unrestricted read access — that's + the published defect this whole project documents. See + + foxhop.net/linux-audio-ipc-attack-surface for the full whitepaper. +

+
+
+ + + + + + +