Expand `chain::Listen` trivially to accept filtered block data
[rust-lightning] / lightning / src / ln / onion_utils.rs
index 4509ee77b6ba743cb5e3b3d5e3d0fd0f99f490ae..0dd6087f82018d9faf56d53672325b76284caa00 100644 (file)
@@ -12,7 +12,7 @@ use ln::channelmanager::HTLCSource;
 use ln::msgs;
 use routing::network_graph::NetworkUpdate;
 use routing::router::RouteHop;
-use util::chacha20::ChaCha20;
+use util::chacha20::{ChaCha20, ChaChaReader};
 use util::errors::{self, APIError};
 use util::ser::{Readable, Writeable, LengthCalculatingWriter};
 use util::logger::Logger;
@@ -28,7 +28,7 @@ use bitcoin::secp256k1::ecdh::SharedSecret;
 use bitcoin::secp256k1;
 
 use prelude::*;
-use io::Cursor;
+use io::{Cursor, Read};
 use core::convert::TryInto;
 use core::ops::Deref;
 
@@ -329,9 +329,10 @@ 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<NetworkUpdate>, bool, Option<u16>, Option<Vec<u8>>) where L::Target: Logger {
+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;
@@ -354,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;
 
+                       // 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[..]);
@@ -376,23 +380,26 @@ 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,
+                                                       15|16|17|18|19|23 => true,
                                                        _ => false,
                                                } && is_from_final_node; // PERM bit observed below even if this error is from the intermediate nodes
 
                                                let mut network_update = None;
+                                               let mut short_channel_id = None;
 
                                                if error_code & NODE == NODE {
-                                                       network_update = Some(NetworkUpdate::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 {
-                                                       network_update = if payment_failed { None } else {
-                                                               let failing_route_hop = if is_from_final_node { route_hop } else { &path[route_hop_idx + 1] };
-                                                               Some(NetworkUpdate::ChannelClosed {
+                                                       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) {
@@ -404,24 +411,31 @@ 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
                                                                                };
-                                                                               network_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(NetworkUpdate::ChannelClosed {
+                                                                                       network_update = Some(NetworkUpdate::ChannelClosed {
                                                                                                short_channel_id: route_hop.short_channel_id,
                                                                                                is_permanent: true,
-                                                                                       })
+                                                                                       });
                                                                                } else {
-                                                                                       Some(NetworkUpdate::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,
                                                                                        })
                                                                                };
@@ -436,7 +450,17 @@ pub(super) fn process_onion_failure<T: secp256k1::Signing, L: Deref>(secp_ctx: &
                                                                        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.
@@ -444,12 +468,13 @@ pub(super) fn process_onion_failure<T: secp256k1::Signing, L: Deref>(secp_ctx: &
                                                                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((network_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 {
@@ -461,24 +486,134 @@ 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(NetworkUpdate::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 io;
@@ -528,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();