Skip to content

Commit afbc5e3

Browse files
committed
Introduce ReceiveAuthKey-based verification in Blinded Payment Paths
Extends the work started in [PR#3917](lightningdevkit#3917) by adding ReceiveAuthKey-based verification for Blinded Payment Paths. This reduces space previously taken by individual ReceiveTlvs and aligns the verification logic with that used for Blinded Message Paths.
1 parent 3f96d12 commit afbc5e3

File tree

14 files changed

+135
-60
lines changed

14 files changed

+135
-60
lines changed

fuzz/src/chanmon_consistency.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,9 @@ impl Router for FuzzRouter {
128128
}
129129

130130
fn create_blinded_payment_paths<T: secp256k1::Signing + secp256k1::Verification>(
131-
&self, _recipient: PublicKey, _first_hops: Vec<ChannelDetails>, _tlvs: ReceiveTlvs,
132-
_amount_msats: Option<u64>, _secp_ctx: &Secp256k1<T>,
131+
&self, _recipient: PublicKey, _receive_auth_key: ReceiveAuthKey,
132+
_first_hops: Vec<ChannelDetails>, _tlvs: ReceiveTlvs, _amount_msats: Option<u64>,
133+
_secp_ctx: &Secp256k1<T>,
133134
) -> Result<Vec<BlindedPaymentPath>, ()> {
134135
unreachable!()
135136
}

fuzz/src/full_stack.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@ use lightning::sign::{
6262
};
6363
use lightning::types::payment::{PaymentHash, PaymentPreimage, PaymentSecret};
6464
use lightning::util::config::{ChannelConfig, UserConfig};
65-
use lightning::util::errors::APIError;
6665
use lightning::util::hash_tables::*;
6766
use lightning::util::logger::Logger;
6867
use lightning::util::ser::{Readable, Writeable};
@@ -157,8 +156,9 @@ impl Router for FuzzRouter {
157156
}
158157

159158
fn create_blinded_payment_paths<T: secp256k1::Signing + secp256k1::Verification>(
160-
&self, _recipient: PublicKey, _first_hops: Vec<ChannelDetails>, _tlvs: ReceiveTlvs,
161-
_amount_msats: Option<u64>, _secp_ctx: &Secp256k1<T>,
159+
&self, _recipient: PublicKey, _receive_auth_key: ReceiveAuthKey,
160+
_first_hops: Vec<ChannelDetails>, _tlvs: ReceiveTlvs, _amount_msats: Option<u64>,
161+
_secp_ctx: &Secp256k1<T>,
162162
) -> Result<Vec<BlindedPaymentPath>, ()> {
163163
unreachable!()
164164
}

fuzz/src/invoice_request_deser.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use lightning::offers::invoice_request::{InvoiceRequest, InvoiceRequestFields};
2121
use lightning::offers::nonce::Nonce;
2222
use lightning::offers::offer::OfferId;
2323
use lightning::offers::parse::Bolt12SemanticError;
24-
use lightning::sign::EntropySource;
24+
use lightning::sign::{EntropySource, ReceiveAuthKey};
2525
use lightning::types::features::BlindedHopFeatures;
2626
use lightning::types::payment::{PaymentHash, PaymentSecret};
2727
use lightning::types::string::UntrustedString;
@@ -85,6 +85,7 @@ fn build_response<T: secp256k1::Signing + secp256k1::Verification>(
8585
let expanded_key = ExpandedKey::new([42; 32]);
8686
let entropy_source = Randomness {};
8787
let nonce = Nonce::from_entropy_source(&entropy_source);
88+
let receive_auth_key = ReceiveAuthKey([41; 32]);
8889

8990
let invoice_request_fields =
9091
if let Ok(ver) = invoice_request.clone().verify_using_metadata(&expanded_key, secp_ctx) {
@@ -136,6 +137,7 @@ fn build_response<T: secp256k1::Signing + secp256k1::Verification>(
136137
let payment_path = BlindedPaymentPath::new(
137138
&intermediate_nodes,
138139
pubkey(42),
140+
receive_auth_key,
139141
payee_tlvs,
140142
u64::MAX,
141143
MIN_FINAL_CLTV_EXPIRY_DELTA,

fuzz/src/refund_deser.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use lightning::offers::invoice::UnsignedBolt12Invoice;
2020
use lightning::offers::nonce::Nonce;
2121
use lightning::offers::parse::Bolt12SemanticError;
2222
use lightning::offers::refund::Refund;
23-
use lightning::sign::EntropySource;
23+
use lightning::sign::{EntropySource, ReceiveAuthKey};
2424
use lightning::types::features::BlindedHopFeatures;
2525
use lightning::types::payment::{PaymentHash, PaymentSecret};
2626
use lightning::util::ser::Writeable;
@@ -71,6 +71,7 @@ fn build_response<T: secp256k1::Signing + secp256k1::Verification>(
7171
) -> Result<UnsignedBolt12Invoice, Bolt12SemanticError> {
7272
let expanded_key = ExpandedKey::new([42; 32]);
7373
let entropy_source = Randomness {};
74+
let receive_auth_key = ReceiveAuthKey([41; 32]);
7475
let nonce = Nonce::from_entropy_source(&entropy_source);
7576
let payment_context = PaymentContext::Bolt12Refund(Bolt12RefundContext {});
7677
let payee_tlvs = UnauthenticatedReceiveTlvs {
@@ -103,6 +104,7 @@ fn build_response<T: secp256k1::Signing + secp256k1::Verification>(
103104
let payment_path = BlindedPaymentPath::new(
104105
&intermediate_nodes,
105106
pubkey(42),
107+
receive_auth_key,
106108
payee_tlvs,
107109
u64::MAX,
108110
MIN_FINAL_CLTV_EXPIRY_DELTA,

lightning/src/blinded_path/payment.rs

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use bitcoin::secp256k1::{self, PublicKey, Secp256k1, SecretKey};
1616

1717
use crate::blinded_path::utils::{self, BlindedPathWithPadding};
1818
use crate::blinded_path::{BlindedHop, BlindedPath, IntroductionNode, NodeIdLookUp};
19-
use crate::crypto::streams::ChaChaPolyReadAdapter;
19+
use crate::crypto::streams::ChaChaDualPolyReadAdapter;
2020
use crate::io;
2121
use crate::io::Cursor;
2222
use crate::ln::channel_state::CounterpartyForwardingInfo;
@@ -28,7 +28,7 @@ use crate::offers::invoice_request::InvoiceRequestFields;
2828
use crate::offers::nonce::Nonce;
2929
use crate::offers::offer::OfferId;
3030
use crate::routing::gossip::{NodeId, ReadOnlyNetworkGraph};
31-
use crate::sign::{EntropySource, NodeSigner, Recipient};
31+
use crate::sign::{EntropySource, NodeSigner, ReceiveAuthKey, Recipient};
3232
use crate::types::features::BlindedHopFeatures;
3333
use crate::types::payment::PaymentSecret;
3434
use crate::types::routing::RoutingFees;
@@ -93,8 +93,8 @@ pub struct BlindedPaymentPath {
9393
impl BlindedPaymentPath {
9494
/// Create a one-hop blinded path for a payment.
9595
pub fn one_hop<ES: Deref, T: secp256k1::Signing + secp256k1::Verification>(
96-
payee_node_id: PublicKey, payee_tlvs: ReceiveTlvs, min_final_cltv_expiry_delta: u16,
97-
entropy_source: ES, secp_ctx: &Secp256k1<T>,
96+
payee_node_id: PublicKey, receive_auth_key: ReceiveAuthKey, payee_tlvs: ReceiveTlvs,
97+
min_final_cltv_expiry_delta: u16, entropy_source: ES, secp_ctx: &Secp256k1<T>,
9898
) -> Result<Self, ()>
9999
where
100100
ES::Target: EntropySource,
@@ -105,6 +105,7 @@ impl BlindedPaymentPath {
105105
Self::new(
106106
&[],
107107
payee_node_id,
108+
receive_auth_key,
108109
payee_tlvs,
109110
htlc_maximum_msat,
110111
min_final_cltv_expiry_delta,
@@ -121,8 +122,8 @@ impl BlindedPaymentPath {
121122
// TODO: make all payloads the same size with padding + add dummy hops
122123
pub fn new<ES: Deref, T: secp256k1::Signing + secp256k1::Verification>(
123124
intermediate_nodes: &[PaymentForwardNode], payee_node_id: PublicKey,
124-
payee_tlvs: ReceiveTlvs, htlc_maximum_msat: u64, min_final_cltv_expiry_delta: u16,
125-
entropy_source: ES, secp_ctx: &Secp256k1<T>,
125+
receive_auth_key: ReceiveAuthKey, payee_tlvs: ReceiveTlvs, htlc_maximum_msat: u64,
126+
min_final_cltv_expiry_delta: u16, entropy_source: ES, secp_ctx: &Secp256k1<T>,
126127
) -> Result<Self, ()>
127128
where
128129
ES::Target: EntropySource,
@@ -150,6 +151,7 @@ impl BlindedPaymentPath {
150151
payee_node_id,
151152
payee_tlvs,
152153
&blinding_secret,
154+
receive_auth_key,
153155
),
154156
},
155157
payinfo: blinded_payinfo,
@@ -226,12 +228,13 @@ impl BlindedPaymentPath {
226228
let control_tlvs_ss =
227229
node_signer.ecdh(Recipient::Node, &self.inner_path.blinding_point, None)?;
228230
let rho = onion_utils::gen_rho_from_shared_secret(&control_tlvs_ss.secret_bytes());
231+
let receive_auth_key = node_signer.get_receive_auth_key();
229232
let encrypted_control_tlvs =
230233
&self.inner_path.blinded_hops.get(0).ok_or(())?.encrypted_payload;
231234
let mut s = Cursor::new(encrypted_control_tlvs);
232235
let mut reader = FixedLengthReader::new(&mut s, encrypted_control_tlvs.len() as u64);
233-
match ChaChaPolyReadAdapter::read(&mut reader, rho) {
234-
Ok(ChaChaPolyReadAdapter { readable, .. }) => Ok((readable, control_tlvs_ss)),
236+
match ChaChaDualPolyReadAdapter::read(&mut reader, (rho, receive_auth_key.0)) {
237+
Ok(ChaChaDualPolyReadAdapter { readable, .. }) => Ok((readable, control_tlvs_ss)),
235238
_ => Err(()),
236239
}
237240
}
@@ -660,12 +663,12 @@ pub(crate) const PAYMENT_PADDING_ROUND_OFF: usize = 30;
660663
/// Construct blinded payment hops for the given `intermediate_nodes` and payee info.
661664
pub(super) fn blinded_hops<T: secp256k1::Signing + secp256k1::Verification>(
662665
secp_ctx: &Secp256k1<T>, intermediate_nodes: &[PaymentForwardNode], payee_node_id: PublicKey,
663-
payee_tlvs: ReceiveTlvs, session_priv: &SecretKey,
666+
payee_tlvs: ReceiveTlvs, session_priv: &SecretKey, local_node_receive_key: ReceiveAuthKey,
664667
) -> Vec<BlindedHop> {
665668
let pks = intermediate_nodes
666669
.iter()
667670
.map(|node| (node.node_id, None))
668-
.chain(core::iter::once((payee_node_id, None)));
671+
.chain(core::iter::once((payee_node_id, Some(local_node_receive_key))));
669672
let tlvs = intermediate_nodes
670673
.iter()
671674
.map(|node| BlindedPaymentTlvsRef::Forward(&node.tlvs))

lightning/src/ln/blinded_payment_tests.rs

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -86,12 +86,13 @@ pub fn blinded_payment_path(
8686

8787
let nonce = Nonce([42u8; 16]);
8888
let expanded_key = keys_manager.get_expanded_key();
89+
let receive_auth_key = keys_manager.get_receive_auth_key();
8990
let payee_tlvs = payee_tlvs.authenticate(nonce, &expanded_key);
9091

9192
let mut secp_ctx = Secp256k1::new();
9293
BlindedPaymentPath::new(
93-
&intermediate_nodes[..], *node_ids.last().unwrap(), payee_tlvs,
94-
intro_node_max_htlc_opt.unwrap_or_else(|| channel_upds.last().unwrap().htlc_maximum_msat),
94+
&intermediate_nodes[..], *node_ids.last().unwrap(), receive_auth_key,
95+
payee_tlvs, intro_node_max_htlc_opt.unwrap_or_else(|| channel_upds.last().unwrap().htlc_maximum_msat),
9596
TEST_FINAL_CLTV as u16, keys_manager, &secp_ctx
9697
).unwrap()
9798
}
@@ -171,11 +172,13 @@ fn do_one_hop_blinded_path(success: bool) {
171172
};
172173
let nonce = Nonce([42u8; 16]);
173174
let expanded_key = chanmon_cfgs[1].keys_manager.get_expanded_key();
175+
let receive_auth_key = chanmon_cfgs[1].keys_manager.get_receive_auth_key();
174176
let payee_tlvs = payee_tlvs.authenticate(nonce, &expanded_key);
175177

176178
let mut secp_ctx = Secp256k1::new();
177179
let blinded_path = BlindedPaymentPath::new(
178-
&[], nodes[1].node.get_our_node_id(), payee_tlvs, u64::MAX, TEST_FINAL_CLTV as u16,
180+
&[], nodes[1].node.get_our_node_id(), receive_auth_key,
181+
payee_tlvs, u64::MAX, TEST_FINAL_CLTV as u16,
179182
&chanmon_cfgs[1].keys_manager, &secp_ctx
180183
).unwrap();
181184

@@ -225,9 +228,11 @@ fn mpp_to_one_hop_blinded_path() {
225228
};
226229
let nonce = Nonce([42u8; 16]);
227230
let expanded_key = chanmon_cfgs[3].keys_manager.get_expanded_key();
231+
let receive_auth_key = chanmon_cfgs[3].keys_manager.get_receive_auth_key();
228232
let payee_tlvs = payee_tlvs.authenticate(nonce, &expanded_key);
229233
let blinded_path = BlindedPaymentPath::new(
230-
&[], nodes[3].node.get_our_node_id(), payee_tlvs, u64::MAX, TEST_FINAL_CLTV as u16,
234+
&[], nodes[3].node.get_our_node_id(), receive_auth_key,
235+
payee_tlvs, u64::MAX, TEST_FINAL_CLTV as u16,
231236
&chanmon_cfgs[3].keys_manager, &secp_ctx
232237
).unwrap();
233238

@@ -1335,10 +1340,12 @@ fn custom_tlvs_to_blinded_path() {
13351340
};
13361341
let nonce = Nonce([42u8; 16]);
13371342
let expanded_key = chanmon_cfgs[1].keys_manager.get_expanded_key();
1343+
let receive_auth_key = chanmon_cfgs[1].keys_manager.get_receive_auth_key();
13381344
let payee_tlvs = payee_tlvs.authenticate(nonce, &expanded_key);
13391345
let mut secp_ctx = Secp256k1::new();
13401346
let blinded_path = BlindedPaymentPath::new(
1341-
&[], nodes[1].node.get_our_node_id(), payee_tlvs, u64::MAX, TEST_FINAL_CLTV as u16,
1347+
&[], nodes[1].node.get_our_node_id(), receive_auth_key,
1348+
payee_tlvs, u64::MAX, TEST_FINAL_CLTV as u16,
13421349
&chanmon_cfgs[1].keys_manager, &secp_ctx
13431350
).unwrap();
13441351

@@ -1389,11 +1396,13 @@ fn fails_receive_tlvs_authentication() {
13891396
};
13901397
let nonce = Nonce([42u8; 16]);
13911398
let expanded_key = chanmon_cfgs[1].keys_manager.get_expanded_key();
1399+
let receive_auth_key = chanmon_cfgs[1].keys_manager.get_receive_auth_key();
13921400
let payee_tlvs = payee_tlvs.authenticate(nonce, &expanded_key);
13931401

13941402
let mut secp_ctx = Secp256k1::new();
13951403
let blinded_path = BlindedPaymentPath::new(
1396-
&[], nodes[1].node.get_our_node_id(), payee_tlvs, u64::MAX, TEST_FINAL_CLTV as u16,
1404+
&[], nodes[1].node.get_our_node_id(), receive_auth_key,
1405+
payee_tlvs, u64::MAX, TEST_FINAL_CLTV as u16,
13971406
&chanmon_cfgs[1].keys_manager, &secp_ctx
13981407
).unwrap();
13991408

@@ -1424,7 +1433,8 @@ fn fails_receive_tlvs_authentication() {
14241433

14251434
let mut secp_ctx = Secp256k1::new();
14261435
let blinded_path = BlindedPaymentPath::new(
1427-
&[], nodes[1].node.get_our_node_id(), payee_tlvs, u64::MAX, TEST_FINAL_CLTV as u16,
1436+
&[], nodes[1].node.get_our_node_id(), receive_auth_key,
1437+
payee_tlvs, u64::MAX, TEST_FINAL_CLTV as u16,
14281438
&chanmon_cfgs[1].keys_manager, &secp_ctx
14291439
).unwrap();
14301440

@@ -1627,7 +1637,7 @@ fn route_blinding_spec_test_vector() {
16271637
&self, _invoice: &RawBolt11Invoice, _recipient: Recipient,
16281638
) -> Result<RecoverableSignature, ()> { unreachable!() }
16291639
fn get_peer_storage_key(&self) -> PeerStorageKey { unreachable!() }
1630-
fn get_receive_auth_key(&self) -> ReceiveAuthKey { unreachable!() }
1640+
fn get_receive_auth_key(&self) -> ReceiveAuthKey { ReceiveAuthKey([41; 32]) }
16311641
fn sign_bolt12_invoice(
16321642
&self, _invoice: &UnsignedBolt12Invoice,
16331643
) -> Result<schnorr::Signature, ()> { unreachable!() }
@@ -1940,7 +1950,7 @@ fn test_trampoline_inbound_payment_decoding() {
19401950
&self, _invoice: &RawBolt11Invoice, _recipient: Recipient,
19411951
) -> Result<RecoverableSignature, ()> { unreachable!() }
19421952
fn get_peer_storage_key(&self) -> PeerStorageKey { unreachable!() }
1943-
fn get_receive_auth_key(&self) -> ReceiveAuthKey { unreachable!() }
1953+
fn get_receive_auth_key(&self) -> ReceiveAuthKey { ReceiveAuthKey([41; 32]) }
19441954
fn sign_bolt12_invoice(
19451955
&self, _invoice: &UnsignedBolt12Invoice,
19461956
) -> Result<schnorr::Signature, ()> { unreachable!() }
@@ -2207,8 +2217,9 @@ fn do_test_trampoline_single_hop_receive(success: bool) {
22072217
};
22082218
let nonce = Nonce([42u8; 16]);
22092219
let expanded_key = nodes[2].keys_manager.get_expanded_key();
2220+
let receive_auth_key = nodes[2].keys_manager.get_receive_auth_key();
22102221
let payee_tlvs = payee_tlvs.authenticate(nonce, &expanded_key);
2211-
let blinded_path = BlindedPaymentPath::new(&[], carol_node_id, payee_tlvs, u64::MAX, 0, nodes[2].keys_manager, &secp_ctx).unwrap();
2222+
let blinded_path = BlindedPaymentPath::new(&[], carol_node_id, receive_auth_key, payee_tlvs, u64::MAX, 0, nodes[2].keys_manager, &secp_ctx).unwrap();
22122223

22132224
let route = Route {
22142225
paths: vec![Path {

lightning/src/ln/channelmanager.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5114,7 +5114,7 @@ where
51145114
let current_height: u32 = self.best_block.read().unwrap().height;
51155115
create_recv_pending_htlc_info(decoded_hop, shared_secret, msg.payment_hash,
51165116
msg.amount_msat, msg.cltv_expiry, None, allow_underpay, msg.skimmed_fee_msat,
5117-
current_height)
5117+
current_height, &*self.logger)
51185118
},
51195119
onion_utils::Hop::Forward { .. } | onion_utils::Hop::BlindedForward { .. } => {
51205120
create_fwd_pending_htlc_info(msg, decoded_hop, shared_secret, next_packet_pubkey_opt)
@@ -7315,6 +7315,7 @@ where
73157315
false,
73167316
None,
73177317
current_height,
7318+
&*self.logger,
73187319
);
73197320
match create_res {
73207321
Ok(info) => phantom_receives.push((
@@ -19251,6 +19252,7 @@ mod tests {
1925119252
let node_chanmgr = create_node_chanmgrs(1, &node_cfg, &[None]);
1925219253
let node = create_network(1, &node_cfg, &node_chanmgr);
1925319254
let sender_intended_amt_msat = 100;
19255+
let logger = node[0].logger;
1925419256
let extra_fee_msat = 10;
1925519257
let hop_data = onion_utils::Hop::Receive {
1925619258
hop_data: msgs::InboundOnionReceivePayload {
@@ -19272,7 +19274,7 @@ mod tests {
1927219274
if let Err(crate::ln::channelmanager::InboundHTLCErr { reason, .. }) =
1927319275
create_recv_pending_htlc_info(hop_data, [0; 32], PaymentHash([0; 32]),
1927419276
sender_intended_amt_msat - extra_fee_msat - 1, 42, None, true, Some(extra_fee_msat),
19275-
current_height)
19277+
current_height, logger)
1927619278
{
1927719279
assert_eq!(reason, LocalHTLCFailureReason::FinalIncorrectHTLCAmount);
1927819280
} else { panic!(); }
@@ -19295,7 +19297,7 @@ mod tests {
1929519297
let current_height: u32 = node[0].node.best_block.read().unwrap().height;
1929619298
assert!(create_recv_pending_htlc_info(hop_data, [0; 32], PaymentHash([0; 32]),
1929719299
sender_intended_amt_msat - extra_fee_msat, 42, None, true, Some(extra_fee_msat),
19298-
current_height).is_ok());
19300+
current_height, logger).is_ok());
1929919301
}
1930019302

1930119303
#[test]
@@ -19305,6 +19307,7 @@ mod tests {
1930519307
let node_cfg = create_node_cfgs(1, &chanmon_cfg);
1930619308
let node_chanmgr = create_node_chanmgrs(1, &node_cfg, &[None]);
1930719309
let node = create_network(1, &node_cfg, &node_chanmgr);
19310+
let logger = node[0].logger;
1930819311

1930919312
let current_height: u32 = node[0].node.best_block.read().unwrap().height;
1931019313
let result = create_recv_pending_htlc_info(onion_utils::Hop::Receive {
@@ -19320,7 +19323,7 @@ mod tests {
1932019323
custom_tlvs: Vec::new(),
1932119324
},
1932219325
shared_secret: SharedSecret::from_bytes([0; 32]),
19323-
}, [0; 32], PaymentHash([0; 32]), 100, TEST_FINAL_CLTV + 1, None, true, None, current_height);
19326+
}, [0; 32], PaymentHash([0; 32]), 100, TEST_FINAL_CLTV + 1, None, true, None, current_height, logger);
1932419327

1932519328
// Should not return an error as this condition:
1932619329
// https://github.com/lightning/bolts/blob/4dcc377209509b13cf89a4b91fde7d478f5b46d8/04-onion-routing.md?plain=1#L334

lightning/src/ln/max_payment_path_len_tests.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,11 +223,13 @@ fn one_hop_blinded_path_with_custom_tlv() {
223223
};
224224
let nonce = Nonce([42u8; 16]);
225225
let expanded_key = chanmon_cfgs[2].keys_manager.get_expanded_key();
226+
let receive_auth_key = chanmon_cfgs[2].keys_manager.get_receive_auth_key();
226227
let payee_tlvs = payee_tlvs.authenticate(nonce, &expanded_key);
227228
let mut secp_ctx = Secp256k1::new();
228229
let blinded_path = BlindedPaymentPath::new(
229230
&[],
230231
nodes[2].node.get_our_node_id(),
232+
receive_auth_key,
231233
payee_tlvs,
232234
u64::MAX,
233235
TEST_FINAL_CLTV as u16,

0 commit comments

Comments
 (0)