Skip to content

Commit ffa96c4

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 the blinded path using the static invoice's corresponding offer id.
1 parent 88e8e71 commit ffa96c4

File tree

4 files changed

+70
-1
lines changed

4 files changed

+70
-1
lines changed

lightning/src/blinded_path/message.rs

+28
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

+6-1
Original file line numberDiff line numberDiff line change
@@ -11747,7 +11747,12 @@ where
1174711747

1174811748
fn handle_release_held_htlc(&self, _message: ReleaseHeldHtlc, _context: AsyncPaymentsContext) {
1174911749
#[cfg(async_payments)] {
11750-
let AsyncPaymentsContext::OutboundPayment { payment_id, hmac, nonce } = _context;
11750+
let (payment_id, nonce, hmac) = match _context {
11751+
AsyncPaymentsContext::OutboundPayment { payment_id, hmac, nonce } => {
11752+
(payment_id, nonce, hmac)
11753+
},
11754+
_ => return
11755+
};
1175111756
if payment_id.verify_for_async_payment(hmac, nonce, &self.inbound_payment_key).is_err() { return }
1175211757
if let Err(e) = self.send_payment_for_static_invoice(payment_id) {
1175311758
log_trace!(

lightning/src/offers/offer.rs

+16
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

+20
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ use crate::ln::channelmanager::PaymentId;
2020
use crate::ln::inbound_payment::{ExpandedKey, IV_LEN};
2121
use crate::offers::merkle::TlvRecord;
2222
use crate::offers::nonce::Nonce;
23+
#[cfg(async_payments)]
24+
use crate::offers::offer::OfferId;
2325
use crate::util::ser::Writeable;
2426

2527
use crate::prelude::*;
@@ -46,6 +48,10 @@ const ASYNC_PAYMENT_ID_HMAC_INPUT: &[u8; 16] = &[6; 16];
4648
// HMAC input for a `PaymentHash`. The HMAC is used in `OffersContext::InboundPayment`.
4749
const PAYMENT_HASH_HMAC_INPUT: &[u8; 16] = &[7; 16];
4850

51+
// HMAC input for an `OfferId`. The HMAC is used in `AsyncPaymentsContext::InboundPayment`.
52+
#[cfg(async_payments)]
53+
const ASYNC_PAYMENT_OFFER_ID_HMAC_INPUT: &[u8; 16] = &[8; 16];
54+
4955
/// Message metadata which possibly is derived from [`MetadataMaterial`] such that it can be
5056
/// verified.
5157
#[derive(Clone)]
@@ -459,3 +465,17 @@ fn hmac_for_payment_id(
459465

460466
Hmac::from_engine(hmac)
461467
}
468+
469+
#[cfg(async_payments)]
470+
pub(crate) fn hmac_for_static_invoice_offer_id(
471+
offer_id: OfferId, nonce: Nonce, expanded_key: &ExpandedKey,
472+
) -> Hmac<Sha256> {
473+
const IV_BYTES: &[u8; IV_LEN] = b"LDK Offer ID ~~~";
474+
let mut hmac = expanded_key.hmac_for_offer();
475+
hmac.input(IV_BYTES);
476+
hmac.input(&nonce.0);
477+
hmac.input(ASYNC_PAYMENT_OFFER_ID_HMAC_INPUT);
478+
hmac.input(&offer_id.0);
479+
480+
Hmac::from_engine(hmac)
481+
}

0 commit comments

Comments
 (0)