Skip to content

Commit 94be75f

Browse files
committed
Log cases where an onion failure cannot be attributed or interpreted
Create more visibility into these edge cases. The non-attributable failure in particular can be used to disrupt sender operation and it is therefore good to at least log these cases clearly.
1 parent 447148d commit 94be75f

File tree

1 file changed

+91
-13
lines changed

1 file changed

+91
-13
lines changed

lightning/src/ln/onion_utils.rs

Lines changed: 91 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -933,13 +933,10 @@ pub(crate) struct DecodedOnionFailure {
933933

934934
/// Note that we always decrypt `packet` in-place here even if the deserialization into
935935
/// [`msgs::DecodedOnionErrorPacket`] ultimately fails.
936-
fn decrypt_onion_error_packet(
937-
packet: &mut Vec<u8>, shared_secret: SharedSecret,
938-
) -> Result<msgs::DecodedOnionErrorPacket, msgs::DecodeError> {
936+
fn decrypt_onion_error_packet(packet: &mut Vec<u8>, shared_secret: SharedSecret) {
939937
let ammag = gen_ammag_from_shared_secret(shared_secret.as_ref());
940938
let mut chacha = ChaCha20::new(&ammag, &[0u8; 8]);
941939
chacha.process_in_place(packet);
942-
msgs::DecodedOnionErrorPacket::read(&mut Cursor::new(packet))
943940
}
944941

945942
/// Process failure we got back from upstream on a payment we sent (implying htlc_source is an
@@ -1021,9 +1018,12 @@ where
10211018
{
10221019
// Actually parse the onion error data in tests so we can check that blinded hops fail
10231020
// back correctly.
1024-
let err_packet =
1025-
decrypt_onion_error_packet(&mut encrypted_packet, shared_secret)
1026-
.unwrap();
1021+
decrypt_onion_error_packet(&mut encrypted_packet, shared_secret);
1022+
let err_packet = msgs::DecodedOnionErrorPacket::read(&mut Cursor::new(
1023+
&encrypted_packet,
1024+
))
1025+
.unwrap();
1026+
10271027
error_code_ret = Some(u16::from_be_bytes(
10281028
err_packet.failuremsg.get(0..2).unwrap().try_into().unwrap(),
10291029
));
@@ -1044,22 +1044,33 @@ where
10441044
let amt_to_forward = htlc_msat - route_hop.fee_msat;
10451045
htlc_msat = amt_to_forward;
10461046

1047-
let err_packet = match decrypt_onion_error_packet(&mut encrypted_packet, shared_secret) {
1048-
Ok(p) => p,
1049-
Err(_) => return,
1050-
};
1047+
decrypt_onion_error_packet(&mut encrypted_packet, shared_secret);
1048+
10511049
let um = gen_um_from_shared_secret(shared_secret.as_ref());
10521050
let mut hmac = HmacEngine::<Sha256>::new(&um);
1053-
hmac.input(&err_packet.encode()[32..]);
1051+
hmac.input(&encrypted_packet[32..]);
10541052

1055-
if !fixed_time_eq(&Hmac::from_engine(hmac).to_byte_array(), &err_packet.hmac) {
1053+
if !fixed_time_eq(&Hmac::from_engine(hmac).to_byte_array(), &encrypted_packet[..32]) {
10561054
return;
10571055
}
1056+
1057+
let err_packet =
1058+
match msgs::DecodedOnionErrorPacket::read(&mut Cursor::new(&encrypted_packet)) {
1059+
Ok(p) => p,
1060+
Err(_) => {
1061+
log_warn!(logger, "Unreadable failure from {}", route_hop.pubkey);
1062+
1063+
return;
1064+
},
1065+
};
1066+
10581067
let error_code_slice = match err_packet.failuremsg.get(0..2) {
10591068
Some(s) => s,
10601069
None => {
10611070
// Useless packet that we can't use but it passed HMAC, so it definitely came from the peer
10621071
// in question
1072+
log_warn!(logger, "Missing error code in failure from {}", route_hop.pubkey);
1073+
10631074
let network_update = Some(NetworkUpdate::NodeFailure {
10641075
node_id: route_hop.pubkey,
10651076
is_permanent: true,
@@ -1219,6 +1230,12 @@ where
12191230
} else {
12201231
// only not set either packet unparseable or hmac does not match with any
12211232
// payment not retryable only when garbage is from the final node
1233+
log_warn!(
1234+
logger,
1235+
"Non-attributable failure encountered on route {}",
1236+
path.hops.iter().map(|h| h.pubkey.to_string()).collect::<Vec<_>>().join("->")
1237+
);
1238+
12221239
DecodedOnionFailure {
12231240
network_update: None,
12241241
short_channel_id: None,
@@ -2155,6 +2172,67 @@ mod tests {
21552172
assert_eq!(decrypted_failure.onion_error_code, Some(0x2002));
21562173
}
21572174

2175+
#[test]
2176+
fn test_non_attributable_failure_packet_onion() {
2177+
// Create a failure packet with bogus data.
2178+
let packet = vec![1u8; 292];
2179+
2180+
// In the current protocol, it is unfortunately not possible to identify the failure source.
2181+
let logger = TestLogger::new();
2182+
let decrypted_failure = test_failure_attribution(&logger, &packet);
2183+
assert_eq!(decrypted_failure.short_channel_id, None);
2184+
2185+
logger.assert_log_contains(
2186+
"lightning::ln::onion_utils",
2187+
"Non-attributable failure encountered",
2188+
1,
2189+
);
2190+
}
2191+
2192+
#[test]
2193+
fn test_missing_error_code() {
2194+
// Create a failure packet with a valid hmac and structure, but no error code.
2195+
let onion_keys: Vec<OnionKeys> = build_test_onion_keys();
2196+
let shared_secret = onion_keys[0].shared_secret.as_ref();
2197+
let um = gen_um_from_shared_secret(&shared_secret);
2198+
2199+
let failuremsg = vec![1];
2200+
let pad = Vec::new();
2201+
let mut packet = msgs::DecodedOnionErrorPacket { hmac: [0; 32], failuremsg, pad };
2202+
2203+
let mut hmac = HmacEngine::<Sha256>::new(&um);
2204+
hmac.input(&packet.encode()[32..]);
2205+
packet.hmac = Hmac::from_engine(hmac).to_byte_array();
2206+
2207+
let packet = encrypt_failure_packet(shared_secret, &packet.encode()[..]);
2208+
2209+
let logger = TestLogger::new();
2210+
let decrypted_failure = test_failure_attribution(&logger, &packet.data);
2211+
assert_eq!(decrypted_failure.short_channel_id, Some(0));
2212+
2213+
logger.assert_log_contains(
2214+
"lightning::ln::onion_utils",
2215+
"Missing error code in failure",
2216+
1,
2217+
);
2218+
}
2219+
2220+
fn test_failure_attribution(logger: &TestLogger, packet: &[u8]) -> DecodedOnionFailure {
2221+
let ctx_full = Secp256k1::new();
2222+
let path = build_test_path();
2223+
let htlc_source = HTLCSource::OutboundRoute {
2224+
path,
2225+
session_priv: get_test_session_key(),
2226+
first_hop_htlc_msat: 0,
2227+
payment_id: PaymentId([1; 32]),
2228+
};
2229+
2230+
let decrypted_failure =
2231+
process_onion_failure(&ctx_full, &logger, &htlc_source, packet.into());
2232+
2233+
decrypted_failure
2234+
}
2235+
21582236
struct RawOnionHopData {
21592237
data: Vec<u8>,
21602238
}

0 commit comments

Comments
 (0)