X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Fonion_message%2Fpacket.rs;h=da8dd561072edd516182b377229788518d431e77;hb=fc9a4c22d195a75ad5942eed271757f285452214;hp=20b1fb0b82fdf5cf0c0b16096f524110d20ba6da;hpb=aa916bb594fbc77c819e82eb2fc269a307376a01;p=rust-lightning diff --git a/lightning/src/onion_message/packet.rs b/lightning/src/onion_message/packet.rs index 20b1fb0b..da8dd561 100644 --- a/lightning/src/onion_message/packet.rs +++ b/lightning/src/onion_message/packet.rs @@ -12,15 +12,15 @@ use bitcoin::secp256k1::PublicKey; use bitcoin::secp256k1::ecdh::SharedSecret; -use ln::msgs::DecodeError; -use ln::onion_utils; +use crate::ln::msgs::DecodeError; +use crate::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 crate::util::chacha20poly1305rfc::{ChaChaPolyReadAdapter, ChaChaPolyWriteAdapter}; +use crate::util::ser::{BigSize, FixedLengthReader, LengthRead, LengthReadable, LengthReadableArgs, MaybeReadableArgs, Readable, ReadableArgs, Writeable, Writer}; use core::cmp; -use io::{self, Read}; -use prelude::*; +use crate::io::{self, Read}; +use crate::prelude::*; // Per the spec, an onion message packet's `hop_data` field length should be // SMALL_PACKET_HOP_DATA_LEN if it fits, else BIG_PACKET_HOP_DATA_LEN if it fits. @@ -92,25 +92,52 @@ 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, } } -// 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. +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 { @@ -132,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)) => { @@ -141,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)) => { @@ -155,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) }) }, } @@ -168,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()), + }) + } } } }