Skip to content

Commit 5a83619

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. We also need to split the construct_keys_callback macro into two macros to avoid an unused assignment warning.
1 parent cfd3998 commit 5a83619

File tree

2 files changed

+241
-10
lines changed

2 files changed

+241
-10
lines changed

lightning/src/ln/onion_utils.rs

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,14 @@ use io::{Cursor, Read};
3434
use core::convert::{TryFrom, TryInto};
3535
use core::ops::Deref;
3636

37-
pub(super) struct OnionKeys {
37+
pub(crate) struct OnionKeys {
3838
#[cfg(test)]
39-
pub(super) shared_secret: SharedSecret,
39+
pub(crate) shared_secret: SharedSecret,
4040
#[cfg(test)]
41-
pub(super) blinding_factor: [u8; 32],
42-
pub(super) ephemeral_pubkey: PublicKey,
43-
pub(super) rho: [u8; 32],
44-
pub(super) mu: [u8; 32],
41+
pub(crate) blinding_factor: [u8; 32],
42+
pub(crate) ephemeral_pubkey: PublicKey,
43+
pub(crate) rho: [u8; 32],
44+
pub(crate) mu: [u8; 32],
4545
}
4646

4747
#[inline]
@@ -232,6 +232,24 @@ pub(super) fn construct_onion_packet(payloads: Vec<msgs::OnionHopData>, onion_ke
232232
payloads, onion_keys, PacketData::Payment(packet_data), Some(associated_data)).try_into().unwrap()
233233
}
234234

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

lightning/src/onion_message.rs

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

31+
// Per the spec, an onion message packet's `hop_data` field length should be
32+
// SMALL_PACKET_HOP_DATA_LEN if it fits, else BIG_PACKET_HOP_DATA_LEN if it fits.
33+
pub(crate) const SMALL_PACKET_HOP_DATA_LEN: usize = 1300;
34+
pub(crate) const BIG_PACKET_HOP_DATA_LEN: usize = 32768;
35+
3136
#[derive(Clone, Debug, PartialEq)]
3237
pub(crate) struct Packet {
3338
pub(crate) version: u8,
@@ -37,6 +42,13 @@ pub(crate) struct Packet {
3742
pub(crate) hmac: [u8; 32],
3843
}
3944

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+
4052
impl Writeable for Packet {
4153
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
4254
self.version.write(w)?;
@@ -74,6 +86,59 @@ impl LengthReadable for Packet {
7486
}
7587
}
7688

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

299+
/// The destination of an onion message.
300+
pub enum Destination {
301+
/// We're sending this onion message to a node.
302+
Node(PublicKey),
303+
/// We're sending this onion message to a blinded route.
304+
BlindedRoute(BlindedRoute),
305+
}
306+
307+
impl Destination {
308+
fn num_hops(&self) -> usize {
309+
match self {
310+
Destination::Node(_) => 1,
311+
Destination::BlindedRoute(BlindedRoute { blinded_hops, .. }) => blinded_hops.len(),
312+
}
313+
}
314+
}
315+
234316
/// A sender, receiver and forwarder of onion messages. In upcoming releases, this object will be
235317
/// used to retrieve invoices and fulfill invoice requests from [offers].
236318
///
@@ -264,6 +346,38 @@ impl<Signer: Sign, K: Deref, L: Deref> OnionMessenger<Signer, K, L>
264346
logger,
265347
}
266348
}
349+
350+
/// Send an empty onion message to `destination`, routing it through `intermediate_nodes`.
351+
pub fn send_onion_message(&self, intermediate_nodes: Vec<PublicKey>, destination: Destination) -> Result<(), secp256k1::Error> {
352+
let blinding_secret_bytes = self.keys_manager.get_secure_random_bytes();
353+
let blinding_secret = SecretKey::from_slice(&blinding_secret_bytes[..]).expect("RNG is busted");
354+
let (introduction_node_id, blinding_point) = if intermediate_nodes.len() != 0 {
355+
(intermediate_nodes[0].clone(), PublicKey::from_secret_key(&self.secp_ctx, &blinding_secret))
356+
} else {
357+
match destination {
358+
Destination::Node(pk) => (pk.clone(), PublicKey::from_secret_key(&self.secp_ctx, &blinding_secret)),
359+
Destination::BlindedRoute(BlindedRoute { introduction_node_id, blinding_point, .. }) =>
360+
(introduction_node_id.clone(), blinding_point.clone()),
361+
}
362+
};
363+
let (encrypted_data_keys, onion_packet_keys) = construct_sending_keys(
364+
&self.secp_ctx, &intermediate_nodes, &destination, &blinding_secret)?;
365+
let payloads = build_payloads(intermediate_nodes, destination, encrypted_data_keys);
366+
367+
let prng_seed = self.keys_manager.get_secure_random_bytes();
368+
let onion_packet = onion_utils::construct_onion_message_packet(payloads, onion_packet_keys, prng_seed);
369+
370+
let mut pending_msg_events = self.pending_msg_events.lock().unwrap();
371+
pending_msg_events.push(MessageSendEvent::SendOnionMessage {
372+
node_id: introduction_node_id,
373+
msg: msgs::OnionMessage {
374+
blinding_point,
375+
len: onion_packet.len(),
376+
onion_routing_packet: onion_packet,
377+
}
378+
});
379+
Ok(())
380+
}
267381
}
268382

269383
impl<Signer: Sign, K: Deref, L: Deref> OnionMessageHandler for OnionMessenger<Signer, K, L>
@@ -285,19 +399,69 @@ impl<Signer: Sign, K: Deref, L: Deref> MessageSendEventsProvider for OnionMessen
285399
}
286400
}
287401

402+
/// Build an onion message's payloads for encoding in the onion packet.
403+
fn build_payloads(intermediate_nodes: Vec<PublicKey>, destination: Destination, mut encrypted_tlvs_keys: Vec<[u8; 32]>) -> Vec<(Payload, [u8; 32])> {
404+
let num_intermediate_nodes = intermediate_nodes.len();
405+
let num_payloads = num_intermediate_nodes + destination.num_hops();
406+
assert_eq!(encrypted_tlvs_keys.len(), num_payloads);
407+
let mut payloads = Vec::with_capacity(num_payloads);
408+
let mut enc_tlv_keys = encrypted_tlvs_keys.drain(..);
409+
for pk in intermediate_nodes.into_iter().skip(1) {
410+
payloads.push((Payload {
411+
encrypted_tlvs: EncryptedTlvs::Unblinded(ControlTlvs::Forward {
412+
next_node_id: pk,
413+
next_blinding_override: None,
414+
})
415+
}, enc_tlv_keys.next().unwrap()));
416+
}
417+
match destination {
418+
Destination::Node(pk) => {
419+
if num_intermediate_nodes != 0 {
420+
payloads.push((Payload {
421+
encrypted_tlvs: EncryptedTlvs::Unblinded(ControlTlvs::Forward {
422+
next_node_id: pk,
423+
next_blinding_override: None,
424+
})
425+
}, enc_tlv_keys.next().unwrap()));
426+
}
427+
payloads.push((Payload {
428+
encrypted_tlvs: EncryptedTlvs::Unblinded(ControlTlvs::Receive {
429+
path_id: None,
430+
})
431+
}, enc_tlv_keys.next().unwrap()));
432+
},
433+
Destination::BlindedRoute(BlindedRoute { introduction_node_id, blinding_point, blinded_hops }) => {
434+
if num_intermediate_nodes != 0 {
435+
payloads.push((Payload {
436+
encrypted_tlvs: EncryptedTlvs::Unblinded(ControlTlvs::Forward {
437+
next_node_id: introduction_node_id,
438+
next_blinding_override: Some(blinding_point),
439+
})
440+
}, enc_tlv_keys.next().unwrap()));
441+
}
442+
for hop in blinded_hops {
443+
payloads.push((Payload {
444+
encrypted_tlvs: EncryptedTlvs::Blinded(hop.encrypted_payload),
445+
}, enc_tlv_keys.next().unwrap()));
446+
}
447+
}
448+
}
449+
payloads
450+
}
451+
288452
#[inline]
289453
fn construct_keys_callback<
290454
T: secp256k1::Signing + secp256k1::Verification,
291455
FType: FnMut(PublicKey, SharedSecret, [u8; 32], PublicKey, [u8; 32])>
292-
(secp_ctx: &Secp256k1<T>, unblinded_path: &Vec<PublicKey>, session_priv: &SecretKey, mut callback: FType)
456+
(secp_ctx: &Secp256k1<T>, unblinded_path: &Vec<PublicKey>, destination: Option<&Destination>, session_priv: &SecretKey, mut callback: FType)
293457
-> Result<(), secp256k1::Error> {
294458
let mut msg_blinding_point_priv = session_priv.clone();
295459
let mut msg_blinding_point = PublicKey::from_secret_key(secp_ctx, &msg_blinding_point_priv);
296460
let mut onion_packet_pubkey_priv = msg_blinding_point_priv.clone();
297461
let mut onion_packet_pubkey = msg_blinding_point.clone();
298462

299463
macro_rules! build_keys {
300-
($pk: expr, $blinded: expr) => {
464+
($pk: expr, $blinded: expr) => {{
301465
let encrypted_data_ss = SharedSecret::new(&$pk, &msg_blinding_point_priv);
302466

303467
let hop_pk_blinding_factor = {
@@ -314,6 +478,13 @@ fn construct_keys_callback<
314478

315479
let (rho, _) = onion_utils::gen_rho_mu_from_shared_secret(encrypted_data_ss.as_ref());
316480
callback(blinded_hop_pk, onion_packet_ss, hop_pk_blinding_factor, onion_packet_pubkey, rho);
481+
(encrypted_data_ss, onion_packet_ss)
482+
}}
483+
}
484+
485+
macro_rules! build_keys_in_loop {
486+
($pk: expr, $blinded: expr) => {
487+
let (encrypted_data_ss, onion_packet_ss) = build_keys!($pk, $blinded);
317488

318489
let msg_blinding_point_blinding_factor = {
319490
let mut sha = Sha256::engine();
@@ -337,7 +508,19 @@ fn construct_keys_callback<
337508
}
338509

339510
for pk in unblinded_path {
340-
build_keys!(pk, false);
511+
build_keys_in_loop!(pk, false);
512+
}
513+
if let Some(dest) = destination {
514+
match dest {
515+
Destination::Node(pk) => {
516+
build_keys!(pk, false);
517+
},
518+
Destination::BlindedRoute(BlindedRoute { blinded_hops, .. }) => {
519+
for hop in blinded_hops {
520+
build_keys_in_loop!(hop.blinded_node_id, true);
521+
}
522+
},
523+
}
341524
}
342525
Ok(())
343526
}
@@ -353,14 +536,44 @@ fn construct_blinded_route_keys<T: secp256k1::Signing + secp256k1::Verification>
353536
let mut encrypted_data_keys = Vec::with_capacity(unblinded_path.len());
354537
let mut blinded_node_pks = Vec::with_capacity(unblinded_path.len());
355538

356-
construct_keys_callback(secp_ctx, unblinded_path, session_priv, |blinded_hop_pubkey, _, _, _, encrypted_data_ss| {
539+
construct_keys_callback(secp_ctx, unblinded_path, None, session_priv, |blinded_hop_pubkey, _, _, _, encrypted_data_ss| {
357540
blinded_node_pks.push(blinded_hop_pubkey);
358541
encrypted_data_keys.push(encrypted_data_ss);
359542
})?;
360543

361544
Ok((encrypted_data_keys, blinded_node_pks))
362545
}
363546

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

0 commit comments

Comments
 (0)