From 9c51bea7906060bbf7b8dc23cab7d542a610eb10 Mon Sep 17 00:00:00 2001 From: Matt Morehouse Date: Thu, 7 Sep 2023 15:23:34 -0500 Subject: [PATCH 1/2] htlcswitch: fuzz lightning-onion onions and payloads Fuzz tests for: - HopData - HopPayload - OnionPacket --- htlcswitch/hop/fuzz_test.go | 80 +++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 htlcswitch/hop/fuzz_test.go diff --git a/htlcswitch/hop/fuzz_test.go b/htlcswitch/hop/fuzz_test.go new file mode 100644 index 000000000..de6d9d182 --- /dev/null +++ b/htlcswitch/hop/fuzz_test.go @@ -0,0 +1,80 @@ +package hop + +import ( + "bytes" + "testing" + + sphinx "github.com/lightningnetwork/lightning-onion" + "github.com/stretchr/testify/require" +) + +const ( + LegacyPayloadSize = sphinx.LegacyHopDataSize - sphinx.HMACSize + MaxOnionPacketSize = 1366 +) + +func FuzzHopData(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + if len(data) > LegacyPayloadSize { + return + } + + r := bytes.NewReader(data) + + var hopData1, hopData2 sphinx.HopData + + if err := hopData1.Decode(r); err != nil { + return + } + + var b bytes.Buffer + require.NoError(t, hopData1.Encode(&b)) + require.NoError(t, hopData2.Decode(&b)) + + require.Equal(t, hopData1, hopData2) + }) +} + +func FuzzHopPayload(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + if len(data) > sphinx.MaxPayloadSize { + return + } + + r := bytes.NewReader(data) + + var hopPayload1, hopPayload2 sphinx.HopPayload + + if err := hopPayload1.Decode(r); err != nil { + return + } + + var b bytes.Buffer + require.NoError(t, hopPayload1.Encode(&b)) + require.NoError(t, hopPayload2.Decode(&b)) + + require.Equal(t, hopPayload1, hopPayload2) + }) +} + +func FuzzOnionPacket(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + if len(data) > MaxOnionPacketSize { + return + } + + r := bytes.NewReader(data) + + var pkt1, pkt2 sphinx.OnionPacket + + if err := pkt1.Decode(r); err != nil { + return + } + + var b bytes.Buffer + require.NoError(t, pkt1.Encode(&b)) + require.NoError(t, pkt2.Decode(&b)) + + require.Equal(t, pkt1, pkt2) + }) +} From 5863b9f2fcab39b85bcfd405aec02a6a6828b3eb Mon Sep 17 00:00:00 2001 From: Matt Morehouse Date: Thu, 7 Sep 2023 15:26:22 -0500 Subject: [PATCH 2/2] htlcswitch: fuzz hop.Payload and route.Hop hop.Payload and route.Hop are analogs, with onion payloads encoded from route.Hops and decoded to hop.Payloads. For checking equality of encoding/decoding, we implement a helper function to convert hop.Payloads into route.Hops. --- htlcswitch/hop/fuzz_test.go | 46 +++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/htlcswitch/hop/fuzz_test.go b/htlcswitch/hop/fuzz_test.go index de6d9d182..82a92eb22 100644 --- a/htlcswitch/hop/fuzz_test.go +++ b/htlcswitch/hop/fuzz_test.go @@ -2,9 +2,11 @@ package hop import ( "bytes" + "errors" "testing" sphinx "github.com/lightningnetwork/lightning-onion" + "github.com/lightningnetwork/lnd/routing/route" "github.com/stretchr/testify/require" ) @@ -78,3 +80,47 @@ func FuzzOnionPacket(f *testing.F) { require.Equal(t, pkt1, pkt2) }) } + +func hopFromPayload(p *Payload) (*route.Hop, uint64) { + return &route.Hop{ + AmtToForward: p.FwdInfo.AmountToForward, + OutgoingTimeLock: p.FwdInfo.OutgoingCTLV, + MPP: p.MPP, + AMP: p.AMP, + Metadata: p.metadata, + CustomRecords: p.customRecords, + }, p.FwdInfo.NextHop.ToUint64() +} + +func FuzzPayload(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + if len(data) > sphinx.MaxPayloadSize { + return + } + + r := bytes.NewReader(data) + + payload1, err := NewPayloadFromReader(r) + if err != nil { + return + } + + var b bytes.Buffer + hop, nextChanID := hopFromPayload(payload1) + err = hop.PackHopPayload(&b, nextChanID) + if errors.Is(err, route.ErrAMPMissingMPP) { + // PackHopPayload refuses to encode an AMP record + // without an MPP record. However, NewPayloadFromReader + // does allow decoding an AMP record without an MPP + // record, since validation is done at a later stage. Do + // not report a bug for this case. + return + } + require.NoError(t, err) + + payload2, err := NewPayloadFromReader(&b) + require.NoError(t, err) + + require.Equal(t, payload1, payload2) + }) +}