Skip to content

Commit 4c03b58

Browse files
Add onion message AsyncPaymentsContext for inbound payments
This context is included in static invoice's blinded message paths, provided back to us in HeldHtlcAvailable onion messages for blinded path authentication. In future work, we will check if this context is valid and respond with a ReleaseHeldHtlc message to release the upstream payment if so. We also add creation methods for the hmac used for authenticating said blinded path.
1 parent 84f200f commit 4c03b58

File tree

4 files changed

+70
-1
lines changed

4 files changed

+70
-1
lines changed

lightning/src/blinded_path/message.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ use crate::ln::msgs::DecodeError;
2525
use crate::ln::onion_utils;
2626
use crate::types::payment::PaymentHash;
2727
use crate::offers::nonce::Nonce;
28+
use crate::offers::offer::OfferId;
2829
use crate::onion_message::packet::ControlTlvs;
2930
use crate::routing::gossip::{NodeId, ReadOnlyNetworkGraph};
3031
use crate::sign::{EntropySource, NodeSigner, Recipient};
@@ -402,6 +403,28 @@ pub enum AsyncPaymentsContext {
402403
/// containing the expected [`PaymentId`].
403404
hmac: Hmac<Sha256>,
404405
},
406+
/// Context contained within the [`BlindedMessagePath`]s we put in static invoices, provided back
407+
/// to us in corresponding [`HeldHtlcAvailable`] messages.
408+
///
409+
/// [`HeldHtlcAvailable`]: crate::onion_message::async_payments::HeldHtlcAvailable
410+
InboundPayment {
411+
/// The ID of the [`Offer`] that this [`BlindedMessagePath`]'s static invoice corresponds to.
412+
/// Useful to authenticate that this blinded path was created by us for asynchronously paying
413+
/// one of our offers.
414+
///
415+
/// [`Offer`]: crate::offers::offer::Offer
416+
offer_id: OfferId,
417+
/// A nonce used for authenticating that a [`HeldHtlcAvailable`] message is valid for a
418+
/// preceding static invoice.
419+
///
420+
/// [`HeldHtlcAvailable`]: crate::onion_message::async_payments::HeldHtlcAvailable
421+
nonce: Nonce,
422+
/// Authentication code for the [`OfferId`].
423+
///
424+
/// Prevents the recipient from being able to deanonymize us by creating a blinded path to us
425+
/// containing the expected [`OfferId`].
426+
hmac: Hmac<Sha256>,
427+
},
405428
}
406429

407430
impl_writeable_tlv_based_enum!(MessageContext,
@@ -433,6 +456,11 @@ impl_writeable_tlv_based_enum!(AsyncPaymentsContext,
433456
(2, nonce, required),
434457
(4, hmac, required),
435458
},
459+
(1, InboundPayment) => {
460+
(0, offer_id, required),
461+
(2, nonce, required),
462+
(4, hmac, required),
463+
},
436464
);
437465

438466
/// Contains a simple nonce for use in a blinded path's context.

lightning/src/ln/channelmanager.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12209,7 +12209,12 @@ where
1220912209

1221012210
fn handle_release_held_htlc(&self, _message: ReleaseHeldHtlc, _context: AsyncPaymentsContext) {
1221112211
#[cfg(async_payments)] {
12212-
let AsyncPaymentsContext::OutboundPayment { payment_id, hmac, nonce } = _context;
12212+
let (payment_id, nonce, hmac) = match _context {
12213+
AsyncPaymentsContext::OutboundPayment { payment_id, hmac, nonce } => {
12214+
(payment_id, nonce, hmac)
12215+
},
12216+
_ => return
12217+
};
1221312218
if payment_id.verify_for_async_payment(hmac, nonce, &self.inbound_payment_key).is_err() { return }
1221412219
if let Err(e) = self.send_payment_for_static_invoice(payment_id) {
1221512220
log_trace!(

lightning/src/offers/offer.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,13 @@ use crate::offers::signer::{Metadata, MetadataMaterial, self};
9898
use crate::util::ser::{CursorReadable, HighZeroBytesDroppedBigSize, Readable, WithoutLength, Writeable, Writer};
9999
use crate::util::string::PrintableString;
100100

101+
#[cfg(async_payments)]
102+
use {
103+
bitcoin::hashes::hmac::Hmac,
104+
bitcoin::hashes::sha256::Hash as Sha256,
105+
crate::ln::inbound_payment,
106+
};
107+
101108
#[cfg(not(c_bindings))]
102109
use {
103110
crate::offers::invoice_request::InvoiceRequestBuilder,
@@ -134,6 +141,15 @@ impl OfferId {
134141
let tagged_hash = TaggedHash::from_tlv_stream(Self::ID_TAG, tlv_stream);
135142
Self(tagged_hash.to_bytes())
136143
}
144+
145+
/// Constructs an HMAC to include in [`AsyncPaymentsContext::InboundPayment`] for the offer id
146+
/// along with the given [`Nonce`].
147+
#[cfg(async_payments)]
148+
pub fn hmac_for_static_invoice(
149+
&self, nonce: Nonce, expanded_key: &inbound_payment::ExpandedKey,
150+
) -> Hmac<Sha256> {
151+
signer::hmac_for_static_invoice_offer_id(*self, nonce, expanded_key)
152+
}
137153
}
138154

139155
impl Borrow<[u8]> for OfferId {

lightning/src/offers/signer.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ use crate::ln::channelmanager::PaymentId;
2121
use crate::ln::inbound_payment::{ExpandedKey, IV_LEN};
2222
use crate::offers::merkle::TlvRecord;
2323
use crate::offers::nonce::Nonce;
24+
#[cfg(async_payments)]
25+
use crate::offers::offer::OfferId;
2426
use crate::util::ser::Writeable;
2527

2628
use crate::prelude::*;
@@ -50,6 +52,10 @@ const PAYMENT_HASH_HMAC_INPUT: &[u8; 16] = &[7; 16];
5052
// HMAC input for `ReceiveTlvs`. The HMAC is used in `blinded_path::payment::PaymentContext`.
5153
const PAYMENT_TLVS_HMAC_INPUT: &[u8; 16] = &[8; 16];
5254

55+
// HMAC input for an `OfferId`. The HMAC is used in `AsyncPaymentsContext::InboundPayment`.
56+
#[cfg(async_payments)]
57+
const ASYNC_PAYMENT_OFFER_ID_HMAC_INPUT: &[u8; 16] = &[9; 16];
58+
5359
/// Message metadata which possibly is derived from [`MetadataMaterial`] such that it can be
5460
/// verified.
5561
#[derive(Clone)]
@@ -483,3 +489,17 @@ pub(crate) fn verify_payment_tlvs(
483489
) -> Result<(), ()> {
484490
if hmac_for_payment_tlvs(receive_tlvs, nonce, expanded_key) == hmac { Ok(()) } else { Err(()) }
485491
}
492+
493+
#[cfg(async_payments)]
494+
pub(crate) fn hmac_for_static_invoice_offer_id(
495+
offer_id: OfferId, nonce: Nonce, expanded_key: &ExpandedKey,
496+
) -> Hmac<Sha256> {
497+
const IV_BYTES: &[u8; IV_LEN] = b"LDK Offer ID ~~~";
498+
let mut hmac = expanded_key.hmac_for_offer();
499+
hmac.input(IV_BYTES);
500+
hmac.input(&nonce.0);
501+
hmac.input(ASYNC_PAYMENT_OFFER_ID_HMAC_INPUT);
502+
hmac.input(&offer_id.0);
503+
504+
Hmac::from_engine(hmac)
505+
}

0 commit comments

Comments
 (0)