bolt4

Onion routing protocol, per BOLT #4 (docs.ppad.tech/bolt4).
git clone git://git.ppad.tech/bolt4.git
Log | Files | Refs | README | LICENSE

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.