lightning/onion_message/
packet.rs

1// This file is Copyright its original authors, visible in version control
2// history.
3//
4// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
5// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
7// You may not use this file except in accordance with one or both of these
8// licenses.
9
10//! Structs and enums useful for constructing and reading an onion message packet.
11
12use bitcoin::secp256k1::ecdh::SharedSecret;
13use bitcoin::secp256k1::PublicKey;
14
15use super::async_payments::AsyncPaymentsMessage;
16use super::dns_resolution::DNSResolverMessage;
17use super::messenger::CustomOnionMessageHandler;
18use super::offers::OffersMessage;
19use crate::blinded_path::message::{
20	BlindedMessagePath, DummyTlv, ForwardTlvs, NextMessageHop, ReceiveTlvs,
21};
22use crate::crypto::streams::{ChaChaDualPolyReadAdapter, ChaChaPolyWriteAdapter};
23use crate::ln::msgs::DecodeError;
24use crate::ln::onion_utils;
25use crate::sign::ReceiveAuthKey;
26use crate::util::logger::Logger;
27use crate::util::ser::{
28	BigSize, FixedLengthReader, LengthLimitedRead, LengthReadable, LengthReadableArgs, Readable,
29	ReadableArgs, Writeable, Writer,
30};
31
32use crate::io::{self, Read};
33use crate::prelude::*;
34use core::cmp;
35use core::fmt;
36
37// Per the spec, an onion message packet's `hop_data` field length should be
38// SMALL_PACKET_HOP_DATA_LEN if it fits, else BIG_PACKET_HOP_DATA_LEN if it fits.
39pub(super) const SMALL_PACKET_HOP_DATA_LEN: usize = 1300;
40pub(super) const BIG_PACKET_HOP_DATA_LEN: usize = 32768;
41
42/// Packet of hop data for next peer
43#[derive(Clone, Hash, PartialEq, Eq)]
44pub struct Packet {
45	/// Bolt 04 version number
46	pub version: u8,
47	/// A random sepc256k1 point, used to build the ECDH shared secret to decrypt hop_data
48	pub public_key: PublicKey,
49	/// Encrypted payload for the next hop
50	//
51	// Unlike the onion packets used for payments, onion message packets can have payloads greater
52	// than 1300 bytes.
53	// TODO: if 1300 ends up being the most common size, optimize this to be:
54	// enum { ThirteenHundred([u8; 1300]), VarLen(Vec<u8>) }
55	pub hop_data: Vec<u8>,
56	/// HMAC to verify the integrity of hop_data
57	pub hmac: [u8; 32],
58}
59
60impl onion_utils::Packet for Packet {
61	type Data = Vec<u8>;
62	fn new(public_key: PublicKey, hop_data: Vec<u8>, hmac: [u8; 32]) -> Packet {
63		Self { version: 0, public_key, hop_data, hmac }
64	}
65}
66
67impl fmt::Debug for Packet {
68	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
69		f.write_fmt(format_args!(
70			"Onion message packet version {} with hmac {:?}",
71			self.version,
72			&self.hmac[..]
73		))
74	}
75}
76
77impl Writeable for Packet {
78	fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
79		self.version.write(w)?;
80		self.public_key.write(w)?;
81		w.write_all(&self.hop_data)?;
82		self.hmac.write(w)?;
83		Ok(())
84	}
85}
86
87impl LengthReadable for Packet {
88	fn read_from_fixed_length_buffer<R: LengthLimitedRead>(r: &mut R) -> Result<Self, DecodeError> {
89		const READ_BUFFER_SIZE: usize = 4096;
90		let hop_data_len = r.remaining_bytes().saturating_sub(66) as usize; // 1 (version) + 33 (pubkey) + 32 (HMAC) = 66
91
92		let version = Readable::read(r)?;
93		let public_key = Readable::read(r)?;
94
95		let mut hop_data = Vec::new();
96		let mut read_idx = 0;
97		while read_idx < hop_data_len {
98			let mut read_buffer = [0; READ_BUFFER_SIZE];
99			let read_amt = cmp::min(hop_data_len - read_idx, READ_BUFFER_SIZE);
100			r.read_exact(&mut read_buffer[..read_amt])?;
101			hop_data.extend_from_slice(&read_buffer[..read_amt]);
102			read_idx += read_amt;
103		}
104
105		let hmac = Readable::read(r)?;
106		Ok(Packet { version, public_key, hop_data, hmac })
107	}
108}
109
110/// Onion message payloads contain "control" TLVs and "data" TLVs. Control TLVs are used to route
111/// the onion message from hop to hop and for path verification, whereas data TLVs contain the onion
112/// message content itself, such as an invoice request.
113pub(super) enum Payload<T: OnionMessageContents> {
114	/// This payload is for an intermediate hop.
115	Forward(ForwardControlTlvs),
116	/// This payload is a dummy hop, and is intended to be peeled.
117	Dummy {
118		/// The payload was authenticated with the additional key that was
119		/// provided to [`ReadableArgs::read`].
120		control_tlvs_authenticated: bool,
121	},
122	/// This payload is for the final hop.
123	Receive {
124		/// The [`ReceiveControlTlvs`] were authenticated with the additional key which was
125		/// provided to [`ReadableArgs::read`].
126		control_tlvs_authenticated: bool,
127		control_tlvs: ReceiveControlTlvs,
128		reply_path: Option<BlindedMessagePath>,
129		message: T,
130	},
131}
132
133/// The contents of an [`OnionMessage`] as read from the wire.
134///
135/// [`OnionMessage`]: crate::ln::msgs::OnionMessage
136#[derive(Clone, Debug)]
137pub enum ParsedOnionMessageContents<T: OnionMessageContents> {
138	/// A message related to BOLT 12 Offers.
139	Offers(OffersMessage),
140	/// A message related to async payments.
141	AsyncPayments(AsyncPaymentsMessage),
142	/// A message requesting or providing a DNSSEC proof
143	DNSResolver(DNSResolverMessage),
144	/// A custom onion message specified by the user.
145	Custom(T),
146}
147
148impl<T: OnionMessageContents> OnionMessageContents for ParsedOnionMessageContents<T> {
149	/// Returns the type that was used to decode the message payload.
150	///
151	/// This is not exported to bindings users as methods on non-cloneable enums are not currently exportable
152	fn tlv_type(&self) -> u64 {
153		match self {
154			&ParsedOnionMessageContents::Offers(ref msg) => msg.tlv_type(),
155			&ParsedOnionMessageContents::AsyncPayments(ref msg) => msg.tlv_type(),
156			&ParsedOnionMessageContents::DNSResolver(ref msg) => msg.tlv_type(),
157			&ParsedOnionMessageContents::Custom(ref msg) => msg.tlv_type(),
158		}
159	}
160	#[cfg(c_bindings)]
161	fn msg_type(&self) -> String {
162		match self {
163			ParsedOnionMessageContents::Offers(ref msg) => msg.msg_type(),
164			ParsedOnionMessageContents::AsyncPayments(ref msg) => msg.msg_type(),
165			ParsedOnionMessageContents::DNSResolver(ref msg) => msg.msg_type(),
166			ParsedOnionMessageContents::Custom(ref msg) => msg.msg_type(),
167		}
168	}
169	#[cfg(not(c_bindings))]
170	fn msg_type(&self) -> &'static str {
171		match self {
172			ParsedOnionMessageContents::Offers(ref msg) => msg.msg_type(),
173			ParsedOnionMessageContents::AsyncPayments(ref msg) => msg.msg_type(),
174			ParsedOnionMessageContents::DNSResolver(ref msg) => msg.msg_type(),
175			ParsedOnionMessageContents::Custom(ref msg) => msg.msg_type(),
176		}
177	}
178}
179
180impl<T: OnionMessageContents> Writeable for ParsedOnionMessageContents<T> {
181	fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
182		match self {
183			ParsedOnionMessageContents::Offers(msg) => msg.write(w),
184			ParsedOnionMessageContents::AsyncPayments(msg) => msg.write(w),
185			ParsedOnionMessageContents::DNSResolver(msg) => msg.write(w),
186			ParsedOnionMessageContents::Custom(msg) => msg.write(w),
187		}
188	}
189}
190
191/// The contents of an onion message.
192pub trait OnionMessageContents: Writeable + core::fmt::Debug {
193	/// Returns the TLV type identifying the message contents. MUST be >= 64.
194	fn tlv_type(&self) -> u64;
195
196	#[cfg(c_bindings)]
197	/// Returns the message type
198	fn msg_type(&self) -> String;
199
200	#[cfg(not(c_bindings))]
201	/// Returns the message type
202	fn msg_type(&self) -> &'static str;
203}
204
205/// Forward control TLVs in their blinded and unblinded form.
206pub(super) enum ForwardControlTlvs {
207	/// If we're sending to a blinded path, the node that constructed the blinded path has provided
208	/// this hop's control TLVs, already encrypted into bytes.
209	Blinded(Vec<u8>),
210	/// If we're constructing an onion message hop through an intermediate unblinded node, we'll need
211	/// to construct the intermediate hop's control TLVs in their unblinded state to avoid encoding
212	/// them into an intermediate Vec. See [`crate::blinded_path::message::ForwardTlvs`] for more
213	/// info.
214	Unblinded(ForwardTlvs),
215}
216
217/// Receive control TLVs in their blinded and unblinded form.
218pub(super) enum ReceiveControlTlvs {
219	/// See [`ForwardControlTlvs::Blinded`].
220	Blinded(Vec<u8>),
221	/// See [`ForwardControlTlvs::Unblinded`] and [`crate::blinded_path::message::ReceiveTlvs`].
222	Unblinded(ReceiveTlvs),
223}
224
225// Uses the provided secret to simultaneously encode and encrypt the unblinded control TLVs.
226impl<T: OnionMessageContents> Writeable for (Payload<T>, [u8; 32]) {
227	fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
228		match &self.0 {
229			Payload::Forward(ForwardControlTlvs::Blinded(encrypted_bytes)) => {
230				_encode_varint_length_prefixed_tlv!(w, { (4, encrypted_bytes, required_vec) })
231			},
232			Payload::Receive {
233				control_tlvs: ReceiveControlTlvs::Blinded(encrypted_bytes),
234				reply_path,
235				message,
236				control_tlvs_authenticated: _,
237			} => {
238				_encode_varint_length_prefixed_tlv!(w, {
239					(2, reply_path, option),
240					(4, encrypted_bytes, required_vec),
241					(message.tlv_type(), message, required)
242				})
243			},
244			Payload::Forward(ForwardControlTlvs::Unblinded(control_tlvs)) => {
245				let write_adapter = ChaChaPolyWriteAdapter::new(self.1, &control_tlvs);
246				_encode_varint_length_prefixed_tlv!(w, { (4, write_adapter, required) })
247			},
248			Payload::Dummy { control_tlvs_authenticated: _ } => {
249				let write_adapter = ChaChaPolyWriteAdapter::new(self.1, &DummyTlv);
250				_encode_varint_length_prefixed_tlv!(w, { (4, write_adapter, required) })
251			},
252			Payload::Receive {
253				control_tlvs: ReceiveControlTlvs::Unblinded(control_tlvs),
254				reply_path,
255				message,
256				control_tlvs_authenticated: _,
257			} => {
258				let write_adapter = ChaChaPolyWriteAdapter::new(self.1, &control_tlvs);
259				_encode_varint_length_prefixed_tlv!(w, {
260					(2, reply_path, option),
261					(4, write_adapter, required),
262					(message.tlv_type(), message, required)
263				})
264			},
265		}
266		Ok(())
267	}
268}
269
270// Uses the provided secret to simultaneously decode and decrypt the control TLVs and data TLV.
271impl<H: CustomOnionMessageHandler + ?Sized, L: Logger + ?Sized>
272	ReadableArgs<(SharedSecret, &H, ReceiveAuthKey, &L)>
273	for Payload<ParsedOnionMessageContents<<H as CustomOnionMessageHandler>::CustomMessage>>
274{
275	fn read<R: Read>(
276		r: &mut R, args: (SharedSecret, &H, ReceiveAuthKey, &L),
277	) -> Result<Self, DecodeError> {
278		let (encrypted_tlvs_ss, handler, receive_tlvs_key, logger) = args;
279
280		let v: BigSize = Readable::read(r)?;
281		let mut rd = FixedLengthReader::new(r, v.0);
282		let mut reply_path: Option<BlindedMessagePath> = None;
283		let mut read_adapter: Option<ChaChaDualPolyReadAdapter<ControlTlvs>> = None;
284		let rho = onion_utils::gen_rho_from_shared_secret(&encrypted_tlvs_ss.secret_bytes());
285		let mut message_type: Option<u64> = None;
286		let mut message = None;
287		decode_tlv_stream_with_custom_tlv_decode!(&mut rd, {
288			(2, reply_path, option),
289			(4, read_adapter, (option: LengthReadableArgs, (rho, receive_tlvs_key.0))),
290		}, |msg_type, msg_reader| {
291			if msg_type < 64 { return Ok(false) }
292			// Don't allow reading more than one data TLV from an onion message.
293			if message_type.is_some() { return Err(DecodeError::InvalidValue) }
294
295			message_type = Some(msg_type);
296			match msg_type {
297				tlv_type if OffersMessage::is_known_type(tlv_type) => {
298					let msg = OffersMessage::read(msg_reader, (tlv_type, logger))?;
299					message = Some(ParsedOnionMessageContents::Offers(msg));
300					Ok(true)
301				},
302				tlv_type if AsyncPaymentsMessage::is_known_type(tlv_type) => {
303					let msg = AsyncPaymentsMessage::read(msg_reader, tlv_type)?;
304					message = Some(ParsedOnionMessageContents::AsyncPayments(msg));
305					Ok(true)
306				},
307				tlv_type if DNSResolverMessage::is_known_type(tlv_type) => {
308					let msg = DNSResolverMessage::read(msg_reader, tlv_type)?;
309					message = Some(ParsedOnionMessageContents::DNSResolver(msg));
310					Ok(true)
311				},
312				_ => match handler.read_custom_message(msg_type, msg_reader)? {
313					Some(msg) => {
314						message = Some(ParsedOnionMessageContents::Custom(msg));
315						Ok(true)
316					},
317					None => Ok(false),
318				},
319			}
320		});
321		rd.eat_remaining().map_err(|_| DecodeError::ShortRead)?;
322
323		match read_adapter {
324			None => return Err(DecodeError::InvalidValue),
325			Some(ChaChaDualPolyReadAdapter { readable: ControlTlvs::Forward(tlvs), used_aad }) => {
326				if used_aad || message_type.is_some() {
327					return Err(DecodeError::InvalidValue);
328				}
329				Ok(Payload::Forward(ForwardControlTlvs::Unblinded(tlvs)))
330			},
331			Some(ChaChaDualPolyReadAdapter { readable: ControlTlvs::Dummy, used_aad }) => {
332				Ok(Payload::Dummy { control_tlvs_authenticated: used_aad })
333			},
334			Some(ChaChaDualPolyReadAdapter { readable: ControlTlvs::Receive(tlvs), used_aad }) => {
335				Ok(Payload::Receive {
336					control_tlvs: ReceiveControlTlvs::Unblinded(tlvs),
337					reply_path,
338					message: message.ok_or(DecodeError::InvalidValue)?,
339					control_tlvs_authenticated: used_aad,
340				})
341			},
342		}
343	}
344}
345
346/// When reading a packet off the wire, we don't know a priori whether the packet is to be forwarded
347/// or received. Thus we read a `ControlTlvs` rather than reading a [`ForwardTlvs`] or
348/// [`ReceiveTlvs`] directly. Also useful on the encoding side to keep forward and receive TLVs in
349/// the same iterator.
350pub(crate) enum ControlTlvs {
351	/// This onion message is intended to be forwarded.
352	Forward(ForwardTlvs),
353	/// This onion message is a dummy, and is intended to be peeled by the final recipient.
354	Dummy,
355	/// This onion message is intended to be received.
356	Receive(ReceiveTlvs),
357}
358
359impl Readable for ControlTlvs {
360	fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
361		_init_and_read_tlv_stream!(r, {
362			// Reasoning: Padding refers to filler data added to a packet to increase
363			// its size and obscure its actual length. Since padding contains no meaningful
364			// information, we can safely omit reading it here.
365			// (1, _padding, option),
366			(2, short_channel_id, option),
367			(4, next_node_id, option),
368			(8, next_blinding_override, option),
369			(65537, context, option),
370			(65539, is_dummy, option),
371		});
372
373		let next_hop = match (short_channel_id, next_node_id) {
374			(Some(_), Some(_)) => return Err(DecodeError::InvalidValue),
375			(Some(scid), None) => Some(NextMessageHop::ShortChannelId(scid)),
376			(None, Some(pubkey)) => Some(NextMessageHop::NodeId(pubkey)),
377			(None, None) => None,
378		};
379
380		let payload_fmt = match (next_hop, next_blinding_override, is_dummy) {
381			(Some(hop), _, None) => {
382				ControlTlvs::Forward(ForwardTlvs { next_hop: hop, next_blinding_override })
383			},
384			(None, None, Some(())) => ControlTlvs::Dummy,
385			(None, None, None) => ControlTlvs::Receive(ReceiveTlvs { context }),
386			_ => return Err(DecodeError::InvalidValue),
387		};
388
389		Ok(payload_fmt)
390	}
391}
392
393impl Writeable for ControlTlvs {
394	fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
395		match self {
396			Self::Forward(tlvs) => tlvs.write(w),
397			Self::Dummy => DummyTlv.write(w),
398			Self::Receive(tlvs) => tlvs.write(w),
399		}
400	}
401}