Skip to content

Commit 4ce56d4

Browse files
committed
Make ReceiveTlvs::authentication required
Having ReceiveTlvs::authentication be an Option makes for an odd interface as ReceiveTlvs first needs to be constructed with it set to None. Afterward, the HMAC can be constructed from it and set on it as Some. Instead, introduce an UnauthenticatedReceiveTlvs struct with a method to convert to an HMAC'ed ReceiveTlvs. This prevents ReceiveTlvs from being in an invalid state.
1 parent 469e9e6 commit 4ce56d4

File tree

9 files changed

+114
-82
lines changed

9 files changed

+114
-82
lines changed

fuzz/src/invoice_request_deser.rs

+8-4
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,15 +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,
102-
authentication: None,
103106
};
107+
let payee_tlvs = payee_tlvs.authenticate(nonce, &expanded_key);
104108
let intermediate_nodes = [PaymentForwardNode {
105109
tlvs: ForwardTlvs {
106110
short_channel_id: 43,
@@ -110,7 +114,7 @@ fn build_response<T: secp256k1::Signing + secp256k1::Verification>(
110114
fee_base_msat: 1,
111115
},
112116
payment_constraints: PaymentConstraints {
113-
max_cltv_expiry: payee_tlvs.payment_constraints.max_cltv_expiry + 40,
117+
max_cltv_expiry: payee_tlvs.tlvs.payment_constraints.max_cltv_expiry + 40,
114118
htlc_minimum_msat: 100,
115119
},
116120
features: BlindedHopFeatures::empty(),

fuzz/src/refund_deser.rs

+8-4
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,17 +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,
79-
authentication: None,
8083
};
84+
let payee_tlvs = payee_tlvs.authenticate(nonce, &expanded_key);
8185
let intermediate_nodes = [PaymentForwardNode {
8286
tlvs: ForwardTlvs {
8387
short_channel_id: 43,
@@ -87,7 +91,7 @@ fn build_response<T: secp256k1::Signing + secp256k1::Verification>(
8791
fee_base_msat: 1,
8892
},
8993
payment_constraints: PaymentConstraints {
90-
max_cltv_expiry: payee_tlvs.payment_constraints.max_cltv_expiry + 40,
94+
max_cltv_expiry: payee_tlvs.tlvs.payment_constraints.max_cltv_expiry + 40,
9195
htlc_minimum_msat: 100,
9296
},
9397
features: BlindedHopFeatures::empty(),

lightning/src/blinded_path/payment.rs

+50-20
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ use crate::io;
2020
use crate::io::Cursor;
2121
use crate::types::payment::PaymentSecret;
2222
use crate::ln::channel_state::CounterpartyForwardingInfo;
23+
use crate::ln::channelmanager::Verification;
2324
use crate::types::features::BlindedHopFeatures;
25+
use crate::ln::inbound_payment::ExpandedKey;
2426
use crate::ln::msgs::DecodeError;
2527
use crate::ln::onion_utils;
2628
use crate::offers::invoice_request::InvoiceRequestFields;
@@ -117,7 +119,7 @@ impl BlindedPaymentPath {
117119
let blinding_secret = SecretKey::from_slice(&blinding_secret_bytes[..]).expect("RNG is busted");
118120

119121
let blinded_payinfo = compute_payinfo(
120-
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
121123
)?;
122124
Ok(Self {
123125
inner_path: BlindedPath {
@@ -255,16 +257,36 @@ pub struct ForwardTlvs {
255257

256258
/// Data to construct a [`BlindedHop`] for receiving a payment. This payload is custom to LDK and
257259
/// may not be valid if received by another lightning implementation.
260+
///
261+
/// Can only be constructed by calling [`UnauthenticatedReceiveTlvs::authenticate`].
258262
#[derive(Clone, Debug)]
259263
pub struct ReceiveTlvs {
264+
/// The TLVs for which the HMAC in `authentication` is derived.
265+
pub 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+
/// An unauthenticated [`ReceiveTlvs`].
271+
#[derive(Clone, Debug)]
272+
pub struct UnauthenticatedReceiveTlvs {
260273
/// Used to authenticate the sender of a payment to the receiver and tie MPP HTLCs together.
261274
pub payment_secret: PaymentSecret,
262275
/// Constraints for the receiver of this payment.
263276
pub payment_constraints: PaymentConstraints,
264277
/// Context for the receiver of this payment.
265278
pub payment_context: PaymentContext,
266-
/// An HMAC of `payment_context` along with a nonce used to construct it.
267-
pub authentication: Option<(Hmac<Sha256>, Nonce)>,
279+
}
280+
281+
impl UnauthenticatedReceiveTlvs {
282+
/// Creates an authenticated [`ReceiveTlvs`], which includes an HMAC and the provide [`Nonce`]
283+
/// that can be use later to verify it authenticity.
284+
pub fn authenticate(self, nonce: Nonce, expanded_key: &ExpandedKey) -> ReceiveTlvs {
285+
ReceiveTlvs {
286+
authentication: (self.hmac_for_offer_payment(nonce, expanded_key), nonce),
287+
tlvs: self,
288+
}
289+
}
268290
}
269291

270292
/// Data to construct a [`BlindedHop`] for sending a payment over.
@@ -392,12 +414,23 @@ impl Writeable for ForwardTlvs {
392414
}
393415

394416
impl Writeable for ReceiveTlvs {
417+
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
418+
encode_tlv_stream!(w, {
419+
(12, self.tlvs.payment_constraints, required),
420+
(65536, self.tlvs.payment_secret, required),
421+
(65537, self.tlvs.payment_context, required),
422+
(65539, self.authentication, required),
423+
});
424+
Ok(())
425+
}
426+
}
427+
428+
impl Writeable for UnauthenticatedReceiveTlvs {
395429
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
396430
encode_tlv_stream!(w, {
397431
(12, self.payment_constraints, required),
398432
(65536, self.payment_secret, required),
399433
(65537, self.payment_context, required),
400-
(65539, self.authentication, option),
401434
});
402435
Ok(())
403436
}
@@ -443,10 +476,12 @@ impl Readable for BlindedPaymentTlvs {
443476
} else {
444477
if payment_relay.is_some() || features.is_some() { return Err(DecodeError::InvalidValue) }
445478
Ok(BlindedPaymentTlvs::Receive(ReceiveTlvs {
446-
payment_secret: payment_secret.ok_or(DecodeError::InvalidValue)?,
447-
payment_constraints: payment_constraints.0.unwrap(),
448-
payment_context: payment_context.ok_or(DecodeError::InvalidValue)?,
449-
authentication,
479+
tlvs: UnauthenticatedReceiveTlvs {
480+
payment_secret: payment_secret.ok_or(DecodeError::InvalidValue)?,
481+
payment_constraints: payment_constraints.0.unwrap(),
482+
payment_context: payment_context.ok_or(DecodeError::InvalidValue)?,
483+
},
484+
authentication: authentication.ok_or(DecodeError::InvalidValue)?,
450485
}))
451486
}
452487
}
@@ -489,7 +524,7 @@ pub(crate) fn amt_to_forward_msat(inbound_amt_msat: u64, payment_relay: &Payment
489524
}
490525

491526
pub(super) fn compute_payinfo(
492-
intermediate_nodes: &[PaymentForwardNode], payee_tlvs: &ReceiveTlvs,
527+
intermediate_nodes: &[PaymentForwardNode], payee_tlvs: &UnauthenticatedReceiveTlvs,
493528
payee_htlc_maximum_msat: u64, min_final_cltv_expiry_delta: u16,
494529
) -> Result<BlindedPayInfo, ()> {
495530
let mut curr_base_fee: u64 = 0;
@@ -613,7 +648,7 @@ impl_writeable_tlv_based!(Bolt12RefundContext, {});
613648
#[cfg(test)]
614649
mod tests {
615650
use bitcoin::secp256k1::PublicKey;
616-
use crate::blinded_path::payment::{Bolt12RefundContext, PaymentForwardNode, ForwardTlvs, ReceiveTlvs, PaymentConstraints, PaymentContext, PaymentRelay};
651+
use crate::blinded_path::payment::{Bolt12RefundContext, PaymentForwardNode, ForwardTlvs, PaymentConstraints, PaymentContext, PaymentRelay, UnauthenticatedReceiveTlvs};
617652
use crate::types::payment::PaymentSecret;
618653
use crate::types::features::BlindedHopFeatures;
619654
use crate::ln::functional_test_utils::TEST_FINAL_CLTV;
@@ -658,14 +693,13 @@ mod tests {
658693
},
659694
htlc_maximum_msat: u64::max_value(),
660695
}];
661-
let recv_tlvs = ReceiveTlvs {
696+
let recv_tlvs = UnauthenticatedReceiveTlvs {
662697
payment_secret: PaymentSecret([0; 32]),
663698
payment_constraints: PaymentConstraints {
664699
max_cltv_expiry: 0,
665700
htlc_minimum_msat: 1,
666701
},
667702
payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}),
668-
authentication: None,
669703
};
670704
let htlc_maximum_msat = 100_000;
671705
let blinded_payinfo = super::compute_payinfo(&intermediate_nodes[..], &recv_tlvs, htlc_maximum_msat, 12).unwrap();
@@ -678,14 +712,13 @@ mod tests {
678712

679713
#[test]
680714
fn compute_payinfo_1_hop() {
681-
let recv_tlvs = ReceiveTlvs {
715+
let recv_tlvs = UnauthenticatedReceiveTlvs {
682716
payment_secret: PaymentSecret([0; 32]),
683717
payment_constraints: PaymentConstraints {
684718
max_cltv_expiry: 0,
685719
htlc_minimum_msat: 1,
686720
},
687721
payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}),
688-
authentication: None,
689722
};
690723
let blinded_payinfo = super::compute_payinfo(&[], &recv_tlvs, 4242, TEST_FINAL_CLTV as u16).unwrap();
691724
assert_eq!(blinded_payinfo.fee_base_msat, 0);
@@ -735,14 +768,13 @@ mod tests {
735768
},
736769
htlc_maximum_msat: u64::max_value()
737770
}];
738-
let recv_tlvs = ReceiveTlvs {
771+
let recv_tlvs = UnauthenticatedReceiveTlvs {
739772
payment_secret: PaymentSecret([0; 32]),
740773
payment_constraints: PaymentConstraints {
741774
max_cltv_expiry: 0,
742775
htlc_minimum_msat: 3,
743776
},
744777
payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}),
745-
authentication: None,
746778
};
747779
let htlc_maximum_msat = 100_000;
748780
let blinded_payinfo = super::compute_payinfo(&intermediate_nodes[..], &recv_tlvs, htlc_maximum_msat, TEST_FINAL_CLTV as u16).unwrap();
@@ -789,14 +821,13 @@ mod tests {
789821
},
790822
htlc_maximum_msat: u64::max_value()
791823
}];
792-
let recv_tlvs = ReceiveTlvs {
824+
let recv_tlvs = UnauthenticatedReceiveTlvs {
793825
payment_secret: PaymentSecret([0; 32]),
794826
payment_constraints: PaymentConstraints {
795827
max_cltv_expiry: 0,
796828
htlc_minimum_msat: 1,
797829
},
798830
payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}),
799-
authentication: None,
800831
};
801832
let htlc_minimum_msat = 3798;
802833
assert!(super::compute_payinfo(&intermediate_nodes[..], &recv_tlvs, htlc_minimum_msat - 1, TEST_FINAL_CLTV as u16).is_err());
@@ -847,14 +878,13 @@ mod tests {
847878
},
848879
htlc_maximum_msat: 10_000
849880
}];
850-
let recv_tlvs = ReceiveTlvs {
881+
let recv_tlvs = UnauthenticatedReceiveTlvs {
851882
payment_secret: PaymentSecret([0; 32]),
852883
payment_constraints: PaymentConstraints {
853884
max_cltv_expiry: 0,
854885
htlc_minimum_msat: 1,
855886
},
856887
payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}),
857-
authentication: None,
858888
};
859889

860890
let blinded_payinfo = super::compute_payinfo(&intermediate_nodes[..], &recv_tlvs, 10_000, TEST_FINAL_CLTV as u16).unwrap();

0 commit comments

Comments
 (0)