Merge pull request #1398 from jkczyz/2022-03-middle-hop-fix
[rust-lightning] / lightning / src / ln / onion_utils.rs
index 8feef1697af40ad33032afe23b7bf5f77e8a6b21..0dd6087f82018d9faf56d53672325b76284caa00 100644 (file)
@@ -7,11 +7,12 @@
 // You may not use this file except in accordance with one or both of these
 // licenses.
 
-use ln::channelmanager::{PaymentHash, PaymentSecret, HTLCSource};
+use ln::{PaymentHash, PaymentPreimage, PaymentSecret};
+use ln::channelmanager::HTLCSource;
 use ln::msgs;
+use routing::network_graph::NetworkUpdate;
 use routing::router::RouteHop;
-use util::byte_utils;
-use util::chacha20::ChaCha20;
+use util::chacha20::{ChaCha20, ChaChaReader};
 use util::errors::{self, APIError};
 use util::ser::{Readable, Writeable, LengthCalculatingWriter};
 use util::logger::Logger;
@@ -26,8 +27,10 @@ use bitcoin::secp256k1::Secp256k1;
 use bitcoin::secp256k1::ecdh::SharedSecret;
 use bitcoin::secp256k1;
 
-use std::io::Cursor;
-use std::ops::Deref;
+use prelude::*;
+use io::{Cursor, Read};
+use core::convert::TryInto;
+use core::ops::Deref;
 
 pub(super) struct OnionKeys {
        #[cfg(test)]
@@ -72,11 +75,11 @@ pub(super) fn gen_ammag_from_shared_secret(shared_secret: &[u8]) -> [u8; 32] {
 
 // can only fail if an intermediary hop has an invalid public key or session_priv is invalid
 #[inline]
-pub(super) fn construct_onion_keys_callback<T: secp256k1::Signing, FType: FnMut(SharedSecret, [u8; 32], PublicKey, &RouteHop)> (secp_ctx: &Secp256k1<T>, path: &Vec<RouteHop>, session_priv: &SecretKey, mut callback: FType) -> Result<(), secp256k1::Error> {
+pub(super) fn construct_onion_keys_callback<T: secp256k1::Signing, FType: FnMut(SharedSecret, [u8; 32], PublicKey, &RouteHop, usize)> (secp_ctx: &Secp256k1<T>, path: &Vec<RouteHop>, session_priv: &SecretKey, mut callback: FType) -> Result<(), secp256k1::Error> {
        let mut blinded_priv = session_priv.clone();
        let mut blinded_pub = PublicKey::from_secret_key(secp_ctx, &blinded_priv);
 
-       for hop in path.iter() {
+       for (idx, hop) in path.iter().enumerate() {
                let shared_secret = SharedSecret::new(&hop.pubkey, &blinded_priv);
 
                let mut sha = Sha256::engine();
@@ -89,7 +92,7 @@ pub(super) fn construct_onion_keys_callback<T: secp256k1::Signing, FType: FnMut(
                blinded_priv.mul_assign(&blinding_factor)?;
                blinded_pub = PublicKey::from_secret_key(secp_ctx, &blinded_priv);
 
-               callback(shared_secret, blinding_factor, ephemeral_pubkey, hop);
+               callback(shared_secret, blinding_factor, ephemeral_pubkey, hop, idx);
        }
 
        Ok(())
@@ -99,7 +102,7 @@ pub(super) fn construct_onion_keys_callback<T: secp256k1::Signing, FType: FnMut(
 pub(super) fn construct_onion_keys<T: secp256k1::Signing>(secp_ctx: &Secp256k1<T>, path: &Vec<RouteHop>, session_priv: &SecretKey) -> Result<Vec<OnionKeys>, secp256k1::Error> {
        let mut res = Vec::with_capacity(path.len());
 
-       construct_onion_keys_callback(secp_ctx, path, session_priv, |shared_secret, _blinding_factor, ephemeral_pubkey, _| {
+       construct_onion_keys_callback(secp_ctx, path, session_priv, |shared_secret, _blinding_factor, ephemeral_pubkey, _, _| {
                let (rho, mu) = gen_rho_mu_from_shared_secret(&shared_secret[..]);
 
                res.push(OnionKeys {
@@ -117,7 +120,7 @@ pub(super) fn construct_onion_keys<T: secp256k1::Signing>(secp_ctx: &Secp256k1<T
 }
 
 /// returns the hop data, as well as the first-hop value_msat and CLTV value we should send.
-pub(super) fn build_onion_payloads(path: &Vec<RouteHop>, total_msat: u64, payment_secret_option: &Option<PaymentSecret>, starting_htlc_offset: u32) -> Result<(Vec<msgs::OnionHopData>, u64, u32), APIError> {
+pub(super) fn build_onion_payloads(path: &Vec<RouteHop>, total_msat: u64, payment_secret_option: &Option<PaymentSecret>, starting_htlc_offset: u32, keysend_preimage: &Option<PaymentPreimage>) -> Result<(Vec<msgs::OnionHopData>, u64, u32), APIError> {
        let mut cur_value_msat = 0u64;
        let mut cur_cltv = starting_htlc_offset;
        let mut last_short_channel_id = 0;
@@ -139,6 +142,7 @@ pub(super) fn build_onion_payloads(path: &Vec<RouteHop>, total_msat: u64, paymen
                                                                total_msat,
                                                        })
                                                } else { None },
+                                               keysend_preimage: *keysend_preimage,
                                        }
                                } else {
                                        msgs::OnionHopDataFormat::NonFinalNode {
@@ -325,20 +329,19 @@ pub(super) fn build_first_hop_failure_packet(shared_secret: &[u8], failure_type:
 
 /// Process failure we got back from upstream on a payment we sent (implying htlc_source is an
 /// OutboundRoute).
-/// Returns update, a boolean indicating that the payment itself failed, and the error code.
+/// Returns update, a boolean indicating that the payment itself failed, the short channel id of
+/// the responsible channel, and the error code.
 #[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>) -> (Option<msgs::HTLCFailChannelUpdate>, bool, Option<u16>, Option<Vec<u8>>) where L::Target: Logger {
-       if let &HTLCSource::OutboundRoute { ref path, ref session_priv, ref first_hop_htlc_msat } = htlc_source {
+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>) -> (Option<NetworkUpdate>, Option<u64>, bool, Option<u16>, Option<Vec<u8>>) where L::Target: Logger {
+       if let &HTLCSource::OutboundRoute { ref path, ref session_priv, ref first_hop_htlc_msat, .. } = htlc_source {
                let mut res = None;
                let mut htlc_msat = *first_hop_htlc_msat;
                let mut error_code_ret = None;
                let mut error_packet_ret = None;
-               let mut next_route_hop_ix = 0;
                let mut is_from_final_node = false;
 
                // Handle packed channel/node updates for passing back for the route handler
-               construct_onion_keys_callback(secp_ctx, path, session_priv, |shared_secret, _, _, route_hop| {
-                       next_route_hop_ix += 1;
+               construct_onion_keys_callback(secp_ctx, path, session_priv, |shared_secret, _, _, route_hop, route_hop_idx| {
                        if res.is_some() { return; }
 
                        let amt_to_forward = htlc_msat - route_hop.fee_msat;
@@ -352,7 +355,10 @@ pub(super) fn process_onion_failure<T: secp256k1::Signing, L: Deref>(secp_ctx: &
                        chacha.process(&packet_decrypted, &mut decryption_tmp[..]);
                        packet_decrypted = decryption_tmp;
 
-                       is_from_final_node = path.last().unwrap().pubkey == route_hop.pubkey;
+                       // The failing hop includes either the inbound channel to the recipient or the outbound
+                       // channel from the current hop (i.e., the next hop's inbound channel).
+                       is_from_final_node = route_hop_idx + 1 == path.len();
+                       let failing_route_hop = if is_from_final_node { route_hop } else { &path[route_hop_idx + 1] };
 
                        if let Ok(err_packet) = msgs::DecodedOnionErrorPacket::read(&mut Cursor::new(&packet_decrypted)) {
                                let um = gen_um_from_shared_secret(&shared_secret[..]);
@@ -365,7 +371,7 @@ pub(super) fn process_onion_failure<T: secp256k1::Signing, L: Deref>(secp_ctx: &
                                                const NODE: u16 = 0x2000;
                                                const UPDATE: u16 = 0x1000;
 
-                                               let error_code = byte_utils::slice_to_be16(&error_code_slice);
+                                               let error_code = u16::from_be_bytes(error_code_slice.try_into().expect("len is 2"));
                                                error_code_ret = Some(error_code);
                                                error_packet_ret = Some(err_packet.failuremsg[2..].to_vec());
 
@@ -373,26 +379,31 @@ pub(super) fn process_onion_failure<T: secp256k1::Signing, L: Deref>(secp_ctx: &
 
                                                // indicate that payment parameter has failed and no need to
                                                // update Route object
-                                               let payment_failed = (match error_code & 0xff {
-                                                       15|16|17|18|19 => true,
+                                               let payment_failed = match error_code & 0xff {
+                                                       15|16|17|18|19|23 => true,
                                                        _ => false,
-                                               } && is_from_final_node) // PERM bit observed below even this error is from the intermediate nodes
-                                               || error_code == 21; // Special case error 21 as the Route object is bogus, TODO: Maybe fail the node if the CLTV was reasonable?
+                                               } && is_from_final_node; // PERM bit observed below even if this error is from the intermediate nodes
 
-                                               let mut fail_channel_update = None;
+                                               let mut network_update = None;
+                                               let mut short_channel_id = None;
 
                                                if error_code & NODE == NODE {
-                                                       fail_channel_update = Some(msgs::HTLCFailChannelUpdate::NodeFailure { node_id: route_hop.pubkey, is_permanent: error_code & PERM == PERM });
+                                                       let is_permanent = error_code & PERM == PERM;
+                                                       network_update = Some(NetworkUpdate::NodeFailure { node_id: route_hop.pubkey, is_permanent });
+                                                       short_channel_id = Some(route_hop.short_channel_id);
                                                }
                                                else if error_code & PERM == PERM {
-                                                       fail_channel_update = if payment_failed {None} else {Some(msgs::HTLCFailChannelUpdate::ChannelClosed {
-                                                               short_channel_id: path[next_route_hop_ix - if next_route_hop_ix == path.len() { 1 } else { 0 }].short_channel_id,
-                                                               is_permanent: true,
-                                                       })};
+                                                       if !payment_failed {
+                                                               network_update = Some(NetworkUpdate::ChannelClosed {
+                                                                       short_channel_id: failing_route_hop.short_channel_id,
+                                                                       is_permanent: true,
+                                                               });
+                                                               short_channel_id = Some(failing_route_hop.short_channel_id);
+                                                       }
                                                }
                                                else if error_code & UPDATE == UPDATE {
                                                        if let Some(update_len_slice) = err_packet.failuremsg.get(debug_field_size+2..debug_field_size+4) {
-                                                               let update_len = byte_utils::slice_to_be16(&update_len_slice) as usize;
+                                                               let update_len = u16::from_be_bytes(update_len_slice.try_into().expect("len is 2")) as usize;
                                                                if let Some(update_slice) = err_packet.failuremsg.get(debug_field_size + 4..debug_field_size + 4 + update_len) {
                                                                        if let Ok(chan_update) = msgs::ChannelUpdate::read(&mut Cursor::new(&update_slice)) {
                                                                                // if channel_update should NOT have caused the failure:
@@ -400,52 +411,70 @@ pub(super) fn process_onion_failure<T: secp256k1::Signing, L: Deref>(secp_ctx: &
                                                                                let is_chan_update_invalid = match error_code & 0xff {
                                                                                        7 => false,
                                                                                        11 => amt_to_forward > chan_update.contents.htlc_minimum_msat,
-                                                                                       12 => {
-                                                                                               let new_fee = amt_to_forward.checked_mul(chan_update.contents.fee_proportional_millionths as u64).and_then(|prop_fee| { (prop_fee / 1000000).checked_add(chan_update.contents.fee_base_msat as u64) });
-                                                                                               new_fee.is_some() && route_hop.fee_msat >= new_fee.unwrap()
-                                                                                       }
+                                                                                       12 => amt_to_forward
+                                                                                               .checked_mul(chan_update.contents.fee_proportional_millionths as u64)
+                                                                                               .map(|prop_fee| prop_fee / 1_000_000)
+                                                                                               .and_then(|prop_fee| prop_fee.checked_add(chan_update.contents.fee_base_msat as u64))
+                                                                                               .map(|fee_msats| route_hop.fee_msat >= fee_msats)
+                                                                                               .unwrap_or(false),
                                                                                        13 => route_hop.cltv_expiry_delta as u16 >= chan_update.contents.cltv_expiry_delta,
                                                                                        14 => false, // expiry_too_soon; always valid?
                                                                                        20 => chan_update.contents.flags & 2 == 0,
                                                                                        _ => false, // unknown error code; take channel_update as valid
                                                                                };
-                                                                               fail_channel_update = if is_chan_update_invalid {
+                                                                               if is_chan_update_invalid {
                                                                                        // This probably indicates the node which forwarded
                                                                                        // to the node in question corrupted something.
-                                                                                       Some(msgs::HTLCFailChannelUpdate::ChannelClosed {
+                                                                                       network_update = Some(NetworkUpdate::ChannelClosed {
                                                                                                short_channel_id: route_hop.short_channel_id,
                                                                                                is_permanent: true,
-                                                                                       })
+                                                                                       });
                                                                                } else {
-                                                                                       Some(msgs::HTLCFailChannelUpdate::ChannelUpdateMessage {
+                                                                                       // Make sure the ChannelUpdate contains the expected
+                                                                                       // short channel id.
+                                                                                       if failing_route_hop.short_channel_id == chan_update.contents.short_channel_id {
+                                                                                               short_channel_id = Some(failing_route_hop.short_channel_id);
+                                                                                       }
+                                                                                       network_update = Some(NetworkUpdate::ChannelUpdateMessage {
                                                                                                msg: chan_update,
                                                                                        })
                                                                                };
                                                                        }
                                                                }
                                                        }
-                                                       if fail_channel_update.is_none() {
+                                                       if network_update.is_none() {
                                                                // They provided an UPDATE which was obviously bogus, not worth
                                                                // trying to relay through them anymore.
-                                                               fail_channel_update = Some(msgs::HTLCFailChannelUpdate::NodeFailure {
+                                                               network_update = Some(NetworkUpdate::NodeFailure {
                                                                        node_id: route_hop.pubkey,
                                                                        is_permanent: true,
                                                                });
                                                        }
-                                               } else if !payment_failed {
+                                                       if short_channel_id.is_none() {
+                                                               short_channel_id = Some(route_hop.short_channel_id);
+                                                       }
+                                               } else if payment_failed {
+                                                       // Only blame the hop when a value in the HTLC doesn't match the
+                                                       // corresponding value in the onion.
+                                                       short_channel_id = match error_code & 0xff {
+                                                               18|19 => Some(route_hop.short_channel_id),
+                                                               _ => None,
+                                                       };
+                                               } else {
                                                        // We can't understand their error messages and they failed to
                                                        // forward...they probably can't understand our forwards so its
                                                        // really not worth trying any further.
-                                                       fail_channel_update = Some(msgs::HTLCFailChannelUpdate::NodeFailure {
+                                                       network_update = Some(NetworkUpdate::NodeFailure {
                                                                node_id: route_hop.pubkey,
                                                                is_permanent: true,
                                                        });
+                                                       short_channel_id = Some(route_hop.short_channel_id);
                                                }
 
                                                // TODO: Here (and a few other places) we assume that BADONION errors
                                                // are always "sourced" from the node previous to the one which failed
                                                // to decode the onion.
-                                               res = Some((fail_channel_update, !(error_code & PERM == PERM && is_from_final_node)));
+                                               res = Some((network_update, short_channel_id, !(error_code & PERM == PERM && is_from_final_node)));
 
                                                let (description, title) = errors::get_onion_error_description(error_code);
                                                if debug_field_size > 0 && err_packet.failuremsg.len() >= 4 + debug_field_size {
@@ -457,27 +486,139 @@ pub(super) fn process_onion_failure<T: secp256k1::Signing, L: Deref>(secp_ctx: &
                                        } else {
                                                // Useless packet that we can't use but it passed HMAC, so it
                                                // definitely came from the peer in question
-                                               res = Some((Some(msgs::HTLCFailChannelUpdate::NodeFailure {
+                                               let network_update = Some(NetworkUpdate::NodeFailure {
                                                        node_id: route_hop.pubkey,
                                                        is_permanent: true,
-                                               }), !is_from_final_node));
+                                               });
+                                               let short_channel_id = Some(route_hop.short_channel_id);
+                                               res = Some((network_update, short_channel_id, !is_from_final_node));
                                        }
                                }
                        }
                }).expect("Route that we sent via spontaneously grew invalid keys in the middle of it?");
-               if let Some((channel_update, payment_retryable)) = res {
-                       (channel_update, payment_retryable, error_code_ret, error_packet_ret)
+               if let Some((channel_update, short_channel_id, payment_retryable)) = res {
+                       (channel_update, short_channel_id, payment_retryable, error_code_ret, error_packet_ret)
                } else {
                        // only not set either packet unparseable or hmac does not match with any
                        // payment not retryable only when garbage is from the final node
-                       (None, !is_from_final_node, None, None)
+                       (None, None, !is_from_final_node, None, None)
                }
        } else { unreachable!(); }
 }
 
+/// Data decrypted from the onion payload.
+pub(crate) enum Hop {
+       /// This onion payload was for us, not for forwarding to a next-hop. Contains information for
+       /// verifying the incoming payment.
+       Receive(msgs::OnionHopData),
+       /// This onion payload needs to be forwarded to a next-hop.
+       Forward {
+               /// Onion payload data used in forwarding the payment.
+               next_hop_data: msgs::OnionHopData,
+               /// HMAC of the next hop's onion packet.
+               next_hop_hmac: [u8; 32],
+               /// Bytes of the onion packet we're forwarding.
+               new_packet_bytes: [u8; 20*65],
+       },
+}
+
+/// Error returned when we fail to decode the onion packet.
+pub(crate) enum OnionDecodeErr {
+       /// The HMAC of the onion packet did not match the hop data.
+       Malformed {
+               err_msg: &'static str,
+               err_code: u16,
+       },
+       /// We failed to decode the onion payload.
+       Relay {
+               err_msg: &'static str,
+               err_code: u16,
+       },
+}
+
+pub(crate) fn decode_next_hop(shared_secret: [u8; 32], hop_data: &[u8], hmac_bytes: [u8; 32], payment_hash: PaymentHash) -> Result<Hop, OnionDecodeErr> {
+       let (rho, mu) = gen_rho_mu_from_shared_secret(&shared_secret);
+       let mut hmac = HmacEngine::<Sha256>::new(&mu);
+       hmac.input(hop_data);
+       hmac.input(&payment_hash.0[..]);
+       if !fixed_time_eq(&Hmac::from_engine(hmac).into_inner(), &hmac_bytes) {
+               return Err(OnionDecodeErr::Malformed {
+                       err_msg: "HMAC Check failed",
+                       err_code: 0x8000 | 0x4000 | 5,
+               });
+       }
+
+       let mut chacha = ChaCha20::new(&rho, &[0u8; 8]);
+       let mut chacha_stream = ChaChaReader { chacha: &mut chacha, read: Cursor::new(&hop_data[..]) };
+       match <msgs::OnionHopData as Readable>::read(&mut chacha_stream) {
+               Err(err) => {
+                       let error_code = match err {
+                               msgs::DecodeError::UnknownVersion => 0x4000 | 1, // unknown realm byte
+                               msgs::DecodeError::UnknownRequiredFeature|
+                               msgs::DecodeError::InvalidValue|
+                               msgs::DecodeError::ShortRead => 0x4000 | 22, // invalid_onion_payload
+                               _ => 0x2000 | 2, // Should never happen
+                       };
+                       return Err(OnionDecodeErr::Relay {
+                               err_msg: "Unable to decode our hop data",
+                               err_code: error_code,
+                       });
+               },
+               Ok(msg) => {
+                       let mut hmac = [0; 32];
+                       if let Err(_) = chacha_stream.read_exact(&mut hmac[..]) {
+                               return Err(OnionDecodeErr::Relay {
+                                       err_msg: "Unable to decode our hop data",
+                                       err_code: 0x4000 | 22,
+                               });
+                       }
+                       if hmac == [0; 32] {
+                               #[cfg(test)]
+                               {
+                                       // In tests, make sure that the initial onion packet data is, at least, non-0.
+                                       // We could do some fancy randomness test here, but, ehh, whatever.
+                                       // This checks for the issue where you can calculate the path length given the
+                                       // onion data as all the path entries that the originator sent will be here
+                                       // as-is (and were originally 0s).
+                                       // Of course reverse path calculation is still pretty easy given naive routing
+                                       // algorithms, but this fixes the most-obvious case.
+                                       let mut next_bytes = [0; 32];
+                                       chacha_stream.read_exact(&mut next_bytes).unwrap();
+                                       assert_ne!(next_bytes[..], [0; 32][..]);
+                                       chacha_stream.read_exact(&mut next_bytes).unwrap();
+                                       assert_ne!(next_bytes[..], [0; 32][..]);
+                               }
+                               return Ok(Hop::Receive(msg));
+                       } else {
+                               let mut new_packet_bytes = [0; 20*65];
+                               let read_pos = chacha_stream.read(&mut new_packet_bytes).unwrap();
+                               #[cfg(debug_assertions)]
+                               {
+                                       // Check two things:
+                                       // a) that the behavior of our stream here will return Ok(0) even if the TLV
+                                       //    read above emptied out our buffer and the unwrap() wont needlessly panic
+                                       // b) that we didn't somehow magically end up with extra data.
+                                       let mut t = [0; 1];
+                                       debug_assert!(chacha_stream.read(&mut t).unwrap() == 0);
+                               }
+                               // Once we've emptied the set of bytes our peer gave us, encrypt 0 bytes until we
+                               // fill the onion hop data we'll forward to our next-hop peer.
+                               chacha_stream.chacha.process_in_place(&mut new_packet_bytes[read_pos..]);
+                               return Ok(Hop::Forward {
+                                       next_hop_data: msg,
+                                       next_hop_hmac: hmac,
+                                       new_packet_bytes,
+                               })
+                       }
+               },
+       }
+}
+
 #[cfg(test)]
 mod tests {
-       use ln::channelmanager::PaymentHash;
+       use io;
+       use prelude::*;
+       use ln::PaymentHash;
        use ln::features::{ChannelFeatures, NodeFeatures};
        use routing::router::{Route, RouteHop};
        use ln::msgs;
@@ -522,6 +663,7 @@ mod tests {
                                                short_channel_id: 0, fee_msat: 0, cltv_expiry_delta: 0 // Test vectors are garbage and not generateble from a RouteHop, we fill in payloads manually
                                        },
                        ]],
+                       payment_params: None,
                };
 
                let session_priv = SecretKey::from_slice(&hex::decode("4141414141414141414141414141414141414141414141414141414141414141").unwrap()[..]).unwrap();
@@ -644,7 +786,7 @@ mod tests {
                }
        }
        impl Writeable for RawOnionHopData {
-               fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ::std::io::Error> {
+               fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
                        writer.write_all(&self.data[..])
                }
        }