From 950b7d777a3fb129759932f5a4220ece44e94f41 Mon Sep 17 00:00:00 2001 From: Valentine Wallace Date: Fri, 5 Aug 2022 18:03:12 -0400 Subject: [PATCH] Support sending and receiving reply paths --- fuzz/src/onion_message.rs | 2 +- lightning/src/onion_message/blinded_route.rs | 39 ++++++++++++++++++- .../src/onion_message/functional_tests.rs | 39 +++++++++++++++---- lightning/src/onion_message/messenger.rs | 17 +++++--- lightning/src/onion_message/packet.rs | 27 ++++++++----- 5 files changed, 99 insertions(+), 25 deletions(-) diff --git a/fuzz/src/onion_message.rs b/fuzz/src/onion_message.rs index 7ab2bd63a..57603dde1 100644 --- a/fuzz/src/onion_message.rs +++ b/fuzz/src/onion_message.rs @@ -122,7 +122,7 @@ mod tests { super::do_test(&::hex::decode(one_hop_om).unwrap(), &logger); { let log_entries = logger.lines.lock().unwrap(); - assert_eq!(log_entries.get(&("lightning::onion_message::messenger".to_string(), "Received an onion message with path_id: None".to_string())), Some(&1)); + assert_eq!(log_entries.get(&("lightning::onion_message::messenger".to_string(), "Received an onion message with path_id: None and no reply_path".to_string())), Some(&1)); } let two_unblinded_hops_om = "020000000000000000000000000000000000000000000000000000000000000e01055600020000000000000000000000000000000000000000000000000000000000000e0135043304210200000000000000000000000000000000000000000000000000000000000000039500000000000000000000000000000058000000000000000000000000000000000000000000000000000000000000001204105e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b300000000000000000000000000000000000000000000000000000000000000"; diff --git a/lightning/src/onion_message/blinded_route.rs b/lightning/src/onion_message/blinded_route.rs index d18372e3b..9f1d8db46 100644 --- a/lightning/src/onion_message/blinded_route.rs +++ b/lightning/src/onion_message/blinded_route.rs @@ -13,10 +13,10 @@ use bitcoin::secp256k1::{self, PublicKey, Secp256k1, SecretKey}; use chain::keysinterface::{KeysInterface, Sign}; use super::utils; +use ln::msgs::DecodeError; use util::chacha20poly1305rfc::ChaChaPolyWriteAdapter; -use util::ser::{VecWriter, Writeable, Writer}; +use util::ser::{Readable, VecWriter, Writeable, Writer}; -use core::iter::FromIterator; use io; use prelude::*; @@ -113,6 +113,41 @@ fn encrypt_payload(payload: P, encrypted_tlvs_ss: [u8; 32]) -> Vec writer.0 } +impl Writeable for BlindedRoute { + fn write(&self, w: &mut W) -> Result<(), io::Error> { + self.introduction_node_id.write(w)?; + self.blinding_point.write(w)?; + (self.blinded_hops.len() as u8).write(w)?; + for hop in &self.blinded_hops { + hop.write(w)?; + } + Ok(()) + } +} + +impl Readable for BlindedRoute { + fn read(r: &mut R) -> Result { + let introduction_node_id = Readable::read(r)?; + let blinding_point = Readable::read(r)?; + let num_hops: u8 = Readable::read(r)?; + if num_hops == 0 { return Err(DecodeError::InvalidValue) } + let mut blinded_hops: Vec = Vec::with_capacity(num_hops.into()); + for _ in 0..num_hops { + blinded_hops.push(Readable::read(r)?); + } + Ok(BlindedRoute { + introduction_node_id, + blinding_point, + blinded_hops, + }) + } +} + +impl_writeable!(BlindedHop, { + blinded_node_id, + encrypted_payload +}); + /// TLVs to encode in an intermediate onion message packet's hop data. When provided in a blinded /// route, they are encoded into [`BlindedHop::encrypted_payload`]. pub(crate) struct ForwardTlvs { diff --git a/lightning/src/onion_message/functional_tests.rs b/lightning/src/onion_message/functional_tests.rs index f3026270f..ccc834434 100644 --- a/lightning/src/onion_message/functional_tests.rs +++ b/lightning/src/onion_message/functional_tests.rs @@ -72,7 +72,7 @@ fn pass_along_path(path: &Vec, expected_path_id: Option<[u8; 32]> fn one_hop() { let nodes = create_nodes(2); - nodes[0].messenger.send_onion_message(&[], Destination::Node(nodes[1].get_node_pk())).unwrap(); + nodes[0].messenger.send_onion_message(&[], Destination::Node(nodes[1].get_node_pk()), None).unwrap(); pass_along_path(&nodes, None); } @@ -80,7 +80,7 @@ fn one_hop() { fn two_unblinded_hops() { let nodes = create_nodes(3); - nodes[0].messenger.send_onion_message(&[nodes[1].get_node_pk()], Destination::Node(nodes[2].get_node_pk())).unwrap(); + nodes[0].messenger.send_onion_message(&[nodes[1].get_node_pk()], Destination::Node(nodes[2].get_node_pk()), None).unwrap(); pass_along_path(&nodes, None); } @@ -91,7 +91,7 @@ fn two_unblinded_two_blinded() { let secp_ctx = Secp256k1::new(); let blinded_route = BlindedRoute::new::(&[nodes[3].get_node_pk(), nodes[4].get_node_pk()], &*nodes[4].keys_manager, &secp_ctx).unwrap(); - nodes[0].messenger.send_onion_message(&[nodes[1].get_node_pk(), nodes[2].get_node_pk()], Destination::BlindedRoute(blinded_route)).unwrap(); + nodes[0].messenger.send_onion_message(&[nodes[1].get_node_pk(), nodes[2].get_node_pk()], Destination::BlindedRoute(blinded_route), None).unwrap(); pass_along_path(&nodes, None); } @@ -102,7 +102,7 @@ fn three_blinded_hops() { let secp_ctx = Secp256k1::new(); let blinded_route = BlindedRoute::new::(&[nodes[1].get_node_pk(), nodes[2].get_node_pk(), nodes[3].get_node_pk()], &*nodes[3].keys_manager, &secp_ctx).unwrap(); - nodes[0].messenger.send_onion_message(&[], Destination::BlindedRoute(blinded_route)).unwrap(); + nodes[0].messenger.send_onion_message(&[], Destination::BlindedRoute(blinded_route), None).unwrap(); pass_along_path(&nodes, None); } @@ -116,7 +116,7 @@ fn too_big_packet_error() { let hop_node_id = PublicKey::from_secret_key(&secp_ctx, &hop_secret); let hops = [hop_node_id; 400]; - let err = nodes[0].messenger.send_onion_message(&hops, Destination::Node(hop_node_id)).unwrap_err(); + let err = nodes[0].messenger.send_onion_message(&hops, Destination::Node(hop_node_id), None).unwrap_err(); assert_eq!(err, SendError::TooBigPacket); } @@ -129,13 +129,38 @@ fn invalid_blinded_route_error() { let secp_ctx = Secp256k1::new(); let mut blinded_route = BlindedRoute::new::(&[nodes[1].get_node_pk(), nodes[2].get_node_pk()], &*nodes[2].keys_manager, &secp_ctx).unwrap(); blinded_route.blinded_hops.clear(); - let err = nodes[0].messenger.send_onion_message(&[], Destination::BlindedRoute(blinded_route)).unwrap_err(); + let err = nodes[0].messenger.send_onion_message(&[], Destination::BlindedRoute(blinded_route), None).unwrap_err(); assert_eq!(err, SendError::TooFewBlindedHops); // 1 hop let mut blinded_route = BlindedRoute::new::(&[nodes[1].get_node_pk(), nodes[2].get_node_pk()], &*nodes[2].keys_manager, &secp_ctx).unwrap(); blinded_route.blinded_hops.remove(0); assert_eq!(blinded_route.blinded_hops.len(), 1); - let err = nodes[0].messenger.send_onion_message(&[], Destination::BlindedRoute(blinded_route)).unwrap_err(); + let err = nodes[0].messenger.send_onion_message(&[], Destination::BlindedRoute(blinded_route), None).unwrap_err(); assert_eq!(err, SendError::TooFewBlindedHops); } + +#[test] +fn reply_path() { + let mut nodes = create_nodes(4); + let secp_ctx = Secp256k1::new(); + + // Destination::Node + let reply_path = BlindedRoute::new::(&[nodes[2].get_node_pk(), nodes[1].get_node_pk(), nodes[0].get_node_pk()], &*nodes[0].keys_manager, &secp_ctx).unwrap(); + nodes[0].messenger.send_onion_message(&[nodes[1].get_node_pk(), nodes[2].get_node_pk()], Destination::Node(nodes[3].get_node_pk()), Some(reply_path)).unwrap(); + pass_along_path(&nodes, None); + // Make sure the last node successfully decoded the reply path. + nodes[3].logger.assert_log_contains( + "lightning::onion_message::messenger".to_string(), + format!("Received an onion message with path_id: None and reply_path").to_string(), 1); + + // Destination::BlindedRoute + let blinded_route = BlindedRoute::new::(&[nodes[1].get_node_pk(), nodes[2].get_node_pk(), nodes[3].get_node_pk()], &*nodes[3].keys_manager, &secp_ctx).unwrap(); + let reply_path = BlindedRoute::new::(&[nodes[2].get_node_pk(), nodes[1].get_node_pk(), nodes[0].get_node_pk()], &*nodes[0].keys_manager, &secp_ctx).unwrap(); + + nodes[0].messenger.send_onion_message(&[], Destination::BlindedRoute(blinded_route), Some(reply_path)).unwrap(); + pass_along_path(&nodes, None); + nodes[3].logger.assert_log_contains( + "lightning::onion_message::messenger".to_string(), + format!("Received an onion message with path_id: None and reply_path").to_string(), 2); +} diff --git a/lightning/src/onion_message/messenger.rs b/lightning/src/onion_message/messenger.rs index c264cbc38..a5438afbb 100644 --- a/lightning/src/onion_message/messenger.rs +++ b/lightning/src/onion_message/messenger.rs @@ -142,7 +142,7 @@ impl OnionMessenger /// Send an empty onion message to `destination`, routing it through `intermediate_nodes`. /// See [`OnionMessenger`] for example usage. - pub fn send_onion_message(&self, intermediate_nodes: &[PublicKey], destination: Destination) -> Result<(), SendError> { + pub fn send_onion_message(&self, intermediate_nodes: &[PublicKey], destination: Destination, reply_path: Option) -> Result<(), SendError> { if let Destination::BlindedRoute(BlindedRoute { ref blinded_hops, .. }) = destination { if blinded_hops.len() < 2 { return Err(SendError::TooFewBlindedHops); @@ -160,7 +160,7 @@ impl OnionMessenger } }; let (packet_payloads, packet_keys) = packet_payloads_and_keys( - &self.secp_ctx, intermediate_nodes, destination, &blinding_secret) + &self.secp_ctx, intermediate_nodes, destination, reply_path, &blinding_secret) .map_err(|e| SendError::Secp256k1(e))?; let prng_seed = self.keys_manager.get_secure_random_bytes(); @@ -209,9 +209,11 @@ impl OnionMessenger msg.onion_routing_packet.hmac, control_tlvs_ss) { Ok((Payload::Receive { - control_tlvs: ReceiveControlTlvs::Unblinded(ReceiveTlvs { path_id }) + control_tlvs: ReceiveControlTlvs::Unblinded(ReceiveTlvs { path_id }), reply_path, }, None)) => { - log_info!(self.logger, "Received an onion message with path_id: {:02x?}", path_id); + log_info!(self.logger, + "Received an onion message with path_id: {:02x?} and {}reply_path", + path_id, if reply_path.is_some() { "" } else { "no " }); }, Ok((Payload::Forward(ForwardControlTlvs::Unblinded(ForwardTlvs { next_node_id, next_blinding_override @@ -299,7 +301,8 @@ pub type SimpleRefOnionMessenger<'a, 'b, L> = OnionMessenger( - secp_ctx: &Secp256k1, unblinded_path: &[PublicKey], destination: Destination, session_priv: &SecretKey + secp_ctx: &Secp256k1, unblinded_path: &[PublicKey], destination: Destination, mut reply_path: + Option, session_priv: &SecretKey ) -> Result<(Vec<(Payload, [u8; 32])>, Vec), secp256k1::Error> { let num_hops = unblinded_path.len() + destination.num_hops(); let mut payloads = Vec::with_capacity(num_hops); @@ -344,6 +347,7 @@ fn packet_payloads_and_keys( } else if let Some(encrypted_payload) = enc_payload_opt { payloads.push((Payload::Receive { control_tlvs: ReceiveControlTlvs::Blinded(encrypted_payload), + reply_path: reply_path.take(), }, control_tlvs_ss)); } @@ -361,7 +365,8 @@ fn packet_payloads_and_keys( if let Some(control_tlvs_ss) = prev_control_tlvs_ss { payloads.push((Payload::Receive { - control_tlvs: ReceiveControlTlvs::Unblinded(ReceiveTlvs { path_id: None, }) + control_tlvs: ReceiveControlTlvs::Unblinded(ReceiveTlvs { path_id: None, }), + reply_path: reply_path.take(), }, control_tlvs_ss)); } diff --git a/lightning/src/onion_message/packet.rs b/lightning/src/onion_message/packet.rs index 5afe57812..4ab53735e 100644 --- a/lightning/src/onion_message/packet.rs +++ b/lightning/src/onion_message/packet.rs @@ -14,7 +14,7 @@ use bitcoin::secp256k1::ecdh::SharedSecret; use ln::msgs::DecodeError; use ln::onion_utils; -use super::blinded_route::{ForwardTlvs, ReceiveTlvs}; +use super::blinded_route::{BlindedRoute, ForwardTlvs, ReceiveTlvs}; use util::chacha20poly1305rfc::{ChaChaPolyReadAdapter, ChaChaPolyWriteAdapter}; use util::ser::{BigSize, FixedLengthReader, LengthRead, LengthReadable, LengthReadableArgs, Readable, ReadableArgs, Writeable, Writer}; @@ -98,8 +98,8 @@ pub(super) enum Payload { /// This payload is for the final hop. Receive { control_tlvs: ReceiveControlTlvs, + reply_path: Option, // Coming soon: - // reply_path: Option, // message: Message, } } @@ -135,21 +135,31 @@ pub(super) enum ReceiveControlTlvs { impl Writeable for (Payload, [u8; 32]) { fn write(&self, w: &mut W) -> Result<(), io::Error> { match &self.0 { - Payload::Forward(ForwardControlTlvs::Blinded(encrypted_bytes)) | - Payload::Receive { control_tlvs: ReceiveControlTlvs::Blinded(encrypted_bytes)} => { + Payload::Forward(ForwardControlTlvs::Blinded(encrypted_bytes)) => { encode_varint_length_prefixed_tlv!(w, { (4, encrypted_bytes, vec_type) }) }, + Payload::Receive { + control_tlvs: ReceiveControlTlvs::Blinded(encrypted_bytes), reply_path + } => { + encode_varint_length_prefixed_tlv!(w, { + (2, reply_path, option), + (4, encrypted_bytes, vec_type) + }) + }, Payload::Forward(ForwardControlTlvs::Unblinded(control_tlvs)) => { let write_adapter = ChaChaPolyWriteAdapter::new(self.1, &control_tlvs); encode_varint_length_prefixed_tlv!(w, { (4, write_adapter, required) }) }, - Payload::Receive { control_tlvs: ReceiveControlTlvs::Unblinded(control_tlvs)} => { + Payload::Receive { + control_tlvs: ReceiveControlTlvs::Unblinded(control_tlvs), reply_path, + } => { let write_adapter = ChaChaPolyWriteAdapter::new(self.1, &control_tlvs); encode_varint_length_prefixed_tlv!(w, { + (2, reply_path, option), (4, write_adapter, required) }) }, @@ -163,12 +173,11 @@ impl ReadableArgs for Payload { fn read(mut r: &mut R, encrypted_tlvs_ss: SharedSecret) -> Result { let v: BigSize = Readable::read(r)?; let mut rd = FixedLengthReader::new(r, v.0); - // TODO: support reply paths - let mut _reply_path_bytes: Option> = Some(Vec::new()); + let mut reply_path: Option = None; let mut read_adapter: Option> = None; let rho = onion_utils::gen_rho_from_shared_secret(&encrypted_tlvs_ss.secret_bytes()); decode_tlv_stream!(&mut rd, { - (2, _reply_path_bytes, vec_type), + (2, reply_path, option), (4, read_adapter, (option: LengthReadableArgs, rho)) }); rd.eat_remaining().map_err(|_| DecodeError::ShortRead)?; @@ -179,7 +188,7 @@ impl ReadableArgs for Payload { Ok(Payload::Forward(ForwardControlTlvs::Unblinded(tlvs))) }, Some(ChaChaPolyReadAdapter { readable: ControlTlvs::Receive(tlvs)}) => { - Ok(Payload::Receive { control_tlvs: ReceiveControlTlvs::Unblinded(tlvs)}) + Ok(Payload::Receive { control_tlvs: ReceiveControlTlvs::Unblinded(tlvs), reply_path }) }, } } -- 2.39.5