@@ -21,24 +21,27 @@ use super::messenger::{
2121 OnionMessagePath , OnionMessenger , Responder , ResponseInstruction , SendError , SendSuccess ,
2222} ;
2323use super :: offers:: { OffersMessage , OffersMessageHandler } ;
24- use super :: packet:: { OnionMessageContents , Packet } ;
24+ use super :: packet:: { ControlTlvs , OnionMessageContents , Packet } ;
2525use crate :: blinded_path:: message:: {
2626 AsyncPaymentsContext , BlindedMessagePath , DNSResolverContext , MessageContext ,
27- MessageForwardNode , NextMessageHop , OffersContext , MESSAGE_PADDING_ROUND_OFF ,
27+ MessageForwardNode , NextMessageHop , OffersContext , ReceiveTlvs , MESSAGE_PADDING_ROUND_OFF ,
2828} ;
2929use crate :: blinded_path:: utils:: is_padded;
3030use crate :: blinded_path:: NodeIdLookUp ;
31+ use crate :: crypto:: streams:: ChaChaPolyReadAdapter ;
3132use crate :: events:: { Event , EventsProvider } ;
3233use crate :: ln:: msgs:: { self , BaseMessageHandler , DecodeError , OnionMessageHandler } ;
34+ use crate :: ln:: onion_utils:: gen_rho_from_shared_secret;
3335use crate :: routing:: gossip:: { NetworkGraph , P2PGossipSync } ;
3436use crate :: routing:: test_utils:: { add_channel, add_or_update_node} ;
3537use crate :: sign:: { NodeSigner , Recipient } ;
3638use crate :: types:: features:: { ChannelFeatures , InitFeatures } ;
37- use crate :: util:: ser:: { FixedLengthReader , LengthReadable , Writeable , Writer } ;
39+ use crate :: util:: ser:: { FixedLengthReader , LengthReadable , LengthReadableArgs , Writeable , Writer } ;
3840use crate :: util:: test_utils:: { TestChainSource , TestKeysInterface , TestLogger , TestNodeSigner } ;
3941
4042use bitcoin:: hex:: FromHex ;
4143use bitcoin:: network:: Network ;
44+ use bitcoin:: secp256k1:: ecdh:: SharedSecret ;
4245use bitcoin:: secp256k1:: { All , PublicKey , Secp256k1 , SecretKey } ;
4346
4447use crate :: io;
@@ -480,6 +483,41 @@ fn one_blinded_hop() {
480483 pass_along_path ( & nodes) ;
481484}
482485
486+ #[ test]
487+ fn blinded_path_for_external_recipient ( ) {
488+ // Check a path for a non-LDK recipient is delivered, and that its recipient hop can be read by a
489+ // spec-standard ChaCha20Poly1305 decryptor with no context.
490+ let nodes = create_nodes ( 2 ) ;
491+ let test_msg = TestCustomMessage :: Pong ;
492+
493+ let secp_ctx = Secp256k1 :: new ( ) ;
494+ let entropy = & * nodes[ 1 ] . entropy_source ;
495+ let node_id = nodes[ 1 ] . node_id ;
496+ let blinded_path =
497+ BlindedMessagePath :: new_for_external_recipient ( & [ ] , node_id, false , entropy, & secp_ctx) ;
498+
499+ // The recipient hop must decrypt under standard ChaCha20Poly1305 (empty AAD) with no context.
500+ {
501+ let ss = SharedSecret :: new ( & blinded_path. blinding_point ( ) , & nodes[ 1 ] . privkey ) ;
502+ let rho = gen_rho_from_shared_secret ( & ss. secret_bytes ( ) ) ;
503+ let encrypted_payload = & blinded_path. blinded_hops ( ) [ 0 ] . encrypted_payload ;
504+ let mut s = io:: Cursor :: new ( encrypted_payload) ;
505+ let mut reader = FixedLengthReader :: new ( & mut s, encrypted_payload. len ( ) as u64 ) ;
506+ let ChaChaPolyReadAdapter { readable } =
507+ <ChaChaPolyReadAdapter < ControlTlvs > >:: read ( & mut reader, rho) . unwrap ( ) ;
508+ match readable {
509+ ControlTlvs :: Receive ( ReceiveTlvs { context } ) => assert ! ( context. is_none( ) ) ,
510+ _ => panic ! ( "Expected a receive-hop control TLV" ) ,
511+ }
512+ }
513+
514+ let destination = Destination :: BlindedPath ( blinded_path) ;
515+ let instructions = MessageSendInstructions :: WithoutReplyPath { destination } ;
516+ nodes[ 0 ] . messenger . send_onion_message ( test_msg, instructions) . unwrap ( ) ;
517+ nodes[ 1 ] . custom_message_handler . expect_message ( TestCustomMessage :: Pong ) ;
518+ pass_along_path ( & nodes) ;
519+ }
520+
483521#[ test]
484522fn blinded_path_with_dummy_hops ( ) {
485523 let nodes = create_nodes ( 2 ) ;
0 commit comments