commit 501b5455752bd45997c9fa5d747ff67c52ebb10e
parent ddc0adda850b8f4451dbcdcdc3318b1bb32dcab7
Author: Jared Tobin <jared@jtobin.io>
Date: Sat, 10 Oct 2026 13:32:36 +0400
test: add payload_length bound tests for process
Diffstat:
| M | test/Main.hs | | | 62 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
1 file changed, 62 insertions(+), 0 deletions(-)
diff --git a/test/Main.hs b/test/Main.hs
@@ -582,6 +582,7 @@ processTests = testGroup "packet processing" [
, testEphemeralKeyValidation
, testHmacValidation
, testProcessBasic
+ , testPayloadLength
]
testVersionValidation :: TestTree
@@ -738,6 +739,67 @@ testProcessBasic = testGroup "basic processing" [
assertFailure "expected Receive, got Forward"
]
+-- | Encrypt 1300 bytes of plaintext hop_payloads into a packet for
+-- the node with secret key 'sessionKey' (using its own public key
+-- as the ephemeral key), with empty associated data.
+mkPacket :: BS.ByteString -> IO OnionPacket
+mkPacket plain = do
+ pub <- demand "derive_pub"
+ (Secp256k1.roll32 sessionKey >>= Secp256k1.derive_pub)
+ ss <- demand "computeSharedSecret" (computeSharedSecret sessionKey pub)
+ let stream = generateStream (deriveRho ss) hopPayloadsSize
+ enc = BS.pack (BS.zipWith xor plain stream)
+ mac = computeHmac (deriveMu ss) enc BS.empty
+ hp <- demand "hopPayloads" (hopPayloads enc)
+ hm <- demand "hmac32" (hmac32 mac)
+ pure (OnionPacket versionByte (Secp256k1.serialize_point pub) hp hm)
+
+-- | Pad (or truncate) plaintext hop_payloads to 1300 bytes.
+pad1300 :: BS.ByteString -> BS.ByteString
+pad1300 bs = BS.take hopPayloadsSize (bs <> BS.replicate hopPayloadsSize 0)
+
+assertInvalidPayload :: Either RejectReason ProcessResult -> Assertion
+assertInvalidPayload r = case r of
+ Left (InvalidPayload _) -> pure ()
+ Left e -> assertFailure ("expected InvalidPayload, got " ++ show e)
+ Right _ -> assertFailure "expected rejection, got success"
+
+testPayloadLength :: TestTree
+testPayloadLength = testGroup "payload_length bounds" [
+ testCase "rejects payload_length below 2" $ do
+ pkt <- mkPacket (pad1300 (BS.pack [0x01, 0x02]))
+ assertInvalidPayload (process sessionKey pkt BS.empty)
+
+ , testCase "rejects payload_length past hop_payloads" $ do
+ -- payload_length 2000, covered by an odd TLV (type 257, length
+ -- 1994) that runs into the zero-extension stream
+ let prefix = BS.pack [0xfd, 0x07, 0xd0, 0xfd, 0x01, 0x01,
+ 0xfd, 0x07, 0xca]
+ pkt <- mkPacket (pad1300 prefix)
+ assertInvalidPayload (process sessionKey pkt BS.empty)
+
+ , testCase "rejects payload_length of 2^64 - 1" $ do
+ pkt <- mkPacket (pad1300 (BS.pack (0xff : replicate 8 0xff)))
+ assertInvalidPayload (process sessionKey pkt BS.empty)
+
+ , testCase "rejects payload one byte too long" $ do
+ -- 3-byte prefix + 1266-byte payload + 32-byte HMAC = 1301
+ let tlv = BS.pack [0x01, 0xfd, 0x04, 0xee] <> BS.replicate 1262 0
+ pkt <- mkPacket (pad1300 (encodeBigSize 1266 <> tlv))
+ assertInvalidPayload (process sessionKey pkt BS.empty)
+
+ , testCase "accepts the largest payload that fits" $ do
+ -- 3-byte prefix + 1265-byte payload + 32-byte HMAC = 1300
+ let val = BS.replicate 1261 0
+ tlv = BS.pack [0x01, 0xfd, 0x04, 0xed] <> val
+ pkt <- mkPacket (pad1300 (encodeBigSize 1265 <> tlv))
+ case process sessionKey pkt BS.empty of
+ Right (Receive ri) ->
+ hpUnknownTlvs (riPayload ri) @?= [TlvRecord 1 val]
+ Right (Forward _) -> assertFailure "expected Receive"
+ Left e -> assertFailure ("expected success, got " ++ show e)
+ ]
+
-- Error tests -------------------------------------------------------------
errorTests :: TestTree