Skip to content

Commit 445ca83

Browse files
valentinewallacea-mpch
authored andcommitted
Compute trampoline session_priv from outer session_priv
This simplifies the code and makes it more straightforward to test unblinded trampoline receives where we need to compute the trampoline session_priv when manually creating the inner onion. (The trampoline onion needs to be manually created because LDK does not natively support sending to unblinded trampolines, just receiving.)
1 parent 0cb7f54 commit 445ca83

File tree

2 files changed

+51
-80
lines changed

2 files changed

+51
-80
lines changed

lightning/src/ln/blinded_payment_tests.rs

Lines changed: 9 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@
1010
// licenses.
1111

1212
use bitcoin::hashes::hex::FromHex;
13-
use bitcoin::hashes::sha256::Hash as Sha256;
14-
use bitcoin::hashes::Hash;
1513
use bitcoin::hex::DisplayHex;
1614
use bitcoin::secp256k1::{PublicKey, Scalar, Secp256k1, SecretKey, schnorr};
1715
use bitcoin::secp256k1::ecdh::SharedSecret;
@@ -1831,7 +1829,7 @@ fn test_combined_trampoline_onion_creation_vectors() {
18311829
let amt_msat = 150_000_000;
18321830
let cur_height = 800_000;
18331831
let recipient_onion_fields = RecipientOnionFields::secret_only(payment_secret);
1834-
let (bob_onion, htlc_msat, htlc_cltv) = onion_utils::create_payment_onion_internal(&secp_ctx, &path, &session_priv, amt_msat, &recipient_onion_fields, cur_height, &associated_data, &None, None, [0; 32], Some(outer_session_key), Some(outer_onion_prng_seed)).unwrap();
1832+
let (bob_onion, htlc_msat, htlc_cltv) = onion_utils::create_payment_onion_internal(&secp_ctx, &path, &outer_session_key, amt_msat, &recipient_onion_fields, cur_height, &associated_data, &None, None, outer_onion_prng_seed, Some(session_priv), Some([0; 32])).unwrap();
18351833

18361834
let outer_onion_packet_hex = bob_onion.encode().to_lower_hex_string();
18371835
assert_eq!(outer_onion_packet_hex, "00025fd60556c134ae97e4baedba220a644037754ee67c54fd05e93bf40c17cbb73362fb9dee96001ff229945595b6edb59437a6bc143406d3f90f749892a84d8d430c6890437d26d5bfc599d565316ef51347521075bbab87c59c57bcf20af7e63d7192b46cf171e4f73cb11f9f603915389105d91ad630224bea95d735e3988add1e24b5bf28f1d7128db64284d90a839ba340d088c74b1fb1bd21136b1809428ec5399c8649e9bdf92d2dcfc694deae5046fa5b2bdf646847aaad73f5e95275763091c90e71031cae1f9a770fdea559642c9c02f424a2a28163dd0957e3874bd28a97bec67d18c0321b0e68bc804aa8345b17cb626e2348ca06c8312a167c989521056b0f25c55559d446507d6c491d50605cb79fa87929ce64b0a9860926eeaec2c431d926a1cadb9a1186e4061cb01671a122fc1f57602cbef06d6c194ec4b715c2e3dd4120baca3172cd81900b49fef857fb6d6afd24c983b608108b0a5ac0c1c6c52011f23b8778059ffadd1bb7cd06e2525417365f485a7fd1d4a9ba3818ede7cdc9e71afee8532252d08e2531ca52538655b7e8d912f7ec6d37bbcce8d7ec690709dbf9321e92c565b78e7fe2c22edf23e0902153d1ca15a112ad32fb19695ec65ce11ddf670da7915f05ad4b86c154fb908cb567315d1124f303f75fa075ebde8ef7bb12e27737ad9e4924439097338ea6d7a6fc3721b88c9b830a34e8d55f4c582b74a3895cc848fe57f4fe29f115dabeb6b3175be15d94408ed6771109cfaf57067ae658201082eae7605d26b1449af4425ae8e8f58cdda5c6265f1fd7a386fc6cea3074e4f25b909b96175883676f7610a00fdf34df9eb6c7b9a4ae89b839c69fd1f285e38cdceb634d782cc6d81179759bc9fd47d7fd060470d0b048287764c6837963274e708314f017ac7dc26d0554d59bfcfd3136225798f65f0b0fea337c6b256ebbb63a90b994c0ab93fd8b1d6bd4c74aebe535d6110014cd3d525394027dfe8faa98b4e9b2bee7949eb1961f1b026791092f84deea63afab66603dbe9b6365a102a1fef2f6b9744bc1bb091a8da9130d34d4d39f25dbad191649cfb67e10246364b7ce0c6ec072f9690cabb459d9fda0c849e17535de4357e9907270c75953fca3c845bb613926ecf73205219c7057a4b6bb244c184362bb4e2f24279dc4e60b94a5b1ec11c34081a628428ba5646c995b9558821053ba9c84a05afbf00dabd60223723096516d2f5668f3ec7e11612b01eb7a3a0506189a2272b88e89807943adb34291a17f6cb5516ffd6f945a1c42a524b21f096d66f350b1dad4db455741ae3d0e023309fbda5ef55fb0dc74f3297041448b2be76c525141963934c6afc53d263fb7836626df502d7c2ee9e79cbbd87afd84bbb8dfbf45248af3cd61ad5fac827e7683ca4f91dfad507a8eb9c17b2c9ac5ec051fe645a4a6cb37136f6f19b611e0ea8da7960af2d779507e55f57305bc74b7568928c5dd5132990fe54c22117df91c257d8c7b61935a018a28c1c3b17bab8e4294fa699161ec21123c9fc4e71079df31f300c2822e1246561e04765d3aab333eafd026c7431ac7616debb0e022746f4538e1c6348b600c988eeb2d051fc60c468dca260a84c79ab3ab8342dc345a764672848ea234e17332bc124799daf7c5fcb2e2358514a7461357e1c19c802c5ee32deccf1776885dd825bedd5f781d459984370a6b7ae885d4483a76ddb19b30f47ed47cd56aa5a079a89793dbcad461c59f2e002067ac98dd5a534e525c9c46c2af730741bf1f8629357ec0bfc0bc9ecb31af96777e507648ff4260dc3673716e098d9111dfd245f1d7c55a6de340deb8bd7a053e5d62d760f184dc70ca8fa255b9023b9b9aedfb6e419a5b5951ba0f83b603793830ee68d442d7b88ee1bbf6bbd1bcd6f68cc1af");
@@ -2011,7 +2009,12 @@ fn do_test_trampoline_single_hop_receive(success: bool) {
20112009
let amt_msat = 1000;
20122010
let (payment_preimage, payment_hash, payment_secret) = get_payment_preimage_hash(&nodes[2], Some(amt_msat), None);
20132011

2014-
let carol_alice_trampoline_session_priv = secret_from_hex("a0f4b8d7b6c2d0ffdfaf718f76e9decaef4d9fb38a8c4addb95c4007cc3eee03");
2012+
// We need the session priv to compute the trampoline session priv and construct an invalid onion packet later.
2013+
let override_random_bytes = [3; 32];
2014+
*nodes[0].keys_manager.override_random_bytes.lock().unwrap() = Some(override_random_bytes);
2015+
2016+
let outer_onion_session_priv = SecretKey::from_slice(&override_random_bytes[..]).unwrap();
2017+
let carol_alice_trampoline_session_priv = onion_utils::compute_trampoline_session_priv(&outer_onion_session_priv);
20152018
let carol_blinding_point = PublicKey::from_secret_key(&secp_ctx, &carol_alice_trampoline_session_priv);
20162019
let carol_blinded_hops = if success {
20172020
let payee_tlvs = UnauthenticatedReceiveTlvs {
@@ -2099,12 +2102,7 @@ fn do_test_trampoline_single_hop_receive(success: bool) {
20992102
route_params: None,
21002103
};
21012104

2102-
// We need the session priv to construct an invalid onion packet later.
2103-
let override_random_bytes = [3; 32];
2104-
*nodes[0].keys_manager.override_random_bytes.lock().unwrap() = Some(override_random_bytes);
2105-
21062105
nodes[0].node.send_payment_with_route(route.clone(), payment_hash, RecipientOnionFields::spontaneous_empty(), PaymentId(payment_hash.0)).unwrap();
2107-
21082106
check_added_monitors!(&nodes[0], 1);
21092107

21102108
if success {
@@ -2113,9 +2111,7 @@ fn do_test_trampoline_single_hop_receive(success: bool) {
21132111
} else {
21142112
let replacement_onion = {
21152113
// create a substitute onion where the last Trampoline hop is a forward
2116-
let trampoline_secret_key = SecretKey::from_slice(&override_random_bytes).unwrap();
21172114
let recipient_onion_fields = RecipientOnionFields::spontaneous_empty();
2118-
21192115
let mut blinded_tail = route.paths[0].blinded_tail.clone().unwrap();
21202116

21212117
// append some dummy blinded hop so the intro hop looks like a forward
@@ -2129,7 +2125,7 @@ fn do_test_trampoline_single_hop_receive(success: bool) {
21292125
// pop the last dummy hop
21302126
trampoline_payloads.pop();
21312127

2132-
let trampoline_onion_keys = onion_utils::construct_trampoline_onion_keys(&secp_ctx, &route.paths[0].blinded_tail.as_ref().unwrap(), &trampoline_secret_key);
2128+
let trampoline_onion_keys = onion_utils::construct_trampoline_onion_keys(&secp_ctx, &route.paths[0].blinded_tail.as_ref().unwrap(), &carol_alice_trampoline_session_priv);
21332129
let trampoline_packet = onion_utils::construct_trampoline_onion_packet(
21342130
trampoline_payloads,
21352131
trampoline_onion_keys,
@@ -2138,13 +2134,8 @@ fn do_test_trampoline_single_hop_receive(success: bool) {
21382134
None,
21392135
).unwrap();
21402136

2141-
let outer_session_priv = {
2142-
let session_priv_hash = Sha256::hash(&override_random_bytes).to_byte_array();
2143-
SecretKey::from_slice(&session_priv_hash[..]).expect("You broke SHA-256!")
2144-
};
2145-
21462137
let (outer_payloads, _, _) = onion_utils::build_onion_payloads(&route.paths[0], outer_total_msat, &recipient_onion_fields, outer_starting_htlc_offset, &None, None, Some(trampoline_packet)).unwrap();
2147-
let outer_onion_keys = onion_utils::construct_onion_keys(&secp_ctx, &route.clone().paths[0], &outer_session_priv);
2138+
let outer_onion_keys = onion_utils::construct_onion_keys(&secp_ctx, &route.clone().paths[0], &outer_onion_session_priv);
21482139
let outer_packet = onion_utils::construct_onion_packet(
21492140
outer_payloads,
21502141
outer_onion_keys,

lightning/src/ln/onion_utils.rs

Lines changed: 42 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -992,41 +992,19 @@ pub fn process_onion_failure<T: secp256k1::Signing, L: Deref>(
992992
where
993993
L::Target: Logger,
994994
{
995-
let (path, primary_session_priv) = match htlc_source {
995+
let (path, session_priv) = match htlc_source {
996996
HTLCSource::OutboundRoute { ref path, ref session_priv, .. } => (path, session_priv),
997997
_ => unreachable!(),
998998
};
999999

1000-
if path.has_trampoline_hops() {
1001-
// If we have Trampoline hops, the outer onion session_priv is a hash of the inner one.
1002-
let session_priv_hash = Sha256::hash(&primary_session_priv.secret_bytes()).to_byte_array();
1003-
let outer_session_priv =
1004-
SecretKey::from_slice(&session_priv_hash[..]).expect("You broke SHA-256!");
1005-
process_onion_failure_inner(
1006-
secp_ctx,
1007-
logger,
1008-
path,
1009-
&outer_session_priv,
1010-
Some(primary_session_priv),
1011-
encrypted_packet,
1012-
)
1013-
} else {
1014-
process_onion_failure_inner(
1015-
secp_ctx,
1016-
logger,
1017-
path,
1018-
primary_session_priv,
1019-
None,
1020-
encrypted_packet,
1021-
)
1022-
}
1000+
process_onion_failure_inner(secp_ctx, logger, path, &session_priv, None, encrypted_packet)
10231001
}
10241002

10251003
/// Process failure we got back from upstream on a payment we sent (implying htlc_source is an
10261004
/// OutboundRoute).
10271005
fn process_onion_failure_inner<T: secp256k1::Signing, L: Deref>(
1028-
secp_ctx: &Secp256k1<T>, logger: &L, path: &Path, outer_session_priv: &SecretKey,
1029-
inner_session_priv: Option<&SecretKey>, mut encrypted_packet: OnionErrorPacket,
1006+
secp_ctx: &Secp256k1<T>, logger: &L, path: &Path, session_priv: &SecretKey,
1007+
trampoline_session_priv_override: Option<SecretKey>, mut encrypted_packet: OnionErrorPacket,
10301008
) -> DecodedOnionFailure
10311009
where
10321010
L::Target: Logger,
@@ -1097,22 +1075,27 @@ where
10971075
let nontrampoline_bt =
10981076
if path.has_trampoline_hops() { None } else { path.blinded_tail.as_ref() };
10991077
let nontrampolines =
1100-
construct_onion_keys_generic(secp_ctx, &path.hops, nontrampoline_bt, outer_session_priv)
1101-
.map(|(shared_secret, _, _, route_hop_option, _)| {
1078+
construct_onion_keys_generic(secp_ctx, &path.hops, nontrampoline_bt, session_priv).map(
1079+
|(shared_secret, _, _, route_hop_option, _)| {
11021080
(route_hop_option.map(|rh| ErrorHop::RouteHop(rh)), shared_secret)
1103-
});
1081+
},
1082+
);
11041083

11051084
let trampolines = if path.has_trampoline_hops() {
11061085
// Trampoline hops are part of the blinded tail, so this can never panic
11071086
let blinded_tail = path.blinded_tail.as_ref();
11081087
let hops = &blinded_tail.unwrap().trampoline_hops;
1109-
let inner_session_priv =
1110-
inner_session_priv.expect("Trampoline hops always have an inner session priv");
1111-
Some(construct_onion_keys_generic(secp_ctx, hops, blinded_tail, inner_session_priv).map(
1112-
|(shared_secret, _, _, route_hop_option, _)| {
1113-
(route_hop_option.map(|tram_hop| ErrorHop::TrampolineHop(tram_hop)), shared_secret)
1114-
},
1115-
))
1088+
let trampoline_session_priv = trampoline_session_priv_override
1089+
.unwrap_or_else(|| compute_trampoline_session_priv(session_priv));
1090+
Some(
1091+
construct_onion_keys_generic(secp_ctx, hops, blinded_tail, &trampoline_session_priv)
1092+
.map(|(shared_secret, _, _, route_hop_option, _)| {
1093+
(
1094+
route_hop_option.map(|tram_hop| ErrorHop::TrampolineHop(tram_hop)),
1095+
shared_secret,
1096+
)
1097+
}),
1098+
)
11161099
} else {
11171100
None
11181101
};
@@ -2513,18 +2496,24 @@ pub fn create_payment_onion<T: secp256k1::Signing>(
25132496
)
25142497
}
25152498

2499+
pub(super) fn compute_trampoline_session_priv(outer_onion_session_priv: &SecretKey) -> SecretKey {
2500+
// When creating the inner trampoline onion, we set the session priv to the hash of the outer
2501+
// onion session priv.
2502+
let session_priv_hash = Sha256::hash(&outer_onion_session_priv.secret_bytes()).to_byte_array();
2503+
SecretKey::from_slice(&session_priv_hash[..]).expect("You broke SHA-256!")
2504+
}
2505+
25162506
/// Build a payment onion, returning the first hop msat and cltv values as well.
25172507
/// `cur_block_height` should be set to the best known block height + 1.
25182508
pub(crate) fn create_payment_onion_internal<T: secp256k1::Signing>(
25192509
secp_ctx: &Secp256k1<T>, path: &Path, session_priv: &SecretKey, total_msat: u64,
25202510
recipient_onion: &RecipientOnionFields, cur_block_height: u32, payment_hash: &PaymentHash,
25212511
keysend_preimage: &Option<PaymentPreimage>, invoice_request: Option<&InvoiceRequest>,
2522-
prng_seed: [u8; 32], secondary_session_priv: Option<SecretKey>,
2523-
secondary_prng_seed: Option<[u8; 32]>,
2512+
prng_seed: [u8; 32], trampoline_session_priv_override: Option<SecretKey>,
2513+
trampoline_prng_seed_override: Option<[u8; 32]>,
25242514
) -> Result<(msgs::OnionPacket, u64, u32), APIError> {
25252515
let mut outer_total_msat = total_msat;
25262516
let mut outer_starting_htlc_offset = cur_block_height;
2527-
let mut outer_session_priv_override = None;
25282517
let mut trampoline_packet_option = None;
25292518

25302519
if let Some(blinded_tail) = &path.blinded_tail {
@@ -2539,12 +2528,15 @@ pub(crate) fn create_payment_onion_internal<T: secp256k1::Signing>(
25392528
keysend_preimage,
25402529
)?;
25412530

2531+
let trampoline_session_priv = trampoline_session_priv_override
2532+
.unwrap_or_else(|| compute_trampoline_session_priv(session_priv));
2533+
let trampoline_prng_seed = trampoline_prng_seed_override.unwrap_or(prng_seed);
25422534
let onion_keys =
2543-
construct_trampoline_onion_keys(&secp_ctx, &blinded_tail, &session_priv);
2535+
construct_trampoline_onion_keys(&secp_ctx, &blinded_tail, &trampoline_session_priv);
25442536
let trampoline_packet = construct_trampoline_onion_packet(
25452537
trampoline_payloads,
25462538
onion_keys,
2547-
prng_seed,
2539+
trampoline_prng_seed,
25482540
payment_hash,
25492541
// TODO: specify a fixed size for privacy in future spec upgrade
25502542
None,
@@ -2554,11 +2546,6 @@ pub(crate) fn create_payment_onion_internal<T: secp256k1::Signing>(
25542546
})?;
25552547

25562548
trampoline_packet_option = Some(trampoline_packet);
2557-
2558-
outer_session_priv_override = Some(secondary_session_priv.unwrap_or_else(|| {
2559-
let session_priv_hash = Sha256::hash(&session_priv.secret_bytes()).to_byte_array();
2560-
SecretKey::from_slice(&session_priv_hash[..]).expect("You broke SHA-256!")
2561-
}));
25622549
}
25632550
}
25642551

@@ -2572,14 +2559,11 @@ pub(crate) fn create_payment_onion_internal<T: secp256k1::Signing>(
25722559
trampoline_packet_option,
25732560
)?;
25742561

2575-
let outer_session_priv = outer_session_priv_override.as_ref().unwrap_or(session_priv);
2576-
let onion_keys = construct_onion_keys(&secp_ctx, &path, outer_session_priv);
2577-
let outer_onion_prng_seed = secondary_prng_seed.unwrap_or(prng_seed);
2578-
let onion_packet =
2579-
construct_onion_packet(onion_payloads, onion_keys, outer_onion_prng_seed, payment_hash)
2580-
.map_err(|_| APIError::InvalidRoute {
2581-
err: "Route size too large considering onion data".to_owned(),
2582-
})?;
2562+
let onion_keys = construct_onion_keys(&secp_ctx, &path, session_priv);
2563+
let onion_packet = construct_onion_packet(onion_payloads, onion_keys, prng_seed, payment_hash)
2564+
.map_err(|_| APIError::InvalidRoute {
2565+
err: "Route size too large considering onion data".to_owned(),
2566+
})?;
25832567
Ok((onion_packet, htlc_msat, htlc_cltv))
25842568
}
25852569

@@ -3571,7 +3555,7 @@ mod tests {
35713555
&logger,
35723556
&build_trampoline_test_path(),
35733557
&outer_session_priv,
3574-
Some(&trampoline_session_priv),
3558+
Some(trampoline_session_priv),
35753559
error_packet,
35763560
);
35773561
assert_eq!(
@@ -3584,19 +3568,15 @@ mod tests {
35843568
// shared secret cryptography sanity tests
35853569
let session_priv = get_test_session_key();
35863570
let path = build_trampoline_test_path();
3571+
let outer_onion_keys = construct_onion_keys(&Secp256k1::new(), &path, &session_priv);
35873572

3573+
let trampoline_session_priv = compute_trampoline_session_priv(&session_priv);
35883574
let trampoline_onion_keys = construct_trampoline_onion_keys(
35893575
&secp_ctx,
35903576
&path.blinded_tail.as_ref().unwrap(),
3591-
&session_priv,
3577+
&trampoline_session_priv,
35923578
);
35933579

3594-
let outer_onion_keys = {
3595-
let session_priv_hash = Sha256::hash(&session_priv.secret_bytes()).to_byte_array();
3596-
let outer_session_priv = SecretKey::from_slice(&session_priv_hash[..]).unwrap();
3597-
construct_onion_keys(&Secp256k1::new(), &path, &outer_session_priv)
3598-
};
3599-
36003580
let htlc_source = HTLCSource::OutboundRoute {
36013581
path,
36023582
session_priv,

0 commit comments

Comments
 (0)