Skip to content

Commit 0c67753

Browse files
authored
Merge pull request #2752 from valentinewallace/2023-11-large-final-onion-payload-fixes
Large final onion payload fixes
2 parents e94af0c + 80ba9ac commit 0c67753

File tree

3 files changed

+112
-16
lines changed

3 files changed

+112
-16
lines changed

lightning/src/ln/onion_payment.rs

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use crate::prelude::*;
2323
use core::ops::Deref;
2424

2525
/// Invalid inbound onion payment.
26+
#[derive(Debug)]
2627
pub struct InboundOnionErr {
2728
/// BOLT 4 error code.
2829
pub err_code: u16,
@@ -448,7 +449,7 @@ pub(super) fn check_incoming_htlc_cltv(
448449
mod tests {
449450
use bitcoin::hashes::Hash;
450451
use bitcoin::hashes::sha256::Hash as Sha256;
451-
use bitcoin::secp256k1::{PublicKey, SecretKey};
452+
use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey};
452453
use crate::ln::{PaymentPreimage, PaymentHash, PaymentSecret};
453454
use crate::ln::ChannelId;
454455
use crate::ln::channelmanager::RecipientOnionFields;
@@ -458,6 +459,38 @@ mod tests {
458459
use crate::routing::router::{Path, RouteHop};
459460
use crate::util::test_utils;
460461

462+
#[test]
463+
fn fail_construct_onion_on_too_big_payloads() {
464+
// Ensure that if we call `construct_onion_packet` and friends where payloads are too large for
465+
// the allotted packet length, we'll fail to construct. Previously, senders would happily
466+
// construct invalid packets by array-shifting the final node's HMAC out of the packet when
467+
// adding an intermediate onion layer, causing the receiver to error with "final payload
468+
// provided for us as an intermediate node."
469+
let secp_ctx = Secp256k1::new();
470+
let bob = crate::sign::KeysManager::new(&[2; 32], 42, 42);
471+
let bob_pk = PublicKey::from_secret_key(&secp_ctx, &bob.get_node_secret_key());
472+
let charlie = crate::sign::KeysManager::new(&[3; 32], 42, 42);
473+
let charlie_pk = PublicKey::from_secret_key(&secp_ctx, &charlie.get_node_secret_key());
474+
475+
let (
476+
session_priv, total_amt_msat, cur_height, mut recipient_onion, keysend_preimage, payment_hash,
477+
prng_seed, hops, ..
478+
) = payment_onion_args(bob_pk, charlie_pk);
479+
480+
// Ensure the onion will not fit all the payloads by adding a large custom TLV.
481+
recipient_onion.custom_tlvs.push((13377331, vec![0; 1156]));
482+
483+
let path = Path { hops, blinded_tail: None, };
484+
let onion_keys = super::onion_utils::construct_onion_keys(&secp_ctx, &path, &session_priv).unwrap();
485+
let (onion_payloads, ..) = super::onion_utils::build_onion_payloads(
486+
&path, total_amt_msat, recipient_onion, cur_height + 1, &Some(keysend_preimage)
487+
).unwrap();
488+
489+
assert!(super::onion_utils::construct_onion_packet(
490+
onion_payloads, onion_keys, prng_seed, &payment_hash
491+
).is_err());
492+
}
493+
461494
#[test]
462495
fn test_peel_payment_onion() {
463496
use super::*;

lightning/src/ln/onion_utils.rs

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -323,8 +323,6 @@ fn construct_onion_packet_with_init_noise<HD: Writeable, P: Packet>(
323323

324324
let mut pos = 0;
325325
for (i, (payload, keys)) in payloads.iter().zip(onion_keys.iter()).enumerate() {
326-
if i == payloads.len() - 1 { break; }
327-
328326
let mut chacha = ChaCha20::new(&keys.rho, &[0u8; 8]);
329327
for _ in 0..(packet_data.len() - pos) { // TODO: Batch this.
330328
let mut dummy = [0; 1];
@@ -338,6 +336,8 @@ fn construct_onion_packet_with_init_noise<HD: Writeable, P: Packet>(
338336
return Err(());
339337
}
340338

339+
if i == payloads.len() - 1 { break; }
340+
341341
res.resize(pos, 0u8);
342342
chacha.process_in_place(&mut res);
343343
}
@@ -1021,18 +1021,20 @@ fn decode_next_hop<T, R: ReadableArgs<T>, N: NextPacketBytes>(shared_secret: [u8
10211021
if hmac == [0; 32] {
10221022
#[cfg(test)]
10231023
{
1024-
// In tests, make sure that the initial onion packet data is, at least, non-0.
1025-
// We could do some fancy randomness test here, but, ehh, whatever.
1026-
// This checks for the issue where you can calculate the path length given the
1027-
// onion data as all the path entries that the originator sent will be here
1028-
// as-is (and were originally 0s).
1029-
// Of course reverse path calculation is still pretty easy given naive routing
1030-
// algorithms, but this fixes the most-obvious case.
1031-
let mut next_bytes = [0; 32];
1032-
chacha_stream.read_exact(&mut next_bytes).unwrap();
1033-
assert_ne!(next_bytes[..], [0; 32][..]);
1034-
chacha_stream.read_exact(&mut next_bytes).unwrap();
1035-
assert_ne!(next_bytes[..], [0; 32][..]);
1024+
if chacha_stream.read.position() < hop_data.len() as u64 - 64 {
1025+
// In tests, make sure that the initial onion packet data is, at least, non-0.
1026+
// We could do some fancy randomness test here, but, ehh, whatever.
1027+
// This checks for the issue where you can calculate the path length given the
1028+
// onion data as all the path entries that the originator sent will be here
1029+
// as-is (and were originally 0s).
1030+
// Of course reverse path calculation is still pretty easy given naive routing
1031+
// algorithms, but this fixes the most-obvious case.
1032+
let mut next_bytes = [0; 32];
1033+
chacha_stream.read_exact(&mut next_bytes).unwrap();
1034+
assert_ne!(next_bytes[..], [0; 32][..]);
1035+
chacha_stream.read_exact(&mut next_bytes).unwrap();
1036+
assert_ne!(next_bytes[..], [0; 32][..]);
1037+
}
10361038
}
10371039
return Ok((msg, None)); // We are the final destination for this packet
10381040
} else {

lightning/src/ln/payment_tests.rs

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,9 @@ use crate::events::{ClosureReason, Event, HTLCDestination, MessageSendEvent, Mes
1919
use crate::ln::channel::{EXPIRE_PREV_CONFIG_TICKS, commit_tx_fee_msat, get_holder_selected_channel_reserve_satoshis, ANCHOR_OUTPUT_VALUE_SATOSHI};
2020
use crate::ln::channelmanager::{BREAKDOWN_TIMEOUT, MPP_TIMEOUT_TICKS, MIN_CLTV_EXPIRY_DELTA, PaymentId, PaymentSendFailure, RecentPaymentDetails, RecipientOnionFields, HTLCForwardInfo, PendingHTLCRouting, PendingAddHTLCInfo};
2121
use crate::ln::features::{Bolt11InvoiceFeatures, ChannelTypeFeatures};
22-
use crate::ln::{msgs, ChannelId, PaymentSecret, PaymentPreimage};
22+
use crate::ln::{msgs, ChannelId, PaymentHash, PaymentSecret, PaymentPreimage};
2323
use crate::ln::msgs::ChannelMessageHandler;
24+
use crate::ln::onion_utils;
2425
use crate::ln::outbound_payment::{IDEMPOTENCY_TIMEOUT_TICKS, Retry};
2526
use crate::routing::gossip::{EffectiveCapacity, RoutingFees};
2627
use crate::routing::router::{get_route, Path, PaymentParameters, Route, Router, RouteHint, RouteHintHop, RouteHop, RouteParameters, find_route};
@@ -31,10 +32,14 @@ use crate::util::errors::APIError;
3132
use crate::util::ser::Writeable;
3233
use crate::util::string::UntrustedString;
3334

35+
use bitcoin::hashes::Hash;
36+
use bitcoin::hashes::sha256::Hash as Sha256;
3437
use bitcoin::network::constants::Network;
38+
use bitcoin::secp256k1::{Secp256k1, SecretKey};
3539

3640
use crate::prelude::*;
3741

42+
use crate::ln::functional_test_utils;
3843
use crate::ln::functional_test_utils::*;
3944
use crate::routing::gossip::NodeId;
4045
#[cfg(feature = "std")]
@@ -4194,3 +4199,59 @@ fn test_htlc_forward_considers_anchor_outputs_value() {
41944199
check_closed_broadcast(&nodes[2], 1, true);
41954200
check_added_monitors(&nodes[2], 1);
41964201
}
4202+
4203+
#[test]
4204+
fn peel_payment_onion_custom_tlvs() {
4205+
let chanmon_cfgs = create_chanmon_cfgs(2);
4206+
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
4207+
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
4208+
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
4209+
create_announced_chan_between_nodes(&nodes, 0, 1);
4210+
let secp_ctx = Secp256k1::new();
4211+
4212+
let amt_msat = 1000;
4213+
let payment_params = PaymentParameters::for_keysend(nodes[1].node.get_our_node_id(),
4214+
TEST_FINAL_CLTV, false);
4215+
let route_params = RouteParameters::from_payment_params_and_value(payment_params, amt_msat);
4216+
let route = functional_test_utils::get_route(&nodes[0], &route_params).unwrap();
4217+
let mut recipient_onion = RecipientOnionFields::spontaneous_empty()
4218+
.with_custom_tlvs(vec![(414141, vec![42; 1200])]).unwrap();
4219+
let prng_seed = chanmon_cfgs[0].keys_manager.get_secure_random_bytes();
4220+
let session_priv = SecretKey::from_slice(&prng_seed[..]).expect("RNG is busted");
4221+
let keysend_preimage = PaymentPreimage([42; 32]);
4222+
let payment_hash = PaymentHash(Sha256::hash(&keysend_preimage.0).to_byte_array());
4223+
4224+
let (onion_routing_packet, first_hop_msat, cltv_expiry) = onion_utils::create_payment_onion(
4225+
&secp_ctx, &route.paths[0], &session_priv, amt_msat, recipient_onion.clone(),
4226+
nodes[0].best_block_info().1, &payment_hash, &Some(keysend_preimage), prng_seed
4227+
).unwrap();
4228+
4229+
let update_add = msgs::UpdateAddHTLC {
4230+
channel_id: ChannelId([0; 32]),
4231+
htlc_id: 42,
4232+
amount_msat: first_hop_msat,
4233+
payment_hash,
4234+
cltv_expiry,
4235+
skimmed_fee_msat: None,
4236+
onion_routing_packet,
4237+
blinding_point: None,
4238+
};
4239+
let peeled_onion = crate::ln::onion_payment::peel_payment_onion(
4240+
&update_add, &&chanmon_cfgs[1].keys_manager, &&chanmon_cfgs[1].logger, &secp_ctx,
4241+
nodes[1].best_block_info().1, true, false
4242+
).unwrap();
4243+
assert_eq!(peeled_onion.incoming_amt_msat, Some(amt_msat));
4244+
match peeled_onion.routing {
4245+
PendingHTLCRouting::ReceiveKeysend {
4246+
payment_data, payment_metadata, custom_tlvs, ..
4247+
} => {
4248+
#[cfg(not(c_bindings))]
4249+
assert_eq!(&custom_tlvs, recipient_onion.custom_tlvs());
4250+
#[cfg(c_bindings)]
4251+
assert_eq!(custom_tlvs, recipient_onion.custom_tlvs());
4252+
assert!(payment_metadata.is_none());
4253+
assert!(payment_data.is_none());
4254+
},
4255+
_ => panic!()
4256+
}
4257+
}

0 commit comments

Comments
 (0)