Skip to content

Commit

Permalink
Verify inbound ReleaseHeldHtlc messages via hmac.
Browse files Browse the repository at this point in the history
See AsyncPaymentsContext::hmac, but this prevents the recipient from
deanonymizing us. Without this, if they are able to guess the correct payment
id, then they could create a blinded path to us and confirm our identity.
  • Loading branch information
valentinewallace committed Sep 5, 2024
1 parent 9a3e1bc commit b9550b3
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 10 deletions.
15 changes: 14 additions & 1 deletion lightning/src/blinded_path/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,18 @@ pub enum AsyncPaymentsContext {
/// which of our pending outbound payments should be released to its often-offline payee.
///
/// [`Offer`]: crate::offers::offer::Offer
payment_id: PaymentId
payment_id: PaymentId,
/// A nonce used for authenticating that a [`ReleaseHeldHtlc`] message is valid for a preceding
/// [`HeldHtlcAvailable`] message.
///
/// [`ReleaseHeldHtlc`]: crate::onion_message::async_payments::ReleaseHeldHtlc
/// [`HeldHtlcAvailable`]: crate::onion_message::async_payments::HeldHtlcAvailable
nonce: Nonce,
/// Authentication code for the [`PaymentId`].
///
/// Prevents the recipient from deanonyimizing us by creating a blinded path to us containing
/// the expected [`PaymentId`].
hmac: Hmac<Sha256>,
},
}

Expand All @@ -397,6 +408,8 @@ impl_writeable_tlv_based_enum!(OffersContext,
impl_writeable_tlv_based_enum!(AsyncPaymentsContext,
(0, OutboundPayment) => {
(0, payment_id, required),
(2, nonce, required),
(4, hmac, required),
},
);

Expand Down
27 changes: 25 additions & 2 deletions lightning/src/ln/channelmanager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,24 @@ impl PaymentId {
) -> Result<(), ()> {
signer::verify_offer_payment_id(*self, hmac, nonce, expanded_key)
}

/// Constructs an HMAC to include in [`AsyncPaymentsContext::OutboundPayment`] for the payment id
/// along with the given [`Nonce`].
#[cfg(async_payments)]
pub fn hmac_for_async_payment(
&self, nonce: Nonce, expanded_key: &inbound_payment::ExpandedKey,
) -> Hmac<Sha256> {
signer::hmac_for_async_payment_id(*self, nonce, expanded_key)
}

/// Authenticates the payment id using an HMAC and a [`Nonce`] taken from an
/// [`AsyncPaymentsContext::OutboundPayment`].
#[cfg(async_payments)]
pub fn verify_for_async_payment(
&self, hmac: Hmac<Sha256>, nonce: Nonce, expanded_key: &inbound_payment::ExpandedKey,
) -> Result<(), ()> {
signer::verify_async_payment_id(*self, hmac, nonce, expanded_key)
}
}

impl Writeable for PaymentId {
Expand Down Expand Up @@ -4319,8 +4337,12 @@ where
}
};

let nonce = Nonce::from_entropy_source(&*self.entropy_source);
let hmac = payment_id.hmac_for_async_payment(nonce, &self.inbound_payment_key);
let reply_paths = match self.create_blinded_paths(
MessageContext::AsyncPayments(AsyncPaymentsContext::OutboundPayment { payment_id })
MessageContext::AsyncPayments(
AsyncPaymentsContext::OutboundPayment { payment_id, nonce, hmac }
)
) {
Ok(paths) => paths,
Err(()) => {
Expand Down Expand Up @@ -11112,7 +11134,8 @@ where

fn release_held_htlc(&self, _message: ReleaseHeldHtlc, _context: AsyncPaymentsContext) {
#[cfg(async_payments)] {
let AsyncPaymentsContext::OutboundPayment { payment_id } = _context;
let AsyncPaymentsContext::OutboundPayment { payment_id, hmac, nonce } = _context;
if payment_id.verify_for_async_payment(hmac, nonce, &self.inbound_payment_key).is_err() { return }
if let Err(e) = self.send_payment_for_static_invoice(payment_id, _message.payment_release_secret) {
log_trace!(
self.logger, "Failed to release held HTLC with payment id {} and release secret {:02x?}: {:?}",
Expand Down
37 changes: 30 additions & 7 deletions lightning/src/offers/signer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ const WITH_ENCRYPTED_PAYMENT_ID_HMAC_INPUT: &[u8; 16] = &[4; 16];

// HMAC input for a `PaymentId`. The HMAC is used in `OffersContext::OutboundPayment`.
const OFFER_PAYMENT_ID_HMAC_INPUT: &[u8; 16] = &[5; 16];
// HMAC input for a `PaymentId`. The HMAC is used in `AsyncPaymentsContext::OutboundPayment`.
#[cfg(async_payments)]
const ASYNC_PAYMENT_ID_HMAC_INPUT: &[u8; 16] = &[5; 16];

/// Message metadata which possibly is derived from [`MetadataMaterial`] such that it can be
/// verified.
Expand Down Expand Up @@ -397,19 +400,39 @@ fn hmac_for_message<'a>(

pub(crate) fn hmac_for_offer_payment_id(
payment_id: PaymentId, nonce: Nonce, expanded_key: &ExpandedKey,
) -> Hmac<Sha256> {
hmac_for_payment_id(payment_id, nonce, OFFER_PAYMENT_ID_HMAC_INPUT, expanded_key)
}

pub(crate) fn verify_offer_payment_id(
payment_id: PaymentId, hmac: Hmac<Sha256>, nonce: Nonce, expanded_key: &ExpandedKey,
) -> Result<(), ()> {
if hmac_for_offer_payment_id(payment_id, nonce, expanded_key) == hmac { Ok(()) } else { Err(()) }
}

#[cfg(async_payments)]
pub(crate) fn hmac_for_async_payment_id(
payment_id: PaymentId, nonce: Nonce, expanded_key: &ExpandedKey,
) -> Hmac<Sha256> {
hmac_for_payment_id(payment_id, nonce, ASYNC_PAYMENT_ID_HMAC_INPUT, expanded_key)
}

#[cfg(async_payments)]
pub(crate) fn verify_async_payment_id(
payment_id: PaymentId, hmac: Hmac<Sha256>, nonce: Nonce, expanded_key: &ExpandedKey,
) -> Result<(), ()> {
if hmac_for_async_payment_id(payment_id, nonce, expanded_key) == hmac { Ok(()) } else { Err(()) }
}

fn hmac_for_payment_id(
payment_id: PaymentId, nonce: Nonce, hmac_input: &[u8; 16], expanded_key: &ExpandedKey,
) -> Hmac<Sha256> {
const IV_BYTES: &[u8; IV_LEN] = b"LDK Payment ID ~";
let mut hmac = expanded_key.hmac_for_offer();
hmac.input(IV_BYTES);
hmac.input(&nonce.0);
hmac.input(OFFER_PAYMENT_ID_HMAC_INPUT);
hmac.input(hmac_input);
hmac.input(&payment_id.0);

Hmac::from_engine(hmac)
}

pub(crate) fn verify_offer_payment_id(
payment_id: PaymentId, hmac: Hmac<Sha256>, nonce: Nonce, expanded_key: &ExpandedKey,
) -> Result<(), ()> {
if hmac_for_offer_payment_id(payment_id, nonce, expanded_key) == hmac { Ok(()) } else { Err(()) }
}

0 comments on commit b9550b3

Please sign in to comment.