Skip to content

Commit 55c02fd

Browse files
committed
Include HMAC and Nonce in payment::ReceiveTlvs
In order to authenticate a PaymentContext, an HMAC and Nonce must be included along with it in payment::ReceiveTlvs. Compute the HMAC when constructing a BlindedPaymentPath and include it in the recipient's BlindedPaymentTlvs. Authentication will be added in an upcoming commit.
1 parent a291530 commit 55c02fd

File tree

9 files changed

+128
-37
lines changed

9 files changed

+128
-37
lines changed

fuzz/src/invoice_request_deser.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,13 @@ use bitcoin::secp256k1::{self, Keypair, Parity, PublicKey, Secp256k1, SecretKey}
1212
use core::convert::TryFrom;
1313
use lightning::blinded_path::payment::{
1414
BlindedPaymentPath, Bolt12OfferContext, ForwardTlvs, PaymentConstraints, PaymentContext,
15-
PaymentForwardNode, PaymentRelay, ReceiveTlvs,
15+
PaymentForwardNode, PaymentRelay, UnauthenticatedReceiveTlvs,
1616
};
1717
use lightning::ln::channelmanager::MIN_FINAL_CLTV_EXPIRY_DELTA;
18+
use lightning::ln::inbound_payment::ExpandedKey;
1819
use lightning::offers::invoice::UnsignedBolt12Invoice;
1920
use lightning::offers::invoice_request::{InvoiceRequest, InvoiceRequestFields};
21+
use lightning::offers::nonce::Nonce;
2022
use lightning::offers::offer::OfferId;
2123
use lightning::offers::parse::Bolt12SemanticError;
2224
use lightning::sign::EntropySource;
@@ -80,7 +82,9 @@ fn privkey(byte: u8) -> SecretKey {
8082
fn build_response<T: secp256k1::Signing + secp256k1::Verification>(
8183
invoice_request: &InvoiceRequest, secp_ctx: &Secp256k1<T>,
8284
) -> Result<UnsignedBolt12Invoice, Bolt12SemanticError> {
85+
let expanded_key = ExpandedKey::new([42; 32]);
8386
let entropy_source = Randomness {};
87+
let nonce = Nonce::from_entropy_source(&entropy_source);
8488
let payment_context = PaymentContext::Bolt12Offer(Bolt12OfferContext {
8589
offer_id: OfferId([42; 32]),
8690
invoice_request: InvoiceRequestFields {
@@ -92,14 +96,15 @@ fn build_response<T: secp256k1::Signing + secp256k1::Verification>(
9296
human_readable_name: None,
9397
},
9498
});
95-
let payee_tlvs = ReceiveTlvs {
99+
let payee_tlvs = UnauthenticatedReceiveTlvs {
96100
payment_secret: PaymentSecret([42; 32]),
97101
payment_constraints: PaymentConstraints {
98102
max_cltv_expiry: 1_000_000,
99103
htlc_minimum_msat: 1,
100104
},
101105
payment_context,
102106
};
107+
let payee_tlvs = payee_tlvs.authenticate(nonce, &expanded_key);
103108
let intermediate_nodes = [PaymentForwardNode {
104109
tlvs: ForwardTlvs {
105110
short_channel_id: 43,
@@ -109,7 +114,7 @@ fn build_response<T: secp256k1::Signing + secp256k1::Verification>(
109114
fee_base_msat: 1,
110115
},
111116
payment_constraints: PaymentConstraints {
112-
max_cltv_expiry: payee_tlvs.payment_constraints.max_cltv_expiry + 40,
117+
max_cltv_expiry: payee_tlvs.tlvs().payment_constraints.max_cltv_expiry + 40,
113118
htlc_minimum_msat: 100,
114119
},
115120
features: BlindedHopFeatures::empty(),

fuzz/src/refund_deser.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,12 @@ use bitcoin::secp256k1::{self, Keypair, PublicKey, Secp256k1, SecretKey};
1212
use core::convert::TryFrom;
1313
use lightning::blinded_path::payment::{
1414
BlindedPaymentPath, Bolt12RefundContext, ForwardTlvs, PaymentConstraints, PaymentContext,
15-
PaymentForwardNode, PaymentRelay, ReceiveTlvs,
15+
PaymentForwardNode, PaymentRelay, UnauthenticatedReceiveTlvs,
1616
};
1717
use lightning::ln::channelmanager::MIN_FINAL_CLTV_EXPIRY_DELTA;
18+
use lightning::ln::inbound_payment::ExpandedKey;
1819
use lightning::offers::invoice::UnsignedBolt12Invoice;
20+
use lightning::offers::nonce::Nonce;
1921
use lightning::offers::parse::Bolt12SemanticError;
2022
use lightning::offers::refund::Refund;
2123
use lightning::sign::EntropySource;
@@ -67,16 +69,19 @@ fn privkey(byte: u8) -> SecretKey {
6769
fn build_response<T: secp256k1::Signing + secp256k1::Verification>(
6870
refund: &Refund, signing_pubkey: PublicKey, secp_ctx: &Secp256k1<T>,
6971
) -> Result<UnsignedBolt12Invoice, Bolt12SemanticError> {
72+
let expanded_key = ExpandedKey::new([42; 32]);
7073
let entropy_source = Randomness {};
74+
let nonce = Nonce::from_entropy_source(&entropy_source);
7175
let payment_context = PaymentContext::Bolt12Refund(Bolt12RefundContext {});
72-
let payee_tlvs = ReceiveTlvs {
76+
let payee_tlvs = UnauthenticatedReceiveTlvs {
7377
payment_secret: PaymentSecret([42; 32]),
7478
payment_constraints: PaymentConstraints {
7579
max_cltv_expiry: 1_000_000,
7680
htlc_minimum_msat: 1,
7781
},
7882
payment_context,
7983
};
84+
let payee_tlvs = payee_tlvs.authenticate(nonce, &expanded_key);
8085
let intermediate_nodes = [PaymentForwardNode {
8186
tlvs: ForwardTlvs {
8287
short_channel_id: 43,
@@ -86,7 +91,7 @@ fn build_response<T: secp256k1::Signing + secp256k1::Verification>(
8691
fee_base_msat: 1,
8792
},
8893
payment_constraints: PaymentConstraints {
89-
max_cltv_expiry: payee_tlvs.payment_constraints.max_cltv_expiry + 40,
94+
max_cltv_expiry: payee_tlvs.tlvs().payment_constraints.max_cltv_expiry + 40,
9095
htlc_minimum_msat: 100,
9196
},
9297
features: BlindedHopFeatures::empty(),

lightning/src/blinded_path/payment.rs

Lines changed: 62 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010
//! Data structures and methods for constructing [`BlindedPaymentPath`]s to send a payment over.
1111
12+
use bitcoin::hashes::hmac::Hmac;
13+
use bitcoin::hashes::sha256::Hash as Sha256;
1214
use bitcoin::secp256k1::{self, PublicKey, Secp256k1, SecretKey};
1315

1416
use crate::blinded_path::{BlindedHop, BlindedPath, IntroductionNode, NodeIdLookUp};
@@ -18,10 +20,13 @@ use crate::io;
1820
use crate::io::Cursor;
1921
use crate::types::payment::PaymentSecret;
2022
use crate::ln::channel_state::CounterpartyForwardingInfo;
23+
use crate::ln::channelmanager::Verification;
2124
use crate::types::features::BlindedHopFeatures;
25+
use crate::ln::inbound_payment::ExpandedKey;
2226
use crate::ln::msgs::DecodeError;
2327
use crate::ln::onion_utils;
2428
use crate::offers::invoice_request::InvoiceRequestFields;
29+
use crate::offers::nonce::Nonce;
2530
use crate::offers::offer::OfferId;
2631
use crate::routing::gossip::{NodeId, ReadOnlyNetworkGraph};
2732
use crate::sign::{EntropySource, NodeSigner, Recipient};
@@ -114,7 +119,7 @@ impl BlindedPaymentPath {
114119
let blinding_secret = SecretKey::from_slice(&blinding_secret_bytes[..]).expect("RNG is busted");
115120

116121
let blinded_payinfo = compute_payinfo(
117-
intermediate_nodes, &payee_tlvs, htlc_maximum_msat, min_final_cltv_expiry_delta
122+
intermediate_nodes, &payee_tlvs.tlvs, htlc_maximum_msat, min_final_cltv_expiry_delta
118123
)?;
119124
Ok(Self {
120125
inner_path: BlindedPath {
@@ -252,8 +257,26 @@ pub struct ForwardTlvs {
252257

253258
/// Data to construct a [`BlindedHop`] for receiving a payment. This payload is custom to LDK and
254259
/// may not be valid if received by another lightning implementation.
260+
///
261+
/// Can only be constructed by calling [`UnauthenticatedReceiveTlvs::authenticate`].
255262
#[derive(Clone, Debug)]
256263
pub struct ReceiveTlvs {
264+
/// The TLVs for which the HMAC in `authentication` is derived.
265+
pub(crate) tlvs: UnauthenticatedReceiveTlvs,
266+
/// An HMAC of `tlvs` along with a nonce used to construct it.
267+
pub(crate) authentication: (Hmac<Sha256>, Nonce),
268+
}
269+
270+
impl ReceiveTlvs {
271+
/// Returns the underlying TLVs.
272+
pub fn tlvs(&self) -> &UnauthenticatedReceiveTlvs {
273+
&self.tlvs
274+
}
275+
}
276+
277+
/// An unauthenticated [`ReceiveTlvs`].
278+
#[derive(Clone, Debug)]
279+
pub struct UnauthenticatedReceiveTlvs {
257280
/// Used to authenticate the sender of a payment to the receiver and tie MPP HTLCs together.
258281
pub payment_secret: PaymentSecret,
259282
/// Constraints for the receiver of this payment.
@@ -262,6 +285,17 @@ pub struct ReceiveTlvs {
262285
pub payment_context: PaymentContext,
263286
}
264287

288+
impl UnauthenticatedReceiveTlvs {
289+
/// Creates an authenticated [`ReceiveTlvs`], which includes an HMAC and the provide [`Nonce`]
290+
/// that can be use later to verify it authenticity.
291+
pub fn authenticate(self, nonce: Nonce, expanded_key: &ExpandedKey) -> ReceiveTlvs {
292+
ReceiveTlvs {
293+
authentication: (self.hmac_for_offer_payment(nonce, expanded_key), nonce),
294+
tlvs: self,
295+
}
296+
}
297+
}
298+
265299
/// Data to construct a [`BlindedHop`] for sending a payment over.
266300
///
267301
/// [`BlindedHop`]: crate::blinded_path::BlindedHop
@@ -400,11 +434,23 @@ impl Writeable for ForwardTlvs {
400434
}
401435

402436
impl Writeable for ReceiveTlvs {
437+
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
438+
encode_tlv_stream!(w, {
439+
(12, self.tlvs.payment_constraints, required),
440+
(65536, self.tlvs.payment_secret, required),
441+
(65537, self.tlvs.payment_context, required),
442+
(65539, self.authentication, required),
443+
});
444+
Ok(())
445+
}
446+
}
447+
448+
impl Writeable for UnauthenticatedReceiveTlvs {
403449
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
404450
encode_tlv_stream!(w, {
405451
(12, self.payment_constraints, required),
406452
(65536, self.payment_secret, required),
407-
(65537, self.payment_context, required)
453+
(65537, self.payment_context, required),
408454
});
409455
Ok(())
410456
}
@@ -432,6 +478,7 @@ impl Readable for BlindedPaymentTlvs {
432478
(14, features, (option, encoding: (BlindedHopFeatures, WithoutLength))),
433479
(65536, payment_secret, option),
434480
(65537, payment_context, (default_value, PaymentContext::unknown())),
481+
(65539, authentication, option),
435482
});
436483
let _padding: Option<utils::Padding> = _padding;
437484

@@ -449,9 +496,12 @@ impl Readable for BlindedPaymentTlvs {
449496
} else {
450497
if payment_relay.is_some() || features.is_some() { return Err(DecodeError::InvalidValue) }
451498
Ok(BlindedPaymentTlvs::Receive(ReceiveTlvs {
452-
payment_secret: payment_secret.ok_or(DecodeError::InvalidValue)?,
453-
payment_constraints: payment_constraints.0.unwrap(),
454-
payment_context: payment_context.0.unwrap(),
499+
tlvs: UnauthenticatedReceiveTlvs {
500+
payment_secret: payment_secret.ok_or(DecodeError::InvalidValue)?,
501+
payment_constraints: payment_constraints.0.unwrap(),
502+
payment_context: payment_context.0.unwrap(),
503+
},
504+
authentication: authentication.ok_or(DecodeError::InvalidValue)?,
455505
}))
456506
}
457507
}
@@ -494,7 +544,7 @@ pub(crate) fn amt_to_forward_msat(inbound_amt_msat: u64, payment_relay: &Payment
494544
}
495545

496546
pub(super) fn compute_payinfo(
497-
intermediate_nodes: &[PaymentForwardNode], payee_tlvs: &ReceiveTlvs,
547+
intermediate_nodes: &[PaymentForwardNode], payee_tlvs: &UnauthenticatedReceiveTlvs,
498548
payee_htlc_maximum_msat: u64, min_final_cltv_expiry_delta: u16,
499549
) -> Result<BlindedPayInfo, ()> {
500550
let mut curr_base_fee: u64 = 0;
@@ -631,7 +681,7 @@ impl_writeable_tlv_based!(Bolt12RefundContext, {});
631681
#[cfg(test)]
632682
mod tests {
633683
use bitcoin::secp256k1::PublicKey;
634-
use crate::blinded_path::payment::{PaymentForwardNode, ForwardTlvs, ReceiveTlvs, PaymentConstraints, PaymentContext, PaymentRelay};
684+
use crate::blinded_path::payment::{PaymentForwardNode, ForwardTlvs, PaymentConstraints, PaymentContext, PaymentRelay, UnauthenticatedReceiveTlvs};
635685
use crate::types::payment::PaymentSecret;
636686
use crate::types::features::BlindedHopFeatures;
637687
use crate::ln::functional_test_utils::TEST_FINAL_CLTV;
@@ -676,7 +726,7 @@ mod tests {
676726
},
677727
htlc_maximum_msat: u64::max_value(),
678728
}];
679-
let recv_tlvs = ReceiveTlvs {
729+
let recv_tlvs = UnauthenticatedReceiveTlvs {
680730
payment_secret: PaymentSecret([0; 32]),
681731
payment_constraints: PaymentConstraints {
682732
max_cltv_expiry: 0,
@@ -695,7 +745,7 @@ mod tests {
695745

696746
#[test]
697747
fn compute_payinfo_1_hop() {
698-
let recv_tlvs = ReceiveTlvs {
748+
let recv_tlvs = UnauthenticatedReceiveTlvs {
699749
payment_secret: PaymentSecret([0; 32]),
700750
payment_constraints: PaymentConstraints {
701751
max_cltv_expiry: 0,
@@ -751,7 +801,7 @@ mod tests {
751801
},
752802
htlc_maximum_msat: u64::max_value()
753803
}];
754-
let recv_tlvs = ReceiveTlvs {
804+
let recv_tlvs = UnauthenticatedReceiveTlvs {
755805
payment_secret: PaymentSecret([0; 32]),
756806
payment_constraints: PaymentConstraints {
757807
max_cltv_expiry: 0,
@@ -804,7 +854,7 @@ mod tests {
804854
},
805855
htlc_maximum_msat: u64::max_value()
806856
}];
807-
let recv_tlvs = ReceiveTlvs {
857+
let recv_tlvs = UnauthenticatedReceiveTlvs {
808858
payment_secret: PaymentSecret([0; 32]),
809859
payment_constraints: PaymentConstraints {
810860
max_cltv_expiry: 0,
@@ -861,7 +911,7 @@ mod tests {
861911
},
862912
htlc_maximum_msat: 10_000
863913
}];
864-
let recv_tlvs = ReceiveTlvs {
914+
let recv_tlvs = UnauthenticatedReceiveTlvs {
865915
payment_secret: PaymentSecret([0; 32]),
866916
payment_constraints: PaymentConstraints {
867917
max_cltv_expiry: 0,

lightning/src/ln/blinded_payment_tests.rs

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use bitcoin::secp256k1::{PublicKey, Scalar, Secp256k1, SecretKey, schnorr};
1212
use bitcoin::secp256k1::ecdh::SharedSecret;
1313
use bitcoin::secp256k1::ecdsa::{RecoverableSignature, Signature};
1414
use crate::blinded_path;
15-
use crate::blinded_path::payment::{BlindedPaymentPath, PaymentForwardNode, ForwardTlvs, PaymentConstraints, PaymentContext, PaymentRelay, ReceiveTlvs};
15+
use crate::blinded_path::payment::{BlindedPaymentPath, PaymentForwardNode, ForwardTlvs, PaymentConstraints, PaymentContext, PaymentRelay, UnauthenticatedReceiveTlvs};
1616
use crate::events::{Event, HTLCDestination, MessageSendEvent, MessageSendEventsProvider, PaymentFailureReason};
1717
use crate::ln::types::ChannelId;
1818
use crate::types::payment::{PaymentHash, PaymentSecret};
@@ -28,6 +28,7 @@ use crate::ln::onion_utils;
2828
use crate::ln::onion_utils::INVALID_ONION_BLINDING;
2929
use crate::ln::outbound_payment::{Retry, IDEMPOTENCY_TIMEOUT_TICKS};
3030
use crate::offers::invoice::UnsignedBolt12Invoice;
31+
use crate::offers::nonce::Nonce;
3132
use crate::prelude::*;
3233
use crate::routing::router::{BlindedTail, Path, Payee, PaymentParameters, RouteHop, RouteParameters};
3334
use crate::sign::{NodeSigner, Recipient};
@@ -70,7 +71,8 @@ fn blinded_payment_path(
7071
.unwrap_or_else(|| channel_upds[idx - 1].htlc_maximum_msat),
7172
});
7273
}
73-
let payee_tlvs = ReceiveTlvs {
74+
75+
let payee_tlvs = UnauthenticatedReceiveTlvs {
7476
payment_secret,
7577
payment_constraints: PaymentConstraints {
7678
max_cltv_expiry: u32::max_value(),
@@ -79,6 +81,11 @@ fn blinded_payment_path(
7981
},
8082
payment_context: PaymentContext::unknown(),
8183
};
84+
85+
let nonce = Nonce([42u8; 16]);
86+
let expanded_key = keys_manager.get_inbound_payment_key();
87+
let payee_tlvs = payee_tlvs.authenticate(nonce, &expanded_key);
88+
8289
let mut secp_ctx = Secp256k1::new();
8390
BlindedPaymentPath::new(
8491
&intermediate_nodes[..], *node_ids.last().unwrap(), payee_tlvs,
@@ -117,14 +124,18 @@ fn do_one_hop_blinded_path(success: bool) {
117124

118125
let amt_msat = 5000;
119126
let (payment_preimage, payment_hash, payment_secret) = get_payment_preimage_hash(&nodes[1], Some(amt_msat), None);
120-
let payee_tlvs = ReceiveTlvs {
127+
let payee_tlvs = UnauthenticatedReceiveTlvs {
121128
payment_secret,
122129
payment_constraints: PaymentConstraints {
123130
max_cltv_expiry: u32::max_value(),
124131
htlc_minimum_msat: chan_upd.htlc_minimum_msat,
125132
},
126133
payment_context: PaymentContext::unknown(),
127134
};
135+
let nonce = Nonce([42u8; 16]);
136+
let expanded_key = chanmon_cfgs[1].keys_manager.get_inbound_payment_key();
137+
let payee_tlvs = payee_tlvs.authenticate(nonce, &expanded_key);
138+
128139
let mut secp_ctx = Secp256k1::new();
129140
let blinded_path = BlindedPaymentPath::new(
130141
&[], nodes[1].node.get_our_node_id(), payee_tlvs, u64::MAX, TEST_FINAL_CLTV as u16,
@@ -161,14 +172,17 @@ fn mpp_to_one_hop_blinded_path() {
161172

162173
let amt_msat = 15_000_000;
163174
let (payment_preimage, payment_hash, payment_secret) = get_payment_preimage_hash(&nodes[3], Some(amt_msat), None);
164-
let payee_tlvs = ReceiveTlvs {
175+
let payee_tlvs = UnauthenticatedReceiveTlvs {
165176
payment_secret,
166177
payment_constraints: PaymentConstraints {
167178
max_cltv_expiry: u32::max_value(),
168179
htlc_minimum_msat: chan_upd_1_3.htlc_minimum_msat,
169180
},
170181
payment_context: PaymentContext::unknown(),
171182
};
183+
let nonce = Nonce([42u8; 16]);
184+
let expanded_key = chanmon_cfgs[3].keys_manager.get_inbound_payment_key();
185+
let payee_tlvs = payee_tlvs.authenticate(nonce, &expanded_key);
172186
let blinded_path = BlindedPaymentPath::new(
173187
&[], nodes[3].node.get_our_node_id(), payee_tlvs, u64::MAX, TEST_FINAL_CLTV as u16,
174188
&chanmon_cfgs[3].keys_manager, &secp_ctx
@@ -303,7 +317,7 @@ fn do_forward_checks_failure(check: ForwardCheckFail, intro_fails: bool) {
303317
let mut route_params = get_blinded_route_parameters(amt_msat, payment_secret, 1, 1_0000_0000,
304318
nodes.iter().skip(1).map(|n| n.node.get_our_node_id()).collect(),
305319
&[&chan_upd_1_2, &chan_upd_2_3], &chanmon_cfgs[3].keys_manager);
306-
route_params.payment_params.max_path_length = 18;
320+
route_params.payment_params.max_path_length = 17;
307321

308322
let route = get_route(&nodes[0], &route_params).unwrap();
309323
node_cfgs[0].router.expect_find_route(route_params.clone(), Ok(route.clone()));
@@ -1366,14 +1380,17 @@ fn custom_tlvs_to_blinded_path() {
13661380

13671381
let amt_msat = 5000;
13681382
let (payment_preimage, payment_hash, payment_secret) = get_payment_preimage_hash(&nodes[1], Some(amt_msat), None);
1369-
let payee_tlvs = ReceiveTlvs {
1383+
let payee_tlvs = UnauthenticatedReceiveTlvs {
13701384
payment_secret,
13711385
payment_constraints: PaymentConstraints {
13721386
max_cltv_expiry: u32::max_value(),
13731387
htlc_minimum_msat: chan_upd.htlc_minimum_msat,
13741388
},
13751389
payment_context: PaymentContext::unknown(),
13761390
};
1391+
let nonce = Nonce([42u8; 16]);
1392+
let expanded_key = chanmon_cfgs[1].keys_manager.get_inbound_payment_key();
1393+
let payee_tlvs = payee_tlvs.authenticate(nonce, &expanded_key);
13771394
let mut secp_ctx = Secp256k1::new();
13781395
let blinded_path = BlindedPaymentPath::new(
13791396
&[], nodes[1].node.get_our_node_id(), payee_tlvs, u64::MAX, TEST_FINAL_CLTV as u16,

0 commit comments

Comments
 (0)