diff options
| -rw-r--r-- | docs/meshbay-draft-v5.md | 41 |
1 files changed, 29 insertions, 12 deletions
diff --git a/docs/meshbay-draft-v5.md b/docs/meshbay-draft-v5.md index 0d64b0c..9e1ddb3 100644 --- a/docs/meshbay-draft-v5.md +++ b/docs/meshbay-draft-v5.md @@ -1,6 +1,6 @@ # MeshBay — Architecture Draft v5 -> Status: Phase 11.5 (security remediation) largely complete — see `devel-phases-next.md`. +> Status: **Phase 11.5 (security remediation) complete.** See `devel-phases-next.md`. > Supersedes `meshbay-draft-v4.md`. Sections not restated here are unchanged from v4. > > v5 exists because the second security review (`second-review.md`, 2026-08-13) found @@ -58,7 +58,7 @@ This replaces the informal assurances scattered through v4 §4.2.x and §13. | Chat content is unreadable | ✅ | ❌ H3 | ❌ plaintext at rest until Phase 15 | ❌ | ✅ | | File index is unreadable | ✅ | ❌ H3 | ❌ | ❌ | ✅ | | Content cannot be modified | ✅ | ✅ | ❌ by design | ✅ | ✅ | -| Node cannot be impersonated | ✅ | ⚠️ pending 11.5.8 pinning | — | ✅ | ✅ | +| Node cannot be impersonated | ✅ | ✅ | — | ✅ | ✅ | | Client code integrity | ❌ **T3, accepted** | ❌ T3 | ✅ | ✅ | ✅ | | Node content authority | ✅ | ✅ | ✅ sovereign | ✅ | ✅ | @@ -89,7 +89,7 @@ means "end-to-end" here describes *client ↔ node*, never *client ↔ client*. | Listener | Role | Status | |---|---|---| | **WebRTC DataChannel** (aiortc) | Primary, browser **and** native clients | Unified handshake enforced | -| **QUIC** (aioquic) | LAN, port-forwarded, hub-less `group://` | Authorization shared; **GEK proof pending** | +| **QUIC** (aioquic) | LAN, port-forwarded, hub-less `group://` | Unified handshake enforced | | ~~TCP + TLS 1.3~~ | — | **Removed** (C6) | | ~~HTTP file API~~ | — | **Removed** (C1) | @@ -147,7 +147,7 @@ a missing fingerprint silently degraded it to nonce-only, dropping MitM detectio | Transport | Anchor | |---|---| | WebRTC | both DTLS certificate fingerprints | -| QUIC | SHA-256 of the server certificate — **unproven**, see 11.5.6. An RFC 5705 exporter would be stronger; aioquic does not currently expose one | +| QUIC | SHA-256 of the server certificate. aioquic exposes no RFC 5705 exporter; the client's access to the peer certificate is a guarded private attribute, and on a **resumed** session — which carries no certificate — the anchor travels with the session ticket | ### 4.3 Authorization rules @@ -163,8 +163,8 @@ with its long-term key. Previously the client received an unverifiable `node_pk` trusted `is_node_admin` from whoever answered — so a peer that had hijacked signaling could serve a forged index, forged chat history and forged permissions. -**Still open (11.5.8):** the client verifies the signature but does not yet *pin* the -key, so a substituted node is caught only by its lack of the GEK. +The client also **pins** `pk_node` per node on first use (11.5.8) and refuses a changed +key outright, with a deliberate reset path in Settings for a legitimate rotation. --- @@ -308,18 +308,35 @@ format. ## 9. Open items +**Phase 11.5 is complete.** All six critical and all seven high findings from +`second-review.md` are closed, bounded, or deferred by explicit decision. + +### Closed since this document was drafted + +| # | Item | Closed by | +|---|---|---| +| **C6** | GEK proof on the QUIC path | QUIC now runs the same challenge/response as WebRTC — client nonce, role-bound transcript, GEK proof, and the node proving itself with a GEK proof plus an Ed25519 signature | +| 11.5.6 | QUIC channel-binding anchor | Certificate hash. Server reads its own certificate from disk; the client's access to the peer certificate is guarded and fails loudly if aioquic moves it. **A resumed TLS session carries no certificate**, so the anchor travels with the session ticket — sound, because the ticket is derived from the handshake where the certificate was presented | +| 11.5.8 | `pk_node` TOFU pinning | Pinned per `node_id` on first handshake; a change is **refused**, with a deliberate reset in Settings | +| **M8** | Node announce proof-of-possession | Announcer signs `meshbay:node_announce:{user_id}:{pk_node}:{timestamp}`; re-announcing updates in place | + +### Still open + | # | Item | Status | |---|---|---| -| **C6** | GEK proof on the QUIC path | **Open** — authorization shared, proof pending. A forged token still injects chat over QUIC | -| 11.5.6 | QUIC channel-binding anchor | Unproven — certificate hash vs RFC 5705 exporter | -| 11.5.8 | `pk_node` TOFU pinning | Open | -| 11.5.10 | Node announce proof-of-possession | Open | -| **H3** | Hub key substitution | **Open** — fixed by Phase 12.1 safety numbers | -| C4 | Remote keypair bundles | Bounded; closes in Phase 13.3 | +| **H3** | Hub key substitution | **Open** — the last unfixed finding. The hub is the public-key directory, so it can be handed the GEK at invite time. Fixed by Phase 12.1 safety numbers | +| C4 | Remote keypair bundles | Bounded (4 pre-proof fetches, audited); closes in Phase 13.3 when the native client stops storing them remotely | | — | Chat encryption (Sender Keys) | Phase 15. Distribution must be **pairwise to identity keys**, never GEK-derived | | — | Chat attachments | Plaintext on the node; decide in Phase 15.7 | | **T3** | Hub serves the SPA | **Accepted permanently** (D1) | +### Scope note on pinning + +With C6 closed, `pk_node` pinning is defence in depth rather than the primary control: a +substituted node already fails the GEK proof. Pinning covers the case where an attacker +*holds* the group key — an ex-member, or a leaked GEK — and swaps the node underneath, +which the proof alone cannot distinguish from the genuine node. + ### Sender Keys — scope correction Sender Keys protects chat from someone holding the node's disk who is **not** a group |