From: Valentine Wallace Date: Fri, 30 Aug 2024 22:41:36 +0000 (-0400) Subject: Support verifying payment_secrets for inbound static invoice payments X-Git-Url: http://git.bitcoin.ninja/?a=commitdiff_plain;h=34c2f258067d50eba13e2a4c55cba10a2989716f;p=rust-lightning Support verifying payment_secrets for inbound static invoice payments Add a new payment type for this, because normally the payment hash is factored into the payment secrets we create for invoices, but static invoices don't have a payment hash since they are paid via keysend. --- diff --git a/lightning/src/ln/inbound_payment.rs b/lightning/src/ln/inbound_payment.rs index d3cdae616..5b8d48e1c 100644 --- a/lightning/src/ln/inbound_payment.rs +++ b/lightning/src/ln/inbound_payment.rs @@ -98,11 +98,13 @@ impl ExpandedKey { } } +/// We currently set aside 3 bits for the `Method` in the `PaymentSecret`. enum Method { LdkPaymentHash = 0, UserPaymentHash = 1, LdkPaymentHashCustomFinalCltv = 2, UserPaymentHashCustomFinalCltv = 3, + SpontaneousPayment = 4, } impl Method { @@ -112,6 +114,7 @@ impl Method { bits if bits == Method::UserPaymentHash as u8 => Ok(Method::UserPaymentHash), bits if bits == Method::LdkPaymentHashCustomFinalCltv as u8 => Ok(Method::LdkPaymentHashCustomFinalCltv), bits if bits == Method::UserPaymentHashCustomFinalCltv as u8 => Ok(Method::UserPaymentHashCustomFinalCltv), + bits if bits == Method::SpontaneousPayment as u8 => Ok(Method::SpontaneousPayment), unknown => Err(unknown), } } @@ -191,6 +194,26 @@ pub fn create_from_hash(keys: &ExpandedKey, min_value_msat: Option, payment Ok(construct_payment_secret(&iv_bytes, &metadata_bytes, &keys.metadata_key)) } +#[cfg(async_payments)] +pub(super) fn create_for_spontaneous_payment( + keys: &ExpandedKey, min_value_msat: Option, invoice_expiry_delta_secs: u32, + current_time: u64, min_final_cltv_expiry_delta: Option +) -> Result { + let metadata_bytes = construct_metadata_bytes( + min_value_msat, Method::SpontaneousPayment, invoice_expiry_delta_secs, current_time, + min_final_cltv_expiry_delta + )?; + + let mut hmac = HmacEngine::::new(&keys.spontaneous_pmt_key); + hmac.input(&metadata_bytes); + let hmac_bytes = Hmac::from_engine(hmac).to_byte_array(); + + let mut iv_bytes = [0 as u8; IV_LEN]; + iv_bytes.copy_from_slice(&hmac_bytes[..IV_LEN]); + + Ok(construct_payment_secret(&iv_bytes, &metadata_bytes, &keys.metadata_key)) +} + fn construct_metadata_bytes(min_value_msat: Option, payment_type: Method, invoice_expiry_delta_secs: u32, highest_seen_timestamp: u64, min_final_cltv_expiry_delta: Option) -> Result<[u8; METADATA_LEN], ()> { if min_value_msat.is_some() && min_value_msat.unwrap() > MAX_VALUE_MSAT { @@ -320,6 +343,14 @@ pub(super) fn verify(payment_hash: PaymentHash, payment_data: &msgs::F } } }, + Ok(Method::SpontaneousPayment) => { + let mut hmac = HmacEngine::::new(&keys.spontaneous_pmt_key); + hmac.input(&metadata_bytes[..]); + if !fixed_time_eq(&iv_bytes, &Hmac::from_engine(hmac).to_byte_array().split_at_mut(IV_LEN).0) { + log_trace!(logger, "Failing async payment HTLC with sender-generated payment_hash {}: unexpected payment_secret", &payment_hash); + return Err(()) + } + }, Err(unknown_bits) => { log_trace!(logger, "Failing HTLC with payment hash {} due to unknown payment type {}", &payment_hash, unknown_bits); return Err(()); @@ -365,6 +396,9 @@ pub(super) fn get_payment_preimage(payment_hash: PaymentHash, payment_secret: Pa Ok(Method::UserPaymentHash) | Ok(Method::UserPaymentHashCustomFinalCltv) => Err(APIError::APIMisuseError { err: "Expected payment type to be LdkPaymentHash, instead got UserPaymentHash".to_string() }), + Ok(Method::SpontaneousPayment) => Err(APIError::APIMisuseError { + err: "Can't extract payment preimage for spontaneous payments".to_string() + }), Err(other) => Err(APIError::APIMisuseError { err: format!("Unknown payment type: {}", other) }), } }