mirror of
https://github.com/jmcorgan/fips.git
synced 2026-07-30 19:46:15 +00:00
Unify Noise IK for both link and session layers
Design change: Session layer now uses Noise IK pattern (previously KK), matching the link layer. Rationale: initiator knows destination npub, responder learns initiator identity from handshake - same asymmetry as link connections. Document consistency fixes: - Auth* events → Noise IK msg1/msg2 terminology in state machines - PeerId → NodeId in routing code examples - BloomUpdate → FilterAnnounce terminology - Updated cross-references and tables
This commit is contained in:
@@ -17,7 +17,7 @@ concepts, and finally the wire-level protocol details.
|
||||
|
||||
| Document | Description |
|
||||
|------------------------------------------------------|-----------------------------------------------------------|
|
||||
| [fips-session-protocol.md](fips-session-protocol.md) | End-to-end session flow, Noise KK encryption, terminology |
|
||||
| [fips-session-protocol.md](fips-session-protocol.md) | End-to-end session flow, Noise IK encryption, terminology |
|
||||
|
||||
### 3. Routing (How Packets Find Their Way)
|
||||
|
||||
|
||||
@@ -307,28 +307,32 @@ Inbound:
|
||||
|
||||
### Peer Lifecycle
|
||||
|
||||
The peer lifecycle uses Noise IK for authentication. Noise IK is a 2-message
|
||||
handshake where the initiator knows the responder's static key. See
|
||||
[fips-wire-protocol.md](fips-wire-protocol.md) §2 for wire format details.
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────┐
|
||||
│ Disconnected │
|
||||
└─────────────────────────────────────────┘
|
||||
│ │
|
||||
[outbound] │ │ [inbound data]
|
||||
[outbound] │ │ [inbound msg1]
|
||||
▼ ▼
|
||||
┌──────────────────┐ ┌──────────────────┐
|
||||
│ Connecting │ │AwaitingAuthInit │
|
||||
│(conn-oriented) │ │ │
|
||||
│ Connecting │ │ ReceivedMsg1 │
|
||||
│(conn-oriented) │ │ (send msg2) │
|
||||
└──────────────────┘ └──────────────────┘
|
||||
│ │
|
||||
[link ready] │ [recv AuthInit]
|
||||
▼ ▼
|
||||
┌──────────────────┐ ┌──────────────────┐
|
||||
│AwaitingChallenge │ │AwaitingComplete │
|
||||
│ (sent AuthInit) │ │(sent AuthChallenge)│
|
||||
└──────────────────┘ └──────────────────┘
|
||||
[link ready] │ │ [recv encrypted]
|
||||
[send msg1] │ │ [verify]
|
||||
▼ │
|
||||
┌──────────────────┐ │
|
||||
│ AwaitingMsg2 │ │
|
||||
│ (sent msg1) │ │
|
||||
└──────────────────┘ │
|
||||
│ │
|
||||
[recv AuthChallenge] │ │ [recv AuthComplete]
|
||||
[verify, send │ │ [verify]
|
||||
AuthComplete] │ │
|
||||
[recv msg2] │ │
|
||||
[verify] │ │
|
||||
▼ ▼
|
||||
┌─────────────────────────────────────────┐
|
||||
│ Active │
|
||||
@@ -347,18 +351,18 @@ Inbound:
|
||||
|
||||
- `Disconnected`: No active connection; for static peers, retry with backoff
|
||||
- `Connecting`: Link establishment in progress (connection-oriented transports only)
|
||||
- `AwaitingAuthInit`: Inbound connection, waiting for peer's AuthInit
|
||||
- `AwaitingChallenge`: Sent AuthInit, waiting for AuthChallenge
|
||||
- `AwaitingComplete`: Sent AuthChallenge, waiting for AuthComplete
|
||||
- `ReceivedMsg1`: Inbound; received Noise IK msg1, sent msg2, awaiting first encrypted frame
|
||||
- `AwaitingMsg2`: Outbound; sent Noise IK msg1, waiting for msg2
|
||||
- `Active`: Authenticated; participating in tree gossip and filter exchange
|
||||
|
||||
**Crossing connection handling:**
|
||||
|
||||
When in `AwaitingChallenge` and we receive an AuthInit from the same peer:
|
||||
When in `AwaitingMsg2` and we receive a msg1 from the same peer (both sides
|
||||
initiated simultaneously):
|
||||
|
||||
- If local npub < remote npub: Ignore incoming AuthInit, remain initiator
|
||||
- If local npub > remote npub: Switch to responder role, send AuthChallenge,
|
||||
transition to `AwaitingComplete`
|
||||
- If local npub < remote npub: Ignore incoming msg1, remain initiator
|
||||
- If local npub > remote npub: Switch to responder role, send msg2,
|
||||
transition to `ReceivedMsg1`
|
||||
|
||||
**Events:**
|
||||
|
||||
@@ -367,11 +371,10 @@ PeerEvent
|
||||
├── Discovered { link_id, transport_addr, hint: Option<PublicKey> }
|
||||
├── LinkConnected
|
||||
├── LinkFailed { reason }
|
||||
├── AuthInitReceived { npub, nonce }
|
||||
├── AuthChallengeReceived { npub, nonce, signature }
|
||||
├── AuthCompleteReceived { signature }
|
||||
├── AuthSuccess { npub, node_id }
|
||||
├── AuthFailed { reason }
|
||||
├── Msg1Received { noise_payload }
|
||||
├── Msg2Received { noise_payload }
|
||||
├── HandshakeComplete { npub, node_id }
|
||||
├── HandshakeFailed { reason }
|
||||
├── TreeAnnounceReceived { declaration, ancestry }
|
||||
├── FilterAnnounceReceived { filter, sequence, ttl }
|
||||
├── Timeout { kind: TimeoutKind }
|
||||
@@ -945,7 +948,7 @@ configure the same underlying cache but are grouped by purpose.
|
||||
### Crypto Session Management
|
||||
|
||||
> **Note**: Crypto sessions provide end-to-end authenticated encryption using
|
||||
> Noise KK. See [fips-session-protocol.md](fips-session-protocol.md) §6 for details.
|
||||
> Noise IK. See [fips-session-protocol.md](fips-session-protocol.md) §6 for details.
|
||||
|
||||
| Parameter | Type | Default | Description |
|
||||
|-----------|------|---------|-------------|
|
||||
|
||||
@@ -103,7 +103,7 @@ adapts these for multi-transport operation and Nostr identity integration.
|
||||
**Encryption**: Link and session encryption use the [Noise Protocol
|
||||
Framework](https://noiseprotocol.org/), the same foundation used by WireGuard,
|
||||
Lightning Network, and other production systems. FIPS uses the IK pattern for
|
||||
link authentication and KK pattern for end-to-end sessions.
|
||||
link authentication and end-to-end sessions.
|
||||
|
||||
**Cryptographic primitives**: FIPS reuses Nostr's cryptographic stack—secp256k1
|
||||
for keys, Schnorr signatures, SHA-256 for hashing, and ChaCha20-Poly1305 for
|
||||
@@ -172,7 +172,7 @@ FIPS uses independent encryption at two layers:
|
||||
| Layer | Scope | Pattern | Purpose |
|
||||
|-------------|-------------|----------|--------------------------------------|
|
||||
| **Link** | Hop-by-hop | Noise IK | Encrypt all traffic on each link |
|
||||
| **Session** | End-to-end | Noise KK | Encrypt payload across multiple hops |
|
||||
| **Session** | End-to-end | Noise IK | Encrypt payload across multiple hops |
|
||||
|
||||
### Link Layer (Peer-to-Peer)
|
||||
|
||||
@@ -188,8 +188,9 @@ the first handshake message.
|
||||
### Session Layer (End-to-End)
|
||||
|
||||
For traffic between non-adjacent nodes, FIPS establishes end-to-end encrypted
|
||||
sessions using Noise KK. Both parties know each other's npub before initiating,
|
||||
enabling mutual authentication in a single round-trip.
|
||||
sessions using Noise IK. The initiator knows the destination's npub; the
|
||||
responder learns the initiator's identity from the handshake—the same asymmetry
|
||||
as link-layer connections.
|
||||
|
||||
A packet from A to D through intermediate node B:
|
||||
|
||||
@@ -460,7 +461,7 @@ FIPS assumes adversaries with varying capabilities:
|
||||
authentication and forward secrecy. An observer on the underlying transport
|
||||
sees only encrypted packets.
|
||||
|
||||
**End-to-end encryption**: Session-layer Noise KK encrypts payloads between
|
||||
**End-to-end encryption**: Session-layer Noise IK encrypts payloads between
|
||||
endpoints. Intermediate routers cannot read application data.
|
||||
|
||||
**Signature verification**: All protocol messages (TreeAnnounce, LookupResponse)
|
||||
@@ -529,7 +530,7 @@ maintaining control over their own identities and peering relationships.
|
||||
|
||||
| Document | Description |
|
||||
|----------|-------------|
|
||||
| [fips-session-protocol.md](fips-session-protocol.md) | End-to-end session flow, Noise KK encryption |
|
||||
| [fips-session-protocol.md](fips-session-protocol.md) | End-to-end session flow, Noise IK encryption |
|
||||
| [fips-wire-protocol.md](fips-wire-protocol.md) | Link-layer transport, Noise IK handshake |
|
||||
| [fips-gossip-protocol.md](fips-gossip-protocol.md) | TreeAnnounce, FilterAnnounce, Lookup formats |
|
||||
| [fips-routing.md](fips-routing.md) | Bloom filters, discovery, greedy routing |
|
||||
|
||||
@@ -92,7 +92,7 @@ node's npub, truncated or used directly as the filter key.
|
||||
Each node maintains a Bloom filter for each peer direction:
|
||||
|
||||
```rust
|
||||
peer_filters: HashMap<PeerId, BloomFilter>
|
||||
peer_filters: HashMap<NodeId, BloomFilter>
|
||||
```
|
||||
|
||||
The filter for peer P answers: "Which destinations are reachable through P?"
|
||||
@@ -248,7 +248,7 @@ Note: Coordinates are ordered self-to-root, so common ancestry is a suffix.
|
||||
### Greedy Routing Algorithm
|
||||
|
||||
```rust
|
||||
fn greedy_next_hop(&self, dest_coords: &[NodeId]) -> PeerId {
|
||||
fn greedy_next_hop(&self, dest_coords: &[NodeId]) -> NodeId {
|
||||
// Check if we are the destination
|
||||
if dest_coords[0] == self.node_id {
|
||||
return LOCAL_DELIVERY;
|
||||
@@ -257,7 +257,7 @@ fn greedy_next_hop(&self, dest_coords: &[NodeId]) -> PeerId {
|
||||
// Check if destination is a direct peer
|
||||
for peer in &self.peers {
|
||||
if peer.node_id == dest_coords[0] {
|
||||
return peer.id;
|
||||
return peer.node_id;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -265,7 +265,7 @@ fn greedy_next_hop(&self, dest_coords: &[NodeId]) -> PeerId {
|
||||
self.peers
|
||||
.iter()
|
||||
.min_by_key(|p| tree_distance(&p.coords, dest_coords))
|
||||
.map(|p| p.id)
|
||||
.map(|p| p.node_id)
|
||||
.expect("no peers")
|
||||
}
|
||||
```
|
||||
@@ -331,7 +331,7 @@ struct SessionSetup {
|
||||
|
||||
// Crypto session establishment (see fips-session-protocol.md §6)
|
||||
// Opaque to routers; only processed by destination
|
||||
handshake_payload: Option<Vec<u8>>, // Noise KK message 1
|
||||
handshake_payload: Option<Vec<u8>>, // Noise IK message 1
|
||||
}
|
||||
|
||||
struct SessionFlags {
|
||||
@@ -346,7 +346,7 @@ struct SessionAck {
|
||||
src_coords: Vec<NodeId>, // Acknowledger's coords (for return caching)
|
||||
|
||||
// Crypto session response (see fips-session-protocol.md §6)
|
||||
handshake_payload: Option<Vec<u8>>, // Noise KK message 2
|
||||
handshake_payload: Option<Vec<u8>>, // Noise IK message 2
|
||||
}
|
||||
|
||||
/// Minimal data packet
|
||||
|
||||
@@ -169,7 +169,7 @@ 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
|
||||
Authentication derives from the Noise IK handshake binding session keys to
|
||||
both parties' static keys. See §6 for cryptographic details.
|
||||
|
||||
### 3.2 Session Establishment Trigger
|
||||
@@ -203,10 +203,9 @@ paths.
|
||||
|
||||
### 3.4 Session Establishment Flow
|
||||
|
||||
FIPS uses Noise KK for session establishment. Both parties know each other's
|
||||
static keys: the initiator from DNS lookup, the responder from the source
|
||||
address in the SessionSetup. The KK pattern provides mutual authentication
|
||||
from the first message.
|
||||
FIPS uses Noise IK for session establishment. The initiator knows the
|
||||
destination's npub; the responder learns the initiator's identity from the
|
||||
handshake. This is the same asymmetry as link-layer connections.
|
||||
|
||||
The handshake is carried inside SessionSetup/SessionAck messages (see §5.5),
|
||||
which also establish routing session state at intermediate nodes.
|
||||
@@ -268,7 +267,7 @@ FIPS uses two distinct session concepts at different layers:
|
||||
### 5.1 Crypto Session
|
||||
|
||||
- Established between two npub identities
|
||||
- Provides confidentiality and authenticity via Noise KK
|
||||
- Provides confidentiality and authenticity via Noise IK
|
||||
- Survives route changes and transport failover
|
||||
- Keyed by: `(local_npub, remote_npub)`
|
||||
|
||||
@@ -291,14 +290,14 @@ crypto handshake, minimizing round-trips.
|
||||
2. SessionSetup + Crypto Init
|
||||
└─► Source sends SessionSetup containing:
|
||||
- src/dest coordinates (for router caching)
|
||||
- Noise KK handshake initiation (for destination)
|
||||
- Noise IK 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)
|
||||
- Noise KK handshake response
|
||||
- Noise IK handshake response
|
||||
└─► Routers cache reverse path
|
||||
└─► Source completes crypto handshake
|
||||
|
||||
@@ -321,7 +320,7 @@ FIPS uses two independent Noise Protocol handshakes at different layers:
|
||||
| Layer | Scope | Pattern | Purpose |
|
||||
|---------|-------------|----------|-------------------------------------------|
|
||||
| Link | Hop-by-hop | Noise IK | Authenticate peers, encrypt link |
|
||||
| Session | End-to-end | Noise KK | Authenticate endpoints, encrypt payload |
|
||||
| Session | End-to-end | Noise IK | Authenticate endpoints, encrypt payload |
|
||||
|
||||
Both use `Noise_IK_secp256k1_ChaChaPoly_SHA256` with the same cryptographic
|
||||
primitives, but with separate keys and sessions.
|
||||
@@ -336,21 +335,21 @@ nodes forward opaque ciphertext without being able to read the contents.
|
||||
|
||||
### 6.2 Session Noise Handshake
|
||||
|
||||
The session-layer Noise KK handshake is carried inside `SessionSetup`/`SessionAck`
|
||||
The session-layer Noise IK handshake is carried inside `SessionSetup`/`SessionAck`
|
||||
messages, which themselves travel through the link-encrypted channel:
|
||||
|
||||
```text
|
||||
Initiator knows destination npub (from DNS lookup)
|
||||
│
|
||||
▼
|
||||
SessionSetup { coords, handshake_payload: Noise KK msg1 }
|
||||
SessionSetup { coords, handshake_payload: Noise IK msg1 }
|
||||
│
|
||||
▼ (travels through link-encrypted hops)
|
||||
│
|
||||
Responder processes msg1, learns initiator identity
|
||||
│
|
||||
▼
|
||||
SessionAck { coords, handshake_payload: Noise KK msg2 }
|
||||
SessionAck { coords, handshake_payload: Noise IK msg2 }
|
||||
│
|
||||
▼
|
||||
Session keys established (independent of link keys)
|
||||
@@ -386,7 +385,7 @@ SessionSetup {
|
||||
dest_addr: Ipv6Addr,
|
||||
|
||||
// Crypto portion (opaque to routers, processed by destination)
|
||||
handshake_payload: Vec<u8>, // Noise KK message 1
|
||||
handshake_payload: Vec<u8>, // Noise IK message 1
|
||||
}
|
||||
|
||||
SessionAck {
|
||||
@@ -394,7 +393,7 @@ SessionAck {
|
||||
src_coords: Vec<NodeId>, // Responder's coordinates
|
||||
|
||||
// Crypto portion
|
||||
handshake_payload: Vec<u8>, // Noise KK message 2
|
||||
handshake_payload: Vec<u8>, // Noise IK message 2
|
||||
}
|
||||
```
|
||||
|
||||
@@ -427,13 +426,12 @@ The ephemeral keys (`e` in Noise notation) provide forward secrecy:
|
||||
Lightning's adaptation of Noise for secp256k1 (BOLT 8) provides a proven
|
||||
reference implementation:
|
||||
|
||||
- Uses Noise XK pattern (different from our KK)
|
||||
- Uses Noise XK pattern (different from our IK)
|
||||
- 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.
|
||||
FIPS can reference BOLT 8's cryptographic details while using the IK pattern.
|
||||
|
||||
### 6.9 Data Packet Authentication
|
||||
|
||||
|
||||
@@ -196,7 +196,7 @@ D receives and merges:
|
||||
|
||||
```
|
||||
D adds B's node_id to its bloom filter
|
||||
D sends BloomUpdate to parent A
|
||||
D sends FilterAnnounce to parent A
|
||||
|
||||
A merges D's bloom filter with its view of D's subtree
|
||||
A now knows "B is reachable through D" (probabilistically)
|
||||
|
||||
Reference in New Issue
Block a user