feat: implement YAW/2.1 forward-secret signaling
Upgrades the signaling layer from static X25519 (2.0) to per-session ephemeral X25519 (2.1). Recorded signaling traffic cannot be decrypted even if long-term Ed25519 keys later leak, because esk is zeroed on session close. Protocol: - Each peer generates a fresh X25519 keypair (esk/epk) per session. - Peers exchange signed `ekey` messages sealed under static keys before the offer/answer. Offer/answer/candidate payloads are then sealed with ephemeral keys (crypto_box(·, peer_epk, my_esk)). - ekey sig binds both peer IDs and the epk to prevent replay to third parties. - Offerer waits up to 2 s for the peer's ekey; if none arrives it falls back to YAW/2.0 static-key sealing and logs "2.0 fallback offer". - 2.0 peers silently ignore the unknown `ekey` kind — full interop preserved. Implementation: - crypto.go: add EphemeralKey.PublicRaw/PrivateRaw/Wipe helpers. - proto.go: add SigEkey kind; EPK/V/EkeySig fields on SignalingPayload. - anchor/client.go: replace flat pcs map with peerSession struct tracking ephemeral keys, peerEPK, and fs flag; openBoxAuto tries ephemeral then static; sealAndSend chooses seal based on session state. - test-network.sh: pipe daemon stderr through tee to daemon.log; add YAW/2.1 FS verification section. - test-tui.sh: same daemon.log capture. - README.md: document 2.1 forward secrecy, file transfer IPC, updated roadmap. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
41
README.md
41
README.md
@@ -100,6 +100,9 @@ Everything is newline-delimited JSON. You can test with `nc 127.0.0.1 17337`.
|
||||
{"type":"send_message","room":"general","body":"hi"}
|
||||
{"type":"send_message","room":"dm:<peer-hex>","body":"hey","to":"<peer-hex>"}
|
||||
{"type":"get_state"}
|
||||
{"type":"get_file_list"} // own share dir
|
||||
{"type":"get_file_list","peer_id":"<64-hex>"} // remote peer's share dir
|
||||
{"type":"send_file","peer_id":"<64-hex>","path":"notes.txt"} // offer a file from share dir
|
||||
{"type":"generate_invite"}
|
||||
```
|
||||
|
||||
@@ -114,7 +117,13 @@ Everything is newline-delimited JSON. You can test with `nc 127.0.0.1 17337`.
|
||||
{"type":"peer_disconnected","peer_id":"<64-hex>"}
|
||||
|
||||
// Incoming message — mid is a 32-hex dedup token, to is set for DMs
|
||||
{"type":"message_received","message":{"mid":"<32-hex>","from":"<64-hex>","to":"<64-hex>","room":"general","body":"hi","sent_at":"..."}}
|
||||
{"type":"message_received","message":{"mid":"<32-hex>","from":"<64-hex>","room":"general","text":"hi","ts":1700000000000}}
|
||||
|
||||
// File events
|
||||
{"type":"incoming_file","peer_id":"<64-hex>","offer":{"xid":"<32-hex>","name":"notes.txt","size":1024,"sha256":"<64-hex>"}}
|
||||
{"type":"file_progress","transfer_id":"<32-hex>","bytes_received":65536,"total_bytes":1048576}
|
||||
{"type":"file_complete","transfer_id":"<32-hex>","path":"/data-dir/downloads-<netid>/notes.txt"}
|
||||
{"type":"file_list","peer_id":"<64-hex>","files":[{"name":"notes.txt","size_bytes":1024}]}
|
||||
|
||||
// Invite generation response
|
||||
{"type":"invite_generated","invite":"waste:<base64>"}
|
||||
@@ -129,12 +138,25 @@ Everything is newline-delimited JSON. You can test with `nc 127.0.0.1 17337`.
|
||||
|---|---|---|
|
||||
| Identity | Ed25519 | Fast, small keys, standard |
|
||||
| Peer ID | Hex-encoded Ed25519 pubkey | 64 lowercase hex chars (YAW/2 §2) |
|
||||
| Signaling encryption | XSalsa20-Poly1305 (`nacl/box`) | X25519 keys derived from Ed25519 identity (YAW/2 §3) |
|
||||
| Signaling encryption (2.0) | XSalsa20-Poly1305 (`nacl/box`) | X25519 keys derived from Ed25519 identity (YAW/2 §3) |
|
||||
| **Signaling encryption (2.1)** | **XSalsa20-Poly1305, ephemeral X25519** | **Per-session keypair; `esk` wiped on close → forward secrecy** |
|
||||
| Transport | WebRTC DataChannels (DTLS+SCTP) | pion/webrtc — ICE, hole punching included |
|
||||
| Hashing | SHA-256 | File integrity, network name hashing |
|
||||
|
||||
Replaces WASTE's original Blowfish/PCBC (broken cipher mode) + RSA.
|
||||
|
||||
### Forward-secret signaling (YAW/2.1)
|
||||
|
||||
By default waste-go speaks **YAW/2.1**: before sending an offer each peer generates a fresh
|
||||
X25519 keypair (`esk`/`epk`), broadcasts its `epk` in a signed `ekey` message, then seals
|
||||
`offer`/`answer`/`candidate` payloads with the *ephemeral* keys. `esk` is zeroed when the
|
||||
session ends. Recorded signaling traffic cannot be decrypted even if the long-term Ed25519
|
||||
keys later leak.
|
||||
|
||||
A 2.0 peer ignores the `ekey` message (unknown type → silently dropped) and the offerer
|
||||
falls back to static-key sealing after a 2 s timeout, so **2.1 ↔ 2.0 sessions work** — the
|
||||
session just isn't forward-secret. The log line `anchor: 2.0 fallback offer to …` flags this.
|
||||
|
||||
> Peer IDs are 64-char lowercase hex (Ed25519 public key). Existing `identity.json` files
|
||||
> on disk are unaffected — only the over-the-wire representation changed from base64url.
|
||||
|
||||
@@ -202,12 +224,20 @@ A self-contained test script boots anchor + three peers, joins them to a named n
|
||||
Data lands at `/tmp/waste-test` (wiped on each run). Inspect after a run:
|
||||
|
||||
```bash
|
||||
sqlite3 /tmp/waste-test/alice/messages.db
|
||||
# DB name includes the network ID (first 8 hex chars of sha256("yaw2-net:"+name))
|
||||
sqlite3 /tmp/waste-test/alice/messages-<netid>.db
|
||||
.headers on
|
||||
SELECT room, from_peer, body, sent_at FROM messages;
|
||||
SELECT room, from_peer, text, sent_at FROM messages;
|
||||
SELECT peer_id, alias, last_seen FROM peers;
|
||||
```
|
||||
|
||||
There is also a TUI integration test that boots the same three-peer network and
|
||||
launches the Bubble Tea UI as alice:
|
||||
|
||||
```bash
|
||||
./test-tui.sh
|
||||
```
|
||||
|
||||
## Roadmap
|
||||
|
||||
- [x] **Crypto layer** — hex peer IDs, `nacl/box` signaling, Ed25519→X25519 key derivation
|
||||
@@ -218,5 +248,6 @@ SELECT peer_id, alias, last_seen FROM peers;
|
||||
- [x] **IPC updates** — `join_network`/`leave_network`; `session_ready` event; DMs via `to` field
|
||||
- [x] **Message persistence** — SQLite (`internal/store`); messages and peer alias cache
|
||||
- [x] **TUI** — Bubble Tea terminal UI (`cmd/tui`); three-pane layout with room switching and DMs
|
||||
- [ ] **File transfer** — chunked binary DataChannel (`f:<xid>`)
|
||||
- [x] **File transfer** — chunked binary DataChannel (`f:<xid>`); SHA-256 verified; backpressure; auto-accept
|
||||
- [x] **Forward-secret signaling (YAW/2.1)** — ephemeral X25519 per session; `esk` wiped on close; 2.0 fallback
|
||||
- [ ] **Native UI** — web frontend with native packaging (Tauri-style)
|
||||
|
||||
Reference in New Issue
Block a user