Refactor onion_utils to encrypt/decrypt OnionErrorPacket types

Prepares for extending OnionErrorPacket with attribution data.
This commit is contained in:
Joost Jager 2025-03-05 17:58:22 +01:00
parent 4c43a5b3df
commit ea0f099ddb
2 changed files with 112 additions and 107 deletions

View file

@ -28,7 +28,7 @@ use crate::ln::msgs::{
OutboundOnionPayload, OutboundTrampolinePayload, MessageSendEvent,
};
use crate::ln::wire::Encode;
use crate::util::ser::{Writeable, Writer, BigSize};
use crate::util::ser::{BigSize, Writeable, Writer};
use crate::util::test_utils;
use crate::util::config::{UserConfig, ChannelConfig, MaxDustHTLCExposure};
use crate::util::errors::APIError;
@ -49,6 +49,8 @@ use crate::blinded_path::BlindedHop;
use crate::ln::functional_test_utils::*;
use crate::ln::onion_utils::{construct_trampoline_onion_keys, construct_trampoline_onion_packet};
use super::msgs::OnionErrorPacket;
fn run_onion_failure_test<F1,F2>(_name: &str, test_case: u8, nodes: &Vec<Node>, route: &Route, payment_hash: &PaymentHash, payment_secret: &PaymentSecret, callback_msg: F1, callback_node: F2, expected_retryable: bool, expected_error_code: Option<u16>, expected_channel_update: Option<NetworkUpdate>, expected_short_channel_id: Option<u64>, expected_htlc_destination: Option<HTLCDestination>)
where F1: for <'a> FnMut(&'a mut msgs::UpdateAddHTLC),
F2: FnMut(),
@ -409,7 +411,7 @@ fn test_onion_failure() {
// and tamper returning error message
let session_priv = SecretKey::from_slice(&[3; 32]).unwrap();
let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap();
msg.reason = onion_utils::build_first_hop_failure_packet(onion_keys[0].shared_secret.as_ref(), NODE|2, &[0;0]);
msg.reason = onion_utils::build_failure_packet(onion_keys[0].shared_secret.as_ref(), NODE|2, &[0;0]);
}, ||{}, true, Some(NODE|2), Some(NetworkUpdate::NodeFailure{node_id: route.paths[0].hops[0].pubkey, is_permanent: false}), Some(route.paths[0].hops[0].short_channel_id), Some(next_hop_failure.clone()));
// final node failure
@ -417,7 +419,7 @@ fn test_onion_failure() {
// and tamper returning error message
let session_priv = SecretKey::from_slice(&[3; 32]).unwrap();
let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap();
msg.reason = onion_utils::build_first_hop_failure_packet(onion_keys[1].shared_secret.as_ref(), NODE|2, &[0;0]);
msg.reason = onion_utils::build_failure_packet(onion_keys[1].shared_secret.as_ref(), NODE|2, &[0;0]);
}, ||{
nodes[2].node.fail_htlc_backwards(&payment_hash);
}, true, Some(NODE|2), Some(NetworkUpdate::NodeFailure{node_id: route.paths[0].hops[1].pubkey, is_permanent: false}), Some(route.paths[0].hops[1].short_channel_id), None);
@ -429,14 +431,14 @@ fn test_onion_failure() {
}, |msg| {
let session_priv = SecretKey::from_slice(&[3; 32]).unwrap();
let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap();
msg.reason = onion_utils::build_first_hop_failure_packet(onion_keys[0].shared_secret.as_ref(), PERM|NODE|2, &[0;0]);
msg.reason = onion_utils::build_failure_packet(onion_keys[0].shared_secret.as_ref(), PERM|NODE|2, &[0;0]);
}, ||{}, true, Some(PERM|NODE|2), Some(NetworkUpdate::NodeFailure{node_id: route.paths[0].hops[0].pubkey, is_permanent: true}), Some(route.paths[0].hops[0].short_channel_id), Some(next_hop_failure.clone()));
// final node failure
run_onion_failure_test_with_fail_intercept("permanent_node_failure", 200, &nodes, &route, &payment_hash, &payment_secret, |_msg| {}, |msg| {
let session_priv = SecretKey::from_slice(&[3; 32]).unwrap();
let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap();
msg.reason = onion_utils::build_first_hop_failure_packet(onion_keys[1].shared_secret.as_ref(), PERM|NODE|2, &[0;0]);
msg.reason = onion_utils::build_failure_packet(onion_keys[1].shared_secret.as_ref(), PERM|NODE|2, &[0;0]);
}, ||{
nodes[2].node.fail_htlc_backwards(&payment_hash);
}, false, Some(PERM|NODE|2), Some(NetworkUpdate::NodeFailure{node_id: route.paths[0].hops[1].pubkey, is_permanent: true}), Some(route.paths[0].hops[1].short_channel_id), None);
@ -448,7 +450,7 @@ fn test_onion_failure() {
}, |msg| {
let session_priv = SecretKey::from_slice(&[3; 32]).unwrap();
let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap();
msg.reason = onion_utils::build_first_hop_failure_packet(onion_keys[0].shared_secret.as_ref(), PERM|NODE|3, &[0;0]);
msg.reason = onion_utils::build_failure_packet(onion_keys[0].shared_secret.as_ref(), PERM|NODE|3, &[0;0]);
}, ||{
nodes[2].node.fail_htlc_backwards(&payment_hash);
}, true, Some(PERM|NODE|3), Some(NetworkUpdate::NodeFailure{node_id: route.paths[0].hops[0].pubkey, is_permanent: true}), Some(route.paths[0].hops[0].short_channel_id), Some(next_hop_failure.clone()));
@ -457,7 +459,7 @@ fn test_onion_failure() {
run_onion_failure_test_with_fail_intercept("required_node_feature_missing", 200, &nodes, &route, &payment_hash, &payment_secret, |_msg| {}, |msg| {
let session_priv = SecretKey::from_slice(&[3; 32]).unwrap();
let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap();
msg.reason = onion_utils::build_first_hop_failure_packet(onion_keys[1].shared_secret.as_ref(), PERM|NODE|3, &[0;0]);
msg.reason = onion_utils::build_failure_packet(onion_keys[1].shared_secret.as_ref(), PERM|NODE|3, &[0;0]);
}, ||{
nodes[2].node.fail_htlc_backwards(&payment_hash);
}, false, Some(PERM|NODE|3), Some(NetworkUpdate::NodeFailure{node_id: route.paths[0].hops[1].pubkey, is_permanent: true}), Some(route.paths[0].hops[1].short_channel_id), None);
@ -487,7 +489,7 @@ fn test_onion_failure() {
}, |msg| {
let session_priv = SecretKey::from_slice(&[3; 32]).unwrap();
let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap();
msg.reason = onion_utils::build_first_hop_failure_packet(onion_keys[0].shared_secret.as_ref(), UPDATE|7, &err_data);
msg.reason = onion_utils::build_failure_packet(onion_keys[0].shared_secret.as_ref(), UPDATE|7, &err_data);
}, ||{}, true, Some(UPDATE|7),
Some(NetworkUpdate::ChannelFailure { short_channel_id, is_permanent: false }),
Some(short_channel_id), Some(next_hop_failure.clone()));
@ -499,7 +501,7 @@ fn test_onion_failure() {
}, |msg| {
let session_priv = SecretKey::from_slice(&[3; 32]).unwrap();
let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap();
msg.reason = onion_utils::build_first_hop_failure_packet(onion_keys[0].shared_secret.as_ref(), UPDATE|7, &err_data_without_type);
msg.reason = onion_utils::build_failure_packet(onion_keys[0].shared_secret.as_ref(), UPDATE|7, &err_data_without_type);
}, ||{}, true, Some(UPDATE|7),
Some(NetworkUpdate::ChannelFailure { short_channel_id, is_permanent: false }),
Some(short_channel_id), Some(next_hop_failure.clone()));
@ -510,7 +512,7 @@ fn test_onion_failure() {
}, |msg| {
let session_priv = SecretKey::from_slice(&[3; 32]).unwrap();
let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap();
msg.reason = onion_utils::build_first_hop_failure_packet(onion_keys[0].shared_secret.as_ref(), PERM|8, &[0;0]);
msg.reason = onion_utils::build_failure_packet(onion_keys[0].shared_secret.as_ref(), PERM|8, &[0;0]);
// short_channel_id from the processing node
}, ||{}, true, Some(PERM|8), Some(NetworkUpdate::ChannelFailure{short_channel_id, is_permanent: true}), Some(short_channel_id), Some(next_hop_failure.clone()));
@ -520,7 +522,7 @@ fn test_onion_failure() {
}, |msg| {
let session_priv = SecretKey::from_slice(&[3; 32]).unwrap();
let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap();
msg.reason = onion_utils::build_first_hop_failure_packet(onion_keys[0].shared_secret.as_ref(), PERM|9, &[0;0]);
msg.reason = onion_utils::build_failure_packet(onion_keys[0].shared_secret.as_ref(), PERM|9, &[0;0]);
// short_channel_id from the processing node
}, ||{}, true, Some(PERM|9), Some(NetworkUpdate::ChannelFailure{short_channel_id, is_permanent: true}), Some(short_channel_id), Some(next_hop_failure.clone()));
@ -652,7 +654,7 @@ fn test_onion_failure() {
// Tamper returning error message
let session_priv = SecretKey::from_slice(&[3; 32]).unwrap();
let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap();
msg.reason = onion_utils::build_first_hop_failure_packet(onion_keys[1].shared_secret.as_ref(), 23, &[0;0]);
msg.reason = onion_utils::build_failure_packet(onion_keys[1].shared_secret.as_ref(), 23, &[0;0]);
}, ||{
nodes[2].node.fail_htlc_backwards(&payment_hash);
}, true, Some(23), None, None, None);
@ -670,8 +672,12 @@ fn test_onion_failure() {
let mut hmac = HmacEngine::<Sha256>::new(&um);
hmac.input(&decoded_err_packet.encode()[32..]);
decoded_err_packet.hmac = Hmac::from_engine(hmac).to_byte_array();
msg.reason = onion_utils::encrypt_failure_packet(
&onion_keys[1].shared_secret.as_ref(), &decoded_err_packet.encode()[..])
let mut onion_error = OnionErrorPacket {
data: decoded_err_packet.encode(),
};
onion_utils::test_crypt_failure_packet(
&onion_keys[1].shared_secret.as_ref(), &mut onion_error);
msg.reason = onion_error;
}, || nodes[2].node.fail_htlc_backwards(&payment_hash), false, None,
Some(NetworkUpdate::NodeFailure { node_id: route.paths[0].hops[1].pubkey, is_permanent: true }),
Some(channels[1].0.contents.short_channel_id), None);
@ -693,8 +699,12 @@ fn test_onion_failure() {
let mut hmac = HmacEngine::<Sha256>::new(&um);
hmac.input(&decoded_err_packet.encode()[32..]);
decoded_err_packet.hmac = Hmac::from_engine(hmac).to_byte_array();
msg.reason = onion_utils::encrypt_failure_packet(
&onion_keys[0].shared_secret.as_ref(), &decoded_err_packet.encode()[..])
let mut onion_error = OnionErrorPacket{
data: decoded_err_packet.encode(),
};
onion_utils::test_crypt_failure_packet(
&onion_keys[0].shared_secret.as_ref(), &mut onion_error);
msg.reason = onion_error;
}, || {}, true, Some(0x1000|7),
Some(NetworkUpdate::ChannelFailure {
short_channel_id: channels[1].0.contents.short_channel_id,
@ -717,8 +727,12 @@ fn test_onion_failure() {
let mut hmac = HmacEngine::<Sha256>::new(&um);
hmac.input(&decoded_err_packet.encode()[32..]);
decoded_err_packet.hmac = Hmac::from_engine(hmac).to_byte_array();
msg.reason = onion_utils::encrypt_failure_packet(
&onion_keys[1].shared_secret.as_ref(), &decoded_err_packet.encode()[..])
let mut onion_error = OnionErrorPacket{
data: decoded_err_packet.encode(),
};
onion_utils::test_crypt_failure_packet(
&onion_keys[1].shared_secret.as_ref(), &mut onion_error);
msg.reason = onion_error;
}, || nodes[2].node.fail_htlc_backwards(&payment_hash), true, Some(0x1000|7),
Some(NetworkUpdate::ChannelFailure {
short_channel_id: channels[1].0.contents.short_channel_id,

View file

@ -7,6 +7,7 @@
// You may not use this file except in accordance with one or both of these
// licenses.
use super::msgs::OnionErrorPacket;
use crate::blinded_path::BlindedHop;
use crate::crypto::chacha20::ChaCha20;
use crate::crypto::streams::ChaChaReader;
@ -869,23 +870,25 @@ fn construct_onion_packet_with_init_noise<HD: Writeable, P: Packet>(
Ok(P::new(onion_keys.first().unwrap().ephemeral_pubkey, packet_data, hmac_res))
}
/// Encrypts a failure packet. raw_packet can either be a
/// msgs::DecodedOnionErrorPacket.encode() result or a msgs::OnionErrorPacket.data element.
pub(super) fn encrypt_failure_packet(
shared_secret: &[u8], raw_packet: &[u8],
) -> msgs::OnionErrorPacket {
/// Encrypts/decrypts a failure packet.
fn crypt_failure_packet(shared_secret: &[u8], packet: &mut OnionErrorPacket) {
let ammag = gen_ammag_from_shared_secret(&shared_secret);
let mut packet_crypted = Vec::with_capacity(raw_packet.len());
packet_crypted.resize(raw_packet.len(), 0);
let mut chacha = ChaCha20::new(&ammag, &[0u8; 8]);
chacha.process(&raw_packet, &mut packet_crypted[..]);
msgs::OnionErrorPacket { data: packet_crypted }
process_chacha(&ammag, &mut packet.data);
}
pub(super) fn build_failure_packet(
#[cfg(test)]
pub(super) fn test_crypt_failure_packet(shared_secret: &[u8], packet: &mut OnionErrorPacket) {
crypt_failure_packet(shared_secret, packet)
}
fn process_chacha(key: &[u8; 32], packet: &mut [u8]) {
let mut chacha = ChaCha20::new(key, &[0u8; 8]);
chacha.process_in_place(packet);
}
fn build_unencrypted_failure_packet(
shared_secret: &[u8], failure_type: u16, failure_data: &[u8],
) -> msgs::DecodedOnionErrorPacket {
) -> OnionErrorPacket {
assert_eq!(shared_secret.len(), 32);
assert!(failure_data.len() <= 256 - 2);
@ -909,15 +912,18 @@ pub(super) fn build_failure_packet(
hmac.input(&packet.encode()[32..]);
packet.hmac = Hmac::from_engine(hmac).to_byte_array();
packet
OnionErrorPacket { data: packet.encode() }
}
#[cfg(test)]
pub(super) fn build_first_hop_failure_packet(
pub(super) fn build_failure_packet(
shared_secret: &[u8], failure_type: u16, failure_data: &[u8],
) -> msgs::OnionErrorPacket {
let failure_packet = build_failure_packet(shared_secret, failure_type, failure_data);
encrypt_failure_packet(shared_secret, &failure_packet.encode()[..])
) -> OnionErrorPacket {
let mut onion_error_packet =
build_unencrypted_failure_packet(shared_secret, failure_type, failure_data);
crypt_failure_packet(shared_secret, &mut onion_error_packet);
onion_error_packet
}
pub(crate) struct DecodedOnionFailure {
@ -931,18 +937,12 @@ pub(crate) struct DecodedOnionFailure {
pub(crate) onion_error_data: Option<Vec<u8>>,
}
/// Decrypt the error packet in-place.
fn decrypt_onion_error_packet(packet: &mut Vec<u8>, shared_secret: SharedSecret) {
let ammag = gen_ammag_from_shared_secret(shared_secret.as_ref());
let mut chacha = ChaCha20::new(&ammag, &[0u8; 8]);
chacha.process_in_place(packet);
}
/// Process failure we got back from upstream on a payment we sent (implying htlc_source is an
/// OutboundRoute).
#[inline]
pub(super) fn process_onion_failure<T: secp256k1::Signing, L: Deref>(
secp_ctx: &Secp256k1<T>, logger: &L, htlc_source: &HTLCSource, mut encrypted_packet: Vec<u8>,
secp_ctx: &Secp256k1<T>, logger: &L, htlc_source: &HTLCSource,
mut encrypted_packet: OnionErrorPacket,
) -> DecodedOnionFailure
where
L::Target: Logger,
@ -975,7 +975,11 @@ where
const UPDATE: u16 = 0x1000;
// Handle packed channel/node updates for passing back for the route handler
let callback = |shared_secret, _, _, route_hop_opt: Option<&RouteHop>, route_hop_idx| {
let callback = |shared_secret: SharedSecret,
_,
_,
route_hop_opt: Option<&RouteHop>,
route_hop_idx| {
if res.is_some() {
return;
}
@ -1017,9 +1021,9 @@ where
{
// Actually parse the onion error data in tests so we can check that blinded hops fail
// back correctly.
decrypt_onion_error_packet(&mut encrypted_packet, shared_secret);
crypt_failure_packet(shared_secret.as_ref(), &mut encrypted_packet);
let err_packet = msgs::DecodedOnionErrorPacket::read(&mut Cursor::new(
&encrypted_packet,
&encrypted_packet.data,
))
.unwrap();
error_code_ret = Some(u16::from_be_bytes(
@ -1042,18 +1046,18 @@ where
let amt_to_forward = htlc_msat - route_hop.fee_msat;
htlc_msat = amt_to_forward;
decrypt_onion_error_packet(&mut encrypted_packet, shared_secret);
crypt_failure_packet(shared_secret.as_ref(), &mut encrypted_packet);
let um = gen_um_from_shared_secret(shared_secret.as_ref());
let mut hmac = HmacEngine::<Sha256>::new(&um);
hmac.input(&encrypted_packet[32..]);
hmac.input(&encrypted_packet.data[32..]);
if !fixed_time_eq(&Hmac::from_engine(hmac).to_byte_array(), &encrypted_packet[..32]) {
if !fixed_time_eq(&Hmac::from_engine(hmac).to_byte_array(), &encrypted_packet.data[..32]) {
return;
}
let err_packet =
match msgs::DecodedOnionErrorPacket::read(&mut Cursor::new(&encrypted_packet)) {
match msgs::DecodedOnionErrorPacket::read(&mut Cursor::new(&encrypted_packet.data)) {
Ok(p) => p,
Err(_) => {
log_warn!(logger, "Unreadable failure from {}", route_hop.pubkey);
@ -1366,27 +1370,22 @@ impl HTLCFailReason {
match self.0 {
HTLCFailReasonRepr::Reason { ref failure_code, ref data } => {
if let Some(secondary_shared_secret) = secondary_shared_secret {
let inner_packet =
build_failure_packet(secondary_shared_secret, *failure_code, &data[..])
.encode();
let encrypted_inner_packet =
encrypt_failure_packet(secondary_shared_secret, &inner_packet);
encrypt_failure_packet(
incoming_packet_shared_secret,
&encrypted_inner_packet.data[..],
)
let mut packet =
build_failure_packet(secondary_shared_secret, *failure_code, &data[..]);
crypt_failure_packet(incoming_packet_shared_secret, &mut packet);
packet
} else {
let packet = build_failure_packet(
incoming_packet_shared_secret,
*failure_code,
&data[..],
)
.encode();
encrypt_failure_packet(incoming_packet_shared_secret, &packet)
build_failure_packet(incoming_packet_shared_secret, *failure_code, &data[..])
}
},
HTLCFailReasonRepr::LightningError { ref err } => {
encrypt_failure_packet(incoming_packet_shared_secret, &err.data)
let mut err = err.clone();
crypt_failure_packet(incoming_packet_shared_secret, &mut err);
err
},
}
}
@ -1399,7 +1398,7 @@ impl HTLCFailReason {
{
match self.0 {
HTLCFailReasonRepr::LightningError { ref err } => {
process_onion_failure(secp_ctx, logger, &htlc_source, err.data.clone())
process_onion_failure(secp_ctx, logger, &htlc_source, err.clone())
},
#[allow(unused)]
HTLCFailReasonRepr::Reason { ref failure_code, ref data, .. } => {
@ -2287,45 +2286,33 @@ mod tests {
// Returning Errors test vectors from BOLT 4
let onion_keys = build_test_onion_keys();
let onion_error =
super::build_failure_packet(onion_keys[4].shared_secret.as_ref(), 0x2002, &[0; 0]);
let hex = "4c2fc8bc08510334b6833ad9c3e79cd1b52ae59dfe5c2a4b23ead50f09f7ee0b0002200200fe0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
assert_eq!(onion_error.encode(), <Vec<u8>>::from_hex(hex).unwrap());
let onion_packet_1 = super::encrypt_failure_packet(
let mut onion_error = super::build_unencrypted_failure_packet(
onion_keys[4].shared_secret.as_ref(),
&onion_error.encode()[..],
0x2002,
&[0; 0],
);
let hex = "4c2fc8bc08510334b6833ad9c3e79cd1b52ae59dfe5c2a4b23ead50f09f7ee0b0002200200fe0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
assert_eq!(onion_error.data, <Vec<u8>>::from_hex(hex).unwrap());
super::crypt_failure_packet(onion_keys[4].shared_secret.as_ref(), &mut onion_error);
let hex = "a5e6bd0c74cb347f10cce367f949098f2457d14c046fd8a22cb96efb30b0fdcda8cb9168b50f2fd45edd73c1b0c8b33002df376801ff58aaa94000bf8a86f92620f343baef38a580102395ae3abf9128d1047a0736ff9b83d456740ebbb4aeb3aa9737f18fb4afb4aa074fb26c4d702f42968888550a3bded8c05247e045b866baef0499f079fdaeef6538f31d44deafffdfd3afa2fb4ca9082b8f1c465371a9894dd8c243fb4847e004f5256b3e90e2edde4c9fb3082ddfe4d1e734cacd96ef0706bf63c9984e22dc98851bcccd1c3494351feb458c9c6af41c0044bea3c47552b1d992ae542b17a2d0bba1a096c78d169034ecb55b6e3a7263c26017f033031228833c1daefc0dedb8cf7c3e37c9c37ebfe42f3225c326e8bcfd338804c145b16e34e4";
assert_eq!(onion_packet_1.data, <Vec<u8>>::from_hex(hex).unwrap());
assert_eq!(onion_error.data, <Vec<u8>>::from_hex(hex).unwrap());
let onion_packet_2 = super::encrypt_failure_packet(
onion_keys[3].shared_secret.as_ref(),
&onion_packet_1.data[..],
);
super::crypt_failure_packet(onion_keys[3].shared_secret.as_ref(), &mut onion_error);
let hex = "c49a1ce81680f78f5f2000cda36268de34a3f0a0662f55b4e837c83a8773c22aa081bab1616a0011585323930fa5b9fae0c85770a2279ff59ec427ad1bbff9001c0cd1497004bd2a0f68b50704cf6d6a4bf3c8b6a0833399a24b3456961ba00736785112594f65b6b2d44d9f5ea4e49b5e1ec2af978cbe31c67114440ac51a62081df0ed46d4a3df295da0b0fe25c0115019f03f15ec86fabb4c852f83449e812f141a9395b3f70b766ebbd4ec2fae2b6955bd8f32684c15abfe8fd3a6261e52650e8807a92158d9f1463261a925e4bfba44bd20b166d532f0017185c3a6ac7957adefe45559e3072c8dc35abeba835a8cb01a71a15c736911126f27d46a36168ca5ef7dccd4e2886212602b181463e0dd30185c96348f9743a02aca8ec27c0b90dca270";
assert_eq!(onion_packet_2.data, <Vec<u8>>::from_hex(hex).unwrap());
assert_eq!(onion_error.data, <Vec<u8>>::from_hex(hex).unwrap());
let onion_packet_3 = super::encrypt_failure_packet(
onion_keys[2].shared_secret.as_ref(),
&onion_packet_2.data[..],
);
super::crypt_failure_packet(onion_keys[2].shared_secret.as_ref(), &mut onion_error);
let hex = "a5d3e8634cfe78b2307d87c6d90be6fe7855b4f2cc9b1dfb19e92e4b79103f61ff9ac25f412ddfb7466e74f81b3e545563cdd8f5524dae873de61d7bdfccd496af2584930d2b566b4f8d3881f8c043df92224f38cf094cfc09d92655989531524593ec6d6caec1863bdfaa79229b5020acc034cd6deeea1021c50586947b9b8e6faa83b81fbfa6133c0af5d6b07c017f7158fa94f0d206baf12dda6b68f785b773b360fd0497e16cc402d779c8d48d0fa6315536ef0660f3f4e1865f5b38ea49c7da4fd959de4e83ff3ab686f059a45c65ba2af4a6a79166aa0f496bf04d06987b6d2ea205bdb0d347718b9aeff5b61dfff344993a275b79717cd815b6ad4c0beb568c4ac9c36ff1c315ec1119a1993c4b61e6eaa0375e0aaf738ac691abd3263bf937e3";
assert_eq!(onion_packet_3.data, <Vec<u8>>::from_hex(hex).unwrap());
assert_eq!(onion_error.data, <Vec<u8>>::from_hex(hex).unwrap());
let onion_packet_4 = super::encrypt_failure_packet(
onion_keys[1].shared_secret.as_ref(),
&onion_packet_3.data[..],
);
super::crypt_failure_packet(onion_keys[1].shared_secret.as_ref(), &mut onion_error);
let hex = "aac3200c4968f56b21f53e5e374e3a2383ad2b1b6501bbcc45abc31e59b26881b7dfadbb56ec8dae8857add94e6702fb4c3a4de22e2e669e1ed926b04447fc73034bb730f4932acd62727b75348a648a1128744657ca6a4e713b9b646c3ca66cac02cdab44dd3439890ef3aaf61708714f7375349b8da541b2548d452d84de7084bb95b3ac2345201d624d31f4d52078aa0fa05a88b4e20202bd2b86ac5b52919ea305a8949de95e935eed0319cf3cf19ebea61d76ba92532497fcdc9411d06bcd4275094d0a4a3c5d3a945e43305a5a9256e333e1f64dbca5fcd4e03a39b9012d197506e06f29339dfee3331995b21615337ae060233d39befea925cc262873e0530408e6990f1cbd233a150ef7b004ff6166c70c68d9f8c853c1abca640b8660db2921";
assert_eq!(onion_packet_4.data, <Vec<u8>>::from_hex(hex).unwrap());
assert_eq!(onion_error.data, <Vec<u8>>::from_hex(hex).unwrap());
let onion_packet_5 = super::encrypt_failure_packet(
onion_keys[0].shared_secret.as_ref(),
&onion_packet_4.data[..],
);
super::crypt_failure_packet(onion_keys[0].shared_secret.as_ref(), &mut onion_error);
let hex = "9c5add3963fc7f6ed7f148623c84134b5647e1306419dbe2174e523fa9e2fbed3a06a19f899145610741c83ad40b7712aefaddec8c6baf7325d92ea4ca4d1df8bce517f7e54554608bf2bd8071a4f52a7a2f7ffbb1413edad81eeea5785aa9d990f2865dc23b4bc3c301a94eec4eabebca66be5cf638f693ec256aec514620cc28ee4a94bd9565bc4d4962b9d3641d4278fb319ed2b84de5b665f307a2db0f7fbb757366067d88c50f7e829138fde4f78d39b5b5802f1b92a8a820865af5cc79f9f30bc3f461c66af95d13e5e1f0381c184572a91dee1c849048a647a1158cf884064deddbf1b0b88dfe2f791428d0ba0f6fb2f04e14081f69165ae66d9297c118f0907705c9c4954a199bae0bb96fad763d690e7daa6cfda59ba7f2c8d11448b604d12d";
assert_eq!(onion_packet_5.data, <Vec<u8>>::from_hex(hex).unwrap());
assert_eq!(onion_error.data, <Vec<u8>>::from_hex(hex).unwrap());
let logger: Arc<TestLogger> = Arc::new(TestLogger::new());
let ctx_full = Secp256k1::new();
@ -2339,7 +2326,7 @@ mod tests {
// Assert that the original failure can be retrieved and that all hmacs check out.
let decrypted_failure =
process_onion_failure(&ctx_full, &logger, &htlc_source, onion_packet_5.data);
process_onion_failure(&ctx_full, &logger, &htlc_source, onion_error);
assert_eq!(decrypted_failure.onion_error_code, Some(0x2002));
}
@ -2348,10 +2335,11 @@ mod tests {
fn test_non_attributable_failure_packet_onion() {
// Create a failure packet with bogus data.
let packet = vec![1u8; 292];
let onion_error_packet = OnionErrorPacket { data: packet };
// In the current protocol, it is unfortunately not possible to identify the failure source.
let logger: TestLogger = TestLogger::new();
let decrypted_failure = test_failure_attribution(&logger, &packet);
let decrypted_failure = test_failure_attribution(&logger, onion_error_packet);
assert_eq!(decrypted_failure.short_channel_id, None);
logger.assert_log_contains(
@ -2376,11 +2364,12 @@ mod tests {
let hmac = Hmac::from_engine(hmac).to_byte_array();
packet[..32].copy_from_slice(&hmac);
let packet = encrypt_failure_packet(shared_secret, &packet);
let mut onion_error_packet = OnionErrorPacket { data: packet.to_vec() };
crypt_failure_packet(shared_secret, &mut onion_error_packet);
// For the unreadable failure, it is still expected that the failing channel can be identified.
let logger: TestLogger = TestLogger::new();
let decrypted_failure = test_failure_attribution(&logger, &packet.data);
let decrypted_failure = test_failure_attribution(&logger, onion_error_packet);
assert_eq!(decrypted_failure.short_channel_id, Some(0));
logger.assert_log_contains("lightning::ln::onion_utils", "Unreadable failure", 1);
@ -2401,10 +2390,11 @@ mod tests {
hmac.input(&packet.encode()[32..]);
packet.hmac = Hmac::from_engine(hmac).to_byte_array();
let packet = encrypt_failure_packet(shared_secret, &packet.encode()[..]);
let mut onion_error_packet = OnionErrorPacket { data: packet.encode() };
crypt_failure_packet(shared_secret, &mut onion_error_packet);
let logger = TestLogger::new();
let decrypted_failure = test_failure_attribution(&logger, &packet.data);
let decrypted_failure = test_failure_attribution(&logger, onion_error_packet);
assert_eq!(decrypted_failure.short_channel_id, Some(0));
logger.assert_log_contains(
@ -2414,7 +2404,9 @@ mod tests {
);
}
fn test_failure_attribution(logger: &TestLogger, packet: &[u8]) -> DecodedOnionFailure {
fn test_failure_attribution(
logger: &TestLogger, packet: OnionErrorPacket,
) -> DecodedOnionFailure {
let ctx_full = Secp256k1::new();
let path = build_test_path();
let htlc_source = HTLCSource::OutboundRoute {
@ -2424,8 +2416,7 @@ mod tests {
payment_id: PaymentId([1; 32]),
};
let decrypted_failure =
process_onion_failure(&ctx_full, &logger, &htlc_source, packet.into());
let decrypted_failure = process_onion_failure(&ctx_full, &logger, &htlc_source, packet);
decrypted_failure
}