Skip to content

Commit 4d145a3

Browse files
committed
Decrypt Trampoline onions
In this commit, we start decrypting the inner onion we receive, and lay some groundwork towards handling the subsequent routing or payment receipt.
1 parent bb3168b commit 4d145a3

File tree

3 files changed

+193
-8
lines changed

3 files changed

+193
-8
lines changed

lightning/src/ln/channelmanager.rs

+48-1
Original file line numberDiff line numberDiff line change
@@ -4479,12 +4479,59 @@ where
44794479
Err(InboundHTLCErr { err_code, err_data, msg }) => return_err!(msg, err_code, &err_data)
44804480
}
44814481
},
4482+
#[cfg(trampoline)]
4483+
onion_utils::Hop::TrampolineReceive { .. } => {
4484+
// OUR PAYMENT!
4485+
let current_height: u32 = self.best_block.read().unwrap().height;
4486+
match create_recv_pending_htlc_info(decoded_hop, shared_secret, msg.payment_hash,
4487+
msg.amount_msat, msg.cltv_expiry, None, allow_underpay, msg.skimmed_fee_msat,
4488+
current_height)
4489+
{
4490+
Ok(info) => {
4491+
// Note that we could obviously respond immediately with an update_fulfill_htlc
4492+
// message, however that would leak that we are the recipient of this payment, so
4493+
// instead we stay symmetric with the forwarding case, only responding (after a
4494+
// delay) once they've sent us a commitment_signed!
4495+
PendingHTLCStatus::Forward(info)
4496+
},
4497+
Err(InboundHTLCErr { err_code, err_data, msg }) => return_err!(msg, err_code, &err_data)
4498+
}
4499+
},
44824500
onion_utils::Hop::Forward { next_hop_hmac, new_packet_bytes, .. } | onion_utils::Hop::BlindedForward { next_hop_hmac, new_packet_bytes, .. } => {
44834501
match create_fwd_pending_htlc_info(msg, decoded_hop, next_hop_hmac,
44844502
new_packet_bytes, shared_secret, next_packet_pubkey_opt) {
44854503
Ok(info) => PendingHTLCStatus::Forward(info),
44864504
Err(InboundHTLCErr { err_code, err_data, msg }) => return_err!(msg, err_code, &err_data)
44874505
}
4506+
},
4507+
#[cfg(trampoline)]
4508+
onion_utils::Hop::TrampolineForward { next_trampoline_hop_data, next_trampoline_hop_hmac, new_trampoline_packet_bytes, trampoline_shared_secret, .. } => {
4509+
debug_assert!(next_packet_pubkey_opt.is_some());
4510+
let next_trampoline_packet_pubkey = match next_packet_pubkey_opt {
4511+
Some(Ok(pubkey)) => pubkey,
4512+
_ => return_err!("Missing next Trampoline hop pubkey from intermediate Trampoline forwarding data ", 0x4000 | 22, Vec::new()),
4513+
};
4514+
let outgoing_packet = msgs::TrampolineOnionPacket {
4515+
version: 0,
4516+
public_key: next_trampoline_packet_pubkey,
4517+
hop_data: new_trampoline_packet_bytes,
4518+
hmac: next_trampoline_hop_hmac,
4519+
};
4520+
PendingHTLCStatus::Forward(PendingHTLCInfo {
4521+
routing: PendingHTLCRouting::TrampolineForward {
4522+
incoming_shared_secret: trampoline_shared_secret.secret_bytes(),
4523+
onion_packet: outgoing_packet,
4524+
node_id: next_trampoline_hop_data.outgoing_node_id,
4525+
incoming_cltv_expiry: msg.cltv_expiry,
4526+
blinded: None
4527+
},
4528+
payment_hash: msg.payment_hash,
4529+
incoming_shared_secret: shared_secret,
4530+
incoming_amt_msat: Some(msg.amount_msat),
4531+
outgoing_amt_msat: next_trampoline_hop_data.amt_to_forward,
4532+
outgoing_cltv_value: next_trampoline_hop_data.outgoing_cltv_value,
4533+
skimmed_fee_msat: None,
4534+
})
44884535
}
44894536
}
44904537
}
@@ -5896,7 +5943,7 @@ where
58965943
// of the onion.
58975944
failed_payment!(err_msg, err_code, sha256_of_onion.to_vec(), None);
58985945
},
5899-
Err(onion_utils::OnionDecodeErr::Relay { err_msg, err_code, shared_secret }) => {
5946+
Err(onion_utils::OnionDecodeErr::Relay { err_msg, err_code, shared_secret, .. }) => {
59005947
let phantom_shared_secret = shared_secret.secret_bytes();
59015948
failed_payment!(err_msg, err_code, Vec::new(), Some(phantom_shared_secret));
59025949
},

lightning/src/ln/onion_payment.rs

+35-5
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,15 @@ pub(super) fn create_fwd_pending_htlc_info(
104104
err_code: 0x4000 | 22,
105105
err_data: Vec::new(),
106106
}),
107+
#[cfg(trampoline)]
108+
onion_utils::Hop::TrampolineReceive { .. } =>
109+
return Err(InboundHTLCErr {
110+
msg: "Final Node OnionHopData provided for us as an intermediary node",
111+
err_code: 0x4000 | 22,
112+
err_data: Vec::new(),
113+
}),
114+
#[cfg(trampoline)]
115+
onion_utils::Hop::TrampolineForward { .. } => panic!("Trampoline forwards are being handled outside of create_fwd_pending_htlc_info")
107116
};
108117

109118
Ok(PendingHTLCInfo {
@@ -165,6 +174,8 @@ pub(super) fn create_recv_pending_htlc_info(
165174
sender_intended_htlc_amt_msat, cltv_expiry_height, None, Some(payment_context),
166175
intro_node_blinding_point.is_none(), true, invoice_request)
167176
}
177+
#[cfg(trampoline)]
178+
onion_utils::Hop::TrampolineReceive { .. } => todo!(),
168179
onion_utils::Hop::Forward { .. } => {
169180
return Err(InboundHTLCErr {
170181
err_code: 0x4000|22,
@@ -179,6 +190,14 @@ pub(super) fn create_recv_pending_htlc_info(
179190
msg: "Got blinded non final data with an HMAC of 0",
180191
})
181192
},
193+
#[cfg(trampoline)]
194+
onion_utils::Hop::TrampolineForward { .. } => {
195+
return Err(InboundHTLCErr {
196+
err_code: 0x4000|22,
197+
err_data: Vec::new(),
198+
msg: "Got Trampoline non final data with an HMAC of 0",
199+
})
200+
},
182201
};
183202
// final_incorrect_cltv_expiry
184203
if onion_cltv_expiry > cltv_expiry {
@@ -392,7 +411,7 @@ where
392411
return_malformed_err!("Unknown onion packet version", 0x8000 | 0x4000 | 4);
393412
}
394413

395-
let encode_relay_error = |message: &str, err_code: u16, shared_secret: [u8; 32], data: &[u8]| {
414+
let encode_relay_error = |message: &str, err_code: u16, shared_secret: [u8; 32], trampoline_shared_secret: Option<[u8; 32]>, data: &[u8]| {
396415
if msg.blinding_point.is_some() {
397416
return_malformed_err!(message, INVALID_ONION_BLINDING)
398417
}
@@ -402,7 +421,7 @@ where
402421
channel_id: msg.channel_id,
403422
htlc_id: msg.htlc_id,
404423
reason: HTLCFailReason::reason(err_code, data.to_vec())
405-
.get_encrypted_failure_packet(&shared_secret, &None),
424+
.get_encrypted_failure_packet(&shared_secret, &trampoline_shared_secret),
406425
}));
407426
};
408427

@@ -414,8 +433,8 @@ where
414433
Err(onion_utils::OnionDecodeErr::Malformed { err_msg, err_code }) => {
415434
return_malformed_err!(err_msg, err_code);
416435
},
417-
Err(onion_utils::OnionDecodeErr::Relay { err_msg, err_code, shared_secret }) => {
418-
return encode_relay_error(err_msg, err_code, shared_secret.secret_bytes(), &[0; 0]);
436+
Err(onion_utils::OnionDecodeErr::Relay { err_msg, err_code, shared_secret, trampoline_shared_secret }) => {
437+
return encode_relay_error(err_msg, err_code, shared_secret.secret_bytes(), trampoline_shared_secret.map(|tss| tss.secret_bytes()), &[0; 0]);
419438
},
420439
};
421440

@@ -435,7 +454,7 @@ where
435454
Ok((amt, cltv)) => (amt, cltv),
436455
Err(()) => {
437456
return encode_relay_error("Underflow calculating outbound amount or cltv value for blinded forward",
438-
INVALID_ONION_BLINDING, shared_secret.secret_bytes(), &[0; 32]);
457+
INVALID_ONION_BLINDING, shared_secret.secret_bytes(), None, &[0; 32]);
439458
}
440459
};
441460
let next_packet_pubkey = onion_utils::next_hop_pubkey(&secp_ctx,
@@ -445,6 +464,17 @@ where
445464
outgoing_cltv_value
446465
})
447466
}
467+
#[cfg(trampoline)]
468+
onion_utils::Hop::TrampolineForward { next_trampoline_hop_data: msgs::InboundTrampolineForwardPayload { amt_to_forward, outgoing_cltv_value, outgoing_node_id }, trampoline_shared_secret, incoming_trampoline_public_key, .. } => {
469+
let next_trampoline_packet_pubkey = onion_utils::next_hop_pubkey(secp_ctx,
470+
incoming_trampoline_public_key, &trampoline_shared_secret.secret_bytes());
471+
Some(NextPacketDetails {
472+
next_packet_pubkey: next_trampoline_packet_pubkey,
473+
outgoing_connector: HopConnector::Trampoline(outgoing_node_id),
474+
outgoing_amt_msat: amt_to_forward,
475+
outgoing_cltv_value,
476+
})
477+
}
448478
_ => None
449479
};
450480

lightning/src/ln/onion_utils.rs

+110-2
Original file line numberDiff line numberDiff line change
@@ -1431,6 +1431,19 @@ pub(crate) enum Hop {
14311431
/// Bytes of the onion packet we're forwarding.
14321432
new_packet_bytes: [u8; ONION_DATA_LEN],
14331433
},
1434+
/// This onion was received via Trampoline, and needs to be forwarded to a subsequent Trampoline
1435+
/// node.
1436+
#[cfg(trampoline)]
1437+
TrampolineForward {
1438+
#[allow(unused)]
1439+
outer_hop_data: msgs::InboundTrampolineEntrypointPayload,
1440+
outer_shared_secret: SharedSecret,
1441+
incoming_trampoline_public_key: PublicKey,
1442+
trampoline_shared_secret: SharedSecret,
1443+
next_trampoline_hop_data: msgs::InboundTrampolineForwardPayload,
1444+
next_trampoline_hop_hmac: [u8; 32],
1445+
new_trampoline_packet_bytes: Vec<u8>,
1446+
},
14341447
/// This onion payload needs to be forwarded to a next-hop.
14351448
BlindedForward {
14361449
/// Onion payload data used in forwarding the payment.
@@ -1458,6 +1471,16 @@ pub(crate) enum Hop {
14581471
/// Shared secret that was used to decrypt hop_data.
14591472
shared_secret: SharedSecret,
14601473
},
1474+
/// This onion payload was for us, not for forwarding to a next-hop, and it was sent to us via
1475+
/// Trampoline. Contains information for verifying the incoming payment.
1476+
#[allow(unused)]
1477+
#[cfg(trampoline)]
1478+
TrampolineReceive {
1479+
outer_hop_data: msgs::InboundTrampolineEntrypointPayload,
1480+
outer_shared_secret: SharedSecret,
1481+
trampoline_hop_data: msgs::InboundOnionReceivePayload,
1482+
trampoline_shared_secret: SharedSecret,
1483+
},
14611484
}
14621485

14631486
impl Hop {
@@ -1478,8 +1501,12 @@ impl Hop {
14781501
match self {
14791502
Hop::Forward { shared_secret, .. } => shared_secret,
14801503
Hop::BlindedForward { shared_secret, .. } => shared_secret,
1504+
#[cfg(trampoline)]
1505+
Hop::TrampolineForward { outer_shared_secret, .. } => outer_shared_secret,
14811506
Hop::Receive { shared_secret, .. } => shared_secret,
14821507
Hop::BlindedReceive { shared_secret, .. } => shared_secret,
1508+
#[cfg(trampoline)]
1509+
Hop::TrampolineReceive { outer_shared_secret, .. } => outer_shared_secret,
14831510
}
14841511
}
14851512
}
@@ -1490,7 +1517,15 @@ pub(crate) enum OnionDecodeErr {
14901517
/// The HMAC of the onion packet did not match the hop data.
14911518
Malformed { err_msg: &'static str, err_code: u16 },
14921519
/// We failed to decode the onion payload.
1493-
Relay { err_msg: &'static str, err_code: u16, shared_secret: SharedSecret },
1520+
///
1521+
/// If the payload we failed to decode belonged to a Trampoline onion, following the successful
1522+
/// decoding of the outer onion, the trampoline_shared_secret field should be set.
1523+
Relay {
1524+
err_msg: &'static str,
1525+
err_code: u16,
1526+
shared_secret: SharedSecret,
1527+
trampoline_shared_secret: Option<SharedSecret>,
1528+
},
14941529
}
14951530

14961531
pub(crate) fn decode_next_payment_hop<NS: Deref>(
@@ -1514,7 +1549,7 @@ where
15141549
hop_data,
15151550
hmac_bytes,
15161551
Some(payment_hash),
1517-
(blinding_point, node_signer),
1552+
(blinding_point, &(*node_signer)),
15181553
);
15191554
match decoded_hop {
15201555
Ok((next_hop_data, Some((next_hop_hmac, FixedSizeOnionPacket(new_packet_bytes))))) => {
@@ -1545,6 +1580,7 @@ where
15451580
err_msg: "Final Node OnionHopData provided for us as an intermediary node",
15461581
err_code: 0x4000 | 22,
15471582
shared_secret,
1583+
trampoline_shared_secret: None,
15481584
})
15491585
},
15501586
}
@@ -1556,6 +1592,75 @@ where
15561592
msgs::InboundOnionPayload::BlindedReceive(hop_data) => {
15571593
Ok(Hop::BlindedReceive { shared_secret, hop_data })
15581594
},
1595+
#[cfg(trampoline)]
1596+
msgs::InboundOnionPayload::TrampolineEntrypoint(hop_data) => {
1597+
let incoming_trampoline_public_key = hop_data.trampoline_packet.public_key;
1598+
let trampoline_shared_secret = node_signer
1599+
.ecdh(
1600+
recipient,
1601+
&incoming_trampoline_public_key,
1602+
blinded_node_id_tweak.as_ref(),
1603+
)
1604+
.unwrap()
1605+
.secret_bytes();
1606+
let decoded_trampoline_hop: Result<
1607+
(msgs::InboundOnionPayload, Option<([u8; 32], Vec<u8>)>),
1608+
_,
1609+
> = decode_next_hop(
1610+
trampoline_shared_secret,
1611+
&hop_data.trampoline_packet.hop_data,
1612+
hop_data.trampoline_packet.hmac,
1613+
Some(payment_hash),
1614+
(blinding_point, node_signer),
1615+
);
1616+
match decoded_trampoline_hop {
1617+
Ok((
1618+
next_trampoline_hop_data,
1619+
Some((next_trampoline_hop_hmac, new_trampoline_packet_bytes)),
1620+
)) => {
1621+
match next_trampoline_hop_data {
1622+
msgs::InboundOnionPayload::TrampolineForward(trampoline_hop_data) => {
1623+
Ok(Hop::TrampolineForward {
1624+
outer_hop_data: hop_data,
1625+
outer_shared_secret: shared_secret,
1626+
incoming_trampoline_public_key,
1627+
trampoline_shared_secret: SharedSecret::from_bytes(
1628+
trampoline_shared_secret,
1629+
),
1630+
next_trampoline_hop_data: trampoline_hop_data,
1631+
next_trampoline_hop_hmac,
1632+
new_trampoline_packet_bytes,
1633+
})
1634+
},
1635+
_ => Err(OnionDecodeErr::Malformed {
1636+
err_msg: "Non-Trampoline onion data provided to us as inner onion",
1637+
// todo: find more suitable error code
1638+
err_code: 0x4000 | 22,
1639+
}),
1640+
}
1641+
},
1642+
Ok((trampoline_hop_data, None)) => {
1643+
match trampoline_hop_data {
1644+
msgs::InboundOnionPayload::Receive(trampoline_hop_data) => {
1645+
Ok(Hop::TrampolineReceive {
1646+
outer_hop_data: hop_data,
1647+
outer_shared_secret: shared_secret,
1648+
trampoline_hop_data,
1649+
trampoline_shared_secret: SharedSecret::from_bytes(
1650+
trampoline_shared_secret,
1651+
),
1652+
})
1653+
},
1654+
_ => Err(OnionDecodeErr::Malformed {
1655+
err_msg: "Non-Trampoline onion data provided to us as inner onion",
1656+
// todo: find more suitable error code
1657+
err_code: 0x4000 | 22,
1658+
}),
1659+
}
1660+
},
1661+
Err(e) => Err(e),
1662+
}
1663+
},
15591664
_ => {
15601665
if blinding_point.is_some() {
15611666
return Err(OnionDecodeErr::Malformed {
@@ -1567,6 +1672,7 @@ where
15671672
err_msg: "Intermediate Node OnionHopData provided for us as a final node",
15681673
err_code: 0x4000 | 22,
15691674
shared_secret,
1675+
trampoline_shared_secret: None,
15701676
})
15711677
},
15721678
},
@@ -1715,6 +1821,7 @@ fn decode_next_hop<T, R: ReadableArgs<T>, N: NextPacketBytes>(
17151821
err_msg: "Unable to decode our hop data",
17161822
err_code: error_code,
17171823
shared_secret: SharedSecret::from_bytes(shared_secret),
1824+
trampoline_shared_secret: None,
17181825
});
17191826
},
17201827
Ok(msg) => {
@@ -1724,6 +1831,7 @@ fn decode_next_hop<T, R: ReadableArgs<T>, N: NextPacketBytes>(
17241831
err_msg: "Unable to decode our hop data",
17251832
err_code: 0x4000 | 22,
17261833
shared_secret: SharedSecret::from_bytes(shared_secret),
1834+
trampoline_shared_secret: None,
17271835
});
17281836
}
17291837
if hmac == [0; 32] {

0 commit comments

Comments
 (0)