Skip to content

Commit 1c157a2

Browse files
authored
Merge pull request #1216 from valentinewallace/2021-12-preimage-getter
Add utility for retrieving an LDK payment preimage
2 parents 86d5944 + 41cfd83 commit 1c157a2

File tree

2 files changed

+98
-21
lines changed

2 files changed

+98
-21
lines changed

lightning/src/ln/channelmanager.rs

+65-20
Original file line numberDiff line numberDiff line change
@@ -73,12 +73,14 @@ use core::ops::Deref;
7373
use std::time::Instant;
7474

7575
mod inbound_payment {
76+
use alloc::string::ToString;
7677
use bitcoin::hashes::{Hash, HashEngine};
7778
use bitcoin::hashes::cmp::fixed_time_eq;
7879
use bitcoin::hashes::hmac::{Hmac, HmacEngine};
7980
use bitcoin::hashes::sha256::Hash as Sha256;
8081
use chain::keysinterface::{KeyMaterial, KeysInterface, Sign};
8182
use ln::{PaymentHash, PaymentPreimage, PaymentSecret};
83+
use ln::channelmanager::APIError;
8284
use ln::msgs;
8385
use ln::msgs::MAX_VALUE_MSAT;
8486
use util::chacha20::ChaCha20;
@@ -113,7 +115,7 @@ mod inbound_payment {
113115

114116
impl ExpandedKey {
115117
pub(super) fn new(key_material: &KeyMaterial) -> ExpandedKey {
116-
hkdf_extract_expand(&vec![0], &key_material)
118+
hkdf_extract_expand(b"LDK Inbound Payment Key Expansion", &key_material)
117119
}
118120
}
119121

@@ -237,15 +239,7 @@ mod inbound_payment {
237239
pub(super) fn verify<L: Deref>(payment_hash: PaymentHash, payment_data: msgs::FinalOnionHopData, highest_seen_timestamp: u64, keys: &ExpandedKey, logger: &L) -> Result<Option<PaymentPreimage>, ()>
238240
where L::Target: Logger
239241
{
240-
let mut iv_bytes = [0; IV_LEN];
241-
let (iv_slice, encrypted_metadata_bytes) = payment_data.payment_secret.0.split_at(IV_LEN);
242-
iv_bytes.copy_from_slice(iv_slice);
243-
244-
let chacha_block = ChaCha20::get_single_block(&keys.metadata_key, &iv_bytes);
245-
let mut metadata_bytes: [u8; METADATA_LEN] = [0; METADATA_LEN];
246-
for i in 0..METADATA_LEN {
247-
metadata_bytes[i] = chacha_block[i] ^ encrypted_metadata_bytes[i];
248-
}
242+
let (iv_bytes, metadata_bytes) = decrypt_metadata(payment_data.payment_secret, keys);
249243

250244
let payment_type_res = Method::from_bits((metadata_bytes[0] & 0b1110_0000) >> METHOD_TYPE_OFFSET);
251245
let mut amt_msat_bytes = [0; AMT_MSAT_LEN];
@@ -269,15 +263,13 @@ mod inbound_payment {
269263
}
270264
},
271265
Ok(Method::LdkPaymentHash) => {
272-
let mut hmac = HmacEngine::<Sha256>::new(&keys.ldk_pmt_hash_key);
273-
hmac.input(&iv_bytes);
274-
hmac.input(&metadata_bytes);
275-
let decoded_payment_preimage = Hmac::from_engine(hmac).into_inner();
276-
if !fixed_time_eq(&payment_hash.0, &Sha256::hash(&decoded_payment_preimage).into_inner()) {
277-
log_trace!(logger, "Failing HTLC with payment_hash {}: payment preimage {} did not match", log_bytes!(payment_hash.0), log_bytes!(decoded_payment_preimage));
278-
return Err(())
266+
match derive_ldk_payment_preimage(payment_hash, &iv_bytes, &metadata_bytes, keys) {
267+
Ok(preimage) => payment_preimage = Some(preimage),
268+
Err(bad_preimage_bytes) => {
269+
log_trace!(logger, "Failing HTLC with payment_hash {} due to mismatching preimage {}", log_bytes!(payment_hash.0), log_bytes!(bad_preimage_bytes));
270+
return Err(())
271+
}
279272
}
280-
payment_preimage = Some(PaymentPreimage(decoded_payment_preimage));
281273
},
282274
Err(unknown_bits) => {
283275
log_trace!(logger, "Failing HTLC with payment hash {} due to unknown payment type {}", log_bytes!(payment_hash.0), unknown_bits);
@@ -298,6 +290,50 @@ mod inbound_payment {
298290
Ok(payment_preimage)
299291
}
300292

293+
pub(super) fn get_payment_preimage(payment_hash: PaymentHash, payment_secret: PaymentSecret, keys: &ExpandedKey) -> Result<PaymentPreimage, APIError> {
294+
let (iv_bytes, metadata_bytes) = decrypt_metadata(payment_secret, keys);
295+
296+
match Method::from_bits((metadata_bytes[0] & 0b1110_0000) >> METHOD_TYPE_OFFSET) {
297+
Ok(Method::LdkPaymentHash) => {
298+
derive_ldk_payment_preimage(payment_hash, &iv_bytes, &metadata_bytes, keys)
299+
.map_err(|bad_preimage_bytes| APIError::APIMisuseError {
300+
err: format!("Payment hash {} did not match decoded preimage {}", log_bytes!(payment_hash.0), log_bytes!(bad_preimage_bytes))
301+
})
302+
},
303+
Ok(Method::UserPaymentHash) => Err(APIError::APIMisuseError {
304+
err: "Expected payment type to be LdkPaymentHash, instead got UserPaymentHash".to_string()
305+
}),
306+
Err(other) => Err(APIError::APIMisuseError { err: format!("Unknown payment type: {}", other) }),
307+
}
308+
}
309+
310+
fn decrypt_metadata(payment_secret: PaymentSecret, keys: &ExpandedKey) -> ([u8; IV_LEN], [u8; METADATA_LEN]) {
311+
let mut iv_bytes = [0; IV_LEN];
312+
let (iv_slice, encrypted_metadata_bytes) = payment_secret.0.split_at(IV_LEN);
313+
iv_bytes.copy_from_slice(iv_slice);
314+
315+
let chacha_block = ChaCha20::get_single_block(&keys.metadata_key, &iv_bytes);
316+
let mut metadata_bytes: [u8; METADATA_LEN] = [0; METADATA_LEN];
317+
for i in 0..METADATA_LEN {
318+
metadata_bytes[i] = chacha_block[i] ^ encrypted_metadata_bytes[i];
319+
}
320+
321+
(iv_bytes, metadata_bytes)
322+
}
323+
324+
// Errors if the payment preimage doesn't match `payment_hash`. Returns the bad preimage bytes in
325+
// this case.
326+
fn derive_ldk_payment_preimage(payment_hash: PaymentHash, iv_bytes: &[u8; IV_LEN], metadata_bytes: &[u8; METADATA_LEN], keys: &ExpandedKey) -> Result<PaymentPreimage, [u8; 32]> {
327+
let mut hmac = HmacEngine::<Sha256>::new(&keys.ldk_pmt_hash_key);
328+
hmac.input(iv_bytes);
329+
hmac.input(metadata_bytes);
330+
let decoded_payment_preimage = Hmac::from_engine(hmac).into_inner();
331+
if !fixed_time_eq(&payment_hash.0, &Sha256::hash(&decoded_payment_preimage).into_inner()) {
332+
return Err(decoded_payment_preimage);
333+
}
334+
return Ok(PaymentPreimage(decoded_payment_preimage))
335+
}
336+
301337
fn hkdf_extract_expand(salt: &[u8], ikm: &KeyMaterial) -> ExpandedKey {
302338
let mut hmac = HmacEngine::<Sha256>::new(salt);
303339
hmac.input(&ikm.0);
@@ -5026,8 +5062,9 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
50265062
/// payment secret fetched via this method or [`create_inbound_payment`], and which is at least
50275063
/// the `min_value_msat` provided here, if one is provided.
50285064
///
5029-
/// The [`PaymentHash`] (and corresponding [`PaymentPreimage`]) must be globally unique. This
5030-
/// method may return an Err if another payment with the same payment_hash is still pending.
5065+
/// The [`PaymentHash`] (and corresponding [`PaymentPreimage`]) should be globally unique, though
5066+
/// note that LDK will not stop you from registering duplicate payment hashes for inbound
5067+
/// payments.
50315068
///
50325069
/// `min_value_msat` should be set if the invoice being generated contains a value. Any payment
50335070
/// received for the returned [`PaymentHash`] will be required to be at least `min_value_msat`
@@ -5079,6 +5116,14 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
50795116
self.set_payment_hash_secret_map(payment_hash, None, min_value_msat, invoice_expiry_delta_secs)
50805117
}
50815118

5119+
/// Gets an LDK-generated payment preimage from a payment hash and payment secret that were
5120+
/// previously returned from [`create_inbound_payment`].
5121+
///
5122+
/// [`create_inbound_payment`]: Self::create_inbound_payment
5123+
pub fn get_payment_preimage(&self, payment_hash: PaymentHash, payment_secret: PaymentSecret) -> Result<PaymentPreimage, APIError> {
5124+
inbound_payment::get_payment_preimage(payment_hash, payment_secret, &self.inbound_payment_key)
5125+
}
5126+
50825127
#[cfg(any(test, feature = "fuzztarget", feature = "_test_utils"))]
50835128
pub fn get_and_clear_pending_events(&self) -> Vec<events::Event> {
50845129
let events = core::cell::RefCell::new(Vec::new());

lightning/src/ln/payment_tests.rs

+33-1
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,10 @@ use chain::{ChannelMonitorUpdateErr, Confirm, Listen, Watch};
1515
use chain::channelmonitor::{ANTI_REORG_DELAY, ChannelMonitor, LATENCY_GRACE_PERIOD_BLOCKS};
1616
use chain::transaction::OutPoint;
1717
use ln::channelmanager::{BREAKDOWN_TIMEOUT, ChannelManager, ChannelManagerReadArgs, PaymentId, PaymentSendFailure};
18-
use ln::features::InitFeatures;
18+
use ln::features::{InitFeatures, InvoiceFeatures};
1919
use ln::msgs;
2020
use ln::msgs::ChannelMessageHandler;
21+
use routing::router::{Payee, get_route};
2122
use util::events::{ClosureReason, Event, MessageSendEvent, MessageSendEventsProvider};
2223
use util::test_utils;
2324
use util::errors::APIError;
@@ -706,3 +707,34 @@ fn test_fulfill_restart_failure() {
706707
// it had already considered the payment fulfilled, and now they just got free money.
707708
assert!(nodes[0].node.get_and_clear_pending_events().is_empty());
708709
}
710+
711+
#[test]
712+
fn get_ldk_payment_preimage() {
713+
// Ensure that `ChannelManager::get_payment_preimage` can successfully be used to claim a payment.
714+
let chanmon_cfgs = create_chanmon_cfgs(2);
715+
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
716+
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
717+
let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs);
718+
create_announced_chan_between_nodes(&nodes, 0, 1, InitFeatures::known(), InitFeatures::known());
719+
720+
let amt_msat = 60_000;
721+
let expiry_secs = 60 * 60;
722+
let (payment_hash, payment_secret) = nodes[1].node.create_inbound_payment(Some(amt_msat), expiry_secs).unwrap();
723+
724+
let payee = Payee::from_node_id(nodes[1].node.get_our_node_id())
725+
.with_features(InvoiceFeatures::known());
726+
let scorer = test_utils::TestScorer::with_fixed_penalty(0);
727+
let route = get_route(
728+
&nodes[0].node.get_our_node_id(), &payee, &nodes[0].network_graph,
729+
Some(&nodes[0].node.list_usable_channels().iter().collect::<Vec<_>>()),
730+
amt_msat, TEST_FINAL_CLTV, nodes[0].logger, &scorer).unwrap();
731+
let _payment_id = nodes[0].node.send_payment(&route, payment_hash, &Some(payment_secret)).unwrap();
732+
check_added_monitors!(nodes[0], 1);
733+
734+
// Make sure to use `get_payment_preimage`
735+
let payment_preimage = nodes[1].node.get_payment_preimage(payment_hash, payment_secret).unwrap();
736+
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
737+
assert_eq!(events.len(), 1);
738+
pass_along_path(&nodes[0], &[&nodes[1]], amt_msat, payment_hash, Some(payment_secret), events.pop().unwrap(), true, Some(payment_preimage));
739+
claim_payment_along_route(&nodes[0], &[&[&nodes[1]]], false, payment_preimage);
740+
}

0 commit comments

Comments
 (0)