IMPL4.md (6433B)
1 # IMPL4: Packet Processing 2 3 **Module**: `Lightning.Protocol.BOLT4.Process` 4 5 **Dependencies**: IMPL1 (Prim), IMPL2 (Types, Codec) 6 7 **Can run in parallel with**: IMPL3, IMPL5 (after IMPL1 and IMPL2 complete) 8 9 ## Overview 10 11 Implement onion packet processing from the receiver's perspective 12 (both intermediate nodes and final destination). 13 14 ## Types 15 16 ```haskell 17 -- | Result of processing an incoming onion packet. 18 data ProcessResult 19 = Forward !ForwardData -- ^ Intermediate node: forward to next hop 20 | Receive !ReceiveData -- ^ Final node: payment reached destination 21 | Reject !RejectReason -- ^ Invalid packet 22 deriving (Eq, Show) 23 24 -- | Data for forwarding to next hop. 25 data ForwardData = ForwardData 26 { fdNextPacket :: !OnionPacket -- ^ Packet for next node 27 , fdPayload :: !HopPayload -- ^ Routing instructions 28 , fdSharedSecret :: !SharedSecret -- ^ For error wrapping 29 } deriving (Eq, Show) 30 31 -- | Data when packet reaches final destination. 32 data ReceiveData = ReceiveData 33 { rdPayload :: !HopPayload -- ^ Payment data 34 , rdSharedSecret :: !SharedSecret -- ^ For error wrapping 35 } deriving (Eq, Show) 36 37 -- | Reasons for rejecting a packet. 38 data RejectReason 39 = InvalidVersion !Word8 -- ^ Version != 0x00 40 | InvalidEphemeralKey -- ^ Malformed public key 41 | HmacMismatch -- ^ HMAC verification failed 42 | InvalidPayload !String -- ^ Malformed payload 43 deriving (Eq, Show) 44 ``` 45 46 ## Main Function 47 48 ```haskell 49 -- | Process an incoming onion packet. 50 -- 51 -- Takes the node's private key, the incoming packet, and associated 52 -- data (typically payment_hash). 53 process 54 :: Secp256k1.SecKey -- ^ this node's private key 55 -> OnionPacket -- ^ incoming packet 56 -> BS.ByteString -- ^ associated data 57 -> ProcessResult 58 ``` 59 60 ## Internal Functions 61 62 ### Validation 63 64 ```haskell 65 -- | Validate packet version. 66 validateVersion :: OnionPacket -> Either RejectReason () 67 68 -- | Parse and validate ephemeral public key. 69 parseEphemeralKey :: OnionPacket -> Either RejectReason Secp256k1.PubKey 70 ``` 71 72 ### Decryption 73 74 ```haskell 75 -- | Decrypt hop_payloads by XORing with rho stream. 76 -- 77 -- Generates a stream of 2*1300 bytes (to handle the shift), 78 -- XORs with hop_payloads extended with 1300 zero bytes. 79 decryptPayloads 80 :: DerivedKey -- ^ rho key 81 -> BS.ByteString -- ^ hop_payloads (1300 bytes) 82 -> BS.ByteString -- ^ decrypted (2600 bytes, first 1300 useful) 83 ``` 84 85 ### Payload Extraction 86 87 ```haskell 88 -- | Extract payload from decrypted buffer. 89 -- 90 -- Parses BigSize length, extracts payload bytes and next HMAC. 91 extractPayload 92 :: BS.ByteString -- ^ decrypted buffer 93 -> Either RejectReason (HopPayload, BS.ByteString, BS.ByteString) 94 -- ^ (payload, next_hmac, remaining_hop_payloads) 95 ``` 96 97 ### HMAC Verification 98 99 ```haskell 100 -- | Verify packet HMAC. 101 -- 102 -- Computes HMAC over (hop_payloads || associated_data) using mu key. 103 -- Returns True if constant-time equal to packet's HMAC. 104 verifyPacketHmac 105 :: DerivedKey -- ^ mu key 106 -> OnionPacket -- ^ packet with HMAC to verify 107 -> BS.ByteString -- ^ associated data 108 -> Bool 109 ``` 110 111 ### Forwarding Preparation 112 113 ```haskell 114 -- | Prepare packet for forwarding to next hop. 115 -- 116 -- Computes blinded ephemeral key, constructs next OnionPacket. 117 prepareForward 118 :: Secp256k1.PubKey -- ^ current ephemeral key 119 -> SharedSecret -- ^ shared secret (for blinding) 120 -> BS.ByteString -- ^ remaining hop_payloads (after shift) 121 -> BS.ByteString -- ^ next HMAC 122 -> Maybe OnionPacket 123 ``` 124 125 Algorithm: 126 1. Compute blinding factor = SHA256(ephemeral || shared_secret) 127 2. Blind ephemeral key: new_ephemeral = ephemeral * blinding_factor 128 3. Truncate remaining_payloads to 1300 bytes (they're already shifted) 129 4. Construct OnionPacket with new ephemeral and next HMAC 130 131 ### Final Detection 132 133 ```haskell 134 -- | Check if this is the final hop. 135 -- 136 -- Final hop is indicated by next_hmac being all zeros. 137 isFinalHop :: BS.ByteString -> Bool 138 isFinalHop hmac = hmac == BS.replicate 32 0 139 ``` 140 141 ## Processing Algorithm 142 143 Full algorithm as pseudocode: 144 145 ``` 146 process(node_seckey, packet, assoc_data): 147 1. Validate version == 0x00 148 If not: return Reject(InvalidVersion) 149 150 2. Parse ephemeral_pubkey from packet 151 If invalid: return Reject(InvalidEphemeralKey) 152 153 3. Compute shared_secret = computeSharedSecret(node_seckey, ephemeral_pubkey) 154 155 4. Derive keys: 156 mu = deriveMu(shared_secret) 157 rho = deriveRho(shared_secret) 158 159 5. Verify HMAC: 160 expected = computeHmac(mu, packet.hop_payloads, assoc_data) 161 If not verifyHmac(packet.hmac, expected): 162 return Reject(HmacMismatch) 163 164 6. Decrypt: 165 decrypted = decryptPayloads(rho, packet.hop_payloads) 166 167 7. Extract payload: 168 (payload, next_hmac, remaining) = extractPayload(decrypted) 169 If error: return Reject(InvalidPayload) 170 171 8. Parse payload TLV: 172 hop_payload = decodeHopPayload(payload) 173 If error: return Reject(InvalidPayload) 174 175 9. Check if final: 176 If isFinalHop(next_hmac): 177 return Receive(ReceiveData { 178 payload = hop_payload, 179 shared_secret = shared_secret 180 }) 181 182 10. Prepare forward packet: 183 next_packet = prepareForward(ephemeral_pubkey, shared_secret, 184 remaining, next_hmac) 185 If error: return Reject(InvalidEphemeralKey) 186 187 11. return Forward(ForwardData { 188 next_packet = next_packet, 189 payload = hop_payload, 190 shared_secret = shared_secret 191 }) 192 ``` 193 194 ## Implementation Notes 195 196 1. HMAC verification MUST be constant-time to prevent timing attacks. 197 198 2. The decryption extends hop_payloads with zeros because after XOR, 199 the "shifted in" portion will contain the next layer's data. 200 201 3. After extracting the payload, the remaining buffer is already 202 positioned for the next hop (left-shifted by payload size). 203 204 4. Shared secret is returned for error message construction - if the 205 node needs to report a failure, it uses this to wrap the error. 206 207 5. The blinding operation on ephemeral key ensures each hop sees a 208 different ephemeral key, unlinkable to previous hops. 209 210 ## Test Vectors 211 212 Using the spec's test route, verify that processing at each hop: 213 - Produces correct shared secret 214 - Extracts correct payload 215 - Generates correct next ephemeral key 216 - Correctly identifies final hop 217 218 Process packet at node 0, verify forward packet matches node 1's view. 219 Process at node 4 (final), verify Receive result.