mirror of
https://github.com/lightningdevkit/rust-lightning.git
synced 2025-02-24 15:02:20 +01:00
Verify inbound ReleaseHeldHtlc messages via hmac.
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. We also move the PAYMENT_HASH_HMAC_INPUT const to use &[7; 16], which is safe because this const was added since the last release. This ordering reads more smoothly.
This commit is contained in:
parent
5a7f52313b
commit
615eefb543
3 changed files with 71 additions and 12 deletions
|
@ -383,7 +383,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 being able to deanonymize us by creating a blinded path to us
|
||||
/// containing the expected [`PaymentId`].
|
||||
hmac: Hmac<Sha256>,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -412,6 +423,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),
|
||||
},
|
||||
);
|
||||
|
||||
|
|
|
@ -453,6 +453,24 @@ pub struct PaymentId(pub [u8; Self::LENGTH]);
|
|||
impl PaymentId {
|
||||
/// Number of bytes in the id.
|
||||
pub const LENGTH: usize = 32;
|
||||
|
||||
/// 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 Verification for PaymentId {
|
||||
|
@ -4353,8 +4371,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(()) => {
|
||||
|
@ -11209,7 +11231,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?}: {:?}",
|
||||
|
|
|
@ -39,9 +39,12 @@ 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] = &[6; 16];
|
||||
|
||||
// HMAC input for a `PaymentHash`. The HMAC is used in `OffersContext::InboundPayment`.
|
||||
const PAYMENT_HASH_HMAC_INPUT: &[u8; 16] = &[6; 16];
|
||||
const PAYMENT_HASH_HMAC_INPUT: &[u8; 16] = &[7; 16];
|
||||
|
||||
/// Message metadata which possibly is derived from [`MetadataMaterial`] such that it can be
|
||||
/// verified.
|
||||
|
@ -402,14 +405,7 @@ fn hmac_for_message<'a>(
|
|||
pub(crate) fn hmac_for_offer_payment_id(
|
||||
payment_id: PaymentId, nonce: Nonce, 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(&payment_id.0);
|
||||
|
||||
Hmac::from_engine(hmac)
|
||||
hmac_for_payment_id(payment_id, nonce, OFFER_PAYMENT_ID_HMAC_INPUT, expanded_key)
|
||||
}
|
||||
|
||||
pub(crate) fn verify_offer_payment_id(
|
||||
|
@ -436,3 +432,30 @@ pub(crate) fn verify_payment_hash(
|
|||
) -> Result<(), ()> {
|
||||
if hmac_for_payment_hash(payment_hash, 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(hmac_input);
|
||||
hmac.input(&payment_id.0);
|
||||
|
||||
Hmac::from_engine(hmac)
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue