From: Valentine Wallace Date: Fri, 21 Oct 2022 19:09:03 +0000 (-0400) Subject: Onion messages: fix edge case where we are the introduction node X-Git-Tag: v0.0.113~63^2~1 X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=commitdiff_plain;h=a4e242ba5f5260dadf4781d9f8c1cf36845b0e18;hp=--cc;p=rust-lightning Onion messages: fix edge case where we are the introduction node If we're sending straight to a blinded route with no unblinded intermediate hops, and we are the introduction node, we need to advance the blinded route by one hop so that the second hop is the new introduction node. --- a4e242ba5f5260dadf4781d9f8c1cf36845b0e18 diff --git a/lightning/src/onion_message/blinded_route.rs b/lightning/src/onion_message/blinded_route.rs index 44967eb4..82a325a9 100644 --- a/lightning/src/onion_message/blinded_route.rs +++ b/lightning/src/onion_message/blinded_route.rs @@ -9,15 +9,21 @@ //! Creating blinded routes and related utilities live here. -use bitcoin::secp256k1::{self, PublicKey, Secp256k1, SecretKey}; +use bitcoin::hashes::{Hash, HashEngine}; +use bitcoin::hashes::sha256::Hash as Sha256; +use bitcoin::secp256k1::{self, PublicKey, Scalar, Secp256k1, SecretKey}; -use crate::chain::keysinterface::KeysInterface; +use crate::chain::keysinterface::{KeysInterface, Recipient}; +use super::packet::ControlTlvs; use super::utils; use crate::ln::msgs::DecodeError; -use crate::util::chacha20poly1305rfc::ChaChaPolyWriteAdapter; -use crate::util::ser::{Readable, VecWriter, Writeable, Writer}; +use crate::ln::onion_utils; +use crate::util::chacha20poly1305rfc::{ChaChaPolyReadAdapter, ChaChaPolyWriteAdapter}; +use crate::util::ser::{FixedLengthReader, LengthReadableArgs, Readable, VecWriter, Writeable, Writer}; -use crate::io; +use core::mem; +use core::ops::Deref; +use crate::io::{self, Cursor}; use crate::prelude::*; /// Onion messages can be sent and received to blinded routes, which serve to hide the identity of @@ -69,6 +75,41 @@ impl BlindedRoute { blinded_hops: blinded_hops(secp_ctx, node_pks, &blinding_secret).map_err(|_| ())?, }) } + + // Advance the blinded route by one hop, so make the second hop into the new introduction node. + pub(super) fn advance_by_one + (&mut self, keys_manager: &K, secp_ctx: &Secp256k1) -> Result<(), ()> + where K::Target: KeysInterface + { + let control_tlvs_ss = keys_manager.ecdh(Recipient::Node, &self.blinding_point, None)?; + let rho = onion_utils::gen_rho_from_shared_secret(&control_tlvs_ss.secret_bytes()); + let encrypted_control_tlvs = self.blinded_hops.remove(0).encrypted_payload; + let mut s = Cursor::new(&encrypted_control_tlvs); + let mut reader = FixedLengthReader::new(&mut s, encrypted_control_tlvs.len() as u64); + match ChaChaPolyReadAdapter::read(&mut reader, rho) { + Ok(ChaChaPolyReadAdapter { readable: ControlTlvs::Forward(ForwardTlvs { + mut next_node_id, next_blinding_override, + })}) => { + let mut new_blinding_point = match next_blinding_override { + Some(blinding_point) => blinding_point, + None => { + let blinding_factor = { + let mut sha = Sha256::engine(); + sha.input(&self.blinding_point.serialize()[..]); + sha.input(control_tlvs_ss.as_ref()); + Sha256::from_engine(sha).into_inner() + }; + self.blinding_point.mul_tweak(secp_ctx, &Scalar::from_be_bytes(blinding_factor).unwrap()) + .map_err(|_| ())? + } + }; + mem::swap(&mut self.blinding_point, &mut new_blinding_point); + mem::swap(&mut self.introduction_node_id, &mut next_node_id); + Ok(()) + }, + _ => Err(()) + } + } } /// Construct blinded hops for the given `unblinded_path`. diff --git a/lightning/src/onion_message/functional_tests.rs b/lightning/src/onion_message/functional_tests.rs index 5c623cf2..991f0d1c 100644 --- a/lightning/src/onion_message/functional_tests.rs +++ b/lightning/src/onion_message/functional_tests.rs @@ -170,6 +170,20 @@ fn too_big_packet_error() { assert_eq!(err, SendError::TooBigPacket); } +#[test] +fn we_are_intro_node() { + // If we are sending straight to a blinded route and we are the introduction node, we need to + // advance the blinded route by 1 hop so the second hop is the new introduction node. + let nodes = create_nodes(3); + let test_msg = TestCustomMessage {}; + + let secp_ctx = Secp256k1::new(); + let blinded_route = BlindedRoute::new(&[nodes[0].get_node_pk(), nodes[1].get_node_pk(), nodes[2].get_node_pk()], &*nodes[2].keys_manager, &secp_ctx).unwrap(); + + nodes[0].messenger.send_onion_message(&[], Destination::BlindedRoute(blinded_route), OnionMessageContents::Custom(test_msg.clone()), None).unwrap(); + pass_along_path(&nodes, None); +} + #[test] fn invalid_blinded_route_error() { // Make sure we error as expected if a provided blinded route has 0 or 1 hops. diff --git a/lightning/src/onion_message/messenger.rs b/lightning/src/onion_message/messenger.rs index ff20f19a..4eb0ada3 100644 --- a/lightning/src/onion_message/messenger.rs +++ b/lightning/src/onion_message/messenger.rs @@ -160,6 +160,15 @@ pub enum SendError { InvalidMessage, /// Our next-hop peer's buffer was full or our total outbound buffer was full. BufferFull, + /// Failed to retrieve our node id from the provided [`KeysInterface`]. + /// + /// [`KeysInterface`]: crate::chain::keysinterface::KeysInterface + GetNodeIdFailed, + /// We attempted to send to a blinded route where we are the introduction node, and failed to + /// advance the blinded route to make the second hop the new introduction node. Either + /// [`KeysInterface::ecdh`] failed, we failed to tweak the current blinding point to get the + /// new blinding point, or we were attempting to send to ourselves. + BlindedRouteAdvanceFailed, } /// Handler for custom onion messages. If you are using [`SimpleArcOnionMessenger`], @@ -201,7 +210,7 @@ impl OnionMessenger(&self, intermediate_nodes: &[PublicKey], destination: Destination, message: OnionMessageContents, reply_path: Option) -> Result<(), SendError> { + pub fn send_onion_message(&self, intermediate_nodes: &[PublicKey], mut destination: Destination, message: OnionMessageContents, reply_path: Option) -> Result<(), SendError> { if let Destination::BlindedRoute(BlindedRoute { ref blinded_hops, .. }) = destination { if blinded_hops.len() < 2 { return Err(SendError::TooFewBlindedHops); @@ -210,6 +219,19 @@ impl OnionMessenger