Skip to content

Commit d5c1b32

Browse files
Support sending onion messages
This adds several utilities in service of then adding OnionMessenger::send_onion_message, which can send to either an unblinded pubkey or a blinded route. Sending custom TLVs and sending an onion message containing a reply path are not yet supported.
1 parent 654db9b commit d5c1b32

File tree

2 files changed

+224
-2
lines changed

2 files changed

+224
-2
lines changed

lightning/src/ln/onion_message.rs

Lines changed: 206 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ use io::{self, Read};
2828
use prelude::*;
2929
use sync::{Arc, Mutex};
3030

31+
pub(crate) const SMALL_PACKET_HOP_DATA_LEN: usize = 1300;
32+
pub(crate) const BIG_PACKET_HOP_DATA_LEN: usize = 32768;
33+
3134
#[derive(Clone, Debug, PartialEq)]
3235
pub(crate) struct Packet {
3336
pub(crate) version: u8,
@@ -39,6 +42,13 @@ pub(crate) struct Packet {
3942
pub(crate) hmac: [u8; 32],
4043
}
4144

45+
impl Packet {
46+
fn len(&self) -> u16 {
47+
// 32 (hmac) + 33 (public_key) + 1 (version) = 66
48+
self.hop_data.len() as u16 + 66
49+
}
50+
}
51+
4252
impl Writeable for Packet {
4353
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
4454
self.version.write(w)?;
@@ -79,6 +89,59 @@ impl ReadableArgs<u16> for Packet {
7989
}
8090
}
8191

92+
/// The payload of an onion message.
93+
pub(crate) struct Payload {
94+
/// Onion message payloads contain an encrypted TLV stream, containing both "control" TLVs and
95+
/// sometimes user-provided custom "data" TLVs. See [`EncryptedTlvs`] for more information.
96+
encrypted_tlvs: EncryptedTlvs,
97+
// Coming soon:
98+
// * custom TLVs
99+
// * `message: Message` field
100+
// * `reply_path: Option<BlindedRoute>` field
101+
}
102+
103+
// Coming soon:
104+
// enum Message {
105+
// InvoiceRequest(InvoiceRequest),
106+
// Invoice(Invoice),
107+
// InvoiceError(InvoiceError),
108+
// CustomMessage<T>,
109+
// }
110+
111+
/// We want to avoid encoding and encrypting separately in order to avoid an intermediate Vec, thus
112+
/// we encode and encrypt at the same time using the `SharedSecret` here.
113+
impl Writeable for (Payload, SharedSecret) {
114+
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
115+
match &self.0.encrypted_tlvs {
116+
EncryptedTlvs::Blinded(encrypted_bytes) => {
117+
encode_varint_length_prefixed_tlv!(w, {
118+
(4, encrypted_bytes, vec_type)
119+
})
120+
},
121+
EncryptedTlvs::Unblinded(control_tlvs) => {
122+
encode_varint_length_prefixed_tlv!(w, {
123+
(4, control_tlvs, (chacha, self.1))
124+
})
125+
}
126+
}
127+
Ok(())
128+
}
129+
}
130+
131+
/// Onion messages contain an encrypted TLV stream. This can be supplied by someone else, in the
132+
/// case that we're sending to a blinded route, or created by us if we're constructing payloads for
133+
/// unblinded hops in the onion message's path.
134+
pub(crate) enum EncryptedTlvs {
135+
/// If we're sending to a blinded route, the node that constructed the blinded route has provided
136+
/// our onion message's `EncryptedTlvs`, already encrypted and encoded into bytes.
137+
Blinded(Vec<u8>),
138+
/// If we're receiving an onion message or constructing an onion message to send through any
139+
/// unblinded nodes, we'll need to construct the onion message's `EncryptedTlvs` in their
140+
/// unblinded state to avoid encoding them into an intermediate `Vec`.
141+
// Below will later have an additional Vec<CustomTlv>
142+
Unblinded(ControlTlvs),
143+
}
144+
82145
/// Onion messages have "control" TLVs and "data" TLVs. Control TLVs are used to control the
83146
/// direction and routing of an onion message from hop to hop, whereas data TLVs contain the onion
84147
/// message content itself.
@@ -243,6 +306,23 @@ impl BlindedRoute {
243306
}
244307
}
245308

309+
/// The destination of an onion message.
310+
pub enum Destination {
311+
/// We're sending this onion message to a node.
312+
Node(PublicKey),
313+
/// We're sending this onion message to a blinded route.
314+
BlindedRoute(BlindedRoute),
315+
}
316+
317+
impl Destination {
318+
fn num_hops(&self) -> usize {
319+
match self {
320+
Destination::Node(_) => 1,
321+
Destination::BlindedRoute(BlindedRoute { blinded_hops, .. }) => blinded_hops.len(),
322+
}
323+
}
324+
}
325+
246326
/// A sender, receiver and forwarder of onion messages. In upcoming releases, this object will be
247327
/// used to retrieve invoices and fulfill invoice requests from offers.
248328
pub struct OnionMessenger<Signer: Sign, K: Deref, L: Deref>
@@ -274,6 +354,38 @@ impl<Signer: Sign, K: Deref, L: Deref> OnionMessenger<Signer, K, L>
274354
logger,
275355
}
276356
}
357+
358+
/// Send an empty onion message to `destination`, routing it through `intermediate_nodes`.
359+
pub fn send_onion_message(&self, intermediate_nodes: Vec<PublicKey>, destination: Destination) -> Result<(), secp256k1::Error> {
360+
let blinding_secret_bytes = self.keys_manager.get_secure_random_bytes();
361+
let blinding_secret = SecretKey::from_slice(&blinding_secret_bytes[..]).expect("RNG is busted");
362+
let (introduction_node_id, blinding_point) = if intermediate_nodes.len() != 0 {
363+
(intermediate_nodes[0].clone(), PublicKey::from_secret_key(&self.secp_ctx, &blinding_secret))
364+
} else {
365+
match destination {
366+
Destination::Node(pk) => (pk.clone(), PublicKey::from_secret_key(&self.secp_ctx, &blinding_secret)),
367+
Destination::BlindedRoute(BlindedRoute { introduction_node_id, blinding_point, .. }) =>
368+
(introduction_node_id.clone(), blinding_point.clone()),
369+
}
370+
};
371+
let (encrypted_data_keys, onion_packet_keys) = construct_sending_keys(
372+
&self.secp_ctx, &intermediate_nodes, &destination, &blinding_secret)?;
373+
let payloads = build_payloads(intermediate_nodes, destination, encrypted_data_keys);
374+
375+
let prng_seed = self.keys_manager.get_secure_random_bytes();
376+
let onion_packet = onion_utils::construct_onion_message_packet(payloads, onion_packet_keys, prng_seed);
377+
378+
let mut pending_msg_events = self.pending_msg_events.lock().unwrap();
379+
pending_msg_events.push(MessageSendEvent::SendOnionMessage {
380+
node_id: introduction_node_id,
381+
msg: msgs::OnionMessage {
382+
blinding_point,
383+
len: onion_packet.len(),
384+
onion_routing_packet: onion_packet,
385+
}
386+
});
387+
Ok(())
388+
}
277389
}
278390

279391
impl<Signer: Sign, K: Deref, L: Deref> OnionMessageHandler for OnionMessenger<Signer, K, L>
@@ -295,9 +407,59 @@ impl<Signer: Sign, K: Deref, L: Deref> MessageSendEventsProvider for OnionMessen
295407
}
296408
}
297409

410+
/// Build an onion message's payloads for encoding in the onion packet.
411+
fn build_payloads(intermediate_nodes: Vec<PublicKey>, destination: Destination, mut encrypted_tlvs_keys: Vec<SharedSecret>) -> Vec<(Payload, SharedSecret)> {
412+
let num_intermediate_nodes = intermediate_nodes.len();
413+
let num_payloads = num_intermediate_nodes + destination.num_hops();
414+
assert_eq!(encrypted_tlvs_keys.len(), num_payloads);
415+
let mut payloads = Vec::with_capacity(num_payloads);
416+
let mut enc_tlv_keys = encrypted_tlvs_keys.drain(..);
417+
for pk in intermediate_nodes.into_iter().skip(1) {
418+
payloads.push((Payload {
419+
encrypted_tlvs: EncryptedTlvs::Unblinded(ControlTlvs::Forward {
420+
next_node_id: pk,
421+
next_blinding_override: None,
422+
})
423+
}, enc_tlv_keys.next().unwrap()));
424+
}
425+
match destination {
426+
Destination::Node(pk) => {
427+
if num_intermediate_nodes != 0 {
428+
payloads.push((Payload {
429+
encrypted_tlvs: EncryptedTlvs::Unblinded(ControlTlvs::Forward {
430+
next_node_id: pk,
431+
next_blinding_override: None,
432+
})
433+
}, enc_tlv_keys.next().unwrap()));
434+
}
435+
payloads.push((Payload {
436+
encrypted_tlvs: EncryptedTlvs::Unblinded(ControlTlvs::Receive {
437+
path_id: None,
438+
})
439+
}, enc_tlv_keys.next().unwrap()));
440+
},
441+
Destination::BlindedRoute(BlindedRoute { introduction_node_id, blinding_point, blinded_hops }) => {
442+
if num_intermediate_nodes != 0 {
443+
payloads.push((Payload {
444+
encrypted_tlvs: EncryptedTlvs::Unblinded(ControlTlvs::Forward {
445+
next_node_id: introduction_node_id,
446+
next_blinding_override: Some(blinding_point),
447+
})
448+
}, enc_tlv_keys.next().unwrap()));
449+
}
450+
for hop in blinded_hops {
451+
payloads.push((Payload {
452+
encrypted_tlvs: EncryptedTlvs::Blinded(hop.encrypted_payload),
453+
}, enc_tlv_keys.next().unwrap()));
454+
}
455+
}
456+
}
457+
payloads
458+
}
459+
298460
#[allow(unused_assignments)]
299461
#[inline]
300-
fn construct_keys_callback<T: secp256k1::Signing + secp256k1::Verification, FType: FnMut(PublicKey, SharedSecret, [u8; 32], PublicKey, SharedSecret)> (secp_ctx: &Secp256k1<T>, unblinded_path: &Vec<PublicKey>, session_priv: &SecretKey, mut callback: FType) -> Result<(), secp256k1::Error> {
462+
fn construct_keys_callback<T: secp256k1::Signing + secp256k1::Verification, FType: FnMut(PublicKey, SharedSecret, [u8; 32], PublicKey, SharedSecret)> (secp_ctx: &Secp256k1<T>, unblinded_path: &Vec<PublicKey>, destination: Option<&Destination>, session_priv: &SecretKey, mut callback: FType) -> Result<(), secp256k1::Error> {
301463
let mut msg_blinding_point_priv = session_priv.clone();
302464
let mut msg_blinding_point = PublicKey::from_secret_key(secp_ctx, &msg_blinding_point_priv);
303465
let mut onion_packet_pubkey_priv = msg_blinding_point_priv.clone();
@@ -345,6 +507,18 @@ fn construct_keys_callback<T: secp256k1::Signing + secp256k1::Verification, FTyp
345507
for pk in unblinded_path {
346508
build_keys!(pk, false);
347509
}
510+
if let Some(dest) = destination {
511+
match dest {
512+
Destination::Node(pk) => {
513+
build_keys!(pk, false);
514+
},
515+
Destination::BlindedRoute(BlindedRoute { blinded_hops, .. }) => {
516+
for hop in blinded_hops {
517+
build_keys!(hop.blinded_node_id, true);
518+
}
519+
},
520+
}
521+
}
348522
Ok(())
349523
}
350524

@@ -359,14 +533,44 @@ fn construct_blinded_route_keys<T: secp256k1::Signing + secp256k1::Verification>
359533
let mut encrypted_data_keys = Vec::with_capacity(unblinded_path.len());
360534
let mut blinded_node_pks = Vec::with_capacity(unblinded_path.len());
361535

362-
construct_keys_callback(secp_ctx, unblinded_path, session_priv, |blinded_hop_pubkey, _, _, _, encrypted_data_ss| {
536+
construct_keys_callback(secp_ctx, unblinded_path, None, session_priv, |blinded_hop_pubkey, _, _, _, encrypted_data_ss| {
363537
blinded_node_pks.push(blinded_hop_pubkey);
364538
encrypted_data_keys.push(encrypted_data_ss);
365539
})?;
366540

367541
Ok((encrypted_data_keys, blinded_node_pks))
368542
}
369543

544+
/// Construct keys for sending an onion message along the given `path`.
545+
///
546+
/// Returns: `(encrypted_tlvs_keys, onion_packet_keys)`
547+
/// where the encrypted tlvs keys are used to encrypt the [`EncryptedTlvs`] of the onion message and the
548+
/// onion packet keys are used to encrypt the onion packet.
549+
fn construct_sending_keys<T: secp256k1::Signing + secp256k1::Verification>(
550+
secp_ctx: &Secp256k1<T>, unblinded_path: &Vec<PublicKey>, destination: &Destination, session_priv: &SecretKey
551+
) -> Result<(Vec<SharedSecret>, Vec<onion_utils::OnionKeys>), secp256k1::Error> {
552+
let num_hops = unblinded_path.len() + destination.num_hops();
553+
let mut encrypted_data_keys = Vec::with_capacity(num_hops);
554+
let mut onion_packet_keys = Vec::with_capacity(num_hops);
555+
556+
construct_keys_callback(secp_ctx, unblinded_path, Some(destination), session_priv, |_, onion_packet_ss, _blinding_factor, ephemeral_pubkey, encrypted_data_ss| {
557+
encrypted_data_keys.push(encrypted_data_ss);
558+
559+
let (rho, mu) = onion_utils::gen_rho_mu_from_shared_secret(onion_packet_ss.as_ref());
560+
onion_packet_keys.push(onion_utils::OnionKeys {
561+
#[cfg(test)]
562+
shared_secret: onion_packet_ss,
563+
#[cfg(test)]
564+
blinding_factor: _blinding_factor,
565+
ephemeral_pubkey,
566+
rho,
567+
mu,
568+
});
569+
})?;
570+
571+
Ok((encrypted_data_keys, onion_packet_keys))
572+
}
573+
370574
/// Useful for simplifying the parameters of [`SimpleArcChannelManager`] and
371575
/// [`SimpleArcPeerManager`]. See their docs for more details.
372576
pub type SimpleArcOnionMessenger<L> = OnionMessenger<InMemorySigner, Arc<KeysManager>, Arc<L>>;

lightning/src/ln/onion_utils.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,24 @@ pub(super) fn construct_onion_packet(payloads: Vec<msgs::OnionHopData>, onion_ke
236236
payloads, onion_keys, PacketData::Payment(packet_data), Some(associated_data)).try_into().unwrap()
237237
}
238238

239+
pub(super) fn construct_onion_message_packet(payloads: Vec<(onion_message::Payload, SharedSecret)>, onion_keys: Vec<OnionKeys>, prng_seed: [u8; 32]) -> onion_message::Packet {
240+
let payloads_serialized_len = payloads.iter()
241+
.fold(0, |total, next_payload| total + next_payload.serialized_length() + 32 /* HMAC */ );
242+
let hop_data_len = if payloads_serialized_len <= onion_message::SMALL_PACKET_HOP_DATA_LEN {
243+
onion_message::SMALL_PACKET_HOP_DATA_LEN
244+
} else if payloads_serialized_len <= onion_message::BIG_PACKET_HOP_DATA_LEN {
245+
onion_message::BIG_PACKET_HOP_DATA_LEN
246+
} else { payloads_serialized_len };
247+
248+
let mut packet_data = vec![0; hop_data_len];
249+
250+
let mut chacha = ChaCha20::new(&prng_seed, &[0; 8]);
251+
chacha.process_in_place(&mut packet_data);
252+
253+
construct_onion_packet_with_init_noise(
254+
payloads, onion_keys, PacketData::Message(packet_data), None).try_into().unwrap()
255+
}
256+
239257
#[cfg(test)]
240258
// Used in testing to write bogus OnionHopDatas, which is otherwise not representable in
241259
// msgs::OnionHopData.

0 commit comments

Comments
 (0)