@@ -393,6 +393,129 @@ func TestOnionRouteBlinding(t *testing.T) {
393393 }
394394}
395395
396+ // TestOnionMessageRouteBlinding tests that an onion message packet can
397+ // correctly be processed by a node in a blinded route.
398+ func TestOnionMessageRouteBlinding (t * testing.T ) {
399+ t .Parallel ()
400+
401+ // First, we'll read out the raw Json file at the target location.
402+ jsonBytes , err := os .ReadFile (blindedOnionMessageOnionTestFileName )
403+ require .NoError (t , err )
404+
405+ // Once we have the raw file, we'll unpack it into our
406+ // onionMessageJsonTestCase struct defined below.
407+ testCase := & onionMessageJsonTestCase {}
408+ require .NoError (t , json .Unmarshal (jsonBytes , testCase ))
409+
410+ // Extract the original onion message packet to be processed.
411+ onion , err := hex .DecodeString (testCase .OnionMessage .OnionMessagePacket )
412+ require .NoError (t , err )
413+
414+ onionBytes := bytes .NewReader (onion )
415+ onionPacket := & OnionPacket {}
416+ require .NoError (t , onionPacket .Decode (onionBytes ))
417+
418+ // peelOnion is a helper closure that can be used to set up a Router
419+ // and use it to process the given onion packet.
420+ peelOnion := func (key * btcec.PrivateKey ,
421+ blindingPoint * btcec.PublicKey ,
422+ onionPacket * OnionPacket ) * ProcessedPacket {
423+
424+ r := NewRouter (& PrivKeyECDH {PrivKey : key }, NewMemoryReplayLog ())
425+
426+ require .NoError (t , r .Start ())
427+ defer r .Stop ()
428+
429+ // Onion messages don't use associated data, so we pass in nil
430+ // here. Also, we set a random value for incomingCLTV as it's
431+ // only used as an accompanying purposefully general type in the
432+ // ReplayLog.
433+ res , err := r .ProcessOnionPacket (
434+ onionPacket , nil , 10 ,
435+ WithBlindingPoint (blindingPoint ),
436+ )
437+ require .NoError (t , err )
438+
439+ return res
440+ }
441+
442+ hops := testCase .Generate .Hops
443+
444+ // There are some things that the processor of the onion will only be
445+ // able to determine from the actual contents of the onion_message and
446+ // the encrypted_recipient_date it receives. These things include the
447+ // first_path_key for the introduction point and the
448+ // next_path_key_override. This test doesn't decode the onion_message
449+ // and the decryption of the encrypted_recipient_data so it doesn't
450+ // extract these values. Instead we provide them manually. It also needs
451+ // to know where the next_path_key_override is located in the route,
452+ // hence it needs the concatIndex, where the part of the blinded route
453+ // constructed by Dave starts.
454+ var (
455+ firstPathKey = pubKeyFromString (
456+ testCase .Route .FirstPathKey ,
457+ )
458+ concatIndex = 1
459+ nextPathKeyOverride = pubKeyFromString (
460+ hops [0 ].EncodedOnionMessageTLVs .NextPathKeyOverride ,
461+ )
462+ )
463+
464+ // Onion message routes are always entirely blinded, so the first hop
465+ // will always use the first path key.
466+ pathKey := firstPathKey
467+
468+ currentOnionPacket := onionPacket
469+ for i , hop := range testCase .Decrypt .Hops {
470+ // We encode the onion message packet to a buffer at each hop to
471+ // compare it to the onion message packet in the test vector.
472+ buff := bytes .NewBuffer (nil )
473+ require .NoError (t , currentOnionPacket .Encode (buff ))
474+
475+ // hop.OnionMessage contains the onion_message hex string. This
476+ // contains the type 513 (two bytes), the path_key (33 bytes)
477+ // and the length of the onion_message_packet (two bytes). We
478+ // are only interested in the onion_message_packet so we only
479+ // check that part. 2 + 33 + 2 = 37 bytes, so we skip the first
480+ // 37 bytes, which equals 74 hex characters.
481+ const onionMessageHexHeaderLen = 74
482+
483+ require .Equal (
484+ t , hop .OnionMessage [onionMessageHexHeaderLen :],
485+ hex .EncodeToString (buff .Bytes ()),
486+ )
487+
488+ priv := privKeyFromString (hop .PrivKey )
489+
490+ if i == concatIndex {
491+ pathKey = nextPathKeyOverride
492+ }
493+
494+ // With peelOnion we call into ProcessOnionPacket (with the
495+ // functional option WithBlindingPoint) and we expect that the
496+ // onion message packet for this hop is processed without error,
497+ // otherwise peelOnion fails the test.
498+ processedPkt := peelOnion (
499+ priv , pathKey , currentOnionPacket ,
500+ )
501+
502+ // We derive the next path key from the current path key and the
503+ // private key of the current hop. The new path key will be used
504+ // to peel the next hop's onion unless it is overridden by a
505+ // path key override.
506+ pathKey , err = NextEphemeral (
507+ & PrivKeyECDH {priv }, pathKey ,
508+ )
509+ require .NoError (t , err )
510+
511+ // We set the current onion packet to the next packet in the
512+ // processed packet. This is the packet that the next hop will
513+ // process. During the next iteration we will run all the above
514+ // checks on this packet.
515+ currentOnionPacket = processedPkt .NextPacket
516+ }
517+ }
518+
396519type onionBlindingJsonTestCase struct {
397520 Generate generateOnionData `json:"generate"`
398521 Decrypt decryptData `json:"decrypt"`
@@ -432,10 +555,10 @@ type blindingJsonTestCase struct {
432555}
433556
434557type onionMessageJsonTestCase struct {
435- Generate generateOnionMessageData `json:"generate"`
436- Route routeOnionMessageData `json:"route"`
437- // OnionMessage onionMessageData `json:"onionmessage"`
438- Decrypt decryptOnionMessageData `json:"decrypt"`
558+ Generate generateOnionMessageData `json:"generate"`
559+ Route routeOnionMessageData `json:"route"`
560+ OnionMessage onionMessageData `json:"onionmessage"`
561+ Decrypt decryptOnionMessageData `json:"decrypt"`
439562}
440563
441564type routeData struct {
@@ -450,6 +573,10 @@ type routeOnionMessageData struct {
450573 Hops []blindedOnionMessageHop `json:"hops"`
451574}
452575
576+ type onionMessageData struct {
577+ OnionMessagePacket string `json:"onion_message_packet"`
578+ }
579+
453580type unblindData struct {
454581 Hops []unblindedHop `json:"hops"`
455582}
0 commit comments