Skip to content

Commit f8ffc1a

Browse files
committed
Introduce test for Padding
Add test to verify blinded message and payment path padding.
1 parent 5d6881f commit f8ffc1a

File tree

3 files changed

+114
-2
lines changed

3 files changed

+114
-2
lines changed

lightning/src/blinded_path/utils.rs

+15
Original file line numberDiff line numberDiff line change
@@ -258,3 +258,18 @@ impl<T: Writeable> Writeable for BlindedPathWithPadding<T> {
258258
self.tlvs.write(writer)
259259
}
260260
}
261+
262+
#[cfg(test)]
263+
/// Checks if all the packets in the blinded path are properly padded.
264+
pub fn is_padded(hops: &[BlindedHop], padding_round_off: usize) -> bool {
265+
let first_hop = hops.first().expect("BlindedPath must have at least one hop");
266+
let first_payload_size = first_hop.encrypted_payload.len();
267+
268+
// The unencrypted payload data is padded before getting encrypted.
269+
// Assuming the first payload is padded properly, get the extra data length.
270+
let extra_length = first_payload_size % padding_round_off;
271+
hops.iter().all(|hop| {
272+
// Check that every packet is padded to the round off length subtracting the extra length.
273+
(hop.encrypted_payload.len() - extra_length) % padding_round_off == 0
274+
})
275+
}

lightning/src/ln/blinded_payment_tests.rs

+38-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ use bitcoin::secp256k1::{PublicKey, Scalar, Secp256k1, SecretKey, schnorr};
1313
use bitcoin::secp256k1::ecdh::SharedSecret;
1414
use bitcoin::secp256k1::ecdsa::{RecoverableSignature, Signature};
1515
use crate::blinded_path;
16-
use crate::blinded_path::payment::{BlindedPaymentPath, Bolt12RefundContext, PaymentForwardNode, ForwardTlvs, PaymentConstraints, PaymentContext, PaymentRelay, UnauthenticatedReceiveTlvs};
16+
use crate::blinded_path::payment::{BlindedPaymentPath, Bolt12RefundContext, ForwardTlvs, PaymentConstraints, PaymentContext, PaymentForwardNode, PaymentRelay, UnauthenticatedReceiveTlvs, PAYMENT_PADDING_ROUND_OFF};
17+
use crate::blinded_path::utils::is_padded;
1718
use crate::events::{Event, HTLCDestination, PaymentFailureReason};
1819
use crate::ln::types::ChannelId;
1920
use crate::types::payment::{PaymentHash, PaymentSecret};
@@ -1428,6 +1429,42 @@ fn fails_receive_tlvs_authentication() {
14281429
);
14291430
}
14301431

1432+
#[test]
1433+
fn blinded_payment_path_padding() {
1434+
// Make sure that for a blinded payment path, all encrypted payloads are padded to equal lengths.
1435+
let chanmon_cfgs = create_chanmon_cfgs(5);
1436+
let node_cfgs = create_node_cfgs(5, &chanmon_cfgs);
1437+
let node_chanmgrs = create_node_chanmgrs(5, &node_cfgs, &[None, None, None, None, None]);
1438+
let mut nodes = create_network(5, &node_cfgs, &node_chanmgrs);
1439+
create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1_000_000, 0);
1440+
create_announced_chan_between_nodes_with_value(&nodes, 1, 2, 1_000_000, 0);
1441+
let chan_upd_2_3 = create_announced_chan_between_nodes_with_value(&nodes, 2, 3, 1_000_000, 0).0.contents;
1442+
let chan_upd_3_4 = create_announced_chan_between_nodes_with_value(&nodes, 3, 4, 1_000_000, 0).0.contents;
1443+
1444+
// Get all our nodes onto the same height so payments don't fail for CLTV violations.
1445+
connect_blocks(&nodes[0], nodes[4].best_block_info().1 - nodes[0].best_block_info().1);
1446+
connect_blocks(&nodes[1], nodes[4].best_block_info().1 - nodes[1].best_block_info().1);
1447+
connect_blocks(&nodes[2], nodes[4].best_block_info().1 - nodes[2].best_block_info().1);
1448+
assert_eq!(nodes[4].best_block_info().1, nodes[3].best_block_info().1);
1449+
1450+
let amt_msat = 5000;
1451+
let (payment_preimage, payment_hash, payment_secret) = get_payment_preimage_hash(&nodes[4], Some(amt_msat), None);
1452+
1453+
let blinded_path = blinded_payment_path(payment_secret, 1, 1_0000_0000,
1454+
nodes.iter().skip(2).map(|n| n.node.get_our_node_id()).collect(), &[&chan_upd_2_3, &chan_upd_3_4],
1455+
&chanmon_cfgs[4].keys_manager
1456+
);
1457+
1458+
assert!(is_padded(&blinded_path.blinded_hops(), PAYMENT_PADDING_ROUND_OFF));
1459+
1460+
let route_params = RouteParameters::from_payment_params_and_value(PaymentParameters::blinded(vec![blinded_path]), amt_msat);
1461+
1462+
nodes[0].node.send_payment(payment_hash, RecipientOnionFields::spontaneous_empty(), PaymentId(payment_hash.0), route_params, Retry::Attempts(0)).unwrap();
1463+
check_added_monitors(&nodes[0], 1);
1464+
pass_along_route(&nodes[0], &[&[&nodes[1], &nodes[2], &nodes[3], &nodes[4]]], amt_msat, payment_hash, payment_secret);
1465+
claim_payment(&nodes[0], &[&nodes[1], &nodes[2], &nodes[3], &nodes[4]], payment_preimage);
1466+
}
1467+
14311468
fn secret_from_hex(hex: &str) -> SecretKey {
14321469
SecretKey::from_slice(&<Vec<u8>>::from_hex(hex).unwrap()).unwrap()
14331470
}

lightning/src/onion_message/functional_tests.rs

+61-1
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,9 @@ use super::offers::{OffersMessage, OffersMessageHandler};
2121
use super::packet::{OnionMessageContents, Packet};
2222
use crate::blinded_path::message::{
2323
AsyncPaymentsContext, BlindedMessagePath, DNSResolverContext, MessageContext,
24-
MessageForwardNode, OffersContext,
24+
MessageForwardNode, OffersContext, MESSAGE_PADDING_ROUND_OFF,
2525
};
26+
use crate::blinded_path::utils::is_padded;
2627
use crate::blinded_path::EmptyNodeIdLookUp;
2728
use crate::events::{Event, EventsProvider};
2829
use crate::ln::msgs::{self, BaseMessageHandler, DecodeError, OnionMessageHandler};
@@ -596,6 +597,65 @@ fn too_big_packet_error() {
596597
assert_eq!(err, SendError::TooBigPacket);
597598
}
598599

600+
#[test]
601+
fn test_blinded_path_padding_for_full_length_path() {
602+
// Check that for a full blinded path, all encrypted payload are padded to rounded-off length.
603+
let nodes = create_nodes(4);
604+
let test_msg = TestCustomMessage::Pong;
605+
606+
let secp_ctx = Secp256k1::new();
607+
let intermediate_nodes = [
608+
MessageForwardNode { node_id: nodes[1].node_id, short_channel_id: None },
609+
MessageForwardNode { node_id: nodes[2].node_id, short_channel_id: None },
610+
];
611+
// Update the context to create a larger final receive TLVs, ensuring that
612+
// the hop sizes vary before padding.
613+
let context = MessageContext::Custom(vec![0u8; 42]);
614+
let blinded_path = BlindedMessagePath::new(
615+
&intermediate_nodes,
616+
nodes[3].node_id,
617+
context,
618+
&*nodes[3].entropy_source,
619+
&secp_ctx,
620+
)
621+
.unwrap();
622+
623+
assert!(is_padded(&blinded_path.blinded_hops(), MESSAGE_PADDING_ROUND_OFF));
624+
625+
let destination = Destination::BlindedPath(blinded_path);
626+
let instructions = MessageSendInstructions::WithoutReplyPath { destination };
627+
628+
nodes[0].messenger.send_onion_message(test_msg, instructions).unwrap();
629+
nodes[3].custom_message_handler.expect_message(TestCustomMessage::Pong);
630+
pass_along_path(&nodes);
631+
}
632+
633+
#[test]
634+
fn test_blinded_path_no_padding_for_compact_path() {
635+
// Check that for a compact blinded path, no padding is applied.
636+
let nodes = create_nodes(4);
637+
let secp_ctx = Secp256k1::new();
638+
639+
// Include some short_channel_id, so that MessageRouter uses this to create compact blinded paths.
640+
let intermediate_nodes = [
641+
MessageForwardNode { node_id: nodes[1].node_id, short_channel_id: Some(24) },
642+
MessageForwardNode { node_id: nodes[2].node_id, short_channel_id: Some(25) },
643+
];
644+
// Update the context to create a larger final receive TLVs, ensuring that
645+
// the hop sizes vary before padding.
646+
let context = MessageContext::Custom(vec![0u8; 42]);
647+
let blinded_path = BlindedMessagePath::new(
648+
&intermediate_nodes,
649+
nodes[3].node_id,
650+
context,
651+
&*nodes[3].entropy_source,
652+
&secp_ctx,
653+
)
654+
.unwrap();
655+
656+
assert!(!is_padded(&blinded_path.blinded_hops(), MESSAGE_PADDING_ROUND_OFF));
657+
}
658+
599659
#[test]
600660
fn we_are_intro_node() {
601661
// If we are sending straight to a blinded path and we are the introduction node, we need to

0 commit comments

Comments
 (0)