@@ -69,7 +69,6 @@ impl BlindedMessagePath {
69
69
/// pubkey in `node_pks` will be the destination node.
70
70
///
71
71
/// Errors if no hops are provided or if `node_pk`(s) are invalid.
72
- // TODO: make all payloads the same size with padding + add dummy hops
73
72
pub fn new < ES : Deref , T : secp256k1:: Signing + secp256k1:: Verification > (
74
73
intermediate_nodes : & [ MessageForwardNode ] , recipient_node_id : PublicKey ,
75
74
context : MessageContext , entropy_source : ES , secp_ctx : & Secp256k1 < T > ,
@@ -89,9 +88,53 @@ impl BlindedMessagePath {
89
88
blinding_point : PublicKey :: from_secret_key ( secp_ctx, & blinding_secret) ,
90
89
blinded_hops : blinded_hops (
91
90
secp_ctx,
91
+ entropy_source,
92
+ None ,
92
93
intermediate_nodes,
93
94
recipient_node_id,
94
95
context,
96
+ 0 ,
97
+ & blinding_secret,
98
+ )
99
+ . map_err ( |_| ( ) ) ?,
100
+ } ) )
101
+ }
102
+
103
+ /// Create a path for an onion message, to be forwarded along `node_pks`.
104
+ ///
105
+ /// Additionally allows appending a number of dummy hops before the final hop,
106
+ /// increasing the total path length and enhancing privacy by obscuring the true
107
+ /// distance between sender and recipient.
108
+ ///
109
+ /// The last node pubkey in `node_pks` will be the destination node.
110
+ ///
111
+ /// Errors if no hops are provided or if `node_pk`(s) are invalid.
112
+ pub fn new_with_dummy_hops < ES : Deref , T : secp256k1:: Signing + secp256k1:: Verification > (
113
+ intermediate_nodes : & [ MessageForwardNode ] , dummy_hops_count : u8 ,
114
+ recipient_node_id : PublicKey , context : MessageContext , entropy_source : ES ,
115
+ expanded_key : & inbound_payment:: ExpandedKey , secp_ctx : & Secp256k1 < T > ,
116
+ ) -> Result < Self , ( ) >
117
+ where
118
+ ES :: Target : EntropySource ,
119
+ {
120
+ let introduction_node = IntroductionNode :: NodeId (
121
+ intermediate_nodes. first ( ) . map_or ( recipient_node_id, |n| n. node_id ) ,
122
+ ) ;
123
+ let blinding_secret_bytes = entropy_source. get_secure_random_bytes ( ) ;
124
+ let blinding_secret =
125
+ SecretKey :: from_slice ( & blinding_secret_bytes[ ..] ) . expect ( "RNG is busted" ) ;
126
+
127
+ Ok ( Self ( BlindedPath {
128
+ introduction_node,
129
+ blinding_point : PublicKey :: from_secret_key ( secp_ctx, & blinding_secret) ,
130
+ blinded_hops : blinded_hops (
131
+ secp_ctx,
132
+ entropy_source,
133
+ Some ( expanded_key) ,
134
+ intermediate_nodes,
135
+ recipient_node_id,
136
+ context,
137
+ dummy_hops_count,
95
138
& blinding_secret,
96
139
)
97
140
. map_err ( |_| ( ) ) ?,
@@ -600,13 +643,23 @@ impl_writeable_tlv_based!(DNSResolverContext, {
600
643
pub ( crate ) const MESSAGE_PADDING_ROUND_OFF : usize = 100 ;
601
644
602
645
/// Construct blinded onion message hops for the given `intermediate_nodes` and `recipient_node_id`.
603
- pub ( super ) fn blinded_hops < T : secp256k1:: Signing + secp256k1:: Verification > (
604
- secp_ctx : & Secp256k1 < T > , intermediate_nodes : & [ MessageForwardNode ] ,
605
- recipient_node_id : PublicKey , context : MessageContext , session_priv : & SecretKey ,
606
- ) -> Result < Vec < BlindedHop > , secp256k1:: Error > {
646
+ pub ( super ) fn blinded_hops < ES : Deref , T : secp256k1:: Signing + secp256k1:: Verification > (
647
+ secp_ctx : & Secp256k1 < T > , entropy_source : ES ,
648
+ expanded_key : Option < & inbound_payment:: ExpandedKey > , intermediate_nodes : & [ MessageForwardNode ] ,
649
+ recipient_node_id : PublicKey , context : MessageContext , dummy_hops_count : u8 ,
650
+ session_priv : & SecretKey ,
651
+ ) -> Result < Vec < BlindedHop > , secp256k1:: Error >
652
+ where
653
+ ES :: Target : EntropySource ,
654
+ {
655
+ if expanded_key. is_none ( ) && dummy_hops_count > 0 {
656
+ debug_assert ! ( false , "Dummy hops are not supported without expanded keys" ) ;
657
+ }
658
+
607
659
let pks = intermediate_nodes
608
660
. iter ( )
609
661
. map ( |node| node. node_id )
662
+ . chain ( ( 0 ..dummy_hops_count) . map ( |_| recipient_node_id) )
610
663
. chain ( core:: iter:: once ( recipient_node_id) ) ;
611
664
let is_compact = intermediate_nodes. iter ( ) . any ( |node| node. short_channel_id . is_some ( ) ) ;
612
665
@@ -621,6 +674,14 @@ pub(super) fn blinded_hops<T: secp256k1::Signing + secp256k1::Verification>(
621
674
. map ( |next_hop| {
622
675
ControlTlvs :: Forward ( ForwardTlvs { next_hop, next_blinding_override : None } )
623
676
} )
677
+ . chain ( ( 0 ..1 ) . filter ( |_| dummy_hops_count > 0 ) . map ( |_| {
678
+ let dummy_tlv = UnauthenticatedDummyTlv { } ;
679
+ let nonce = Nonce :: from_entropy_source ( & * entropy_source) ;
680
+ let hmac = dummy_tlv. hmac_data ( nonce, expanded_key. unwrap ( ) ) ;
681
+ let tlv = PrimaryDummyTlv { dummy_tlv, authentication : ( hmac, nonce) } ;
682
+ ControlTlvs :: Dummy ( DummyTlv :: Primary ( tlv) )
683
+ } ) )
684
+ . chain ( ( 1 ..dummy_hops_count) . map ( |_| ControlTlvs :: Dummy ( DummyTlv :: Subsequent ) ) )
624
685
. chain ( core:: iter:: once ( ControlTlvs :: Receive ( ReceiveTlvs { context : Some ( context) } ) ) ) ;
625
686
626
687
if is_compact {
0 commit comments