X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Fonion_message%2Fpacket.rs;h=510f0ea025a0d615b0f54292d865602ac7e103a6;hb=59778dac488cff735004671cdefb3f4ac1f920fd;hp=2fb2407dbdd093bdbe4ae260e31f12fbe4722d8a;hpb=c3de782a2baab0c742332bea4783c393edb83311;p=rust-lightning diff --git a/lightning/src/onion_message/packet.rs b/lightning/src/onion_message/packet.rs index 2fb2407d..510f0ea0 100644 --- a/lightning/src/onion_message/packet.rs +++ b/lightning/src/onion_message/packet.rs @@ -12,11 +12,15 @@ use bitcoin::secp256k1::PublicKey; use bitcoin::secp256k1::ecdh::SharedSecret; -use crate::blinded_path::{BlindedPath, ForwardTlvs, ReceiveTlvs}; +use crate::blinded_path::BlindedPath; +use crate::blinded_path::message::{ForwardTlvs, NextHop, ReceiveTlvs}; +use crate::blinded_path::utils::Padding; use crate::ln::msgs::DecodeError; use crate::ln::onion_utils; use super::messenger::CustomOnionMessageHandler; -use crate::util::chacha20poly1305rfc::{ChaChaPolyReadAdapter, ChaChaPolyWriteAdapter}; +use super::offers::OffersMessage; +use crate::crypto::streams::{ChaChaPolyReadAdapter, ChaChaPolyWriteAdapter}; +use crate::util::logger::Logger; use crate::util::ser::{BigSize, FixedLengthReader, LengthRead, LengthReadable, LengthReadableArgs, Readable, ReadableArgs, Writeable, Writer}; use core::cmp; @@ -28,16 +32,22 @@ use crate::prelude::*; pub(super) const SMALL_PACKET_HOP_DATA_LEN: usize = 1300; pub(super) const BIG_PACKET_HOP_DATA_LEN: usize = 32768; -#[derive(Clone, Debug, PartialEq, Eq)] -pub(crate) struct Packet { - pub(super) version: u8, - pub(super) public_key: PublicKey, +/// Packet of hop data for next peer +#[derive(Clone, Debug, Hash, PartialEq, Eq)] +pub struct Packet { + /// Bolt 04 version number + pub version: u8, + /// A random sepc256k1 point, used to build the ECDH shared secret to decrypt hop_data + pub public_key: PublicKey, + /// Encrypted payload for the next hop + // // Unlike the onion packets used for payments, onion message packets can have payloads greater // than 1300 bytes. // TODO: if 1300 ends up being the most common size, optimize this to be: // enum { ThirteenHundred([u8; 1300]), VarLen(Vec) } - pub(super) hop_data: Vec, - pub(super) hmac: [u8; 32], + pub hop_data: Vec, + /// HMAC to verify the integrity of hop_data + pub hmac: [u8; 32], } impl onion_utils::Packet for Packet { @@ -93,51 +103,51 @@ 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, - message: OnionMessageContents, + message: T, } } +/// The contents of an [`OnionMessage`] as read from the wire. +/// +/// [`OnionMessage`]: crate::ln::msgs::OnionMessage #[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 { - // Coming soon: - // Invoice, - // InvoiceRequest, - // InvoiceError, +pub enum ParsedOnionMessageContents { + /// A message related to BOLT 12 Offers. + Offers(OffersMessage), /// A custom onion message specified by the user. Custom(T), } -impl OnionMessageContents { +impl OnionMessageContents for ParsedOnionMessageContents { /// Returns the type that was used to decode the message payload. /// /// This is not exported to bindings users as methods on non-cloneable enums are not currently exportable - pub fn tlv_type(&self) -> u64 { + fn tlv_type(&self) -> u64 { match self { - &OnionMessageContents::Custom(ref msg) => msg.tlv_type(), + &ParsedOnionMessageContents::Offers(ref msg) => msg.tlv_type(), + &ParsedOnionMessageContents::Custom(ref msg) => msg.tlv_type(), } } } -/// This is not exported to bindings users as methods on non-cloneable enums are not currently exportable -impl Writeable for OnionMessageContents { +impl Writeable for ParsedOnionMessageContents { fn write(&self, w: &mut W) -> Result<(), io::Error> { match self { - OnionMessageContents::Custom(msg) => Ok(msg.write(w)?), + ParsedOnionMessageContents::Offers(msg) => Ok(msg.write(w)?), + ParsedOnionMessageContents::Custom(msg) => Ok(msg.write(w)?), } } } -/// The contents of a custom onion message. -pub trait CustomOnionMessageContents: Writeable { +/// The contents of an onion message. +pub trait OnionMessageContents: Writeable + core::fmt::Debug { /// Returns the TLV type identifying the message contents. MUST be >= 64. fn tlv_type(&self) -> u64; } @@ -149,7 +159,8 @@ pub(super) enum ForwardControlTlvs { Blinded(Vec), /// If we're constructing an onion message hop through an intermediate unblinded node, we'll need /// to construct the intermediate hop's control TLVs in their unblinded state to avoid encoding - /// them into an intermediate Vec. See [`crate::blinded_path::ForwardTlvs`] for more info. + /// them into an intermediate Vec. See [`crate::blinded_path::message::ForwardTlvs`] for more + /// info. Unblinded(ForwardTlvs), } @@ -157,17 +168,17 @@ pub(super) enum ForwardControlTlvs { pub(super) enum ReceiveControlTlvs { /// See [`ForwardControlTlvs::Blinded`]. Blinded(Vec), - /// See [`ForwardControlTlvs::Unblinded`] and [`crate::blinded_path::ReceiveTlvs`]. + /// See [`ForwardControlTlvs::Unblinded`] and [`crate::blinded_path::message::ReceiveTlvs`]. Unblinded(ReceiveTlvs), } // 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)) => { _encode_varint_length_prefixed_tlv!(w, { - (4, *encrypted_bytes, vec_type) + (4, *encrypted_bytes, required_vec) }) }, Payload::Receive { @@ -175,7 +186,7 @@ impl Writeable for (Payload, [u8; 32]) { } => { _encode_varint_length_prefixed_tlv!(w, { (2, reply_path, option), - (4, *encrypted_bytes, vec_type), + (4, *encrypted_bytes, required_vec), (message.tlv_type(), message, required) }) }, @@ -201,9 +212,10 @@ impl Writeable for (Payload, [u8; 32]) { } // Uses the provided secret to simultaneously decode and decrypt the control TLVs and data TLV. -impl ReadableArgs<(SharedSecret, &H)> for Payload<::CustomMessage> { - fn read(r: &mut R, args: (SharedSecret, &H)) -> Result { - let (encrypted_tlvs_ss, handler) = args; +impl ReadableArgs<(SharedSecret, &H, &L)> +for Payload::CustomMessage>> { + fn read(r: &mut R, args: (SharedSecret, &H, &L)) -> Result { + let (encrypted_tlvs_ss, handler, logger) = args; let v: BigSize = Readable::read(r)?; let mut rd = FixedLengthReader::new(r, v.0); @@ -221,13 +233,19 @@ impl ReadableArgs<(SharedSecret, &H)> for Payload< if message_type.is_some() { return Err(DecodeError::InvalidValue) } message_type = Some(msg_type); - match handler.read_custom_message(msg_type, msg_reader) { - Ok(Some(msg)) => { - message = Some(msg); + match msg_type { + tlv_type if OffersMessage::is_known_type(tlv_type) => { + let msg = OffersMessage::read(msg_reader, (tlv_type, logger))?; + message = Some(ParsedOnionMessageContents::Offers(msg)); Ok(true) }, - Ok(None) => Ok(false), - Err(e) => Err(e), + _ => match handler.read_custom_message(msg_type, msg_reader)? { + Some(msg) => { + message = Some(ParsedOnionMessageContents::Custom(msg)); + Ok(true) + }, + None => Ok(false), + }, } }); rd.eat_remaining().map_err(|_| DecodeError::ShortRead)?; @@ -241,20 +259,20 @@ impl ReadableArgs<(SharedSecret, &H)> for Payload< Ok(Payload::Forward(ForwardControlTlvs::Unblinded(tlvs))) }, Some(ChaChaPolyReadAdapter { readable: ControlTlvs::Receive(tlvs)}) => { - if message.is_none() { return Err(DecodeError::InvalidValue) } Ok(Payload::Receive { control_tlvs: ReceiveControlTlvs::Unblinded(tlvs), reply_path, - message: OnionMessageContents::Custom(message.unwrap()), + message: message.ok_or(DecodeError::InvalidValue)?, }) - } + }, } } } /// When reading a packet off the wire, we don't know a priori whether the packet is to be forwarded -/// or received. Thus we read a ControlTlvs rather than reading a ForwardControlTlvs or -/// ReceiveControlTlvs directly. +/// or received. Thus we read a `ControlTlvs` rather than reading a [`ForwardTlvs`] or +/// [`ReceiveTlvs`] directly. Also useful on the encoding side to keep forward and receive TLVs in +/// the same iterator. pub(crate) enum ControlTlvs { /// This onion message is intended to be forwarded. Forward(ForwardTlvs), @@ -263,26 +281,29 @@ pub(crate) enum ControlTlvs { } impl Readable for ControlTlvs { - fn read(mut r: &mut R) -> Result { - let mut _padding: Option = None; - let mut _short_channel_id: Option = None; - let mut next_node_id: Option = None; - let mut path_id: Option<[u8; 32]> = None; - let mut next_blinding_override: Option = None; - decode_tlv_stream!(&mut r, { + fn read(r: &mut R) -> Result { + _init_and_read_tlv_stream!(r, { (1, _padding, option), - (2, _short_channel_id, option), + (2, short_channel_id, option), (4, next_node_id, option), (6, path_id, option), (8, next_blinding_override, option), }); + let _padding: Option = _padding; - let valid_fwd_fmt = next_node_id.is_some() && path_id.is_none(); - let valid_recv_fmt = next_node_id.is_none() && next_blinding_override.is_none(); + let next_hop = match (short_channel_id, next_node_id) { + (Some(_), Some(_)) => return Err(DecodeError::InvalidValue), + (Some(scid), None) => Some(NextHop::ShortChannelId(scid)), + (None, Some(pubkey)) => Some(NextHop::NodeId(pubkey)), + (None, None) => None, + }; + + let valid_fwd_fmt = next_hop.is_some() && path_id.is_none(); + let valid_recv_fmt = next_hop.is_none() && next_blinding_override.is_none(); let payload_fmt = if valid_fwd_fmt { ControlTlvs::Forward(ForwardTlvs { - next_node_id: next_node_id.unwrap(), + next_hop: next_hop.unwrap(), next_blinding_override, }) } else if valid_recv_fmt { @@ -297,15 +318,11 @@ impl Readable for ControlTlvs { } } -/// Reads padding to the end, ignoring what's read. -pub(crate) struct Padding {} -impl Readable for Padding { - #[inline] - fn read(reader: &mut R) -> Result { - loop { - let mut buf = [0; 8192]; - if reader.read(&mut buf[..])? == 0 { break; } +impl Writeable for ControlTlvs { + fn write(&self, w: &mut W) -> Result<(), io::Error> { + match self { + Self::Forward(tlvs) => tlvs.write(w), + Self::Receive(tlvs) => tlvs.write(w), } - Ok(Self {}) } }