Skip to content

Commit 1fd127b

Browse files
committed
public static peel_onion method on OnionMessenger
1 parent 827b17c commit 1fd127b

File tree

2 files changed

+125
-81
lines changed

2 files changed

+125
-81
lines changed

lightning/src/onion_message/messenger.rs

Lines changed: 124 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,21 @@ pub trait CustomOnionMessageHandler {
246246
fn read_custom_message<R: io::Read>(&self, message_type: u64, buffer: &mut R) -> Result<Option<Self::CustomMessage>, msgs::DecodeError>;
247247
}
248248

249+
/// An processed incoming onion message, containing either a Forward (another onion)
250+
/// or a Receive payload with decrypted contents
251+
pub enum PeeledOnion<CMH: Deref> where
252+
CMH::Target: CustomOnionMessageHandler,
253+
{
254+
/// Forwarded onion, with the next node id and a new onion
255+
Forward(PublicKey, msgs::OnionMessage),
256+
/// Received onion message, with decrypted contents, path_id, and reply path
257+
Receive(OnionMessageContents<<<CMH as Deref>::Target as CustomOnionMessageHandler>::CustomMessage>, Option<[u8; 32]>, Option<BlindedPath>)
258+
}
259+
260+
/// Errors that may occur when [receiving an onion message].
261+
#[derive(Debug, PartialEq, Eq)]
262+
pub struct ReceiveError { }
263+
249264
impl<ES: Deref, NS: Deref, L: Deref, MR: Deref, OMH: Deref, CMH: Deref>
250265
OnionMessenger<ES, NS, L, MR, OMH, CMH>
251266
where
@@ -360,6 +375,103 @@ where
360375
}))
361376
}
362377

378+
/// Decode one layer of an incoming onion message
379+
/// Returns either a Forward (another onion message), or Receive (decrypted content)
380+
pub fn peel_onion<T: CustomOnionMessageContents>(
381+
node_signer: &NS,
382+
secp_ctx: &Secp256k1<secp256k1::All>,
383+
logger: &L,
384+
custom_handler: &CMH,
385+
msg: &msgs::OnionMessage,
386+
) -> Result<PeeledOnion<CMH>, ReceiveError> {
387+
let control_tlvs_ss = match node_signer.ecdh(Recipient::Node, &msg.blinding_point, None) {
388+
Ok(ss) => ss,
389+
Err(e) => {
390+
log_error!(logger, "Failed to retrieve node secret: {:?}", e);
391+
return Err(ReceiveError {});
392+
}
393+
};
394+
let onion_decode_ss = {
395+
let blinding_factor = {
396+
let mut hmac = HmacEngine::<Sha256>::new(b"blinded_node_id");
397+
hmac.input(control_tlvs_ss.as_ref());
398+
Hmac::from_engine(hmac).into_inner()
399+
};
400+
match node_signer.ecdh(Recipient::Node, &msg.onion_routing_packet.public_key,
401+
Some(&Scalar::from_be_bytes(blinding_factor).unwrap()))
402+
{
403+
Ok(ss) => ss.secret_bytes(),
404+
Err(()) => {
405+
log_trace!(logger, "Failed to compute onion packet shared secret");
406+
return Err(ReceiveError {});
407+
}
408+
}
409+
};
410+
match onion_utils::decode_next_untagged_hop(
411+
onion_decode_ss, &msg.onion_routing_packet.hop_data[..], msg.onion_routing_packet.hmac,
412+
(control_tlvs_ss, custom_handler.deref(), logger.deref())
413+
) {
414+
Ok((Payload::Receive::<<<CMH as Deref>::Target as CustomOnionMessageHandler>::CustomMessage> {
415+
message, control_tlvs: ReceiveControlTlvs::Unblinded(ReceiveTlvs { path_id }), reply_path,
416+
}, None)) => {
417+
log_trace!(logger,
418+
"Received an onion message with path_id {:02x?} and {} reply_path",
419+
path_id, if reply_path.is_some() { "a" } else { "no" });
420+
421+
Ok(PeeledOnion::Receive(message, path_id, reply_path))
422+
},
423+
Ok((Payload::Forward(ForwardControlTlvs::Unblinded(ForwardTlvs {
424+
next_node_id, next_blinding_override
425+
})), Some((next_hop_hmac, new_packet_bytes)))) => {
426+
// TODO: we need to check whether `next_node_id` is our node, in which case this is a dummy
427+
// blinded hop and this onion message is destined for us. In this situation, we should keep
428+
// unwrapping the onion layers to get to the final payload. Since we don't have the option
429+
// of creating blinded paths with dummy hops currently, we should be ok to not handle this
430+
// for now.
431+
let new_pubkey = match onion_utils::next_hop_pubkey(&secp_ctx, msg.onion_routing_packet.public_key, &onion_decode_ss) {
432+
Ok(pk) => pk,
433+
Err(e) => {
434+
log_trace!(logger, "Failed to compute next hop packet pubkey: {}", e);
435+
return Err(ReceiveError {})
436+
}
437+
};
438+
let outgoing_packet = Packet {
439+
version: 0,
440+
public_key: new_pubkey,
441+
hop_data: new_packet_bytes,
442+
hmac: next_hop_hmac,
443+
};
444+
let onion_message = msgs::OnionMessage {
445+
blinding_point: match next_blinding_override {
446+
Some(blinding_point) => blinding_point,
447+
None => {
448+
match onion_utils::next_hop_pubkey(
449+
&secp_ctx, msg.blinding_point, control_tlvs_ss.as_ref()
450+
) {
451+
Ok(bp) => bp,
452+
Err(e) => {
453+
log_trace!(logger, "Failed to compute next blinding point: {}", e);
454+
return Err(ReceiveError {})
455+
}
456+
}
457+
}
458+
},
459+
onion_routing_packet: outgoing_packet,
460+
};
461+
462+
Ok(PeeledOnion::Forward(next_node_id, onion_message))
463+
},
464+
Err(e) => {
465+
log_trace!(logger, "Errored decoding onion message packet: {:?}", e);
466+
Err(ReceiveError {})
467+
},
468+
_ => {
469+
log_trace!(logger, "Received bogus onion message packet, either the sender encoded a final hop as a forwarding hop or vice versa");
470+
Err(ReceiveError {})
471+
},
472+
}
473+
}
474+
363475
fn respond_with_onion_message<T: CustomOnionMessageContents>(
364476
&self, response: OnionMessageContents<T>, path_id: Option<[u8; 32]>,
365477
reply_path: Option<BlindedPath>
@@ -460,40 +572,14 @@ where
460572
/// soon we'll delegate the onion message to a handler that can generate invoices or send
461573
/// payments.
462574
fn handle_onion_message(&self, _peer_node_id: &PublicKey, msg: &msgs::OnionMessage) {
463-
let control_tlvs_ss = match self.node_signer.ecdh(Recipient::Node, &msg.blinding_point, None) {
464-
Ok(ss) => ss,
465-
Err(e) => {
466-
log_error!(self.logger, "Failed to retrieve node secret: {:?}", e);
467-
return
468-
}
469-
};
470-
let onion_decode_ss = {
471-
let blinding_factor = {
472-
let mut hmac = HmacEngine::<Sha256>::new(b"blinded_node_id");
473-
hmac.input(control_tlvs_ss.as_ref());
474-
Hmac::from_engine(hmac).into_inner()
475-
};
476-
match self.node_signer.ecdh(Recipient::Node, &msg.onion_routing_packet.public_key,
477-
Some(&Scalar::from_be_bytes(blinding_factor).unwrap()))
478-
{
479-
Ok(ss) => ss.secret_bytes(),
480-
Err(()) => {
481-
log_trace!(self.logger, "Failed to compute onion packet shared secret");
482-
return
483-
}
484-
}
485-
};
486-
match onion_utils::decode_next_untagged_hop(
487-
onion_decode_ss, &msg.onion_routing_packet.hop_data[..], msg.onion_routing_packet.hmac,
488-
(control_tlvs_ss, &*self.custom_handler, &*self.logger)
575+
match Self::peel_onion::<<<CMH as Deref>::Target as CustomOnionMessageHandler>::CustomMessage>(
576+
&self.node_signer,
577+
&self.secp_ctx,
578+
&self.logger,
579+
&self.custom_handler,
580+
msg
489581
) {
490-
Ok((Payload::Receive::<<<CMH as Deref>::Target as CustomOnionMessageHandler>::CustomMessage> {
491-
message, control_tlvs: ReceiveControlTlvs::Unblinded(ReceiveTlvs { path_id }), reply_path,
492-
}, None)) => {
493-
log_trace!(self.logger,
494-
"Received an onion message with path_id {:02x?} and {} reply_path",
495-
path_id, if reply_path.is_some() { "a" } else { "no" });
496-
582+
Ok(PeeledOnion::Receive(message, path_id, reply_path)) => {
497583
let response = match message {
498584
OnionMessageContents::Offers(msg) => {
499585
self.offers_handler.handle_message(msg)
@@ -504,50 +590,11 @@ where
504590
.map(|msg| OnionMessageContents::Custom(msg))
505591
},
506592
};
507-
508593
if let Some(response) = response {
509594
self.respond_with_onion_message(response, path_id, reply_path);
510595
}
511596
},
512-
Ok((Payload::Forward(ForwardControlTlvs::Unblinded(ForwardTlvs {
513-
next_node_id, next_blinding_override
514-
})), Some((next_hop_hmac, new_packet_bytes)))) => {
515-
// TODO: we need to check whether `next_node_id` is our node, in which case this is a dummy
516-
// blinded hop and this onion message is destined for us. In this situation, we should keep
517-
// unwrapping the onion layers to get to the final payload. Since we don't have the option
518-
// of creating blinded paths with dummy hops currently, we should be ok to not handle this
519-
// for now.
520-
let new_pubkey = match onion_utils::next_hop_pubkey(&self.secp_ctx, msg.onion_routing_packet.public_key, &onion_decode_ss) {
521-
Ok(pk) => pk,
522-
Err(e) => {
523-
log_trace!(self.logger, "Failed to compute next hop packet pubkey: {}", e);
524-
return
525-
}
526-
};
527-
let outgoing_packet = Packet {
528-
version: 0,
529-
public_key: new_pubkey,
530-
hop_data: new_packet_bytes,
531-
hmac: next_hop_hmac,
532-
};
533-
let onion_message = msgs::OnionMessage {
534-
blinding_point: match next_blinding_override {
535-
Some(blinding_point) => blinding_point,
536-
None => {
537-
match onion_utils::next_hop_pubkey(
538-
&self.secp_ctx, msg.blinding_point, control_tlvs_ss.as_ref()
539-
) {
540-
Ok(bp) => bp,
541-
Err(e) => {
542-
log_trace!(self.logger, "Failed to compute next blinding point: {}", e);
543-
return
544-
}
545-
}
546-
}
547-
},
548-
onion_routing_packet: outgoing_packet,
549-
};
550-
597+
Ok(PeeledOnion::Forward(next_node_id, onion_message)) => {
551598
let mut pending_per_peer_msgs = self.pending_messages.lock().unwrap();
552599
if outbound_buffer_full(&next_node_id, &pending_per_peer_msgs) {
553600
log_trace!(self.logger, "Dropping forwarded onion message to peer {:?}: outbound buffer full", next_node_id);
@@ -566,15 +613,12 @@ where
566613
e.get_mut().push_back(onion_message);
567614
log_trace!(self.logger, "Forwarding an onion message to peer {}", next_node_id);
568615
}
569-
};
616+
}
570617
},
571618
Err(e) => {
572-
log_trace!(self.logger, "Errored decoding onion message packet: {:?}", e);
573-
},
574-
_ => {
575-
log_trace!(self.logger, "Received bogus onion message packet, either the sender encoded a final hop as a forwarding hop or vice versa");
576-
},
577-
};
619+
log_error!(self.logger, "Failed to process onion message {:?}", e);
620+
}
621+
}
578622
}
579623

580624
fn peer_connected(&self, their_node_id: &PublicKey, init: &msgs::Init, _inbound: bool) -> Result<(), ()> {

lightning/src/onion_message/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ mod packet;
2727
mod functional_tests;
2828

2929
// Re-export structs so they can be imported with just the `onion_message::` module prefix.
30-
pub use self::messenger::{CustomOnionMessageContents, CustomOnionMessageHandler, DefaultMessageRouter, Destination, MessageRouter, OnionMessageContents, OnionMessagePath, OnionMessenger, SendError, SimpleArcOnionMessenger, SimpleRefOnionMessenger};
30+
pub use self::messenger::{CustomOnionMessageContents, CustomOnionMessageHandler, DefaultMessageRouter, Destination, MessageRouter, OnionMessageContents, OnionMessagePath, OnionMessenger, SendError, SimpleArcOnionMessenger, SimpleRefOnionMessenger, PeeledOnion};
3131
pub use self::offers::{OffersMessage, OffersMessageHandler};
3232
pub use self::packet::Packet;
3333
pub(crate) use self::packet::ControlTlvs;

0 commit comments

Comments
 (0)