X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Fln%2Fonion_utils.rs;h=f81c619d7353997c1019e60ac3b967da9b6ac1b0;hb=28c9b56113ff1ebb1b505a2c979c55c1626aa06b;hp=914c8e03c804b2b1078fa74e57f3bd065230185d;hpb=33ff2746ef3d1a36aab4f07cd1a7b512b6e411da;p=rust-lightning diff --git a/lightning/src/ln/onion_utils.rs b/lightning/src/ln/onion_utils.rs index 914c8e03..f81c619d 100644 --- a/lightning/src/ln/onion_utils.rs +++ b/lightning/src/ln/onion_utils.rs @@ -15,7 +15,7 @@ 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::ser::{Readable, ReadableArgs, Writeable, LengthCalculatingWriter}; use util::logger::Logger; use bitcoin::hashes::{Hash, HashEngine}; @@ -33,14 +33,14 @@ use io::{Cursor, Read}; use core::convert::{AsMut, TryInto}; use core::ops::Deref; -pub(super) struct OnionKeys { +pub(crate) struct OnionKeys { #[cfg(test)] - pub(super) shared_secret: SharedSecret, + pub(crate) shared_secret: SharedSecret, #[cfg(test)] - pub(super) blinding_factor: [u8; 32], - pub(super) ephemeral_pubkey: PublicKey, - pub(super) rho: [u8; 32], - pub(super) mu: [u8; 32], + pub(crate) blinding_factor: [u8; 32], + pub(crate) ephemeral_pubkey: PublicKey, + pub(crate) rho: [u8; 32], + pub(crate) mu: [u8; 32], } #[inline] @@ -52,7 +52,7 @@ pub(crate) fn gen_rho_from_shared_secret(shared_secret: &[u8]) -> [u8; 32] { } #[inline] -pub(super) fn gen_rho_mu_from_shared_secret(shared_secret: &[u8]) -> ([u8; 32], [u8; 32]) { +pub(crate) fn gen_rho_mu_from_shared_secret(shared_secret: &[u8]) -> ([u8; 32], [u8; 32]) { assert_eq!(shared_secret.len(), 32); ({ let mut hmac = HmacEngine::::new(&[0x72, 0x68, 0x6f]); // rho @@ -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, mut packet_pubkey: PublicKey, packet_shared_secret: &[u8; 32]) -> Result { let blinding_factor = { let mut sha = Sha256::engine(); sha.input(&packet_pubkey.serialize()[..]); @@ -260,7 +260,23 @@ impl AsMut<[u8]> for FixedSizeOnionPacket { } } -/// panics if route_size_insane(payloads) +pub(crate) fn payloads_serialized_length(payloads: &Vec) -> usize { + payloads.iter().map(|p| p.serialized_length() + 32 /* HMAC */).sum() +} + +/// panics if payloads_serialized_length(payloads) > packet_data_len +pub(crate) fn construct_onion_message_packet>>( + payloads: Vec, onion_keys: Vec, prng_seed: [u8; 32], packet_data_len: usize) -> P +{ + let mut packet_data = vec![0; packet_data_len]; + + let mut chacha = ChaCha20::new(&prng_seed, &[0; 8]); + chacha.process_in_place(&mut packet_data); + + construct_onion_packet_with_init_noise::<_, _>(payloads, onion_keys, packet_data, None) +} + +/// panics if payloads_serialized_length(payloads) > packet_data.len() fn construct_onion_packet_with_init_noise( mut payloads: Vec, onion_keys: Vec, mut packet_data: P::Data, associated_data: Option<&PaymentHash>) -> P { @@ -564,7 +580,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. @@ -576,11 +635,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 { @@ -594,11 +654,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", @@ -608,7 +684,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 @@ -646,10 +722,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: @@ -661,12 +738,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 } }, }