@@ -2,14 +2,36 @@ package hop
22
33import (
44 "bytes"
5+ "encoding/binary"
6+ "errors"
57 "fmt"
68 "io"
9+ "time"
710
811 "github.com/btcsuite/btcd/btcec/v2"
912 sphinx "github.com/lightningnetwork/lightning-onion"
1013 "github.com/lightningnetwork/lnd/lnwire"
14+ "github.com/lightningnetwork/lnd/tlv"
1115)
1216
17+ const (
18+ // A set of tlv type definitions used to serialize the encrypter to the
19+ // database.
20+ //
21+ // NOTE: A migration should be added whenever this list changes. This
22+ // prevents against the database being rolled back to an older
23+ // format where the surrounding logic might assume a different set of
24+ // fields are known.
25+ creationTimeType tlv.Type = 0
26+ )
27+
28+ // AttrErrorStruct defines the message structure for an attributable error. Use
29+ // a maximum route length of 20, a fixed payload length of 4 bytes to
30+ // accommodate the a 32-bit hold time in milliseconds and use 4 byte hmacs.
31+ // Total size including a 256 byte message from the error source works out to
32+ // 1200 bytes.
33+ var AttrErrorStruct = sphinx .NewAttrErrorStructure (20 , 4 , 4 )
34+
1335// EncrypterType establishes an enum used in serialization to indicate how to
1436// decode a concrete instance of the ErrorEncrypter interface.
1537type EncrypterType byte
@@ -27,6 +49,8 @@ const (
2749 EncrypterTypeMock = 2
2850)
2951
52+ var byteOrder = binary .BigEndian
53+
3054// SharedSecretGenerator defines a function signature that extracts a shared
3155// secret from an sphinx OnionPacket.
3256type SharedSecretGenerator func (* btcec.PublicKey ) (sphinx.Hash256 ,
@@ -80,9 +104,12 @@ type ErrorEncrypter interface {
80104// result, all errors handled are themselves wrapped in layers of onion
81105// encryption and must be treated as such accordingly.
82106type SphinxErrorEncrypter struct {
83- * sphinx. OnionErrorEncrypter
107+ encrypter interface {}
84108
85109 EphemeralKey * btcec.PublicKey
110+ CreatedAt time.Time
111+
112+ attrError bool
86113}
87114
88115// NewSphinxErrorEncrypterUninitialized initializes a blank sphinx error
@@ -94,8 +121,7 @@ type SphinxErrorEncrypter struct {
94121// OnionProcessor.
95122func NewSphinxErrorEncrypterUninitialized () * SphinxErrorEncrypter {
96123 return & SphinxErrorEncrypter {
97- OnionErrorEncrypter : nil ,
98- EphemeralKey : & btcec.PublicKey {},
124+ EphemeralKey : & btcec.PublicKey {},
99125 }
100126}
101127
@@ -104,10 +130,18 @@ func NewSphinxErrorEncrypterUninitialized() *SphinxErrorEncrypter {
104130// SphinxErrorEncrypter, use the NewSphinxErrorEncrypterUninitialized
105131// constructor.
106132func NewSphinxErrorEncrypter (ephemeralKey * btcec.PublicKey ,
107- sharedSecret sphinx.Hash256 ) * SphinxErrorEncrypter {
133+ sharedSecret sphinx.Hash256 ,
134+ attrError bool ) * SphinxErrorEncrypter {
108135
109136 encrypter := & SphinxErrorEncrypter {
110137 EphemeralKey : ephemeralKey ,
138+ attrError : attrError ,
139+ }
140+
141+ if attrError {
142+ // Set creation time rounded to nanosecond to avoid differences
143+ // after serialization.
144+ encrypter .CreatedAt = time .Now ().Truncate (time .Nanosecond )
111145 }
112146
113147 encrypter .initialize (sharedSecret )
@@ -116,9 +150,40 @@ func NewSphinxErrorEncrypter(ephemeralKey *btcec.PublicKey,
116150}
117151
118152func (s * SphinxErrorEncrypter ) initialize (sharedSecret sphinx.Hash256 ) {
119- s .OnionErrorEncrypter = sphinx .NewOnionErrorEncrypter (
120- sharedSecret ,
121- )
153+ if s .attrError {
154+ s .encrypter = sphinx .NewOnionAttrErrorEncrypter (
155+ sharedSecret , AttrErrorStruct ,
156+ )
157+ } else {
158+ s .encrypter = sphinx .NewOnionErrorEncrypter (
159+ sharedSecret ,
160+ )
161+ }
162+ }
163+
164+ func (s * SphinxErrorEncrypter ) getHoldTimeMs () uint32 {
165+ return uint32 (time .Since (s .CreatedAt ).Milliseconds ())
166+ }
167+
168+ func (s * SphinxErrorEncrypter ) encrypt (initial bool ,
169+ data []byte ) (lnwire.OpaqueReason , error ) {
170+
171+ switch encrypter := s .encrypter .(type ) {
172+ case * sphinx.OnionErrorEncrypter :
173+ return encrypter .EncryptError (initial , data ), nil
174+
175+ case * sphinx.OnionAttrErrorEncrypter :
176+ // Pass hold time as the payload.
177+ holdTimeMs := s .getHoldTimeMs ()
178+
179+ var payload [4 ]byte
180+ byteOrder .PutUint32 (payload [:], holdTimeMs )
181+
182+ return encrypter .EncryptError (initial , data , payload [:])
183+
184+ default :
185+ panic ("unexpected encrypter type" )
186+ }
122187}
123188
124189// EncryptFirstHop transforms a concrete failure message into an encrypted
@@ -135,9 +200,7 @@ func (s *SphinxErrorEncrypter) EncryptFirstHop(
135200 return nil , err
136201 }
137202
138- // We pass a true as the first parameter to indicate that a MAC should
139- // be added.
140- return s .EncryptError (true , b .Bytes ()), nil
203+ return s .encrypt (true , b .Bytes ())
141204}
142205
143206// EncryptMalformedError is similar to EncryptFirstHop (it adds the MAC), but
@@ -150,7 +213,7 @@ func (s *SphinxErrorEncrypter) EncryptFirstHop(
150213func (s * SphinxErrorEncrypter ) EncryptMalformedError (
151214 reason lnwire.OpaqueReason ) (lnwire.OpaqueReason , error ) {
152215
153- return s .EncryptError (true , reason ), nil
216+ return s .encrypt (true , reason )
154217}
155218
156219// IntermediateEncrypt wraps an already encrypted opaque reason error in an
@@ -163,7 +226,24 @@ func (s *SphinxErrorEncrypter) EncryptMalformedError(
163226func (s * SphinxErrorEncrypter ) IntermediateEncrypt (
164227 reason lnwire.OpaqueReason ) (lnwire.OpaqueReason , error ) {
165228
166- return s .EncryptError (false , reason ), nil
229+ encrypted , err := s .encrypt (false , reason )
230+ switch {
231+ // If the structure of the error received from downstream is invalid,
232+ // then generate a new failure message with a valid structure so that
233+ // the sender is able to penalize the offending node.
234+ case errors .Is (err , sphinx .ErrInvalidStructure ):
235+ // Use an all-zeroes failure message. This is not a defined
236+ // message, but the sender will at least know where the error
237+ // occurred.
238+ reason = make ([]byte , lnwire .FailureMessageLength + 2 + 2 )
239+
240+ return s .encrypt (true , reason )
241+
242+ case err != nil :
243+ return lnwire.OpaqueReason {}, err
244+ }
245+
246+ return encrypted , nil
167247}
168248
169249// Type returns the identifier for a sphinx error encrypter.
@@ -176,7 +256,25 @@ func (s *SphinxErrorEncrypter) Type() EncrypterType {
176256func (s * SphinxErrorEncrypter ) Encode (w io.Writer ) error {
177257 ephemeral := s .EphemeralKey .SerializeCompressed ()
178258 _ , err := w .Write (ephemeral )
179- return err
259+ if err != nil {
260+ return err
261+ }
262+
263+ // Stop here for legacy errors.
264+ if ! s .attrError {
265+ return nil
266+ }
267+
268+ var creationTime = uint64 (s .CreatedAt .UnixNano ())
269+
270+ tlvStream , err := tlv .NewStream (
271+ tlv .MakePrimitiveRecord (creationTimeType , & creationTime ),
272+ )
273+ if err != nil {
274+ return err
275+ }
276+
277+ return tlvStream .Encode (w )
180278}
181279
182280// Decode reconstructs the error encrypter's ephemeral public key from the
@@ -193,6 +291,30 @@ func (s *SphinxErrorEncrypter) Decode(r io.Reader) error {
193291 return err
194292 }
195293
294+ // Try decode attributable error structure.
295+ var creationTime uint64
296+
297+ tlvStream , err := tlv .NewStream (
298+ tlv .MakePrimitiveRecord (creationTimeType , & creationTime ),
299+ )
300+ if err != nil {
301+ return err
302+ }
303+
304+ typeMap , err := tlvStream .DecodeWithParsedTypes (r )
305+ if err != nil {
306+ return err
307+ }
308+
309+ // Return early if this encrypter is not for attributable errors.
310+ if len (typeMap ) == 0 {
311+ return nil
312+ }
313+
314+ // Set attributable error flag and creation time.
315+ s .attrError = true
316+ s .CreatedAt = time .Unix (0 , int64 (creationTime ))
317+
196318 return nil
197319}
198320
0 commit comments