X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Fln%2Fonion_utils.rs;h=598edcf0367952b011e290bfe48938ec92364ecb;hb=a257906743d528c32c862b053b652d4b728aa990;hp=ce91d0d04f753e20a6689133e13923a8903eaff3;hpb=9051c38ebe42e171fd0fcfa22d2b9ff6a1607b3b;p=rust-lightning diff --git a/lightning/src/ln/onion_utils.rs b/lightning/src/ln/onion_utils.rs index ce91d0d0..598edcf0 100644 --- a/lightning/src/ln/onion_utils.rs +++ b/lightning/src/ln/onion_utils.rs @@ -7,29 +7,29 @@ // You may not use this file except in accordance with one or both of these // licenses. -use ln::{PaymentHash, PaymentPreimage, PaymentSecret}; -use ln::channelmanager::HTLCSource; -use ln::msgs; -use ln::wire::Encode; -use routing::gossip::NetworkUpdate; -use routing::router::RouteHop; -use util::chacha20::{ChaCha20, ChaChaReader}; -use util::errors::{self, APIError}; -use util::ser::{Readable, Writeable, LengthCalculatingWriter}; -use util::logger::Logger; +use crate::ln::{PaymentHash, PaymentPreimage, PaymentSecret}; +use crate::ln::channelmanager::HTLCSource; +use crate::ln::msgs; +use crate::ln::wire::Encode; +use crate::routing::gossip::NetworkUpdate; +use crate::routing::router::RouteHop; +use crate::util::chacha20::{ChaCha20, ChaChaReader}; +use crate::util::errors::{self, APIError}; +use crate::util::ser::{Readable, ReadableArgs, Writeable, LengthCalculatingWriter}; +use crate::util::logger::Logger; use bitcoin::hashes::{Hash, HashEngine}; use bitcoin::hashes::cmp::fixed_time_eq; use bitcoin::hashes::hmac::{Hmac, HmacEngine}; use bitcoin::hashes::sha256::Hash as Sha256; -use bitcoin::secp256k1::{SecretKey,PublicKey}; +use bitcoin::secp256k1::{SecretKey, PublicKey, Scalar}; use bitcoin::secp256k1::Secp256k1; use bitcoin::secp256k1::ecdh::SharedSecret; use bitcoin::secp256k1; -use prelude::*; -use io::{Cursor, Read}; +use crate::prelude::*; +use crate::io::{Cursor, Read}; use core::convert::{AsMut, TryInto}; use core::ops::Deref; @@ -82,7 +82,7 @@ pub(super) fn gen_ammag_from_shared_secret(shared_secret: &[u8]) -> [u8; 32] { Hmac::from_engine(hmac).into_inner() } -pub(super) fn next_hop_packet_pubkey(secp_ctx: &Secp256k1, mut packet_pubkey: PublicKey, packet_shared_secret: &[u8; 32]) -> Result { +pub(crate) fn next_hop_packet_pubkey(secp_ctx: &Secp256k1, packet_pubkey: PublicKey, packet_shared_secret: &[u8; 32]) -> Result { let blinding_factor = { let mut sha = Sha256::engine(); sha.input(&packet_pubkey.serialize()[..]); @@ -90,7 +90,7 @@ pub(super) fn next_hop_packet_pubkey(secp_ctx: & if fixed_time_eq(&Hmac::from_engine(hmac).into_inner(), &err_packet.hmac) { if let Some(error_code_slice) = err_packet.failuremsg.get(0..2) { + const BADONION: u16 = 0x8000; const PERM: u16 = 0x4000; const NODE: u16 = 0x2000; const UPDATE: u16 = 0x1000; @@ -445,12 +446,24 @@ pub(super) fn process_onion_failure(secp_ctx: & let mut network_update = None; let mut short_channel_id = None; - if error_code & NODE == NODE { + if error_code & BADONION == BADONION { + // If the error code has the BADONION bit set, always blame the channel + // from the node "originating" the error to its next hop. The + // "originator" is ultimately actually claiming that its counterparty + // is the one who is failing the HTLC. + // If the "originator" here isn't lying we should really mark the + // next-hop node as failed entirely, but we can't be confident in that, + // as it would allow any node to get us to completely ban one of its + // counterparties. Instead, we simply remove the channel in question. + network_update = Some(NetworkUpdate::ChannelFailure { + short_channel_id: failing_route_hop.short_channel_id, + is_permanent: true, + }); + } else if error_code & NODE == NODE { 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 { + } else if error_code & PERM == PERM { if !payment_failed { network_update = Some(NetworkUpdate::ChannelFailure { short_channel_id: failing_route_hop.short_channel_id, @@ -458,8 +471,7 @@ pub(super) fn process_onion_failure(secp_ctx: & }); short_channel_id = Some(failing_route_hop.short_channel_id); } - } - else if error_code & UPDATE == UPDATE { + } 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 = u16::from_be_bytes(update_len_slice.try_into().expect("len is 2")) as usize; if let Some(mut update_slice) = err_packet.failuremsg.get(debug_field_size + 4..debug_field_size + 4 + update_len) { @@ -545,9 +557,6 @@ pub(super) fn process_onion_failure(secp_ctx: & 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, short_channel_id, !(error_code & PERM == PERM && is_from_final_node))); let (description, title) = errors::get_onion_error_description(error_code); @@ -580,7 +589,50 @@ pub(super) fn process_onion_failure(secp_ctx: & } else { unreachable!(); } } -/// Data decrypted from the onion payload. +/// An input used when decoding an onion packet. +pub(crate) trait DecodeInput { + type Arg; + /// If Some, this is the input when checking the hmac of the onion packet. + fn payment_hash(&self) -> Option<&PaymentHash>; + /// Read argument when decrypting our hop payload. + fn read_arg(self) -> Self::Arg; +} + +impl DecodeInput for PaymentHash { + type Arg = (); + fn payment_hash(&self) -> Option<&PaymentHash> { + Some(self) + } + fn read_arg(self) -> Self::Arg { () } +} + +impl DecodeInput for SharedSecret { + type Arg = SharedSecret; + fn payment_hash(&self) -> Option<&PaymentHash> { + None + } + fn read_arg(self) -> Self::Arg { self } +} + +/// Allows `decode_next_hop` to return the next hop packet bytes for either payments or onion +/// message forwards. +pub(crate) trait NextPacketBytes: AsMut<[u8]> { + fn new(len: usize) -> Self; +} + +impl NextPacketBytes for FixedSizeOnionPacket { + fn new(_len: usize) -> Self { + Self([0 as u8; ONION_DATA_LEN]) + } +} + +impl NextPacketBytes for Vec { + fn new(len: usize) -> Self { + vec![0 as u8; len] + } +} + +/// Data decrypted from a payment's 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. @@ -592,11 +644,12 @@ pub(crate) enum Hop { /// 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], + new_packet_bytes: [u8; ONION_DATA_LEN], }, } /// Error returned when we fail to decode the onion packet. +#[derive(Debug)] pub(crate) enum OnionDecodeErr { /// The HMAC of the onion packet did not match the hop data. Malformed { @@ -610,11 +663,27 @@ pub(crate) enum OnionDecodeErr { }, } -pub(crate) fn decode_next_hop(shared_secret: [u8; 32], hop_data: &[u8], hmac_bytes: [u8; 32], payment_hash: PaymentHash) -> Result { +pub(crate) fn decode_next_payment_hop(shared_secret: [u8; 32], hop_data: &[u8], hmac_bytes: [u8; 32], payment_hash: PaymentHash) -> Result { + match decode_next_hop(shared_secret, hop_data, hmac_bytes, payment_hash) { + Ok((next_hop_data, None)) => Ok(Hop::Receive(next_hop_data)), + Ok((next_hop_data, Some((next_hop_hmac, FixedSizeOnionPacket(new_packet_bytes))))) => { + Ok(Hop::Forward { + next_hop_data, + next_hop_hmac, + new_packet_bytes + }) + }, + Err(e) => Err(e), + } +} + +pub(crate) fn decode_next_hop, N: NextPacketBytes>(shared_secret: [u8; 32], hop_data: &[u8], hmac_bytes: [u8; 32], decode_input: D) -> Result<(R, Option<([u8; 32], N)>), OnionDecodeErr> { let (rho, mu) = gen_rho_mu_from_shared_secret(&shared_secret); let mut hmac = HmacEngine::::new(&mu); hmac.input(hop_data); - hmac.input(&payment_hash.0[..]); + if let Some(payment_hash) = decode_input.payment_hash() { + 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", @@ -624,7 +693,7 @@ pub(crate) fn decode_next_hop(shared_secret: [u8; 32], hop_data: &[u8], hmac_byt let mut chacha = ChaCha20::new(&rho, &[0u8; 8]); let mut chacha_stream = ChaChaReader { chacha: &mut chacha, read: Cursor::new(&hop_data[..]) }; - match ::read(&mut chacha_stream) { + match R::read(&mut chacha_stream, decode_input.read_arg()) { Err(err) => { let error_code = match err { msgs::DecodeError::UnknownVersion => 0x4000 | 1, // unknown realm byte @@ -662,10 +731,11 @@ pub(crate) fn decode_next_hop(shared_secret: [u8; 32], hop_data: &[u8], hmac_byt chacha_stream.read_exact(&mut next_bytes).unwrap(); assert_ne!(next_bytes[..], [0; 32][..]); } - return Ok(Hop::Receive(msg)); + return Ok((msg, None)); // We are the final destination for this packet } else { - let mut new_packet_bytes = [0; 20*65]; - let read_pos = chacha_stream.read(&mut new_packet_bytes).unwrap(); + let mut new_packet_bytes = N::new(hop_data.len()); + let read_pos = hop_data.len() - chacha_stream.read.position() as usize; + chacha_stream.read_exact(&mut new_packet_bytes.as_mut()[..read_pos]).unwrap(); #[cfg(debug_assertions)] { // Check two things: @@ -677,12 +747,8 @@ pub(crate) fn decode_next_hop(shared_secret: [u8; 32], hop_data: &[u8], hmac_byt } // 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, - }) + chacha_stream.chacha.process_in_place(&mut new_packet_bytes.as_mut()[read_pos..]); + return Ok((msg, Some((hmac, new_packet_bytes)))) // This packet needs forwarding } }, } @@ -690,13 +756,13 @@ pub(crate) fn decode_next_hop(shared_secret: [u8; 32], hop_data: &[u8], hmac_byt #[cfg(test)] mod tests { - use io; - use prelude::*; - use ln::PaymentHash; - use ln::features::{ChannelFeatures, NodeFeatures}; - use routing::router::{Route, RouteHop}; - use ln::msgs; - use util::ser::{Writeable, Writer}; + use crate::io; + use crate::prelude::*; + use crate::ln::PaymentHash; + use crate::ln::features::{ChannelFeatures, NodeFeatures}; + use crate::routing::router::{Route, RouteHop}; + use crate::ln::msgs; + use crate::util::ser::{Writeable, Writer}; use hex;