@@ -375,6 +375,121 @@ func TestOnionRouteBlinding(t *testing.T) {
375375 }
376376}
377377
378+ // TestOnionMessageRouteBlinding tests that an onion message packet can
379+ // correctly be processed by a node in a blinded route.
380+ func TestOnionMessageRouteBlinding (t * testing.T ) {
381+ t .Parallel ()
382+
383+ // First, we'll read out the raw Json file at the target location.
384+ jsonBytes , err := os .ReadFile (blindedOnionMessageOnionTestFileName )
385+ require .NoError (t , err )
386+
387+ // Once we have the raw file, we'll unpack it into our
388+ // onionMessageJsonTestCase struct defined above.
389+ testCase := & onionMessageJsonTestCase {}
390+ require .NoError (t , json .Unmarshal (jsonBytes , testCase ))
391+
392+ // Extract the original onion message packet to be processed.
393+ onion , err := hex .DecodeString (testCase .OnionMessage .OnionMessagePacket )
394+ require .NoError (t , err )
395+
396+ onionBytes := bytes .NewReader (onion )
397+ onionPacket := & OnionPacket {}
398+ require .NoError (t , onionPacket .Decode (onionBytes ))
399+
400+ // peelOnion is a helper closure that can be used to set up a Router
401+ // and use it to process the given onion packet.
402+ peelOnion := func (key * btcec.PrivateKey ,
403+ blindingPoint * btcec.PublicKey ,
404+ onionPacket * OnionPacket ) * ProcessedPacket {
405+
406+ r := NewRouter (& PrivKeyECDH {PrivKey : key }, NewMemoryReplayLog ())
407+
408+ require .NoError (t , r .Start ())
409+ defer r .Stop ()
410+
411+ res , err := r .ProcessOnionPacket (
412+ onionPacket , nil , 10 ,
413+ WithBlindingPoint (blindingPoint ),
414+ )
415+ require .NoError (t , err )
416+
417+ return res
418+ }
419+
420+ hops := testCase .Generate .Hops
421+
422+ // There are some things that the processor of the onion packet will
423+ // only be able to determine from the actual contents of the encrypted
424+ // data it receives. These things include the next_blinding_point for
425+ // the introduction point and the next_blinding_override. The decryption
426+ // of this data is dependent on the encoding chosen by higher layers.
427+ // The test uses TLVs. Since the extraction of this data is dependent
428+ // on layers outside the scope of this library, we provide handle these
429+ // cases manually for the sake of the test.
430+ var (
431+ firstBlinding = pubKeyFromString (testCase .Route .FirstPathKey )
432+ concatIndex = 1
433+ blindingOverride = pubKeyFromString (
434+ hops [0 ].EncodedOnionMessageTLVs .NextPathKeyOverride ,
435+ )
436+ )
437+
438+ // Onion message routes are always entirely blinded, so
439+ // the first hop will always use the first blinding
440+ // point.
441+ blindingPoint := firstBlinding
442+ currentOnionPacket := onionPacket
443+ for i , hop := range testCase .Decrypt .Hops {
444+ // We encode the onion message packet to a buffer at each hop to
445+ // compare it to the onion message packet in the test vector.
446+ buff := bytes .NewBuffer (nil )
447+ require .NoError (t , currentOnionPacket .Encode (buff ))
448+
449+ // hop.OnionMessage contains the onion_message hex string. This
450+ // contains the type 513 (two bytes), the path_key (33 bytes)
451+ // and the length of the onion_message_packet (two bytes). We
452+ // are only interested in the onion_message_packet so we only
453+ // check that part. 2 + 33 + 2 = 37 bytes, so we skip the first
454+ // 37 bytes, which equals 74 hex characters.
455+ const onionMessageHexHeaderLen = 74
456+
457+ require .Equal (
458+ t , hop .OnionMessage [onionMessageHexHeaderLen :],
459+ hex .EncodeToString (buff .Bytes ()),
460+ )
461+
462+ priv := privKeyFromString (hop .PrivKey )
463+
464+ if i == concatIndex {
465+ blindingPoint = blindingOverride
466+ }
467+
468+ // With peelOnion we call into ProcessOnionPacket (with the
469+ // functional option WithBlindingPoint) and we expect that the
470+ // onion message packet for this hop is processed without error,
471+ // otherwise peelOnion fails the test.
472+ processedPkt := peelOnion (
473+ priv , blindingPoint , currentOnionPacket ,
474+ )
475+
476+ // We derive the next blinding point from the current blinding
477+ // point and the private key of the current hop. The new
478+ // blindingPoint will be used to peel the next hop's onion
479+ // unless it is overridden by a blinding override.
480+ blindingPoint , err = NextEphemeral (
481+ & PrivKeyECDH {priv }, blindingPoint ,
482+ )
483+ require .NoError (t , err )
484+
485+ // We set the current onion packet to the next packet in the
486+ // processed packet. This is the packet that the next hop will
487+ // process. During the next iteration we will run all the above
488+ // checks on this packet.
489+ currentOnionPacket = processedPkt .NextPacket
490+ }
491+ }
492+
378493type onionBlindingJsonTestCase struct {
379494 Generate generateOnionData `json:"generate"`
380495 Decrypt decryptData `json:"decrypt"`
@@ -413,10 +528,10 @@ type blindingJsonTestCase struct {
413528}
414529
415530type onionMessageJsonTestCase struct {
416- Generate generateOnionMessageData `json:"generate"`
417- Route routeOnionMessageData `json:"route"`
418- // OnionMessage onionMessageData `json:"onionmessage"`
419- Decrypt decryptOnionMessageData `json:"decrypt"`
531+ Generate generateOnionMessageData `json:"generate"`
532+ Route routeOnionMessageData `json:"route"`
533+ OnionMessage onionMessageData `json:"onionmessage"`
534+ Decrypt decryptOnionMessageData `json:"decrypt"`
420535}
421536
422537type routeData struct {
@@ -431,6 +546,10 @@ type routeOnionMessageData struct {
431546 Hops []blindedOnionMessageHop `json:"hops"`
432547}
433548
549+ type onionMessageData struct {
550+ OnionMessagePacket string `json:"onion_message_packet"`
551+ }
552+
434553type unblindData struct {
435554 Hops []unblindedHop `json:"hops"`
436555}
0 commit comments