From a031ff952bee42aad38edd29ede6aaa05a4cb6cb Mon Sep 17 00:00:00 2001 From: Valentine Wallace Date: Fri, 7 Oct 2022 15:22:00 -0400 Subject: [PATCH 1/9] Specify full import path in decode_tlv macro Prevents unresolved import error --- lightning/src/util/ser_macros.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lightning/src/util/ser_macros.rs b/lightning/src/util/ser_macros.rs index 94990fcb8a1..782c0ef27d5 100644 --- a/lightning/src/util/ser_macros.rs +++ b/lightning/src/util/ser_macros.rs @@ -157,17 +157,17 @@ macro_rules! decode_tlv { decode_tlv!($reader, $field, required) }}; ($reader: expr, $field: ident, required) => {{ - $field = ser::Readable::read(&mut $reader)?; + $field = ::util::ser::Readable::read(&mut $reader)?; }}; ($reader: expr, $field: ident, vec_type) => {{ - let f: ::util::ser::VecReadWrapper<_> = ser::Readable::read(&mut $reader)?; + let f: ::util::ser::VecReadWrapper<_> = ::util::ser::Readable::read(&mut $reader)?; $field = Some(f.0); }}; ($reader: expr, $field: ident, option) => {{ - $field = Some(ser::Readable::read(&mut $reader)?); + $field = Some(::util::ser::Readable::read(&mut $reader)?); }}; ($reader: expr, $field: ident, ignorable) => {{ - $field = ser::MaybeReadable::read(&mut $reader)?; + $field = ::util::ser::MaybeReadable::read(&mut $reader)?; }}; ($reader: expr, $field: ident, (option: $trait: ident $(, $read_arg: expr)?)) => {{ $field = Some($trait::read(&mut $reader $(, $read_arg)*)?); From a40e32b197895812e86eb479ff9e59c05875f871 Mon Sep 17 00:00:00 2001 From: Valentine Wallace Date: Mon, 17 Oct 2022 16:05:32 -0400 Subject: [PATCH 2/9] ser: Add MaybeReadableArgs trait Useful in decoding a custom message, so (a) the message type can be provided to the handler and (b) None can be returned if the message type is unknown. Used in upcoming commit(s) to support custom onion messages. --- lightning/src/util/ser.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lightning/src/util/ser.rs b/lightning/src/util/ser.rs index 852aa8f1589..6b17ec7ed6b 100644 --- a/lightning/src/util/ser.rs +++ b/lightning/src/util/ser.rs @@ -269,6 +269,15 @@ impl MaybeReadable for T { } } +/// A trait that various rust-lightning types implement allowing them to (maybe) be read in from a +/// Read, given some additional set of arguments which is required to deserialize. +/// +/// (C-not exported) as we only export serialization to/from byte arrays instead +pub trait MaybeReadableArgs

{ + /// Reads a Self in from the given Read + fn read(reader: &mut R, params: P) -> Result, DecodeError> where Self: Sized; +} + pub(crate) struct OptionDeserWrapper(pub Option); impl Readable for OptionDeserWrapper { #[inline] From 2a8179edb79268b6804417f07f55706b23354097 Mon Sep 17 00:00:00 2001 From: Valentine Wallace Date: Mon, 17 Oct 2022 16:07:40 -0400 Subject: [PATCH 3/9] Optionally parameterize decode_tlv_stream with custom decode closure Useful for decoding custom or user-provided TLVs. See macro docs for more info. Used in upcoming commit(s) to support custom onion message TLVs --- lightning/src/util/ser_macros.rs | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/lightning/src/util/ser_macros.rs b/lightning/src/util/ser_macros.rs index 782c0ef27d5..8c0a38670b5 100644 --- a/lightning/src/util/ser_macros.rs +++ b/lightning/src/util/ser_macros.rs @@ -174,8 +174,13 @@ macro_rules! decode_tlv { }}; } +// `$decode_custom_tlv` is a closure that may be optionally provided to handle custom message types. +// If it is provided, it will be called with the custom type and the `FixedLengthReader` containing +// the message contents. It should return `Ok(true)` if the custom message is successfully parsed, +// `Ok(false)` if the message type is unknown, and `Err(DecodeError)` if parsing fails. macro_rules! decode_tlv_stream { - ($stream: expr, {$(($type: expr, $field: ident, $fieldty: tt)),* $(,)*}) => { { + ($stream: expr, {$(($type: expr, $field: ident, $fieldty: tt)),* $(,)*} + $(, $decode_custom_tlv: expr)?) => { { use ln::msgs::DecodeError; let mut last_seen_type: Option = None; let mut stream_ref = $stream; @@ -226,10 +231,19 @@ macro_rules! decode_tlv_stream { return Err(DecodeError::InvalidValue); } },)* - x if x % 2 == 0 => { - return Err(DecodeError::UnknownRequiredFeature); - }, - _ => {}, + t => { + $( + if $decode_custom_tlv(t, &mut s)? { + // If a custom TLV was successfully read (i.e. decode_custom_tlv returns true), + // continue to the next TLV read. + s.eat_remaining()?; + continue 'tlv_read; + } + )? + if t % 2 == 0 { + return Err(DecodeError::UnknownRequiredFeature); + } + } } s.eat_remaining()?; } From 75fd0f3cbbd1b788a8f9e3956ff78831669df55b Mon Sep 17 00:00:00 2001 From: Valentine Wallace Date: Mon, 17 Oct 2022 16:32:17 -0400 Subject: [PATCH 4/9] Parameterize OnionMessenger by new CustomOnionMessageHandler trait OnionMessenger::new will now take a custom onion message handler trait implementation. This handler will be used in upcoming commit(s) to handle inbound custom onion messages. The new trait also specifies what custom messages are supported via its associated type, CustomMessage. This associated type must implement a new CustomOnionMessagesContents trait, which requires custom messages to support being written, being read, and supplying their TLV type. --- fuzz/src/onion_message.rs | 41 +++++++++-- lightning/src/ln/peer_handler.rs | 21 +++++- .../src/onion_message/functional_tests.rs | 47 ++++++++++-- lightning/src/onion_message/messenger.rs | 72 +++++++++++++++---- lightning/src/onion_message/mod.rs | 2 +- lightning/src/onion_message/packet.rs | 9 ++- 6 files changed, 168 insertions(+), 24 deletions(-) diff --git a/fuzz/src/onion_message.rs b/fuzz/src/onion_message.rs index a2fe88afc83..f5f9d26befa 100644 --- a/fuzz/src/onion_message.rs +++ b/fuzz/src/onion_message.rs @@ -10,12 +10,12 @@ use lightning::ln::msgs::{self, DecodeError, OnionMessageHandler}; use lightning::ln::script::ShutdownScript; use lightning::util::enforcing_trait_impls::EnforcingSigner; use lightning::util::logger::Logger; -use lightning::util::ser::{Readable, Writer}; -use lightning::onion_message::OnionMessenger; +use lightning::util::ser::{MaybeReadableArgs, Readable, Writeable, Writer}; +use lightning::onion_message::{CustomOnionMessageContents, CustomOnionMessageHandler, OnionMessenger}; use utils::test_logger; -use std::io::Cursor; +use std::io::{self, Cursor}; use std::sync::atomic::{AtomicU64, Ordering}; #[inline] @@ -29,7 +29,8 @@ pub fn do_test(data: &[u8], logger: &L) { node_secret: secret, counter: AtomicU64::new(0), }; - let onion_messenger = OnionMessenger::new(&keys_manager, logger); + let custom_msg_handler = TestCustomMessageHandler {}; + let onion_messenger = OnionMessenger::new(&keys_manager, logger, &custom_msg_handler); let mut pk = [2; 33]; pk[1] = 0xff; let peer_node_id_not_used = PublicKey::from_slice(&pk).unwrap(); onion_messenger.handle_onion_message(&peer_node_id_not_used, &msg); @@ -49,6 +50,38 @@ pub extern "C" fn onion_message_run(data: *const u8, datalen: usize) { do_test(unsafe { std::slice::from_raw_parts(data, datalen) }, &logger); } +struct TestCustomMessage {} + +const CUSTOM_MESSAGE_TYPE: u64 = 4242; +const CUSTOM_MESSAGE_CONTENTS: [u8; 32] = [42; 32]; + +impl CustomOnionMessageContents for TestCustomMessage { + fn tlv_type(&self) -> u64 { + CUSTOM_MESSAGE_TYPE + } +} + +impl Writeable for TestCustomMessage { + fn write(&self, w: &mut W) -> Result<(), io::Error> { + Ok(CUSTOM_MESSAGE_CONTENTS.write(w)?) + } +} + +impl MaybeReadableArgs for TestCustomMessage { + fn read(buffer: &mut R, _message_type: u64,) -> Result, DecodeError> where Self: Sized { + let mut buf = Vec::new(); + buffer.read_to_end(&mut buf)?; + return Ok(Some(TestCustomMessage {})) + } +} + +struct TestCustomMessageHandler {} + +impl CustomOnionMessageHandler for TestCustomMessageHandler { + type CustomMessage = TestCustomMessage; + fn handle_custom_message(&self, _msg: Self::CustomMessage) {} +} + pub struct VecWriter(pub Vec); impl Writer for VecWriter { fn write_all(&mut self, buf: &[u8]) -> Result<(), ::std::io::Error> { diff --git a/lightning/src/ln/peer_handler.rs b/lightning/src/ln/peer_handler.rs index d38afcbacb3..8a7b4632fd5 100644 --- a/lightning/src/ln/peer_handler.rs +++ b/lightning/src/ln/peer_handler.rs @@ -21,11 +21,11 @@ use ln::features::{InitFeatures, NodeFeatures}; use ln::msgs; use ln::msgs::{ChannelMessageHandler, LightningError, NetAddress, OnionMessageHandler, RoutingMessageHandler}; use ln::channelmanager::{SimpleArcChannelManager, SimpleRefChannelManager}; -use util::ser::{VecWriter, Writeable, Writer}; +use util::ser::{MaybeReadableArgs, VecWriter, Writeable, Writer}; use ln::peer_channel_encryptor::{PeerChannelEncryptor,NextNoiseStep}; use ln::wire; use ln::wire::Encode; -use onion_message::{SimpleArcOnionMessenger, SimpleRefOnionMessenger}; +use onion_message::{CustomOnionMessageContents, CustomOnionMessageHandler, SimpleArcOnionMessenger, SimpleRefOnionMessenger}; use routing::gossip::{NetworkGraph, P2PGossipSync}; use util::atomic_counter::AtomicCounter; use util::crypto::sign; @@ -95,6 +95,23 @@ impl OnionMessageHandler for IgnoringMessageHandler { InitFeatures::empty() } } +impl CustomOnionMessageHandler for IgnoringMessageHandler { + type CustomMessage = Infallible; + fn handle_custom_message(&self, _msg: Self::CustomMessage) { + // Since we always return `None` in the read the handle method should never be called. + unreachable!(); + } +} +impl MaybeReadableArgs for Infallible { + fn read(_buffer: &mut R, _msg_type: u64) -> Result, msgs::DecodeError> where Self: Sized { + Ok(None) + } +} + +impl CustomOnionMessageContents for Infallible { + fn tlv_type(&self) -> u64 { unreachable!(); } +} + impl Deref for IgnoringMessageHandler { type Target = IgnoringMessageHandler; fn deref(&self) -> &Self { self } diff --git a/lightning/src/onion_message/functional_tests.rs b/lightning/src/onion_message/functional_tests.rs index 22389bf5203..8c416bf65bf 100644 --- a/lightning/src/onion_message/functional_tests.rs +++ b/lightning/src/onion_message/functional_tests.rs @@ -11,19 +11,21 @@ use chain::keysinterface::{KeysInterface, Recipient}; use ln::features::InitFeatures; -use ln::msgs::{self, OnionMessageHandler}; -use super::{BlindedRoute, Destination, OnionMessenger, SendError}; +use ln::msgs::{self, DecodeError, OnionMessageHandler}; +use super::{BlindedRoute, CustomOnionMessageContents, CustomOnionMessageHandler, Destination, OnionMessenger, SendError}; use util::enforcing_trait_impls::EnforcingSigner; +use util::ser::{MaybeReadableArgs, Writeable, Writer}; use util::test_utils; use bitcoin::network::constants::Network; use bitcoin::secp256k1::{PublicKey, Secp256k1}; +use io; use sync::Arc; struct MessengerNode { keys_manager: Arc, - messenger: OnionMessenger, Arc>, + messenger: OnionMessenger, Arc, Arc>, logger: Arc, } @@ -34,6 +36,43 @@ impl MessengerNode { } } +#[derive(Clone)] +struct TestCustomMessage {} + +const CUSTOM_MESSAGE_TYPE: u64 = 4242; +const CUSTOM_MESSAGE_CONTENTS: [u8; 32] = [42; 32]; + +impl CustomOnionMessageContents for TestCustomMessage { + fn tlv_type(&self) -> u64 { + CUSTOM_MESSAGE_TYPE + } +} + +impl Writeable for TestCustomMessage { + fn write(&self, w: &mut W) -> Result<(), io::Error> { + Ok(CUSTOM_MESSAGE_CONTENTS.write(w)?) + } +} + +impl MaybeReadableArgs for TestCustomMessage { + fn read(buffer: &mut R, message_type: u64) -> Result, DecodeError> where Self: Sized { + if message_type == CUSTOM_MESSAGE_TYPE { + let mut buf = Vec::new(); + buffer.read_to_end(&mut buf)?; + assert_eq!(buf, CUSTOM_MESSAGE_CONTENTS); + return Ok(Some(TestCustomMessage {})) + } + Ok(None) + } +} + +struct TestCustomMessageHandler {} + +impl CustomOnionMessageHandler for TestCustomMessageHandler { + type CustomMessage = TestCustomMessage; + fn handle_custom_message(&self, _msg: Self::CustomMessage) {} +} + fn create_nodes(num_messengers: u8) -> Vec { let mut nodes = Vec::new(); for i in 0..num_messengers { @@ -42,7 +81,7 @@ fn create_nodes(num_messengers: u8) -> Vec { let keys_manager = Arc::new(test_utils::TestKeysInterface::new(&seed, Network::Testnet)); nodes.push(MessengerNode { keys_manager: keys_manager.clone(), - messenger: OnionMessenger::new(keys_manager, logger.clone()), + messenger: OnionMessenger::new(keys_manager, logger.clone(), Arc::new(TestCustomMessageHandler {})), logger, }); } diff --git a/lightning/src/onion_message/messenger.rs b/lightning/src/onion_message/messenger.rs index e2409fc45d6..42be0202fb1 100644 --- a/lightning/src/onion_message/messenger.rs +++ b/lightning/src/onion_message/messenger.rs @@ -19,7 +19,9 @@ use chain::keysinterface::{InMemorySigner, KeysInterface, KeysManager, Recipient use ln::features::{InitFeatures, NodeFeatures}; use ln::msgs::{self, OnionMessageHandler}; use ln::onion_utils; +use ln::peer_handler::IgnoringMessageHandler; use super::blinded_route::{BlindedRoute, ForwardTlvs, ReceiveTlvs}; +pub use super::packet::CustomOnionMessageContents; use super::packet::{BIG_PACKET_HOP_DATA_LEN, ForwardControlTlvs, Packet, Payload, ReceiveControlTlvs, SMALL_PACKET_HOP_DATA_LEN}; use super::utils; use util::events::OnionMessageProvider; @@ -41,8 +43,12 @@ use prelude::*; /// # use bitcoin::hashes::_export::_core::time::Duration; /// # use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey}; /// # use lightning::chain::keysinterface::{InMemorySigner, KeysManager, KeysInterface}; -/// # use lightning::onion_message::{BlindedRoute, Destination, OnionMessenger}; +/// # use lightning::ln::msgs::DecodeError; +/// # use lightning::ln::peer_handler::IgnoringMessageHandler; +/// # use lightning::onion_message::{BlindedRoute, CustomOnionMessageContents, Destination, OnionMessenger}; /// # use lightning::util::logger::{Logger, Record}; +/// # use lightning::util::ser::{MaybeReadableArgs, Writeable, Writer}; +/// # use lightning::io; /// # use std::sync::Arc; /// # struct FakeLogger {}; /// # impl Logger for FakeLogger { @@ -58,12 +64,31 @@ use prelude::*; /// # let (hop_node_id2, hop_node_id3, hop_node_id4) = (hop_node_id1, hop_node_id1, /// hop_node_id1); /// # let destination_node_id = hop_node_id1; -/// # +/// # let your_custom_message_handler = IgnoringMessageHandler {}; /// // Create the onion messenger. This must use the same `keys_manager` as is passed to your /// // ChannelManager. -/// let onion_messenger = OnionMessenger::new(&keys_manager, logger); +/// let onion_messenger = OnionMessenger::new(&keys_manager, logger, your_custom_message_handler); /// -/// // Send an empty onion message to a node id. +/// # struct YourCustomMessage {} +/// impl Writeable for YourCustomMessage { +/// fn write(&self, w: &mut W) -> Result<(), io::Error> { +/// # Ok(()) +/// // Write your custom onion message to `w` +/// } +/// } +/// impl CustomOnionMessageContents for YourCustomMessage { +/// fn tlv_type(&self) -> u64 { +/// # let your_custom_message_type = 42; +/// your_custom_message_type +/// } +/// } +/// impl MaybeReadableArgs for YourCustomMessage { +/// fn read(r: &mut R, message_type: u64) -> Result, DecodeError> { +/// # unreachable!() +/// // Read your custom onion message of type `message_type` from `r`, or return `None` +/// // if the message type is unknown +/// } +/// } /// let intermediate_hops = [hop_node_id1, hop_node_id2]; /// let reply_path = None; /// onion_messenger.send_onion_message(&intermediate_hops, Destination::Node(destination_node_id), reply_path); @@ -81,17 +106,18 @@ use prelude::*; /// /// [offers]: /// [`OnionMessenger`]: crate::onion_message::OnionMessenger -pub struct OnionMessenger +pub struct OnionMessenger where K::Target: KeysInterface, L::Target: Logger, + CMH:: Target: CustomOnionMessageHandler, { keys_manager: K, logger: L, pending_messages: Mutex>>, secp_ctx: Secp256k1, + custom_handler: CMH, // Coming soon: // invoice_handler: InvoiceHandler, - // custom_handler: CustomHandler, // handles custom onion messages } /// The destination of an onion message. @@ -130,13 +156,32 @@ pub enum SendError { BufferFull, } -impl OnionMessenger +/// Handler for custom onion messages. If you are using [`SimpleArcOnionMessenger`], +/// [`SimpleRefOnionMessenger`], or prefer to ignore inbound custom onion messages, +/// [`IgnoringMessageHandler`] must be provided to [`OnionMessenger::new`]. Otherwise, a custom +/// implementation of this trait must be provided, with [`CustomMessage`] specifying the supported +/// message types. +/// +/// See [`OnionMessenger`] for example usage. +/// +/// [`IgnoringMessageHandler`]: crate::ln::peer_handler::IgnoringMessageHandler +/// [`CustomMessage`]: Self::CustomMessage +pub trait CustomOnionMessageHandler { + /// The message known to the handler. To support multiple message types, you may want to make this + /// an enum with a variant for each supported message. + type CustomMessage: CustomOnionMessageContents; + /// Called with the custom message that was received. + fn handle_custom_message(&self, msg: Self::CustomMessage); +} + +impl OnionMessenger where K::Target: KeysInterface, L::Target: Logger, + CMH::Target: CustomOnionMessageHandler, { /// Constructs a new `OnionMessenger` to send, forward, and delegate received onion messages to /// their respective handlers. - pub fn new(keys_manager: K, logger: L) -> Self { + pub fn new(keys_manager: K, logger: L, custom_handler: CMH) -> Self { let mut secp_ctx = Secp256k1::new(); secp_ctx.seeded_randomize(&keys_manager.get_secure_random_bytes()); OnionMessenger { @@ -144,6 +189,7 @@ impl OnionMessenger pending_messages: Mutex::new(HashMap::new()), secp_ctx, logger, + custom_handler, } } @@ -221,9 +267,10 @@ fn outbound_buffer_full(peer_node_id: &PublicKey, buffer: &HashMap OnionMessageHandler for OnionMessenger +impl OnionMessageHandler for OnionMessenger where K::Target: KeysInterface, L::Target: Logger, + CMH::Target: CustomOnionMessageHandler, { /// Handle an incoming onion message. Currently, if a message was destined for us we will log, but /// soon we'll delegate the onion message to a handler that can generate invoices or send @@ -361,9 +408,10 @@ impl OnionMessageHandler for OnionMessenger OnionMessageProvider for OnionMessenger +impl OnionMessageProvider for OnionMessenger where K::Target: KeysInterface, L::Target: Logger, + CMH::Target: CustomOnionMessageHandler, { fn next_onion_message_for_peer(&self, peer_node_id: PublicKey) -> Option { let mut pending_msgs = self.pending_messages.lock().unwrap(); @@ -383,7 +431,7 @@ impl OnionMessageProvider for OnionMessenger = OnionMessenger, Arc>; +pub type SimpleArcOnionMessenger = OnionMessenger, Arc, IgnoringMessageHandler>; /// Useful for simplifying the parameters of [`SimpleRefChannelManager`] and /// [`SimpleRefPeerManager`]. See their docs for more details. /// @@ -391,7 +439,7 @@ pub type SimpleArcOnionMessenger = OnionMessenger = OnionMessenger; +pub type SimpleRefOnionMessenger<'a, 'b, L> = OnionMessenger; /// Construct onion packet payloads and keys for sending an onion message along the given /// `unblinded_path` to the given `destination`. diff --git a/lightning/src/onion_message/mod.rs b/lightning/src/onion_message/mod.rs index 2e23b76ada3..b80c540a6e1 100644 --- a/lightning/src/onion_message/mod.rs +++ b/lightning/src/onion_message/mod.rs @@ -29,5 +29,5 @@ mod functional_tests; // Re-export structs so they can be imported with just the `onion_message::` module prefix. pub use self::blinded_route::{BlindedRoute, BlindedHop}; -pub use self::messenger::{Destination, OnionMessenger, SendError, SimpleArcOnionMessenger, SimpleRefOnionMessenger}; +pub use self::messenger::{CustomOnionMessageContents, CustomOnionMessageHandler, Destination, OnionMessenger, SendError, SimpleArcOnionMessenger, SimpleRefOnionMessenger}; pub(crate) use self::packet::Packet; diff --git a/lightning/src/onion_message/packet.rs b/lightning/src/onion_message/packet.rs index 1337bdb14d5..2afdbdd6570 100644 --- a/lightning/src/onion_message/packet.rs +++ b/lightning/src/onion_message/packet.rs @@ -16,7 +16,7 @@ use ln::msgs::DecodeError; use ln::onion_utils; use super::blinded_route::{BlindedRoute, ForwardTlvs, ReceiveTlvs}; use util::chacha20poly1305rfc::{ChaChaPolyReadAdapter, ChaChaPolyWriteAdapter}; -use util::ser::{BigSize, FixedLengthReader, LengthRead, LengthReadable, LengthReadableArgs, Readable, ReadableArgs, Writeable, Writer}; +use util::ser::{BigSize, FixedLengthReader, LengthRead, LengthReadable, LengthReadableArgs, MaybeReadableArgs, Readable, ReadableArgs, Writeable, Writer}; use core::cmp; use io::{self, Read}; @@ -112,6 +112,13 @@ pub(super) enum Payload { // CustomMessage, // } +/// The contents of a custom onion message. Must implement `MaybeReadableArgs` where the `u64` +/// is the custom TLV type attempting to be read, and return `Ok(None)` if the TLV type is unknown. +pub trait CustomOnionMessageContents: Writeable + MaybeReadableArgs { + /// Returns the TLV type identifying the message contents. MUST be >= 64. + fn tlv_type(&self) -> u64; +} + /// Forward control TLVs in their blinded and unblinded form. pub(super) enum ForwardControlTlvs { /// If we're sending to a blinded route, the node that constructed the blinded route has provided From 333be69bb47f90b05e6becb736e377c8740d28e6 Mon Sep 17 00:00:00 2001 From: Valentine Wallace Date: Tue, 18 Oct 2022 12:14:15 -0400 Subject: [PATCH 5/9] Tweak OnionMessenger log on inbound onion message receipt --- fuzz/src/onion_message.rs | 3 ++- lightning/src/onion_message/functional_tests.rs | 4 ++-- lightning/src/onion_message/messenger.rs | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/fuzz/src/onion_message.rs b/fuzz/src/onion_message.rs index f5f9d26befa..8858478f433 100644 --- a/fuzz/src/onion_message.rs +++ b/fuzz/src/onion_message.rs @@ -155,7 +155,8 @@ 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 and no reply_path".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/functional_tests.rs b/lightning/src/onion_message/functional_tests.rs index 8c416bf65bf..1521b9d77ef 100644 --- a/lightning/src/onion_message/functional_tests.rs +++ b/lightning/src/onion_message/functional_tests.rs @@ -197,7 +197,7 @@ fn reply_path() { // 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); + format!("Received an onion message with path_id None and a 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(); @@ -207,7 +207,7 @@ fn reply_path() { 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); + format!("Received an onion message with path_id None and a reply_path").to_string(), 2); } #[test] diff --git a/lightning/src/onion_message/messenger.rs b/lightning/src/onion_message/messenger.rs index 42be0202fb1..da00a5760f5 100644 --- a/lightning/src/onion_message/messenger.rs +++ b/lightning/src/onion_message/messenger.rs @@ -306,8 +306,8 @@ impl OnionMessageHandler for Onion control_tlvs: ReceiveControlTlvs::Unblinded(ReceiveTlvs { path_id }), reply_path, }, None)) => { log_info!(self.logger, - "Received an onion message with path_id: {:02x?} and {}reply_path", - path_id, if reply_path.is_some() { "" } else { "no " }); + "Received an onion message with path_id {:02x?} and {} reply_path", + path_id, if reply_path.is_some() { "a" } else { "no" }); }, Ok((Payload::Forward(ForwardControlTlvs::Unblinded(ForwardTlvs { next_node_id, next_blinding_override From 87b637c990e4c7aec7f62544f2d313e2d59db115 Mon Sep 17 00:00:00 2001 From: Valentine Wallace Date: Tue, 18 Oct 2022 13:14:35 -0400 Subject: [PATCH 6/9] Fix accidental newline in OnionMessenger docs --- lightning/src/onion_message/messenger.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lightning/src/onion_message/messenger.rs b/lightning/src/onion_message/messenger.rs index da00a5760f5..9db37469436 100644 --- a/lightning/src/onion_message/messenger.rs +++ b/lightning/src/onion_message/messenger.rs @@ -61,8 +61,7 @@ use prelude::*; /// # let node_secret = SecretKey::from_slice(&hex::decode("0101010101010101010101010101010101010101010101010101010101010101").unwrap()[..]).unwrap(); /// # let secp_ctx = Secp256k1::new(); /// # let hop_node_id1 = PublicKey::from_secret_key(&secp_ctx, &node_secret); -/// # let (hop_node_id2, hop_node_id3, hop_node_id4) = (hop_node_id1, hop_node_id1, -/// hop_node_id1); +/// # let (hop_node_id2, hop_node_id3, hop_node_id4) = (hop_node_id1, hop_node_id1, hop_node_id1); /// # let destination_node_id = hop_node_id1; /// # let your_custom_message_handler = IgnoringMessageHandler {}; /// // Create the onion messenger. This must use the same `keys_manager` as is passed to your From ec538d181677bdd4a9dd7b29f619fff032fc2120 Mon Sep 17 00:00:00 2001 From: Valentine Wallace Date: Tue, 18 Oct 2022 13:22:12 -0400 Subject: [PATCH 7/9] Update send_onion_message API to take new OnionMessageContents enum OnionMessageContents specifies the data TLV that the sender wants in the onion message. This enum only has one variant for now, Custom. When offers are added, additional variants for invoice, invoice_request, and invoice_error will be added. This commit does not actually implement sending the custom OM contents, just the API change. --- .../src/onion_message/functional_tests.rs | 32 ++++++++++------- lightning/src/onion_message/messenger.rs | 21 ++++++----- lightning/src/onion_message/mod.rs | 2 +- lightning/src/onion_message/packet.rs | 35 +++++++++++++++---- 4 files changed, 62 insertions(+), 28 deletions(-) diff --git a/lightning/src/onion_message/functional_tests.rs b/lightning/src/onion_message/functional_tests.rs index 1521b9d77ef..356e1f3cdaf 100644 --- a/lightning/src/onion_message/functional_tests.rs +++ b/lightning/src/onion_message/functional_tests.rs @@ -12,7 +12,7 @@ use chain::keysinterface::{KeysInterface, Recipient}; use ln::features::InitFeatures; use ln::msgs::{self, DecodeError, OnionMessageHandler}; -use super::{BlindedRoute, CustomOnionMessageContents, CustomOnionMessageHandler, Destination, OnionMessenger, SendError}; +use super::{BlindedRoute, CustomOnionMessageContents, CustomOnionMessageHandler, Destination, OnionMessageContents, OnionMessenger, SendError}; use util::enforcing_trait_impls::EnforcingSigner; use util::ser::{MaybeReadableArgs, Writeable, Writer}; use util::test_utils; @@ -119,38 +119,42 @@ fn pass_along_path(path: &Vec, expected_path_id: Option<[u8; 32]> #[test] fn one_hop() { let nodes = create_nodes(2); + let test_msg = OnionMessageContents::Custom(TestCustomMessage {}); - nodes[0].messenger.send_onion_message(&[], Destination::Node(nodes[1].get_node_pk()), None).unwrap(); + nodes[0].messenger.send_onion_message(&[], Destination::Node(nodes[1].get_node_pk()), test_msg, None).unwrap(); pass_along_path(&nodes, None); } #[test] fn two_unblinded_hops() { let nodes = create_nodes(3); + let test_msg = OnionMessageContents::Custom(TestCustomMessage {}); - nodes[0].messenger.send_onion_message(&[nodes[1].get_node_pk()], Destination::Node(nodes[2].get_node_pk()), None).unwrap(); + nodes[0].messenger.send_onion_message(&[nodes[1].get_node_pk()], Destination::Node(nodes[2].get_node_pk()), test_msg, None).unwrap(); pass_along_path(&nodes, None); } #[test] fn two_unblinded_two_blinded() { let nodes = create_nodes(5); + let test_msg = OnionMessageContents::Custom(TestCustomMessage {}); 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), None).unwrap(); + nodes[0].messenger.send_onion_message(&[nodes[1].get_node_pk(), nodes[2].get_node_pk()], Destination::BlindedRoute(blinded_route), test_msg, None).unwrap(); pass_along_path(&nodes, None); } #[test] fn three_blinded_hops() { let nodes = create_nodes(4); + let test_msg = OnionMessageContents::Custom(TestCustomMessage {}); 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), None).unwrap(); + nodes[0].messenger.send_onion_message(&[], Destination::BlindedRoute(blinded_route), test_msg, None).unwrap(); pass_along_path(&nodes, None); } @@ -158,10 +162,11 @@ fn three_blinded_hops() { fn too_big_packet_error() { // Make sure we error as expected if a packet is too big to send. let nodes = create_nodes(2); + let test_msg = OnionMessageContents::Custom(TestCustomMessage {}); let hop_node_id = nodes[1].get_node_pk(); let hops = [hop_node_id; 400]; - let err = nodes[0].messenger.send_onion_message(&hops, Destination::Node(hop_node_id), None).unwrap_err(); + let err = nodes[0].messenger.send_onion_message(&hops, Destination::Node(hop_node_id), test_msg, None).unwrap_err(); assert_eq!(err, SendError::TooBigPacket); } @@ -169,30 +174,32 @@ fn too_big_packet_error() { fn invalid_blinded_route_error() { // Make sure we error as expected if a provided blinded route has 0 or 1 hops. let nodes = create_nodes(3); + let test_msg = TestCustomMessage {}; // 0 hops 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), None).unwrap_err(); + let err = nodes[0].messenger.send_onion_message(&[], Destination::BlindedRoute(blinded_route), OnionMessageContents::Custom(test_msg.clone()), 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), None).unwrap_err(); + let err = nodes[0].messenger.send_onion_message(&[], Destination::BlindedRoute(blinded_route), OnionMessageContents::Custom(test_msg), None).unwrap_err(); assert_eq!(err, SendError::TooFewBlindedHops); } #[test] fn reply_path() { let nodes = create_nodes(4); + let test_msg = TestCustomMessage {}; 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(); + nodes[0].messenger.send_onion_message(&[nodes[1].get_node_pk(), nodes[2].get_node_pk()], Destination::Node(nodes[3].get_node_pk()), OnionMessageContents::Custom(test_msg.clone()), 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( @@ -203,7 +210,7 @@ fn reply_path() { 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(); + nodes[0].messenger.send_onion_message(&[], Destination::BlindedRoute(blinded_route), OnionMessageContents::Custom(test_msg), Some(reply_path)).unwrap(); pass_along_path(&nodes, None); nodes[3].logger.assert_log_contains( "lightning::onion_message::messenger".to_string(), @@ -213,9 +220,10 @@ fn reply_path() { #[test] fn peer_buffer_full() { let nodes = create_nodes(2); + let test_msg = TestCustomMessage {}; for _ in 0..188 { // Based on MAX_PER_PEER_BUFFER_SIZE in OnionMessenger - nodes[0].messenger.send_onion_message(&[], Destination::Node(nodes[1].get_node_pk()), None).unwrap(); + nodes[0].messenger.send_onion_message(&[], Destination::Node(nodes[1].get_node_pk()), OnionMessageContents::Custom(test_msg.clone()), None).unwrap(); } - let err = nodes[0].messenger.send_onion_message(&[], Destination::Node(nodes[1].get_node_pk()), None).unwrap_err(); + let err = nodes[0].messenger.send_onion_message(&[], Destination::Node(nodes[1].get_node_pk()), OnionMessageContents::Custom(test_msg), None).unwrap_err(); assert_eq!(err, SendError::BufferFull); } diff --git a/lightning/src/onion_message/messenger.rs b/lightning/src/onion_message/messenger.rs index 9db37469436..1f7fe530843 100644 --- a/lightning/src/onion_message/messenger.rs +++ b/lightning/src/onion_message/messenger.rs @@ -21,7 +21,7 @@ use ln::msgs::{self, OnionMessageHandler}; use ln::onion_utils; use ln::peer_handler::IgnoringMessageHandler; use super::blinded_route::{BlindedRoute, ForwardTlvs, ReceiveTlvs}; -pub use super::packet::CustomOnionMessageContents; +pub use super::packet::{CustomOnionMessageContents, OnionMessageContents}; use super::packet::{BIG_PACKET_HOP_DATA_LEN, ForwardControlTlvs, Packet, Payload, ReceiveControlTlvs, SMALL_PACKET_HOP_DATA_LEN}; use super::utils; use util::events::OnionMessageProvider; @@ -34,7 +34,7 @@ use prelude::*; /// A sender, receiver and forwarder of onion messages. In upcoming releases, this object will be /// used to retrieve invoices and fulfill invoice requests from [offers]. Currently, only sending -/// and receiving empty onion messages is supported. +/// and receiving custom onion messages is supported. /// /// # Example /// @@ -45,7 +45,7 @@ use prelude::*; /// # use lightning::chain::keysinterface::{InMemorySigner, KeysManager, KeysInterface}; /// # use lightning::ln::msgs::DecodeError; /// # use lightning::ln::peer_handler::IgnoringMessageHandler; -/// # use lightning::onion_message::{BlindedRoute, CustomOnionMessageContents, Destination, OnionMessenger}; +/// # use lightning::onion_message::{BlindedRoute, CustomOnionMessageContents, Destination, OnionMessageContents, OnionMessenger}; /// # use lightning::util::logger::{Logger, Record}; /// # use lightning::util::ser::{MaybeReadableArgs, Writeable, Writer}; /// # use lightning::io; @@ -88,19 +88,24 @@ use prelude::*; /// // if the message type is unknown /// } /// } +/// // Send a custom onion message to a node id. /// let intermediate_hops = [hop_node_id1, hop_node_id2]; /// let reply_path = None; -/// onion_messenger.send_onion_message(&intermediate_hops, Destination::Node(destination_node_id), reply_path); +/// # let your_custom_message = YourCustomMessage {}; +/// let message = OnionMessageContents::Custom(your_custom_message); +/// onion_messenger.send_onion_message(&intermediate_hops, Destination::Node(destination_node_id), message, reply_path); /// /// // Create a blinded route to yourself, for someone to send an onion message to. /// # let your_node_id = hop_node_id1; /// let hops = [hop_node_id3, hop_node_id4, your_node_id]; /// let blinded_route = BlindedRoute::new(&hops, &keys_manager, &secp_ctx).unwrap(); /// -/// // Send an empty onion message to a blinded route. +/// // Send a custom onion message to a blinded route. /// # let intermediate_hops = [hop_node_id1, hop_node_id2]; /// let reply_path = None; -/// onion_messenger.send_onion_message(&intermediate_hops, Destination::BlindedRoute(blinded_route), reply_path); +/// # let your_custom_message = YourCustomMessage {}; +/// let message = OnionMessageContents::Custom(your_custom_message); +/// onion_messenger.send_onion_message(&intermediate_hops, Destination::BlindedRoute(blinded_route), message, reply_path); /// ``` /// /// [offers]: @@ -192,9 +197,9 @@ impl OnionMessenger) -> Result<(), SendError> { + pub fn send_onion_message(&self, intermediate_nodes: &[PublicKey], 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); diff --git a/lightning/src/onion_message/mod.rs b/lightning/src/onion_message/mod.rs index b80c540a6e1..3c522e30c51 100644 --- a/lightning/src/onion_message/mod.rs +++ b/lightning/src/onion_message/mod.rs @@ -29,5 +29,5 @@ mod functional_tests; // Re-export structs so they can be imported with just the `onion_message::` module prefix. pub use self::blinded_route::{BlindedRoute, BlindedHop}; -pub use self::messenger::{CustomOnionMessageContents, CustomOnionMessageHandler, Destination, OnionMessenger, SendError, SimpleArcOnionMessenger, SimpleRefOnionMessenger}; +pub use self::messenger::{CustomOnionMessageContents, CustomOnionMessageHandler, Destination, OnionMessageContents, OnionMessenger, SendError, SimpleArcOnionMessenger, SimpleRefOnionMessenger}; pub(crate) use self::packet::Packet; diff --git a/lightning/src/onion_message/packet.rs b/lightning/src/onion_message/packet.rs index 2afdbdd6570..8738091a942 100644 --- a/lightning/src/onion_message/packet.rs +++ b/lightning/src/onion_message/packet.rs @@ -104,13 +104,34 @@ pub(super) enum Payload { } } -// Coming soon: -// enum Message { -// InvoiceRequest(InvoiceRequest), -// Invoice(Invoice), -// InvoiceError(InvoiceError), -// CustomMessage, -// } +#[derive(Debug)] +/// The contents of an onion message. In the context of offers, this would be the invoice, invoice +/// request, or invoice error. +pub enum OnionMessageContents where T: CustomOnionMessageContents { + // Coming soon: + // Invoice, + // InvoiceRequest, + // InvoiceError, + /// A custom onion message specified by the user. + Custom(T), +} + +impl OnionMessageContents where T: CustomOnionMessageContents { + /// Returns the type that was used to decode the message payload. + pub fn tlv_type(&self) -> u64 { + match self { + &OnionMessageContents::Custom(ref msg) => msg.tlv_type(), + } + } +} + +impl Writeable for OnionMessageContents { + fn write(&self, w: &mut W) -> Result<(), io::Error> { + match self { + OnionMessageContents::Custom(msg) => Ok(msg.write(w)?), + } + } +} /// The contents of a custom onion message. Must implement `MaybeReadableArgs` where the `u64` /// is the custom TLV type attempting to be read, and return `Ok(None)` if the TLV type is unknown. From 7664957bc958217a98effda67428cc32f239e750 Mon Sep 17 00:00:00 2001 From: Valentine Wallace Date: Tue, 18 Oct 2022 13:29:43 -0400 Subject: [PATCH 8/9] Implement sending and receiving custom onion messages This uses the work done in the preceding commits to implement encoding a user's custom TLV in outbound onion messages, and decoding custom TLVs in inbound onion messages, to be provided to the new CustomOnionMessageHandler. --- fuzz/src/onion_message.rs | 14 +++---- lightning/src/onion_message/messenger.rs | 37 ++++++++++------- lightning/src/onion_message/packet.rs | 51 ++++++++++++++++++------ 3 files changed, 68 insertions(+), 34 deletions(-) diff --git a/fuzz/src/onion_message.rs b/fuzz/src/onion_message.rs index 8858478f433..27bf522c589 100644 --- a/fuzz/src/onion_message.rs +++ b/fuzz/src/onion_message.rs @@ -150,7 +150,7 @@ mod tests { #[test] fn test_no_onion_message_breakage() { - let one_hop_om = "020000000000000000000000000000000000000000000000000000000000000e01055600020000000000000000000000000000000000000000000000000000000000000e01120410950000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009300000000000000000000000000000000000000000000000000000000000000"; + let one_hop_om = "020000000000000000000000000000000000000000000000000000000000000e01055600020000000000000000000000000000000000000000000000000000000000000e0136041095000000000000000000000000000000fd1092202a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e800000000000000000000000000000000000000000000000000000000000000"; let logger = TrackingLogger { lines: Mutex::new(HashMap::new()) }; super::do_test(&::hex::decode(one_hop_om).unwrap(), &logger); { @@ -159,28 +159,28 @@ mod tests { "Received an onion message with path_id None and no reply_path".to_string())), Some(&1)); } - let two_unblinded_hops_om = "020000000000000000000000000000000000000000000000000000000000000e01055600020000000000000000000000000000000000000000000000000000000000000e0135043304210200000000000000000000000000000000000000000000000000000000000000039500000000000000000000000000000058000000000000000000000000000000000000000000000000000000000000001204105e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b300000000000000000000000000000000000000000000000000000000000000"; + let two_unblinded_hops_om = "020000000000000000000000000000000000000000000000000000000000000e01055600020000000000000000000000000000000000000000000000000000000000000e0135043304210200000000000000000000000000000000000000000000000000000000000000029500000000000000000000000000000036000000000000000000000000000000000000000000000000000000000000003604104b000000000000000000000000000000fd1092202a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b200000000000000000000000000000000000000000000000000000000000000"; let logger = TrackingLogger { lines: Mutex::new(HashMap::new()) }; super::do_test(&::hex::decode(two_unblinded_hops_om).unwrap(), &logger); { let log_entries = logger.lines.lock().unwrap(); - assert_eq!(log_entries.get(&("lightning::onion_message::messenger".to_string(), "Forwarding an onion message to peer 020000000000000000000000000000000000000000000000000000000000000003".to_string())), Some(&1)); + assert_eq!(log_entries.get(&("lightning::onion_message::messenger".to_string(), "Forwarding an onion message to peer 020000000000000000000000000000000000000000000000000000000000000002".to_string())), Some(&1)); } - let two_unblinded_two_blinded_om = "020000000000000000000000000000000000000000000000000000000000000e01055600020000000000000000000000000000000000000000000000000000000000000e01350433042102000000000000000000000000000000000000000000000000000000000000000395000000000000000000000000000000530000000000000000000000000000000000000000000000000000000000000058045604210200000000000000000000000000000000000000000000000000000000000000040821020000000000000000000000000000000000000000000000000000000000000e015e0000000000000000000000000000006b0000000000000000000000000000000000000000000000000000000000000035043304210200000000000000000000000000000000000000000000000000000000000000054b000000000000000000000000000000e800000000000000000000000000000000000000000000000000000000000000120410ee00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b300000000000000000000000000000000000000000000000000000000000000"; + let two_unblinded_two_blinded_om = "020000000000000000000000000000000000000000000000000000000000000e01055600020000000000000000000000000000000000000000000000000000000000000e01350433042102000000000000000000000000000000000000000000000000000000000000000295000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000058045604210200000000000000000000000000000000000000000000000000000000000000020821020000000000000000000000000000000000000000000000000000000000000e014b000000000000000000000000000000b20000000000000000000000000000000000000000000000000000000000000035043304210200000000000000000000000000000000000000000000000000000000000000029500000000000000000000000000000036000000000000000000000000000000000000000000000000000000000000003604104b000000000000000000000000000000fd1092202a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b200000000000000000000000000000000000000000000000000000000000000"; let logger = TrackingLogger { lines: Mutex::new(HashMap::new()) }; super::do_test(&::hex::decode(two_unblinded_two_blinded_om).unwrap(), &logger); { let log_entries = logger.lines.lock().unwrap(); - assert_eq!(log_entries.get(&("lightning::onion_message::messenger".to_string(), "Forwarding an onion message to peer 020000000000000000000000000000000000000000000000000000000000000003".to_string())), Some(&1)); + assert_eq!(log_entries.get(&("lightning::onion_message::messenger".to_string(), "Forwarding an onion message to peer 020000000000000000000000000000000000000000000000000000000000000002".to_string())), Some(&1)); } - let three_blinded_om = "020000000000000000000000000000000000000000000000000000000000000e01055600020000000000000000000000000000000000000000000000000000000000000e013504330421020000000000000000000000000000000000000000000000000000000000000003950000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000035043304210200000000000000000000000000000000000000000000000000000000000000045e0000000000000000000000000000004c000000000000000000000000000000000000000000000000000000000000001204104a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b300000000000000000000000000000000000000000000000000000000000000"; + let three_blinded_om = "020000000000000000000000000000000000000000000000000000000000000e01055600020000000000000000000000000000000000000000000000000000000000000e013504330421020000000000000000000000000000000000000000000000000000000000000002950000000000000000000000000000006c0000000000000000000000000000000000000000000000000000000000000035043304210200000000000000000000000000000000000000000000000000000000000000024b000000000000000000000000000000ac00000000000000000000000000000000000000000000000000000000000000360410d1000000000000000000000000000000fd1092202a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b200000000000000000000000000000000000000000000000000000000000000"; let logger = TrackingLogger { lines: Mutex::new(HashMap::new()) }; super::do_test(&::hex::decode(three_blinded_om).unwrap(), &logger); { let log_entries = logger.lines.lock().unwrap(); - assert_eq!(log_entries.get(&("lightning::onion_message::messenger".to_string(), "Forwarding an onion message to peer 020000000000000000000000000000000000000000000000000000000000000003".to_string())), Some(&1)); + assert_eq!(log_entries.get(&("lightning::onion_message::messenger".to_string(), "Forwarding an onion message to peer 020000000000000000000000000000000000000000000000000000000000000002".to_string())), Some(&1)); } } } diff --git a/lightning/src/onion_message/messenger.rs b/lightning/src/onion_message/messenger.rs index 1f7fe530843..9c86a44e0a0 100644 --- a/lightning/src/onion_message/messenger.rs +++ b/lightning/src/onion_message/messenger.rs @@ -217,7 +217,7 @@ impl OnionMessenger OnionMessageHandler for Onion match onion_utils::decode_next_hop(onion_decode_ss, &msg.onion_routing_packet.hop_data[..], msg.onion_routing_packet.hmac, control_tlvs_ss) { - Ok((Payload::Receive { - control_tlvs: ReceiveControlTlvs::Unblinded(ReceiveTlvs { path_id }), reply_path, + Ok((Payload::Receive::<<::Target as CustomOnionMessageHandler>::CustomMessage> { + message, control_tlvs: ReceiveControlTlvs::Unblinded(ReceiveTlvs { path_id }), reply_path, }, None)) => { log_info!(self.logger, "Received an onion message with path_id {:02x?} and {} reply_path", path_id, if reply_path.is_some() { "a" } else { "no" }); + match message { + OnionMessageContents::Custom(msg) => self.custom_handler.handle_custom_message(msg), + } }, Ok((Payload::Forward(ForwardControlTlvs::Unblinded(ForwardTlvs { next_node_id, next_blinding_override @@ -447,10 +450,10 @@ pub type SimpleRefOnionMessenger<'a, 'b, L> = OnionMessenger( - secp_ctx: &Secp256k1, unblinded_path: &[PublicKey], destination: Destination, mut reply_path: - Option, session_priv: &SecretKey -) -> Result<(Vec<(Payload, [u8; 32])>, Vec), secp256k1::Error> { +fn packet_payloads_and_keys( + secp_ctx: &Secp256k1, unblinded_path: &[PublicKey], destination: Destination, + message: OnionMessageContents, 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); let mut onion_packet_keys = Vec::with_capacity(num_hops); @@ -463,6 +466,7 @@ fn packet_payloads_and_keys( let mut unblinded_path_idx = 0; let mut blinded_path_idx = 0; let mut prev_control_tlvs_ss = None; + let mut final_control_tlvs = None; utils::construct_keys_callback(secp_ctx, unblinded_path, Some(destination), session_priv, |_, onion_packet_ss, ephemeral_pubkey, control_tlvs_ss, unblinded_pk_opt, enc_payload_opt| { if num_unblinded_hops != 0 && unblinded_path_idx < num_unblinded_hops { if let Some(ss) = prev_control_tlvs_ss.take() { @@ -492,10 +496,8 @@ fn packet_payloads_and_keys( control_tlvs_ss)); blinded_path_idx += 1; } 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)); + final_control_tlvs = Some(ReceiveControlTlvs::Blinded(encrypted_payload)); + prev_control_tlvs_ss = Some(control_tlvs_ss); } let (rho, mu) = onion_utils::gen_rho_mu_from_shared_secret(onion_packet_ss.as_ref()); @@ -510,18 +512,25 @@ fn packet_payloads_and_keys( }); })?; - if let Some(control_tlvs_ss) = prev_control_tlvs_ss { + if let Some(control_tlvs) = final_control_tlvs { + payloads.push((Payload::Receive { + control_tlvs, + reply_path: reply_path.take(), + message, + }, prev_control_tlvs_ss.unwrap())); + } else { payloads.push((Payload::Receive { control_tlvs: ReceiveControlTlvs::Unblinded(ReceiveTlvs { path_id: None, }), reply_path: reply_path.take(), - }, control_tlvs_ss)); + message, + }, prev_control_tlvs_ss.unwrap())); } Ok((payloads, onion_packet_keys)) } /// Errors if the serialized payload size exceeds onion_message::BIG_PACKET_HOP_DATA_LEN -fn construct_onion_message_packet(payloads: Vec<(Payload, [u8; 32])>, onion_keys: Vec, prng_seed: [u8; 32]) -> Result { +fn construct_onion_message_packet(payloads: Vec<(Payload, [u8; 32])>, onion_keys: Vec, prng_seed: [u8; 32]) -> Result { // Spec rationale: // "`len` allows larger messages to be sent than the standard 1300 bytes allowed for an HTLC // onion, but this should be used sparingly as it is reduces anonymity set, hence the diff --git a/lightning/src/onion_message/packet.rs b/lightning/src/onion_message/packet.rs index 8738091a942..74d253bf574 100644 --- a/lightning/src/onion_message/packet.rs +++ b/lightning/src/onion_message/packet.rs @@ -92,15 +92,14 @@ impl LengthReadable for Packet { /// Onion message payloads contain "control" TLVs and "data" TLVs. Control TLVs are used to route /// the onion message from hop to hop and for path verification, whereas data TLVs contain the onion /// message content itself, such as an invoice request. -pub(super) enum Payload { +pub(super) enum Payload { /// This payload is for an intermediate hop. Forward(ForwardControlTlvs), /// This payload is for the final hop. Receive { control_tlvs: ReceiveControlTlvs, reply_path: Option, - // Coming soon: - // message: Message, + message: OnionMessageContents, } } @@ -160,7 +159,7 @@ pub(super) enum ReceiveControlTlvs { } // Uses the provided secret to simultaneously encode and encrypt the unblinded control TLVs. -impl Writeable for (Payload, [u8; 32]) { +impl Writeable for (Payload, [u8; 32]) { fn write(&self, w: &mut W) -> Result<(), io::Error> { match &self.0 { Payload::Forward(ForwardControlTlvs::Blinded(encrypted_bytes)) => { @@ -169,11 +168,12 @@ impl Writeable for (Payload, [u8; 32]) { }) }, Payload::Receive { - control_tlvs: ReceiveControlTlvs::Blinded(encrypted_bytes), reply_path + control_tlvs: ReceiveControlTlvs::Blinded(encrypted_bytes), reply_path, message, } => { encode_varint_length_prefixed_tlv!(w, { (2, reply_path, option), - (4, encrypted_bytes, vec_type) + (4, encrypted_bytes, vec_type), + (message.tlv_type(), message, required) }) }, Payload::Forward(ForwardControlTlvs::Unblinded(control_tlvs)) => { @@ -183,12 +183,13 @@ impl Writeable for (Payload, [u8; 32]) { }) }, Payload::Receive { - control_tlvs: ReceiveControlTlvs::Unblinded(control_tlvs), reply_path, + control_tlvs: ReceiveControlTlvs::Unblinded(control_tlvs), reply_path, message, } => { let write_adapter = ChaChaPolyWriteAdapter::new(self.1, &control_tlvs); encode_varint_length_prefixed_tlv!(w, { (2, reply_path, option), - (4, write_adapter, required) + (4, write_adapter, required), + (message.tlv_type(), message, required) }) }, } @@ -196,28 +197,52 @@ impl Writeable for (Payload, [u8; 32]) { } } -// Uses the provided secret to simultaneously decode and decrypt the control TLVs. -impl ReadableArgs for Payload { +// Uses the provided secret to simultaneously decode and decrypt the control TLVs and data TLV. +impl ReadableArgs for Payload { fn read(r: &mut R, encrypted_tlvs_ss: SharedSecret) -> Result { let v: BigSize = Readable::read(r)?; let mut rd = FixedLengthReader::new(r, v.0); 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()); + let mut message_type: Option = None; + let mut message = None; decode_tlv_stream!(&mut rd, { (2, reply_path, option), - (4, read_adapter, (option: LengthReadableArgs, rho)) + (4, read_adapter, (option: LengthReadableArgs, rho)), + }, |msg_type, msg_reader| { + if msg_type < 64 { return Ok(false) } + // Don't allow reading more than one data TLV from an onion message. + if message_type.is_some() { return Err(DecodeError::InvalidValue) } + + message_type = Some(msg_type); + match T::read(msg_reader, msg_type) { + Ok(Some(msg)) => { + message = Some(msg); + Ok(true) + }, + Ok(None) => Ok(false), + Err(e) => Err(e), + } }); rd.eat_remaining().map_err(|_| DecodeError::ShortRead)?; match read_adapter { None => return Err(DecodeError::InvalidValue), Some(ChaChaPolyReadAdapter { readable: ControlTlvs::Forward(tlvs)}) => { + if message_type.is_some() { + return Err(DecodeError::InvalidValue) + } Ok(Payload::Forward(ForwardControlTlvs::Unblinded(tlvs))) }, Some(ChaChaPolyReadAdapter { readable: ControlTlvs::Receive(tlvs)}) => { - Ok(Payload::Receive { control_tlvs: ReceiveControlTlvs::Unblinded(tlvs), reply_path }) - }, + if message.is_none() { return Err(DecodeError::InvalidValue) } + Ok(Payload::Receive { + control_tlvs: ReceiveControlTlvs::Unblinded(tlvs), + reply_path, + message: OnionMessageContents::Custom(message.unwrap()), + }) + } } } } From 4905268b65b36d2d1468e2763408cb056cf63b36 Mon Sep 17 00:00:00 2001 From: Valentine Wallace Date: Tue, 18 Oct 2022 13:33:45 -0400 Subject: [PATCH 9/9] Disallow sending invalid custom OM TLVs Onion message data TLV types must be >= 64, enforce this on send --- .../src/onion_message/functional_tests.rs | 27 +++++++++++++++++++ lightning/src/onion_message/messenger.rs | 5 ++++ 2 files changed, 32 insertions(+) diff --git a/lightning/src/onion_message/functional_tests.rs b/lightning/src/onion_message/functional_tests.rs index 356e1f3cdaf..66ea62bbcec 100644 --- a/lightning/src/onion_message/functional_tests.rs +++ b/lightning/src/onion_message/functional_tests.rs @@ -217,6 +217,33 @@ fn reply_path() { format!("Received an onion message with path_id None and a reply_path").to_string(), 2); } +#[test] +fn invalid_custom_message_type() { + let nodes = create_nodes(2); + + struct InvalidCustomMessage{} + impl CustomOnionMessageContents for InvalidCustomMessage { + fn tlv_type(&self) -> u64 { + // Onion message contents must have a TLV >= 64. + 63 + } + } + + impl Writeable for InvalidCustomMessage { + fn write(&self, _w: &mut W) -> Result<(), io::Error> { unreachable!() } + } + + impl MaybeReadableArgs for InvalidCustomMessage { + fn read(_buffer: &mut R, _message_type: u64) -> Result, DecodeError> where Self: Sized { + unreachable!() + } + } + + let test_msg = OnionMessageContents::Custom(InvalidCustomMessage {}); + let err = nodes[0].messenger.send_onion_message(&[], Destination::Node(nodes[1].get_node_pk()), test_msg, None).unwrap_err(); + assert_eq!(err, SendError::InvalidMessage); +} + #[test] fn peer_buffer_full() { let nodes = create_nodes(2); diff --git a/lightning/src/onion_message/messenger.rs b/lightning/src/onion_message/messenger.rs index 9c86a44e0a0..5cf2e1e0dce 100644 --- a/lightning/src/onion_message/messenger.rs +++ b/lightning/src/onion_message/messenger.rs @@ -156,6 +156,8 @@ pub enum SendError { TooFewBlindedHops, /// Our next-hop peer was offline or does not support onion message forwarding. InvalidFirstHop, + /// Onion message contents must have a TLV type >= 64. + InvalidMessage, /// Our next-hop peer's buffer was full or our total outbound buffer was full. BufferFull, } @@ -205,6 +207,9 @@ impl OnionMessenger