Skip to content

Commit ae11752

Browse files
Drop need to store pending inbound payments
and replace payment_secret with encrypted metadata See docs on verify_inbound_payment for details
1 parent 92020a1 commit ae11752

File tree

3 files changed

+224
-113
lines changed

3 files changed

+224
-113
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 207 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ use core::{cmp, mem};
6565
use core::cell::RefCell;
6666
use io::{Cursor, Read};
6767
use sync::{Arc, Condvar, Mutex, MutexGuard, RwLock, RwLockReadGuard};
68+
use core::convert::TryInto;
6869
use core::sync::atomic::{AtomicUsize, Ordering};
6970
use core::time::Duration;
7071
#[cfg(any(test, feature = "allow_wallclock_use"))]
@@ -642,6 +643,8 @@ pub struct ChannelManager<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref,
642643
our_network_key: SecretKey,
643644
our_network_pubkey: PublicKey,
644645

646+
inbound_payment_key: [u8; 32],
647+
645648
/// Used to track the last value sent in a node_announcement "timestamp" field. We ensure this
646649
/// value increases strictly since we don't assume access to a time source.
647650
last_node_announcement_serial: AtomicUsize,
@@ -1342,6 +1345,8 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
13421345
our_network_pubkey: PublicKey::from_secret_key(&secp_ctx, &keys_manager.get_node_secret()),
13431346
secp_ctx,
13441347

1348+
inbound_payment_key: keys_manager.get_inbound_payment_secret(),
1349+
13451350
last_node_announcement_serial: AtomicUsize::new(0),
13461351
highest_seen_timestamp: AtomicUsize::new(0),
13471352

@@ -2589,6 +2594,75 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
25892594
}
25902595
}
25912596

2597+
// Check that an inbound payment's `payment_data` field is sane.
2598+
//
2599+
// LDK does not store any data for pending inbound payments. Instead, we construct our payment
2600+
// secret (and, if supplied by LDK, our payment preimage) to include encrypted metadata about the
2601+
// payment.
2602+
//
2603+
// The metadata is constructed as:
2604+
// payment method (4 bits) || payment amount (8 bytes - 4 bits) || expiry (8 bytes)
2605+
//
2606+
// Then on payment receipt, we verify in this method that the payment preimage and payment secret
2607+
// match what was constructed.
2608+
fn verify_inbound_payment_data(&self, payment_hash: PaymentHash, payment_data: msgs::FinalOnionHopData) -> Result<Option<PaymentPreimage>, ()> {
2609+
let (metadata_key, user_pmt_hash_key, ldk_pmt_hash_key) = hkdf_extract_expand(&vec![0], &self.inbound_payment_key);
2610+
let (iv_bytes, encrypted_metadata_bytes) = payment_data.payment_secret.0.split_at(16);
2611+
2612+
let chacha_block = ChaCha20::get_single_block(&metadata_key, iv_bytes);
2613+
let mut metadata_bytes: [u8; 16] = [0; 16];
2614+
for i in 0..16 {
2615+
metadata_bytes[i] = chacha_block[i] ^ encrypted_metadata_bytes[i];
2616+
}
2617+
2618+
// Make sure to check to check the HMAC before doing the other checks below, to mitigate DoS.
2619+
let is_user_payment_hash = metadata_bytes[0] & 1 << 4 != 0;
2620+
let mut payment_preimage = None;
2621+
if is_user_payment_hash {
2622+
let mut hmac = HmacEngine::<Sha256>::new(&user_pmt_hash_key);
2623+
hmac.input(&metadata_bytes[..]);
2624+
hmac.input(&payment_hash.0);
2625+
if !fixed_time_eq(iv_bytes, &Hmac::from_engine(hmac).into_inner().split_at_mut(16).0) {
2626+
log_trace!(self.logger, "Failing HTLC with user-generated payment_hash {}: unexpected payment_secret", log_bytes!(payment_hash.0));
2627+
return Err(())
2628+
}
2629+
} else {
2630+
let mut hmac = HmacEngine::<Sha256>::new(&ldk_pmt_hash_key);
2631+
hmac.input(iv_bytes);
2632+
hmac.input(&metadata_bytes);
2633+
let decoded_payment_preimage = Hmac::from_engine(hmac).into_inner();
2634+
if !fixed_time_eq(&payment_hash.0, &Sha256::hash(&decoded_payment_preimage).into_inner()) {
2635+
log_trace!(self.logger, "Failing HTLC with payment_hash {}: payment preimage {} did not match", log_bytes!(payment_hash.0), log_bytes!(decoded_payment_preimage));
2636+
return Err(())
2637+
}
2638+
payment_preimage = Some(PaymentPreimage(decoded_payment_preimage));
2639+
}
2640+
2641+
let payment_type: u8 = (metadata_bytes[0] & 0b1111_0000) >> 4;
2642+
if payment_type > 1 { // We only support payment method types of 0000 or 0001
2643+
log_trace!(self.logger, "Failing HTLC with payment hash {} due to unknown payment type {}", log_bytes!(payment_hash.0), payment_type);
2644+
return Err(());
2645+
}
2646+
2647+
let mut amt_msat_bytes = [0; 8];
2648+
amt_msat_bytes.copy_from_slice(&metadata_bytes[..8]);
2649+
// Zero out the bits reserved to indicate the payment type.
2650+
amt_msat_bytes[0] &= 0b00001111;
2651+
let min_amt_msat = u64::from_be_bytes(amt_msat_bytes.try_into().unwrap());
2652+
if payment_data.total_msat < min_amt_msat {
2653+
log_trace!(self.logger, "Failing HTLC with payment_hash {} due to total_msat {} being less than the minimum amount of {} msat", log_bytes!(payment_hash.0), payment_data.total_msat, min_amt_msat);
2654+
return Err(())
2655+
}
2656+
2657+
let expiry = u64::from_be_bytes(metadata_bytes[8..].try_into().unwrap());
2658+
if expiry < self.highest_seen_timestamp.load(Ordering::Acquire) as u64 {
2659+
log_trace!(self.logger, "Failing HTLC with payment_hash {}: expired payment", log_bytes!(payment_hash.0));
2660+
return Err(())
2661+
}
2662+
2663+
Ok(payment_preimage)
2664+
}
2665+
25922666
/// Processes HTLCs which are pending waiting on random forward delay.
25932667
///
25942668
/// Should only really ever be called in response to a PendingHTLCsForwardable event.
@@ -2864,9 +2938,17 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
28642938
match payment_secrets.entry(payment_hash) {
28652939
hash_map::Entry::Vacant(_) => {
28662940
match claimable_htlc.onion_payload {
2867-
OnionPayload::Invoice(_) => {
2868-
log_trace!(self.logger, "Failing new HTLC with payment_hash {} as we didn't have a corresponding inbound payment.", log_bytes!(payment_hash.0));
2869-
fail_htlc!(claimable_htlc);
2941+
OnionPayload::Invoice(ref payment_data) => {
2942+
let payment_preimage = match self.verify_inbound_payment_data(payment_hash, payment_data.clone()) {
2943+
Ok(payment_preimage) => payment_preimage,
2944+
Err(()) => {
2945+
fail_htlc!(claimable_htlc);
2946+
continue
2947+
}
2948+
};
2949+
let payment_data_total_msat = payment_data.total_msat;
2950+
let payment_secret = payment_data.payment_secret.clone();
2951+
check_total_value!(payment_data_total_msat, payment_secret, payment_preimage);
28702952
},
28712953
OnionPayload::Spontaneous(preimage) => {
28722954
match channel_state.claimable_htlcs.entry(payment_hash) {
@@ -4527,56 +4609,67 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
45274609
}
45284610
}
45294611

4530-
fn set_payment_hash_secret_map(&self, payment_hash: PaymentHash, payment_preimage: Option<PaymentPreimage>, min_value_msat: Option<u64>, invoice_expiry_delta_secs: u32) -> Result<PaymentSecret, APIError> {
4531-
assert!(invoice_expiry_delta_secs <= 60*60*24*365); // Sadly bitcoin timestamps are u32s, so panic before 2106
4532-
4533-
let payment_secret = PaymentSecret(self.keys_manager.get_secure_random_bytes());
4534-
4535-
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(&self.total_consistency_lock, &self.persistence_notifier);
4536-
let mut payment_secrets = self.pending_inbound_payments.lock().unwrap();
4537-
match payment_secrets.entry(payment_hash) {
4538-
hash_map::Entry::Vacant(e) => {
4539-
e.insert(PendingInboundPayment {
4540-
payment_secret, min_value_msat, payment_preimage,
4541-
user_payment_id: 0, // For compatibility with version 0.0.103 and earlier
4542-
// We assume that highest_seen_timestamp is pretty close to the current time -
4543-
// its updated when we receive a new block with the maximum time we've seen in
4544-
// a header. It should never be more than two hours in the future.
4545-
// Thus, we add two hours here as a buffer to ensure we absolutely
4546-
// never fail a payment too early.
4547-
// Note that we assume that received blocks have reasonably up-to-date
4548-
// timestamps.
4549-
expiry_time: self.highest_seen_timestamp.load(Ordering::Acquire) as u64 + invoice_expiry_delta_secs as u64 + 7200,
4550-
});
4551-
},
4552-
hash_map::Entry::Occupied(_) => return Err(APIError::APIMisuseError { err: "Duplicate payment hash".to_owned() }),
4553-
}
4554-
Ok(payment_secret)
4555-
}
4556-
45574612
/// Gets a payment secret and payment hash for use in an invoice given to a third party wishing
45584613
/// to pay us.
45594614
///
45604615
/// This differs from [`create_inbound_payment_for_hash`] only in that it generates the
4561-
/// [`PaymentHash`] and [`PaymentPreimage`] for you, returning the first and storing the second.
4616+
/// [`PaymentHash`] and [`PaymentPreimage`] for you.
45624617
///
45634618
/// The [`PaymentPreimage`] will ultimately be returned to you in the [`PaymentReceived`], which
45644619
/// will have the [`PaymentReceived::payment_preimage`] field filled in. That should then be
45654620
/// passed directly to [`claim_funds`].
45664621
///
45674622
/// See [`create_inbound_payment_for_hash`] for detailed documentation on behavior and requirements.
45684623
///
4624+
/// Note that a malicious eavesdropper could theoretically determine whether an inbound payment
4625+
/// was created by `create_inbound_payment` or `create_inbound_payment_for_hash`, by flipping the
4626+
/// relevant bit in the payment secret and observing the difference in processing time.
4627+
///
45694628
/// [`claim_funds`]: Self::claim_funds
45704629
/// [`PaymentReceived`]: events::Event::PaymentReceived
45714630
/// [`PaymentReceived::payment_preimage`]: events::Event::PaymentReceived::payment_preimage
45724631
/// [`create_inbound_payment_for_hash`]: Self::create_inbound_payment_for_hash
4632+
// For details on the implementation of this method, see `verify_inbound_payment_data`.
45734633
pub fn create_inbound_payment(&self, min_value_msat: Option<u64>, invoice_expiry_delta_secs: u32) -> (PaymentHash, PaymentSecret) {
4574-
let payment_preimage = PaymentPreimage(self.keys_manager.get_secure_random_bytes());
4575-
let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0).into_inner());
4634+
let (metadata_key, _, ldk_pmt_hash_key) = hkdf_extract_expand(&vec![0], &self.inbound_payment_key);
4635+
let min_amt_msat_bytes: [u8; 8] = match min_value_msat {
4636+
Some(amt) => amt.to_be_bytes(),
4637+
None => [0; 8],
4638+
};
4639+
// We assume that highest_seen_timestamp is pretty close to the current time - its updated when
4640+
// we receive a new block with the maximum time we've seen in a header. It should never be more
4641+
// than two hours in the future. Thus, we add two hours here as a buffer to ensure we
4642+
// absolutely never fail a payment too early.
4643+
// Note that we assume that received blocks have reasonably up-to-date timestamps.
4644+
let expiry_bytes = (self.highest_seen_timestamp.load(Ordering::Acquire) as u64 + invoice_expiry_delta_secs as u64 + 7200).to_be_bytes();
4645+
let mut metadata_bytes: [u8; 16] = [0; 16];
4646+
{
4647+
let (min_amt_msat_slice, expiry_slice) = metadata_bytes.split_at_mut(8);
4648+
min_amt_msat_slice.copy_from_slice(&min_amt_msat_bytes);
4649+
expiry_slice.copy_from_slice(&expiry_bytes);
4650+
}
4651+
4652+
let rand_bytes = self.keys_manager.get_secure_random_bytes();
4653+
let iv_bytes = &rand_bytes[..16];
4654+
4655+
let mut hmac = HmacEngine::<Sha256>::new(&ldk_pmt_hash_key);
4656+
hmac.input(iv_bytes);
4657+
hmac.input(&metadata_bytes);
4658+
let payment_preimage_bytes = Hmac::from_engine(hmac).into_inner();
45764659

4577-
(payment_hash,
4578-
self.set_payment_hash_secret_map(payment_hash, Some(payment_preimage), min_value_msat, invoice_expiry_delta_secs)
4579-
.expect("RNG Generated Duplicate PaymentHash"))
4660+
let mut payment_secret_bytes: [u8; 32] = [0; 32];
4661+
{
4662+
let (iv_slice, encrypted_metadata_slice) = payment_secret_bytes.split_at_mut(16);
4663+
iv_slice.copy_from_slice(iv_bytes);
4664+
4665+
let chacha_block = ChaCha20::get_single_block(&metadata_key, iv_bytes);
4666+
for i in 0..16 {
4667+
encrypted_metadata_slice[i] = chacha_block[i] ^ metadata_bytes[i];
4668+
}
4669+
}
4670+
4671+
let payment_hash = PaymentHash(Sha256::hash(&payment_preimage_bytes).into_inner());
4672+
(payment_hash, PaymentSecret(payment_secret_bytes))
45804673
}
45814674

45824675
/// Gets a [`PaymentSecret`] for a given [`PaymentHash`], for which the payment preimage is
@@ -4598,27 +4691,65 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
45984691
/// in excess of the current time. This should roughly match the expiry time set in the invoice.
45994692
/// After this many seconds, we will remove the inbound payment, resulting in any attempts to
46004693
/// pay the invoice failing. The BOLT spec suggests 3,600 secs as a default validity time for
4601-
/// invoices when no timeout is set.
4602-
///
4603-
/// Note that we use block header time to time-out pending inbound payments (with some margin
4604-
/// to compensate for the inaccuracy of block header timestamps). Thus, in practice we will
4605-
/// accept a payment and generate a [`PaymentReceived`] event for some time after the expiry.
4606-
/// If you need exact expiry semantics, you should enforce them upon receipt of
4607-
/// [`PaymentReceived`].
4608-
///
4609-
/// Pending inbound payments are stored in memory and in serialized versions of this
4610-
/// [`ChannelManager`]. If potentially unbounded numbers of inbound payments may exist and
4611-
/// space is limited, you may wish to rate-limit inbound payment creation.
4694+
/// invoices when no timeout is set. Note that we use block header time to time-out pending
4695+
/// inbound payments (with some margin to compensate for the inaccuracy of block header
4696+
/// timestamps). Thus, in practice we will accept a payment and generate a [`PaymentReceived`]
4697+
/// event for some time after the expiry. If you need exact expiry semantics, you should enforce
4698+
/// them upon receipt of [`PaymentReceived`].
46124699
///
46134700
/// May panic if `invoice_expiry_delta_secs` is greater than one year.
46144701
///
46154702
/// Note that invoices generated for inbound payments should have their `min_final_cltv_expiry`
46164703
/// set to at least [`MIN_FINAL_CLTV_EXPIRY`].
46174704
///
4705+
/// Note that a malicious eavesdropper could theoretically determine whether an inbound payment
4706+
/// was created by `create_inbound_payment` or `create_inbound_payment_for_hash`, by flipping the
4707+
/// relevant bit in the payment secret and observing the difference in processing time.
4708+
///
46184709
/// [`create_inbound_payment`]: Self::create_inbound_payment
46194710
/// [`PaymentReceived`]: events::Event::PaymentReceived
4711+
// For details on the implementation of this method, see `verify_inbound_payment_data`.
46204712
pub fn create_inbound_payment_for_hash(&self, payment_hash: PaymentHash, min_value_msat: Option<u64>, invoice_expiry_delta_secs: u32) -> Result<PaymentSecret, APIError> {
4621-
self.set_payment_hash_secret_map(payment_hash, None, min_value_msat, invoice_expiry_delta_secs)
4713+
let (metadata_key, user_pmt_hash_key, _) = hkdf_extract_expand(&vec![0], &self.inbound_payment_key);
4714+
let mut min_amt_msat_bytes: [u8; 8] = match min_value_msat {
4715+
Some(amt) => amt.to_be_bytes(),
4716+
None => [0; 8],
4717+
};
4718+
// Set the payment method bits (the top 4 bits of the metadata bytes) to 0001
4719+
// indicating the payment type is paying a user-generated payment hash.
4720+
min_amt_msat_bytes[0] |= 1 << 4;
4721+
4722+
// We assume that highest_seen_timestamp is pretty close to the current time - its updated when
4723+
// we receive a new block with the maximum time we've seen in a header. It should never be more
4724+
// than two hours in the future. Thus, we add two hours here as a buffer to ensure we
4725+
// absolutely never fail a payment too early.
4726+
// Note that we assume that received blocks have reasonably up-to-date timestamps.
4727+
let expiry_bytes = (self.highest_seen_timestamp.load(Ordering::Acquire) as u64 + invoice_expiry_delta_secs as u64 + 7200).to_be_bytes();
4728+
4729+
let mut metadata_bytes: [u8; 16] = [0; 16];
4730+
{
4731+
let (min_amt_msat_slice, expiry_slice) = metadata_bytes.split_at_mut(8);
4732+
min_amt_msat_slice.copy_from_slice(&min_amt_msat_bytes);
4733+
expiry_slice.copy_from_slice(&expiry_bytes);
4734+
}
4735+
4736+
let mut hmac = HmacEngine::<Sha256>::new(&user_pmt_hash_key);
4737+
hmac.input(&metadata_bytes);
4738+
hmac.input(&payment_hash.0);
4739+
let hmac_bytes = Hmac::from_engine(hmac).into_inner();
4740+
let iv_bytes = &hmac_bytes[..16];
4741+
4742+
let mut payment_secret_bytes: [u8; 32] = [0; 32];
4743+
{
4744+
let (iv_slice, encrypted_metadata_slice) = payment_secret_bytes.split_at_mut(16);
4745+
iv_slice.copy_from_slice(iv_bytes);
4746+
4747+
let chacha_block = ChaCha20::get_single_block(&metadata_key, iv_bytes);
4748+
for i in 0..16 {
4749+
encrypted_metadata_slice[i] = chacha_block[i] ^ metadata_bytes[i];
4750+
}
4751+
}
4752+
Ok(PaymentSecret(payment_secret_bytes))
46224753
}
46234754

46244755
#[cfg(any(test, feature = "fuzztarget", feature = "_test_utils"))]
@@ -5690,6 +5821,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> Writeable f
56905821
}
56915822
write_tlv_fields!(writer, {
56925823
(1, pending_outbound_payments_no_retry, required),
5824+
(2, self.inbound_payment_key, required),
56935825
(3, pending_outbound_payments, required),
56945826
});
56955827

@@ -5985,10 +6117,15 @@ impl<'a, Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>
59856117
// pending_outbound_payments_no_retry is for compatibility with 0.0.101 clients.
59866118
let mut pending_outbound_payments_no_retry: Option<HashMap<PaymentId, HashSet<[u8; 32]>>> = None;
59876119
let mut pending_outbound_payments = None;
6120+
let mut inbound_payment_key = None;
59886121
read_tlv_fields!(reader, {
59896122
(1, pending_outbound_payments_no_retry, option),
6123+
(2, inbound_payment_key, option),
59906124
(3, pending_outbound_payments, option),
59916125
});
6126+
if inbound_payment_key.is_none() {
6127+
inbound_payment_key = Some(args.keys_manager.get_inbound_payment_secret());
6128+
}
59926129
if pending_outbound_payments.is_none() && pending_outbound_payments_no_retry.is_none() {
59936130
pending_outbound_payments = Some(pending_outbound_payments_compat);
59946131
} else if pending_outbound_payments.is_none() {
@@ -6066,6 +6203,7 @@ impl<'a, Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>
60666203
claimable_htlcs,
60676204
pending_msg_events: Vec::new(),
60686205
}),
6206+
inbound_payment_key: inbound_payment_key.unwrap(),
60696207
pending_inbound_payments: Mutex::new(pending_inbound_payments),
60706208
pending_outbound_payments: Mutex::new(pending_outbound_payments.unwrap()),
60716209

@@ -6099,6 +6237,27 @@ impl<'a, Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>
60996237
}
61006238
}
61016239

6240+
fn hkdf_extract_expand(salt: &[u8], ikm: &[u8]) -> ([u8; 32], [u8; 32], [u8; 32]) {
6241+
let mut hmac = HmacEngine::<Sha256>::new(salt);
6242+
hmac.input(ikm);
6243+
let prk = Hmac::from_engine(hmac).into_inner();
6244+
let mut hmac = HmacEngine::<Sha256>::new(&prk[..]);
6245+
hmac.input(&[1; 1]);
6246+
let t1 = Hmac::from_engine(hmac).into_inner();
6247+
6248+
let mut hmac = HmacEngine::<Sha256>::new(&prk[..]);
6249+
hmac.input(&t1);
6250+
hmac.input(&[2; 1]);
6251+
let t2 = Hmac::from_engine(hmac).into_inner();
6252+
6253+
let mut hmac = HmacEngine::<Sha256>::new(&prk[..]);
6254+
hmac.input(&t2);
6255+
hmac.input(&[3; 1]);
6256+
let t3 = Hmac::from_engine(hmac).into_inner();
6257+
6258+
(t1, t2, t3)
6259+
}
6260+
61026261
#[cfg(test)]
61036262
mod tests {
61046263
use bitcoin::hashes::Hash;

0 commit comments

Comments
 (0)