diff --git a/common/nym-lp/README.md b/common/nym-lp/README.md index a9fd7173d5..185cdaceee 100644 --- a/common/nym-lp/README.md +++ b/common/nym-lp/README.md @@ -1,71 +1,309 @@ # Nym Lewes Protocol -The Lewes Protocol (LP) is a secure network communication protocol implemented in Rust. This README provides an overview of the protocol's session management and replay protection mechanisms. +The Lewes Protocol (LP) is a secure network communication protocol implemented in Rust. It provides authenticated, encrypted sessions with replay protection and supports nested session forwarding for privacy-preserving multi-hop connections. ## Architecture Overview ``` -+-----------------+ +----------------+ +---------------+ -| Transport Layer |<--->| LP Session |<--->| LP Codec | -| (UDP/TCP) | | - Replay prot. | | - Enc/dec only| -+-----------------+ | - Crypto state | +---------------+ - +----------------+ +┌─────────────────┐ ┌────────────────┐ ┌───────────────┐ +│ Transport Layer │◄───►│ LP Session │◄───►│ LP Codec │ +│ (TCP) │ │ - State machine│ │ - Serialize │ +└─────────────────┘ │ - Noise crypto │ │ - Deserialize │ + │ - Replay prot. │ └───────────────┘ + └────────────────┘ ``` ## Packet Structure -The protocol uses a structured packet format: +The protocol uses a length-prefixed packet format over TCP: ``` -+------------------+-------------------+------------------+ -| Header (16B) | Message | Trailer (16B) | -| - Version (1B) | - Type (2B) | - Authentication | -| - Reserved (3B) | - Content | - tag/MAC | -| - SenderIdx (4B) | | | -| - Counter (8B) | | | -+------------------+-------------------+------------------+ +Wire Format: +┌────────────────────┬─────────────────────────────────────────┐ +│ Length (4B BE u32) │ LpPacket │ +└────────────────────┴─────────────────────────────────────────┘ + +LpPacket: +┌──────────────────┬───────────────────┬──────────────────┐ +│ Header (16B) │ Message │ Trailer (16B) │ +├──────────────────┼───────────────────┼──────────────────┤ +│ Version (1B) │ Type (2B LE u16) │ Reserved │ +│ Reserved (3B) │ Content (var) │ (16 bytes) │ +│ SessionID (4B LE)│ │ │ +│ Counter (8B LE) │ │ │ +└──────────────────┴───────────────────┴──────────────────┘ ``` -- Header contains protocol version, sender identification, and counter for replay protection -- Message carries the actual payload with a type identifier -- Trailer provides authentication and integrity verification (16 bytes) -- Total packet size is constrained by MTU (1500 bytes), accounting for network overhead +- **Header**: Protocol version (1), session identifier, monotonic counter +- **Message**: Type discriminant + variable-length content +- **Trailer**: Reserved for future use (16 bytes) + +## Message Types + +| Type | Value | Purpose | +|------|-------|---------| +| `Busy` | 0x0000 | Server congestion signal | +| `Handshake` | 0x0001 | Noise protocol handshake messages | +| `EncryptedData` | 0x0002 | Encrypted application data | +| `ClientHello` | 0x0003 | Initial session negotiation | +| `KKTRequest` | 0x0004 | KEM Key Transfer request | +| `KKTResponse` | 0x0005 | KEM Key Transfer response | +| `ForwardPacket` | 0x0006 | Nested session forwarding | + +## Session Establishment + +### Session ID + +Sessions are identified by a deterministic 32-bit ID computed from both parties' X25519 public keys: + +``` +session_id = make_lp_id(client_x25519_pub, gateway_x25519_pub) +``` + +The computation is order-independent, allowing both sides to derive the same ID independently. + +**BOOTSTRAP_SESSION_ID (0)**: A special session ID used only for the initial `ClientHello` packet, since neither side can compute the final ID until both X25519 keys are known. + +### Handshake Flow + +``` +┌────────┐ ┌─────────┐ +│ Client │ │ Gateway │ +└───┬────┘ └────┬────┘ + │ │ + │ 1. ClientHello (session_id=0) │ + │ [client_x25519, client_ed25519, salt]│ + │───────────────────────────────────────►│ + │ │ (computes session_id) + │ │ (stores state machine) + │ │ + │ 2. KKTRequest (session_id=N) │ + │ [signed request for KEM key] │ + │───────────────────────────────────────►│ + │ │ + │ 3. KKTResponse │ + │ [gateway KEM key + signature] │ + │◄───────────────────────────────────────│ + │ │ + │ 4. Noise Handshake Msg 1 │ + │ [PSQ payload + noise message] │ + │───────────────────────────────────────►│ + │ │ (derives PSK from PSQ) + │ 5. Noise Handshake Msg 2 │ + │ [PSK handle + noise message] │ + │◄───────────────────────────────────────│ + │ │ + │ 6. Noise Handshake Msg 3 │ + │───────────────────────────────────────►│ + │ │ + │ ═══════ Session Established ═══════ │ + │ │ + │ 7. EncryptedData │ + │ [encrypted application data] │ + │◄──────────────────────────────────────►│ + │ │ +``` + +### ClientHello Data + +```rust +struct ClientHelloData { + client_lp_public_key: [u8; 32], // X25519 (derived from Ed25519) + client_ed25519_public_key: [u8; 32], // For authentication + salt: [u8; 32], // timestamp (8B) + nonce (24B) +} +``` + +## Packet-Per-Connection Model + +The gateway processes **exactly one packet per TCP connection**, then closes. State persists between connections via in-memory maps: + +``` +TCP Connect → Receive Packet → Process → Send Response → TCP Close +``` + +**State Storage:** +- `handshake_states`: Maps `session_id → LpStateMachine` (during handshake) +- `session_states`: Maps `session_id → LpSession` (after handshake complete) + +Both maps use TTL-based cleanup to remove stale entries (default: 5 min handshake, 1 hour session). + +### Gateway Packet Routing + +``` +Packet Received + │ + ├─► session_id == 0 (BOOTSTRAP) + │ └─► handle_client_hello() + │ └─► Create state machine, store in handshake_states + │ + ├─► session_id in handshake_states + │ └─► handle_handshake_packet() + │ └─► Process KKT/Noise, move to session_states when complete + │ + └─► session_id in session_states + └─► handle_transport_packet() + └─► Decrypt, process registration or forwarding +``` + +## Session Forwarding + +Forwarding enables a client to establish an independent session with an exit gateway through an entry gateway, providing network-level privacy. + +### Architecture + +``` +┌──────────┐ +│ Client │ +└────┬─────┘ + │ Outer LP Session (established, encrypted) + │ + ▼ +┌────────────────┐ +│ Entry Gateway │ Sees: Client IP +│ │ Doesn't see: Exit destination +└────────┬───────┘ + │ Forwards inner packets (TCP) + │ + ▼ +┌────────────────┐ +│ Exit Gateway │ Sees: Entry Gateway IP +│ │ Doesn't see: Client IP +└────────────────┘ +``` + +### ForwardPacket Message + +```rust +struct ForwardPacketData { + target_gateway_identity: [u8; 32], // Exit gateway's Ed25519 key + target_lp_address: String, // e.g., "2.2.2.2:41264" + inner_packet_bytes: Vec, // Complete LP packet for exit +} +``` + +### Forwarding Flow + +1. **Client** establishes outer LP session with entry gateway +2. **Client** creates `ClientHello` packet for exit gateway +3. **Client** wraps inner packet in `ForwardPacketData`: + - Sets `target_gateway_identity` to exit's Ed25519 key + - Sets `target_lp_address` to exit's LP listener address + - Serializes complete LP packet as `inner_packet_bytes` +4. **Client** encrypts `ForwardPacketData` using outer session +5. **Client** sends as `EncryptedData` to entry gateway + +6. **Entry Gateway** decrypts, sees `ForwardPacketData` +7. **Entry Gateway** connects to exit gateway (new TCP) +8. **Entry Gateway** sends `inner_packet_bytes` directly +9. **Entry Gateway** receives exit's response +10. **Entry Gateway** encrypts response using outer session +11. **Entry Gateway** sends encrypted response to client + +12. **Client** decrypts response, processes in inner session state + +### NestedLpSession + +The `NestedLpSession` struct manages the inner session from the client's perspective: + +```rust +struct NestedLpSession { + exit_identity: [u8; 32], // Exit gateway Ed25519 + exit_address: String, // Exit LP address + client_keypair: Arc, + exit_public_key: ed25519::PublicKey, + state_machine: Option, +} +``` + +**Usage:** +```rust +// Create nested session targeting exit gateway +let nested = NestedLpSession::new(exit_identity, exit_address, keypair, exit_pubkey); + +// Perform handshake through outer session +nested.handshake_and_register(&mut outer_client).await?; + +// Inner session now established with exit gateway +``` + +## State Machine States + +``` +ReadyToHandshake + │ + ▼ + KKTExchange ◄─── KKTRequest/KKTResponse + │ + ▼ + Handshaking ◄─── Noise messages + PSQ + │ + ▼ + Transport ◄─── EncryptedData + │ + ▼ + Closed +``` + +## Cryptography + +### Key Types +- **Ed25519**: Identity keys, signing +- **X25519**: Key exchange (derived from Ed25519 via RFC 7748) + +### Noise Protocol +- Pattern: `Noise_XKpsk3_25519_ChaChaPoly_SHA256` +- Provides: Forward secrecy, mutual authentication, PSK binding + +### PSK Derivation (PSQ) +The Pre-Shared Key is derived via Post-Quantum Secure Key Exchange: +1. Client encapsulates using authenticated KEM key from KKT +2. Produces 32-byte PSK + ciphertext +3. Gateway decapsulates to derive same PSK +4. PSK injected into Noise at position 3 + +### Replay Protection + +- **Monotonic counter**: Each packet has incrementing 64-bit counter +- **Sliding window**: Bitmap tracks received counters (1024 packet window) +- **SIMD optimized**: Branchless validation for constant-time operation + +```rust +// Validation flow +validator.will_accept_branchless(counter) // Check before decrypt +validator.mark_did_receive_branchless(counter) // Mark after decrypt +``` ## Sessions -- Sessions are managed by `LPSession` and `SessionManager` classes -- Each session has unique receiving and sending indices to identify connections -- Sessions maintain: - - Cryptographic state (currently mocked implementations) - - Counter for outgoing packets - - Replay protection mechanism for incoming packets +### LpSession Fields +```rust +struct LpSession { + id: u32, // Session identifier + is_initiator: bool, // Client or gateway role + noise_state: NoiseState, // Noise transport state + kkt_state: KktState, // KKT exchange progress + psq_state: PsqState, // PSQ handshake progress + psk_handle: Option>,// PSK handle from responder + sending_counter: AtomicU64, // Outgoing packet counter + receiving_counter: Validator, // Replay protection + psk_injected: AtomicBool, // Safety: real PSK injected? +} +``` -## Session Management +### PSK Safety +Sessions initialize with a dummy PSK. The `psk_injected` flag must be `true` before `encrypt_data()` or `decrypt_data()` will operate, preventing accidental use of the insecure dummy. -- `SessionManager` handles session lifecycle (creation, retrieval, removal) -- Sessions are stored in a thread-safe HashMap indexed by receiving index -- The manager generates unique indices for new sessions -- Sessions are Arc-wrapped for safe concurrent access +## File Structure -## Replay Protection - -- Implemented in the `ReceivingKeyCounterValidator` class -- Uses a bitmap-based approach to track received packet counters -- The bitmap allows reordering of up to 1024 packets (configurable) -- SIMD optimizations are used when available for performance - -## Replay Protection Process - -1. Quick validation (`will_accept_branchless`): - - Checks if counter is valid before expensive operations - - Detects duplicates, out-of-window packets - -2. Marking packets (`mark_did_receive_branchless`): - - Updates the bitmap to mark counter as received - - Updates statistics and sliding window as needed - -3. Window Sliding: - - Automatically slides the acceptance window when new higher counters arrive - - Clears bits for old counters that fall outside the window - -This architecture effectively prevents replay attacks while allowing some packet reordering, an essential feature for secure network protocols. \ No newline at end of file +``` +common/nym-lp/src/ +├── lib.rs # Module exports +├── message.rs # LpMessage enum, ClientHelloData, ForwardPacketData +├── packet.rs # LpPacket, LpHeader, BOOTSTRAP_SESSION_ID +├── codec.rs # Serialization/deserialization +├── session.rs # LpSession, cryptographic operations +├── state_machine.rs # LpStateMachine, state transitions +├── psk.rs # PSK derivation utilities +└── error.rs # Error types +```