diff --git a/docs/design/fips-protocol-flow.md b/docs/design/fips-protocol-flow.md new file mode 100644 index 0000000..ed434f5 --- /dev/null +++ b/docs/design/fips-protocol-flow.md @@ -0,0 +1,701 @@ +# FIPS Protocol Traffic Flow + +Design discussion from Session 39. + +## Overview + +This document captures design considerations for FIPS protocol message flow, +including peer discovery, authentication, tree announcements, and data routing. + +--- + +## 1. Application-Initiated Traffic Flow + +Traffic flow begins at the application layer with a DNS query, which triggers +a cascade of events through the FIPS stack. + +### 1.1 DNS as Entry Point + +An application wants to send IPv6 traffic to another FIPS node, identified by +an npub. The flow: + +1. **DNS Query**: Application queries a local FIPS DNS server for the npub + (format TBD - perhaps `npub1xxx...xxx.fips` or similar) + +2. **FIPS DNS Server** performs two functions: + - **Address derivation**: Converts the npub to an identity and derives the + corresponding `fd::/8` IPv6 address + - **Cache priming**: Stores the identity mapping (IPv6 address ↔ npub ↔ node_id) + in the local FIPS routing cache + +3. **DNS Response**: Returns the derived IPv6 address to the application + +4. **Packet Transmission**: Application sends IPv6 packet to the returned address, + which routes to the TUN interface via the `fd::/8` route + +5. **TUN Processing**: When the packet arrives at the TUN, FIPS already has the + cached mapping from the DNS lookup, enabling immediate routing decisions + +### 1.2 Design Rationale + +Using DNS as the trigger point ensures the routing cache is populated *before* +packets arrive. This avoids: + +- Blocking packets while performing identity lookups +- Packet drops during cold-cache scenarios +- Complex async lookup machinery in the hot path + +The DNS server acts as a "routing intent" signal - if an application queries +for a destination, it likely intends to send traffic there. + +### 1.3 DNS Name Format + +NPUBs are represented as DNS names in the format: + +```text +npub1xxxxxx...xxxxx.fips +``` + +The FIPS DNS server recognizes names ending in `.fips` and extracts the npub +for address derivation. + +### 1.4 Identity Cache Lifetime + +The identity cache (IPv6 address ↔ npub ↔ node_id) has the following lifetime +semantics: + +- **Configurable timeout**: Cache entries expire after a configured duration +- **Traffic refresh**: Timer resets to zero whenever traffic is sent to that + destination (LRU-style keep-alive) +- **TTL relationship**: Cache timeout MUST be longer than DNS TTL + +The TTL constraint ensures that while an application believes its DNS resolution +is valid (within TTL), the corresponding FIPS routing entry remains present. +Example: DNS TTL = 300s, Cache timeout = 600s. + +```text +DNS query → cache entry created (timeout = 600s) + ...traffic... → timeout reset to 600s + ...traffic... → timeout reset to 600s +DNS TTL expires (300s) → app may re-query, but cache still valid + ...no traffic for 600s... +Cache entry expires +``` + +### 1.5 Traffic Without Prior DNS Lookup + +A packet may arrive at the TUN for an `fd::/8` destination without a prior +DNS lookup. Scenarios: + +- Application has cached/hardcoded address from previous session +- Manual configuration bypassing DNS +- Reply packets (covered separately - source already known from outbound) + +Options for handling cold-cache outbound packets: + +1. **Drop with ICMPv6**: Return Destination Unreachable, require DNS lookup +2. **Reverse derive**: Attempt to derive npub from address (not possible - + address is hash of npub, not reversible) +3. **Query protocol**: Initiate network query to discover identity for address +4. **Hold and query**: Buffer packet while performing discovery + +The address derivation is one-way (SHA-256), so reverse derivation is impossible. +Without the npub, we cannot determine the node_id needed for routing. + +**Decision**: Return ICMPv6 Destination Unreachable (Code 0: No route to +destination) for packets to unknown addresses. The identity cache MUST be +populated through some mechanism before traffic can be routed. + +Known cache population mechanisms: + +- DNS lookup (primary path, described above) +- Inbound traffic from authenticated peers (described in §X) +- Additional mechanisms TBD as design progresses + +--- + +## 2. TUN Reader Processing + +After DNS resolution, the application sends an IPv6 datagram to the destination +address. The kernel routes it to the TUN interface (via the `fd::/8` route), +where the FIPS TUN reader receives it. + +### 2.1 Packet Arrival + +```text +Application + │ + ▼ +IPv6 datagram (src=local_addr, dst=target_addr) + │ + ▼ +Kernel routing table: fd::/8 → fips0 + │ + ▼ +TUN reader receives raw IPv6 packet +``` + +### 2.2 TUN Reader Actions + +On receiving a packet, the TUN reader: + +1. **Validate IPv6 header**: Version = 6, payload length sane, etc. + +2. **Extract destination address**: The `fd::/8` address from the IPv6 header + +3. **Identity cache lookup**: Query cache for destination address + - **Miss**: Return ICMPv6 Destination Unreachable (see §1.5) + - **Hit**: Proceed with routing + +4. **Retrieve routing identity**: Cache hit provides: + - `npub`: The Nostr public key of the destination + - `node_id`: SHA-256(npub), used for spanning tree routing + +5. **Session lookup**: Check for existing FIPS session with destination npub + - **Hit**: Use existing session for encryption/signing + - **Miss**: Initiate session establishment (see §3) + +6. **Route determination**: Using node_id, determine next hop (covered in §X) + +7. **Packet forwarding**: Encapsulate and send via appropriate transport + +--- + +## 3. FIPS Sessions + +A FIPS session represents a bidirectionally authenticated, encrypted channel +between two FIPS nodes. + +### 3.1 Session Properties + +Each session contains: + +- **Peer identity**: The remote node's npub and node_id +- **Symmetric session keys**: Directional keys for encryption (send_key, recv_key) +- **Nonce counters**: Per-direction counters for replay protection + +Payloads within a session are: + +1. **Encrypted** with the session key (provides confidentiality) +2. **Authenticated** via AEAD tag (session keys bound to npub identities) + +Authentication derives from the Noise KK handshake binding session keys to +both parties' static keys. See §6 for cryptographic details. + +### 3.2 Session Establishment Trigger + +When the TUN reader has a packet for a destination with no existing session: + +```text +TUN reader + │ + ├─► Identity cache lookup → node_id + │ + ├─► Session lookup (by npub) → MISS + │ + └─► Initiate session establishment +``` + +The original packet that triggered session establishment must be handled: + +- ~~**Option A**: Drop packet, let application retry (simple, may cause timeout)~~ +- **Option B**: Queue packet, send after session established +- **Option C**: Send packet optimistically during handshake (may fail) + +**Decision**: Either queue or optimistic send. Dropping is not acceptable as it +causes unnecessary latency and potential application timeouts. + +Queuing is simpler to reason about but requires bounded buffer management. +Optimistic send (0-RTT style) improves latency but requires careful replay +protection. + +### 3.3 Session Independence from Transport + +FIPS sessions exist above the routing layer. A session between two npubs +survives: + +- Transport failover (UDP → Tor → back to UDP) +- Route changes (different intermediate hops) +- IP address changes on either end + +The session is bound to **npub identities**, not network addresses or transport +paths. This is similar to QUIC's connection migration but at the FIPS layer. + +### 3.4 Session Establishment Flow + +TBD - handshake protocol for mutual authentication and key exchange. + +Considerations: + +- Must work over unreliable transports (UDP) +- Must handle packet loss/reordering during handshake +- Should minimize round trips for latency +- Must bind session to both npub identities cryptographically + +### 3.5 Simultaneous Session Initiation (Crossing Hellos) + +When both nodes attempt to establish a session simultaneously, we have +"crossing hellos" - two handshakes in flight at once. + +Options: + +1. **Deterministic tie-breaker**: Lower npub (lexicographically) is always + the "initiator." When a node receives an initiation from a higher npub + while it has an outbound initiation pending to that same npub, it defers + to the lower npub's handshake. + +2. **Both complete, then deduplicate**: Let both handshakes run to completion. + Both sides end up with the same session key (if protocol is designed for + this). Discard the "extra" session state using tie-breaker. + +3. **Detect and merge**: When receiving an initiation while one is pending, + recognize the crossing and merge into a single handshake with contributions + from both sides. + +**Considerations**: + +- Option 1 is simplest but may add latency (one side backs off) +- Option 2 wastes bandwidth but is robust +- Option 3 is elegant but complex to implement correctly + +The npub comparison provides a consistent, globally-agreed ordering without +any coordination. + +**Decision**: Option 1 - deterministic tie-breaker using npub ordering. + +When a node detects a crossing hello (receives initiation while its own +initiation to the same peer is pending): + +- If local npub < remote npub: Continue as initiator, ignore incoming initiation +- If local npub > remote npub: Abort own initiation, switch to responder role + +This ensures exactly one handshake completes with minimal wasted effort. The +latency cost is bounded to one round-trip in the crossing case, which should +be rare. + +--- + +## 4. FIPS Mesh Routing + +Below the session layer, all FIPS packets (session handshake messages, encrypted +payloads, control traffic) must be routed through the mesh to their destination. + +### 4.1 Routing Layer Entry Points + +The routing layer handles packets from two sources: + +1. **Session establishment**: Handshake packets for new sessions +2. **Session data**: Encrypted payloads over established sessions + +Both require determining how to reach the destination node_id. + +### 4.2 Route Cache + +The routing layer maintains a route cache mapping: + +```text +node_id → (coordinates, next_hop_peer) +``` + +Where: + +- `coordinates`: The destination's spanning tree coordinates +- `next_hop_peer`: A direct peer for greedy forwarding toward those coordinates + +### 4.3 Routing Decision Flow + +When sending a packet to a destination node_id: + +```text +Packet to send (dest = node_id) + │ + ├─► Route cache lookup + │ ├─► HIT: Coordinates known → greedy route via next_hop + │ └─► MISS: Proceed to discovery + │ + └─► Route discovery (see §4.4) +``` + +If the route cache has coordinates for the destination, greedy routing proceeds +immediately - no discovery needed. + +### 4.4 Route Discovery Protocol + +When the route cache has no entry for the destination, discovery must determine +how to reach node_id X. + +**Discovery flow**: + +```text +Route discovery for node_id X + │ + ├─► Check peer bloom filters + │ ├─► Match in peer P's filter → query P for coordinates + │ └─► No match in any filter → proceed to flooding + │ + └─► Send LookupRequest (flooding with TTL) + └─► Await LookupResponse with coordinates +``` + +**Bloom filter role**: Bloom filters don't provide routes directly - they +indicate which peers *might* know about a destination. A bloom match triggers +a targeted query to that peer rather than blind flooding. + +**LookupRequest flooding**: When no bloom filter matches, flood the query +through the spanning tree with bounded TTL. Nodes that know the destination +(have it in their bloom filter or route cache) respond with coordinates. + +### 4.5 Packet Handling During Discovery + +Packets arriving while route discovery is in progress: + +- **Queue**: Buffer packets while discovery completes (bounded queue) +- **Drop with error**: Return to session layer, which may retry + +**Decision needed**: Packet handling during route discovery? + +### 4.6 Route Cache Population + +Routes are learned through: + +- Successful route discovery (explicit) +- Receiving packets from a source (reverse path learning) +- Spanning tree announcements (implicit reachability for nearby nodes) +- Bloom filter updates combined with coordinate queries + +### 4.7 Route Cache Lifetime + +Route cache entries should: + +- Expire after configurable timeout +- Refresh on successful packet delivery +- Invalidate when peer link goes down +- Invalidate on spanning tree topology changes affecting the path + +--- + +## 5. Terminology Reconciliation + +The existing design docs ([fips-routing.md](design/fips-routing.md)) and this +document use "session" differently. This section clarifies the terminology. + +### 5.1 Two Distinct Concepts + +| Term | Layer | Purpose | Endpoints | +|------|-------|---------|-----------| +| **Crypto Session** (§3) | End-to-end | Authentication + encryption | Source ↔ Destination | +| **Routing Session** (existing doc §4) | Hop-by-hop | Cache coordinates at routers | Along the path | + +**Crypto Session** (what §3 of this document describes): + +- Established between two npub identities +- Provides confidentiality (encryption) and authenticity (signatures) +- Survives route changes and transport failover +- Keyed by: `(local_npub, remote_npub)` + +**Routing Session** (what fips-routing.md §Part 4 describes): + +- Warms coordinate caches at intermediate routers +- Enables minimal 36-byte data packet headers +- Must be re-established when router caches expire +- Keyed by: `(src_addr, dest_addr)` at each router + +### 5.2 Relationship Between Sessions + +These are complementary, not conflicting: + +```text +┌─────────────────────────────────────────────────────────────────────┐ +│ Crypto Session (end-to-end) │ +│ ┌───────────────────────────────────────────────────────────────┐ │ +│ │ Routing Session (hop-by-hop cache state) │ │ +│ │ │ │ +│ │ Source ──► Router1 ──► Router2 ──► ... ──► Destination │ │ +│ │ (cache) (cache) │ │ +│ └───────────────────────────────────────────────────────────────┘ │ +│ │ +│ Encrypted payload travels inside routing session │ +└─────────────────────────────────────────────────────────────────────┘ +``` + +### 5.3 Establishment Order + +**Option A: Sequential (Crypto first, then Routing)** + +```text +1. Discover destination coordinates (LookupRequest/Response) +2. Establish crypto session (handshake for keys) +3. Send SessionSetup to warm router caches +4. Send encrypted data packets (minimal headers) +``` + +Pros: Clean separation, crypto session exists before any data flows +Cons: Additional round-trips before first data + +**Option B: Combined (Routing carries Crypto handshake)** + +```text +1. Discover destination coordinates (LookupRequest/Response) +2. Send SessionSetup carrying crypto handshake initiation +3. Routers cache coordinates; destination receives handshake +4. Destination responds with SessionAck + crypto response +5. Send encrypted data packets +``` + +Pros: Fewer round-trips, single establishment phase +Cons: Couples two concerns, SessionSetup becomes more complex + +**Option C: Crypto-only (No Routing Session)** + +```text +1. Discover destination coordinates +2. Establish crypto session +3. Every data packet carries full coordinates +``` + +Pros: Simplest, no router cache state +Cons: Larger packets (~400 bytes vs 36 bytes), more bandwidth + +### 5.4 Recommended Terminology + +To avoid confusion going forward: + +| Use This | Instead Of | Meaning | +|----------|------------|---------| +| **Crypto session** | "FIPS session" | End-to-end authenticated encryption | +| **Routing session** | "Session" (from routing doc) | Router cache state for a flow | +| **Route discovery** | — | Finding destination coordinates | +| **Session setup** | — | Warming router caches (routing session) | + +### 5.5 Decision: Option B - Combined Establishment + +**Decision**: Use combined establishment where routing session setup carries +the crypto handshake. + +```text +Combined Establishment Flow: + +1. Route discovery (if needed) + └─► LookupRequest/Response → obtain destination coordinates + +2. SessionSetup + Crypto Init + └─► Source sends SessionSetup containing: + - src/dest coordinates (for router caching) + - Crypto handshake initiation (for destination) + └─► Routers cache coordinates as packet transits + └─► Destination receives crypto init, begins handshake + +3. SessionAck + Crypto Response + └─► Destination sends SessionAck containing: + - Its coordinates (for reverse path caching) + - Crypto handshake response + └─► Routers cache reverse path + └─► Source completes crypto handshake + +4. Data flow + └─► Encrypted payloads with minimal 36-byte headers + └─► Both crypto session and routing session now active +``` + +**Benefits of combined approach**: + +- Single round-trip establishes both sessions +- Router caches warm as handshake transits +- No additional latency vs crypto-only +- Bidirectional routing session from the start (SessionAck warms return path) + +**Message structure implications**: + +SessionSetup and SessionAck messages must carry both: + +- Routing information (coordinates for router caching) +- Crypto payload (handshake messages, opaque to routers) + +Routers process the routing portion and forward; only endpoints process +the crypto portion. + +--- + +## 6. Crypto Session Handshake + +The crypto session uses the Noise Protocol Framework with secp256k1, aligning +with Nostr's cryptographic primitives. + +### 6.1 Design Decision: Noise with secp256k1 + +**Decision**: Use Noise Protocol Framework adapted for secp256k1. + +Rationale: + +- Well-analyzed framework with formal security proofs +- Used successfully in WireGuard, Lightning (BOLT 8), Signal +- Lightning Network already adapted Noise for secp256k1 (precedent) +- Reuses Nostr's existing key infrastructure (npub/nsec) +- Provides forward secrecy via ephemeral keys + +### 6.2 Pattern Selection + +Since both parties know each other's static public key (npub) before the +handshake begins (from DNS lookup / identity cache), the **Noise KK** pattern +is appropriate: + +```text +Noise_KK_secp256k1_ChaChaPoly_SHA256 + +KK: + -> e, es, ss + <- e, ee, se +``` + +**Message 1 (Initiator → Responder):** + +- `e`: Initiator's ephemeral public key +- `es`: DH(initiator_ephemeral, responder_static) +- `ss`: DH(initiator_static, responder_static) + +**Message 2 (Responder → Initiator):** + +- `e`: Responder's ephemeral public key +- `ee`: DH(initiator_ephemeral, responder_ephemeral) +- `se`: DH(responder_ephemeral, initiator_static) + +After both messages, both parties derive identical symmetric keys for +encryption in each direction. + +### 6.3 Why KK (not IK or XX) + +| Pattern | Knowledge | Messages | FIPS Fit | +|---------|-----------|----------|----------| +| **KK** | Both know both static keys | 1 RT | Best - we have npubs | +| IK | Initiator knows responder | 1 RT | Works but asymmetric | +| XX | Neither knows | 2 RT | Unnecessary overhead | + +KK provides mutual authentication in a single round-trip since FIPS always +knows the peer's npub before initiating (from DNS/identity cache). + +### 6.4 Cryptographic Primitives + +Following Lightning's BOLT 8 adaptation: + +| Component | Choice | Notes | +|-----------|--------|-------| +| Curve | secp256k1 | Nostr-native | +| DH | ECDH on secp256k1 | Standard EC Diffie-Hellman | +| Cipher | ChaCha20-Poly1305 | AEAD, same as NIP-44 | +| Hash | SHA-256 | Nostr-native | +| Key derivation | HKDF-SHA256 | Standard Noise KDF | + +### 6.5 Handshake Integration with SessionSetup + +The Noise handshake messages embed in SessionSetup/SessionAck: + +```text +SessionSetup { + // Routing portion (processed by routers) + src_coords: Vec, + dest_coords: Vec, + src_addr: Ipv6Addr, + dest_addr: Ipv6Addr, + + // Crypto portion (opaque to routers, processed by destination) + handshake_payload: Vec, // Noise KK message 1 +} + +SessionAck { + // Routing portion + src_coords: Vec, // Responder's coordinates + + // Crypto portion + handshake_payload: Vec, // Noise KK message 2 +} +``` + +### 6.6 Session Keys + +After handshake completion, Noise produces two symmetric keys: + +- `send_key`: For encrypting outbound packets +- `recv_key`: For decrypting inbound packets + +These are used with ChaCha20-Poly1305 for all subsequent data packets. + +### 6.7 Nonce Management + +ChaCha20-Poly1305 requires unique nonces. Options: + +1. **Counter-based**: Each side maintains a 64-bit send counter, incremented + per packet. Nonce = counter (no coordination needed since keys are + directional). + +2. **Random nonces**: 96-bit random nonce per packet, included in header. + Simpler but adds 12 bytes per packet. + +**Recommendation**: Counter-based nonces (like WireGuard/Lightning). The +counter also enables replay detection - reject packets with nonce ≤ last seen. + +### 6.8 Forward Secrecy + +The ephemeral keys (`e` in Noise notation) provide forward secrecy: + +- Compromise of static keys (npub/nsec) doesn't reveal past session keys +- Each session has unique ephemeral keys +- Session keys derived from ephemeral-ephemeral DH (`ee`) + +### 6.9 Reference: Lightning BOLT 8 + +Lightning's adaptation of Noise for secp256k1 (BOLT 8) provides a proven +reference implementation: + +- Uses Noise XK pattern (different from our KK) +- Same secp256k1 + ChaCha20-Poly1305 + SHA-256 stack +- Handles the secp256k1 ECDH correctly +- Open source implementations available in multiple languages + +FIPS can reference BOLT 8's cryptographic details while using the KK pattern +appropriate for our mutual-knowledge scenario. + +### 6.10 Data Packet Authentication + +**Decision**: Use AEAD authentication only (no per-packet signatures). + +The Noise handshake binds session keys to both parties' static keys. After +handshake completion: + +- Session keys are cryptographically tied to both npubs +- AEAD (ChaCha20-Poly1305) provides integrity and authenticity +- Only the holder of the session key can produce valid ciphertext +- Session keys can only be derived by holders of the corresponding nsecs + +Per-packet signatures would add: + +- 64 bytes overhead per packet +- Signing CPU cost (secp256k1 Schnorr) +- Verification CPU cost at receiver + +Since Noise already provides authentication through key binding, signatures +are redundant. This matches WireGuard and Lightning's approach. + +**Reconciliation note**: §3.1 mentions packets being "signed by source" - +this should be updated to reflect AEAD-only authentication. + +--- + +## 7. Document Reconciliation + +This section tracks items that need reconciliation with existing design docs +or earlier sections of this document. + +### 7.1 Pending Updates + +| Location | Status | Notes | +|----------|--------|-------| +| §3.1 | ✓ Done | Updated to AEAD authentication | +| fips-routing.md §Part 4 | Pending | Clarify "Session" → "Routing Session" per §5.4 | + +### 7.2 Design Doc Alignment + +The following decisions in this document affect existing design docs: + +1. **fips-routing.md**: "Session" → "Routing Session" terminology +2. **Combined establishment** (§5.5): SessionSetup carries crypto handshake +3. **Noise KK**: Replaces any unspecified handshake references