/// the hops can be of variable length.
pub(crate) const ONION_DATA_LEN: usize = 20*65;
+pub(super) const INVALID_ONION_BLINDING: u16 = 0x8000 | 0x4000 | 24;
+
#[inline]
fn shift_slice_right(arr: &mut [u8], amt: usize) {
for i in (amt..arr.len()).rev() {
pub(crate) onion_error_data: Option<Vec<u8>>,
}
+/// Note that we always decrypt `packet` in-place here even if the deserialization into
+/// [`msgs::DecodedOnionErrorPacket`] ultimately fails.
+fn decrypt_onion_error_packet(
+ packet: &mut Vec<u8>, shared_secret: SharedSecret
+) -> Result<msgs::DecodedOnionErrorPacket, msgs::DecodeError> {
+ let ammag = gen_ammag_from_shared_secret(shared_secret.as_ref());
+ let mut chacha = ChaCha20::new(&ammag, &[0u8; 8]);
+ chacha.process_in_place(packet);
+ msgs::DecodedOnionErrorPacket::read(&mut Cursor::new(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 packet_decrypted: Vec<u8>
+ secp_ctx: &Secp256k1<T>, logger: &L, htlc_source: &HTLCSource, mut encrypted_packet: Vec<u8>
) -> DecodedOnionFailure where L::Target: Logger {
let (path, session_priv, first_hop_htlc_msat) = if let &HTLCSource::OutboundRoute {
ref path, ref session_priv, ref first_hop_htlc_msat, ..
Some(hop) => hop,
None => {
// The failing hop is within a multi-hop blinded path.
- error_code_ret = Some(BADONION | PERM | 24); // invalid_onion_blinding
- error_packet_ret = Some(vec![0; 32]);
+ #[cfg(not(test))] {
+ error_code_ret = Some(BADONION | PERM | 24); // invalid_onion_blinding
+ error_packet_ret = Some(vec![0; 32]);
+ }
+ #[cfg(test)] {
+ // Actually parse the onion error data in tests so we can check that blinded hops fail
+ // back correctly.
+ let err_packet = decrypt_onion_error_packet(
+ &mut encrypted_packet, shared_secret
+ ).unwrap();
+ error_code_ret =
+ Some(u16::from_be_bytes(err_packet.failuremsg.get(0..2).unwrap().try_into().unwrap()));
+ error_packet_ret = Some(err_packet.failuremsg[2..].to_vec());
+ }
+
res = Some(FailureLearnings {
network_update: None, short_channel_id: None, payment_failed_permanently: false
});
let amt_to_forward = htlc_msat - route_hop.fee_msat;
htlc_msat = amt_to_forward;
- let ammag = gen_ammag_from_shared_secret(shared_secret.as_ref());
-
- let mut decryption_tmp = Vec::with_capacity(packet_decrypted.len());
- decryption_tmp.resize(packet_decrypted.len(), 0);
- let mut chacha = ChaCha20::new(&ammag, &[0u8; 8]);
- chacha.process(&packet_decrypted, &mut decryption_tmp[..]);
- packet_decrypted = decryption_tmp;
-
- let err_packet = match msgs::DecodedOnionErrorPacket::read(&mut Cursor::new(&packet_decrypted)) {
+ let err_packet = match decrypt_onion_error_packet(&mut encrypted_packet, shared_secret) {
Ok(p) => p,
Err(_) => return
};
}
#[derive(Clone)] // See Channel::revoke_and_ack for why, tl;dr: Rust bug
+#[cfg_attr(test, derive(PartialEq))]
pub(super) struct HTLCFailReason(HTLCFailReasonRepr);
#[derive(Clone)] // See Channel::revoke_and_ack for why, tl;dr: Rust bug
+#[cfg_attr(test, derive(PartialEq))]
enum HTLCFailReasonRepr {
LightningError {
err: msgs::OnionErrorPacket,