Skip to content

Commit d44fca2

Browse files
Fix debug panic in onion utils on large custom TLVs or metadata.
We previously assumed that the final node's payload would be ~93 bytes, and had code to ensure that the filler encoded after that payload is not all 0s. Now with custom TLVs and metadata supported, the final node's payload may take up the entire onion packet, so we can't assume that there are 64 bytes of filler to check.
1 parent f07f4b9 commit d44fca2

File tree

3 files changed

+76
-13
lines changed

3 files changed

+76
-13
lines changed

lightning/src/ln/onion_payment.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use crate::prelude::*;
1919
use core::ops::Deref;
2020

2121
/// Invalid inbound onion payment.
22+
#[derive(Debug)]
2223
pub struct InboundOnionErr {
2324
/// BOLT 4 error code.
2425
pub err_code: u16,

lightning/src/ln/onion_utils.rs

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1001,18 +1001,20 @@ fn decode_next_hop<T, R: ReadableArgs<T>, N: NextPacketBytes>(shared_secret: [u8
10011001
if hmac == [0; 32] {
10021002
#[cfg(test)]
10031003
{
1004-
// In tests, make sure that the initial onion packet data is, at least, non-0.
1005-
// We could do some fancy randomness test here, but, ehh, whatever.
1006-
// This checks for the issue where you can calculate the path length given the
1007-
// onion data as all the path entries that the originator sent will be here
1008-
// as-is (and were originally 0s).
1009-
// Of course reverse path calculation is still pretty easy given naive routing
1010-
// algorithms, but this fixes the most-obvious case.
1011-
let mut next_bytes = [0; 32];
1012-
chacha_stream.read_exact(&mut next_bytes).unwrap();
1013-
assert_ne!(next_bytes[..], [0; 32][..]);
1014-
chacha_stream.read_exact(&mut next_bytes).unwrap();
1015-
assert_ne!(next_bytes[..], [0; 32][..]);
1004+
if chacha_stream.read.position() < hop_data.len() as u64 - 64 {
1005+
// In tests, make sure that the initial onion packet data is, at least, non-0.
1006+
// We could do some fancy randomness test here, but, ehh, whatever.
1007+
// This checks for the issue where you can calculate the path length given the
1008+
// onion data as all the path entries that the originator sent will be here
1009+
// as-is (and were originally 0s).
1010+
// Of course reverse path calculation is still pretty easy given naive routing
1011+
// algorithms, but this fixes the most-obvious case.
1012+
let mut next_bytes = [0; 32];
1013+
chacha_stream.read_exact(&mut next_bytes).unwrap();
1014+
assert_ne!(next_bytes[..], [0; 32][..]);
1015+
chacha_stream.read_exact(&mut next_bytes).unwrap();
1016+
assert_ne!(next_bytes[..], [0; 32][..]);
1017+
}
10161018
}
10171019
return Ok((msg, None)); // We are the final destination for this packet
10181020
} else {

lightning/src/ln/payment_tests.rs

Lines changed: 61 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,58 @@ 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+
};
4238+
let peeled_onion = crate::ln::onion_payment::peel_payment_onion(
4239+
&update_add, &&chanmon_cfgs[1].keys_manager, &&chanmon_cfgs[1].logger, &secp_ctx,
4240+
nodes[1].best_block_info().1, true
4241+
).unwrap();
4242+
assert_eq!(peeled_onion.incoming_amt_msat, Some(amt_msat));
4243+
match peeled_onion.routing {
4244+
PendingHTLCRouting::ReceiveKeysend {
4245+
payment_data, payment_metadata, custom_tlvs, ..
4246+
} => {
4247+
#[cfg(not(c_bindings))]
4248+
assert_eq!(&custom_tlvs, recipient_onion.custom_tlvs());
4249+
#[cfg(c_bindings)]
4250+
assert_eq!(custom_tlvs, recipient_onion.custom_tlvs());
4251+
assert!(payment_metadata.is_none());
4252+
assert!(payment_data.is_none());
4253+
},
4254+
_ => panic!()
4255+
}
4256+
}

0 commit comments

Comments
 (0)