@@ -41,26 +41,26 @@ const (
4141 LegacyHopDataSize = (RealmByteSize + AddressSize + AmtForwardSize +
4242 OutgoingCLTVSize + NumPaddingBytes + HMACSize )
4343
44- // MaxPayloadSize is the maximum size a payload for a single hop can be.
45- // This is the worst case scenario of a single hop, consuming all
46- // available space. We need to know this in order to generate a
47- // sufficiently long stream of pseudo-random bytes when
48- // encrypting/decrypting the payload.
49- MaxPayloadSize = routingInfoSize
50-
51- // routingInfoSize is the fixed size of the the routing info. This
52- // consists of a addressSize byte address and a HMACSize byte HMAC for
44+ // MaxPayloadSize is the maximum size an `update_add_htlc` payload for a
45+ // single hop can be. This is the worst case scenario of a single hop,
46+ // consuming all available space. We need to know this in order to
47+ // generate a sufficiently long stream of pseudo-random bytes when
48+ // encrypting/decrypting the payload. This field is here for backwards
49+ // compatibility. Throughout the code we use StandardRoutingInfoSize
50+ // because of the more apt naming.
51+ MaxPayloadSize = standardRoutingInfoSize
52+ StandardRoutingInfoSize = standardRoutingInfoSize
53+
54+ // standardRoutingInfoSize is the fixed size of the the routing info. This
55+ // consists of an addressSize byte address and a HMACSize byte HMAC for
5356 // each hop of the route, the first pair in cleartext and the following
5457 // pairs increasingly obfuscated. If not all space is used up, the
5558 // remainder is padded with null-bytes, also obfuscated.
56- routingInfoSize = 1300
59+ standardRoutingInfoSize = 1300
5760
58- // numStreamBytes is the number of bytes produced by our CSPRG for the
59- // key stream implementing our stream cipher to encrypt/decrypt the mix
60- // header. The MaxPayloadSize bytes at the end are used to
61- // encrypt/decrypt the fillers when processing the packet of generating
62- // the HMACs when creating the packet.
63- numStreamBytes = routingInfoSize * 2
61+ // JumboRoutingInfoSize is the size of the routing info for a jumbo
62+ // onion packet.
63+ JumboRoutingInfoSize = 32768
6464
6565 // keyLen is the length of the keys used to generate cipher streams and
6666 // encrypt payloads. Since we use SHA256 to generate the keys, the
@@ -72,8 +72,15 @@ const (
7272)
7373
7474var (
75- ErrMaxRoutingInfoSizeExceeded = fmt .Errorf (
76- "max routing info size of %v bytes exceeded" , routingInfoSize )
75+ ErrStandardRoutingPayloadSizeExceeded = fmt .Errorf (
76+ "max routing info size of %v bytes exceeded" ,
77+ StandardRoutingInfoSize ,
78+ )
79+
80+ ErrMessageRoutingPayloadSizeExceeded = fmt .Errorf (
81+ "max onion message routing info size of %v bytes exceeded" ,
82+ JumboRoutingInfoSize ,
83+ )
7784)
7885
7986// OnionPacket is the onion wrapped hop-to-hop routing information necessary to
@@ -102,7 +109,7 @@ type OnionPacket struct {
102109 // RoutingInfo is the full routing information for this onion packet.
103110 // This encodes all the forwarding instructions for this current hop
104111 // and all the hops in the route.
105- RoutingInfo [routingInfoSize ]byte
112+ RoutingInfo []byte
106113
107114 // HeaderMAC is an HMAC computed with the shared secret of the routing
108115 // data and the associated data for this route. Including the
@@ -193,12 +200,8 @@ func generateSharedSecrets(paymentPath []*btcec.PublicKey,
193200// NewOnionPacket creates a new onion packet which is capable of obliviously
194201// routing a message through the mix-net path outline by 'paymentPath'.
195202func NewOnionPacket (paymentPath * PaymentPath , sessionKey * btcec.PrivateKey ,
196- assocData []byte , pktFiller PacketFiller ) (* OnionPacket , error ) {
197-
198- // Check whether total payload size doesn't exceed the hard maximum.
199- if paymentPath .TotalPayloadSize () > routingInfoSize {
200- return nil , ErrMaxRoutingInfoSizeExceeded
201- }
203+ assocData []byte , pktFiller PacketFiller ,
204+ isOnionMessage bool ) (* OnionPacket , error ) {
202205
203206 // If we don't actually have a partially populated route, then we'll
204207 // exit early.
@@ -207,6 +210,34 @@ func NewOnionPacket(paymentPath *PaymentPath, sessionKey *btcec.PrivateKey,
207210 return nil , fmt .Errorf ("route of length zero passed in" )
208211 }
209212
213+ totalPayloadSize := paymentPath .TotalPayloadSize ()
214+
215+ routingInfoLen := StandardRoutingInfoSize
216+ maxRoutingInfoErr := ErrStandardRoutingPayloadSizeExceeded
217+ if isOnionMessage && totalPayloadSize > StandardRoutingInfoSize {
218+ routingInfoLen = JumboRoutingInfoSize
219+ maxRoutingInfoErr = ErrMessageRoutingPayloadSizeExceeded
220+ }
221+
222+ // Check whether total payload size doesn't exceed the hard maximum.
223+ if totalPayloadSize > routingInfoLen {
224+ return nil , maxRoutingInfoErr
225+ }
226+
227+ // Before we proceed, we'll check that the payload types of each hop
228+ // in the payment path match the type of onion packet we're creating.
229+ for i := 0 ; i < numHops ; i ++ {
230+ hopPayload := (* paymentPath )[i ].HopPayload
231+ isLegacy := hopPayload .Type == PayloadLegacy
232+
233+ // If this is an onion message, we only expect TLV
234+ // payloads.
235+ if isOnionMessage && isLegacy {
236+ return nil , fmt .Errorf ("hop %d has legacy payload, " +
237+ "but onion messages require TLV" , i )
238+ }
239+ }
240+
210241 // We'll force the caller to provide a packet filler, as otherwise we
211242 // may default to an insecure filling method (which should only really
212243 // be used to generate test vectors).
@@ -222,18 +253,20 @@ func NewOnionPacket(paymentPath *PaymentPath, sessionKey *btcec.PrivateKey,
222253 }
223254
224255 // Generate the padding, called "filler strings" in the paper.
225- filler := generateHeaderPadding ("rho" , paymentPath , hopSharedSecrets )
256+ filler := generateHeaderPadding (
257+ "rho" , paymentPath , hopSharedSecrets , routingInfoLen ,
258+ )
226259
227260 // Allocate zero'd out byte slices to store the final mix header packet
228261 // and the hmac for each hop.
229262 var (
230- mixHeader [ routingInfoSize ]byte
263+ mixHeader = make ([ ]byte , routingInfoLen )
231264 nextHmac [HMACSize ]byte
232265 hopPayloadBuf bytes.Buffer
233266 )
234267
235268 // Fill the packet using the caller specified methodology.
236- if err := pktFiller (sessionKey , & mixHeader ); err != nil {
269+ if err := pktFiller (sessionKey , mixHeader ); err != nil {
237270 return nil , err
238271 }
239272
@@ -254,26 +287,26 @@ func NewOnionPacket(paymentPath *PaymentPath, sessionKey *btcec.PrivateKey,
254287 // Next, using the key dedicated for our stream cipher, we'll
255288 // generate enough bytes to obfuscate this layer of the onion
256289 // packet.
257- streamBytes := generateCipherStream (rhoKey , routingInfoSize )
290+ streamBytes := generateCipherStream (rhoKey , uint ( routingInfoLen ) )
258291 payload := paymentPath [i ].HopPayload
259292
260293 // Before we assemble the packet, we'll shift the current
261294 // mix-header to the right in order to make room for this next
262295 // per-hop data.
263296 shiftSize := payload .NumBytes ()
264- rightShift (mixHeader [:] , shiftSize )
297+ rightShift (mixHeader , shiftSize )
265298
266299 err := payload .Encode (& hopPayloadBuf )
267300 if err != nil {
268301 return nil , err
269302 }
270303
271- copy (mixHeader [:] , hopPayloadBuf .Bytes ())
304+ copy (mixHeader , hopPayloadBuf .Bytes ())
272305
273306 // Once the packet for this hop has been assembled, we'll
274307 // re-encrypt the packet by XOR'ing with a stream of bytes
275308 // generated using our shared secret.
276- xor (mixHeader [:] , mixHeader [:] , streamBytes [:] )
309+ xor (mixHeader , mixHeader , streamBytes )
277310
278311 // If this is the "last" hop, then we'll override the tail of
279312 // the hop data.
@@ -285,7 +318,7 @@ func NewOnionPacket(paymentPath *PaymentPath, sessionKey *btcec.PrivateKey,
285318 // calculating the MAC, we'll also include the optional
286319 // associated data which can allow higher level applications to
287320 // prevent replay attacks.
288- packet := append (mixHeader [:] , assocData ... )
321+ packet := append (mixHeader , assocData ... )
289322 nextHmac = calcMac (muKey , packet )
290323
291324 hopPayloadBuf .Reset ()
@@ -322,7 +355,9 @@ func rightShift(slice []byte, num int) {
322355// leaving only the original "filler" bytes produced by this function at the
323356// last hop. Using this methodology, the size of the field stays constant at
324357// each hop.
325- func generateHeaderPadding (key string , path * PaymentPath , sharedSecrets []Hash256 ) []byte {
358+ func generateHeaderPadding (key string , path * PaymentPath ,
359+ sharedSecrets []Hash256 , routingInfoLen int ) []byte {
360+
326361 numHops := path .TrueRouteLength ()
327362
328363 // We have to generate a filler that matches all but the last hop (the
@@ -332,18 +367,20 @@ func generateHeaderPadding(key string, path *PaymentPath, sharedSecrets []Hash25
332367
333368 for i := 0 ; i < numHops - 1 ; i ++ {
334369 // Sum up how many frames were used by prior hops.
335- fillerStart := routingInfoSize
370+ fillerStart := routingInfoLen
336371 for _ , p := range path [:i ] {
337372 fillerStart -= p .HopPayload .NumBytes ()
338373 }
339374
340375 // The filler is the part dangling off of the end of the
341376 // routingInfo, so offset it from there, and use the current
342377 // hop's frame count as its size.
343- fillerEnd := routingInfoSize + path [i ].HopPayload .NumBytes ()
378+ fillerEnd := routingInfoLen + path [i ].HopPayload .NumBytes ()
344379
345380 streamKey := generateKey (key , & sharedSecrets [i ])
346- streamBytes := generateCipherStream (streamKey , numStreamBytes )
381+ streamBytes := generateCipherStream (
382+ streamKey , numStreamBytes (routingInfoLen ),
383+ )
347384
348385 xor (filler , filler , streamBytes [fillerStart :fillerEnd ])
349386 }
@@ -365,7 +402,7 @@ func (f *OnionPacket) Encode(w io.Writer) error {
365402 return err
366403 }
367404
368- if _ , err := w .Write (f .RoutingInfo [:] ); err != nil {
405+ if _ , err := w .Write (f .RoutingInfo ); err != nil {
369406 return err
370407 }
371408
@@ -404,14 +441,24 @@ func (f *OnionPacket) Decode(r io.Reader) error {
404441 return ErrInvalidOnionKey
405442 }
406443
407- if _ , err := io .ReadFull (r , f .RoutingInfo [:]); err != nil {
444+ // To figure out the length of the routing info, we'll read all the
445+ // remaining bytes from the reader.
446+ routingInfoAndMAC , err := io .ReadAll (r )
447+ if err != nil {
408448 return err
409449 }
410450
411- if _ , err := io .ReadFull (r , f .HeaderMAC [:]); err != nil {
412- return err
451+ // The packet must have at least enough bytes for the HMAC.
452+ if len (routingInfoAndMAC ) < HMACSize {
453+ return fmt .Errorf ("onion packet is too small, missing HMAC" )
413454 }
414455
456+ // With the remainder of the packet read, we can now properly slice the
457+ // routing information and the MAC.
458+ routingInfoLen := len (routingInfoAndMAC ) - HMACSize
459+ f .RoutingInfo = routingInfoAndMAC [:routingInfoLen ]
460+ copy (f .HeaderMAC [:], routingInfoAndMAC [routingInfoLen :])
461+
415462 return nil
416463}
417464
@@ -644,11 +691,12 @@ func unwrapPacket(onionPkt *OnionPacket, sharedSecret *Hash256,
644691 dhKey := onionPkt .EphemeralKey
645692 routeInfo := onionPkt .RoutingInfo
646693 headerMac := onionPkt .HeaderMAC
694+ routingInfoLen := len (routeInfo )
647695
648696 // Using the derived shared secret, ensure the integrity of the routing
649697 // information by checking the attached MAC without leaking timing
650698 // information.
651- message := append (routeInfo [:] , assocData ... )
699+ message := append (routeInfo , assocData ... )
652700 calculatedMac := calcMac (generateKey ("mu" , sharedSecret ), message )
653701 if ! hmac .Equal (headerMac [:], calculatedMac [:]) {
654702 return nil , nil , ErrInvalidOnionHMAC
@@ -658,13 +706,14 @@ func unwrapPacket(onionPkt *OnionPacket, sharedSecret *Hash256,
658706 // layer off the routing info revealing the routing information for the
659707 // next hop.
660708 streamBytes := generateCipherStream (
661- generateKey ("rho" , sharedSecret ), numStreamBytes ,
709+ generateKey ("rho" , sharedSecret ),
710+ numStreamBytes (routingInfoLen ),
662711 )
663- zeroBytes := bytes .Repeat ([]byte {0 }, MaxPayloadSize )
664- headerWithPadding := append (routeInfo [:] , zeroBytes ... )
712+ zeroBytes := bytes .Repeat ([]byte {0 }, routingInfoLen )
713+ headerWithPadding := append (routeInfo , zeroBytes ... )
665714
666- var hopInfo [ numStreamBytes ]byte
667- xor (hopInfo [:] , headerWithPadding , streamBytes )
715+ hopInfo := make ([ ]byte , numStreamBytes ( routingInfoLen ))
716+ xor (hopInfo , headerWithPadding , streamBytes )
668717
669718 // Randomize the DH group element for the next hop using the
670719 // deterministic blinding factor.
@@ -682,15 +731,15 @@ func unwrapPacket(onionPkt *OnionPacket, sharedSecret *Hash256,
682731 // payload is treated as such.
683732 hopPayload .Type = PayloadTLV
684733 }
685- err := hopPayload .Decode (bytes .NewReader (hopInfo [:] ))
734+ err := hopPayload .Decode (bytes .NewReader (hopInfo ))
686735 if err != nil {
687736 return nil , nil , err
688737 }
689738
690739 // With the necessary items extracted, we'll copy of the onion packet
691740 // for the next node, snipping off our per-hop data.
692- var nextMixHeader [ routingInfoSize ]byte
693- copy (nextMixHeader [:] , hopInfo [hopPayload .NumBytes ():])
741+ var nextMixHeader = make ([ ]byte , routingInfoLen )
742+ copy (nextMixHeader , hopInfo [hopPayload .NumBytes ():])
694743 innerPkt := & OnionPacket {
695744 Version : onionPkt .Version ,
696745 EphemeralKey : nextDHKey ,
@@ -853,3 +902,12 @@ func (t *Tx) Commit() ([]ProcessedPacket, *ReplaySet, error) {
853902
854903 return t .packets , rs , err
855904}
905+
906+ // numStreamBytes is the number of bytes that needs to be produced by our CSPRG
907+ // for the key stream implementing our stream cipher to encrypt/decrypt the mix
908+ // header. The routingInfoSize bytes at the end are used to encrypt/decrypt the
909+ // fillers when processing the packet of generating the HMACs when creating the
910+ // packet.
911+ func numStreamBytes (routingInfoSize int ) uint {
912+ return uint (routingInfoSize * 2 )
913+ }
0 commit comments