Skip to content

Commit bf007ea

Browse files
Implement receiving and forwarding onion messages
This required adapting `onion_utils::decode_next_hop` to work for both payments and onion messages. Currently we just print out the path_id of any onion messages we receive. In the future, these received onion messages will be redirected to their respective handlers: i.e. an invoice_request will go to an InvoiceHandler, custom onion messages will go to a custom handler, etc.
1 parent 9051c38 commit bf007ea

File tree

5 files changed

+277
-27
lines changed

5 files changed

+277
-27
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2159,7 +2159,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
21592159
}
21602160
}
21612161

2162-
let next_hop = match onion_utils::decode_next_hop(shared_secret, &msg.onion_routing_packet.hop_data[..], msg.onion_routing_packet.hmac, msg.payment_hash) {
2162+
let next_hop = match onion_utils::decode_next_payment_hop(shared_secret, &msg.onion_routing_packet.hop_data[..], msg.onion_routing_packet.hmac, msg.payment_hash) {
21632163
Ok(res) => res,
21642164
Err(onion_utils::OnionDecodeErr::Malformed { err_msg, err_code }) => {
21652165
return_malformed_err!(err_msg, err_code);
@@ -3058,7 +3058,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
30583058
let phantom_secret_res = self.keys_manager.get_node_secret(Recipient::PhantomNode);
30593059
if phantom_secret_res.is_ok() && fake_scid::is_valid_phantom(&self.fake_scid_rand_bytes, short_chan_id) {
30603060
let phantom_shared_secret = SharedSecret::new(&onion_packet.public_key.unwrap(), &phantom_secret_res.unwrap()).secret_bytes();
3061-
let next_hop = match onion_utils::decode_next_hop(phantom_shared_secret, &onion_packet.hop_data, onion_packet.hmac, payment_hash) {
3061+
let next_hop = match onion_utils::decode_next_payment_hop(phantom_shared_secret, &onion_packet.hop_data, onion_packet.hmac, payment_hash) {
30623062
Ok(res) => res,
30633063
Err(onion_utils::OnionDecodeErr::Malformed { err_msg, err_code }) => {
30643064
let sha256_of_onion = Sha256::hash(&onion_packet.hop_data).into_inner();

lightning/src/ln/msgs.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ use io_extras::read_to_end;
4242

4343
use util::events::MessageSendEventsProvider;
4444
use util::logger;
45-
use util::ser::{LengthReadable, Readable, Writeable, Writer, FixedLengthReader, HighZeroBytesDroppedVarInt, Hostname};
45+
use util::ser::{LengthReadable, Readable, ReadableArgs, Writeable, Writer, FixedLengthReader, HighZeroBytesDroppedVarInt, Hostname};
4646

4747
use ln::{PaymentPreimage, PaymentHash, PaymentSecret};
4848

@@ -1417,6 +1417,14 @@ impl Writeable for OnionHopData {
14171417
}
14181418
}
14191419

1420+
// ReadableArgs because we need onion_utils::decode_next_hop to accommodate payment packets and
1421+
// onion message packets.
1422+
impl ReadableArgs<()> for OnionHopData {
1423+
fn read<R: Read>(r: &mut R, _arg: ()) -> Result<Self, DecodeError> {
1424+
<Self as Readable>::read(r)
1425+
}
1426+
}
1427+
14201428
impl Readable for OnionHopData {
14211429
fn read<R: Read>(mut r: &mut R) -> Result<Self, DecodeError> {
14221430
use bitcoin::consensus::encode::{Decodable, Error, VarInt};

lightning/src/ln/onion_utils.rs

Lines changed: 72 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use routing::gossip::NetworkUpdate;
1515
use routing::router::RouteHop;
1616
use util::chacha20::{ChaCha20, ChaChaReader};
1717
use util::errors::{self, APIError};
18-
use util::ser::{Readable, Writeable, LengthCalculatingWriter};
18+
use util::ser::{Readable, ReadableArgs, Writeable, LengthCalculatingWriter};
1919
use util::logger::Logger;
2020

2121
use bitcoin::hashes::{Hash, HashEngine};
@@ -82,7 +82,7 @@ pub(super) fn gen_ammag_from_shared_secret(shared_secret: &[u8]) -> [u8; 32] {
8282
Hmac::from_engine(hmac).into_inner()
8383
}
8484

85-
pub(super) fn next_hop_packet_pubkey<T: secp256k1::Signing + secp256k1::Verification>(secp_ctx: &Secp256k1<T>, mut packet_pubkey: PublicKey, packet_shared_secret: &[u8; 32]) -> Result<PublicKey, secp256k1::Error> {
85+
pub(crate) fn next_hop_packet_pubkey<T: secp256k1::Signing + secp256k1::Verification>(secp_ctx: &Secp256k1<T>, mut packet_pubkey: PublicKey, packet_shared_secret: &[u8; 32]) -> Result<PublicKey, secp256k1::Error> {
8686
let blinding_factor = {
8787
let mut sha = Sha256::engine();
8888
sha.input(&packet_pubkey.serialize()[..]);
@@ -580,7 +580,50 @@ pub(super) fn process_onion_failure<T: secp256k1::Signing, L: Deref>(secp_ctx: &
580580
} else { unreachable!(); }
581581
}
582582

583-
/// Data decrypted from the onion payload.
583+
/// An input used when decoding an onion packet.
584+
pub(crate) trait DecodeInput {
585+
type Arg;
586+
/// If Some, this is the input when checking the hmac of the onion packet.
587+
fn payment_hash(&self) -> Option<&PaymentHash>;
588+
/// Read argument when decrypting our hop payload.
589+
fn read_arg(self) -> Self::Arg;
590+
}
591+
592+
impl DecodeInput for PaymentHash {
593+
type Arg = ();
594+
fn payment_hash(&self) -> Option<&PaymentHash> {
595+
Some(self)
596+
}
597+
fn read_arg(self) -> Self::Arg { () }
598+
}
599+
600+
impl DecodeInput for SharedSecret {
601+
type Arg = SharedSecret;
602+
fn payment_hash(&self) -> Option<&PaymentHash> {
603+
None
604+
}
605+
fn read_arg(self) -> Self::Arg { self }
606+
}
607+
608+
/// Allows `decode_next_hop` to return the next hop packet bytes for either payments or onion
609+
/// message forwards.
610+
pub(crate) trait NextPacketBytes: AsMut<[u8]> {
611+
fn new(len: usize) -> Self;
612+
}
613+
614+
impl NextPacketBytes for FixedSizeOnionPacket {
615+
fn new(_len: usize) -> Self {
616+
Self([0 as u8; ONION_DATA_LEN])
617+
}
618+
}
619+
620+
impl NextPacketBytes for Vec<u8> {
621+
fn new(len: usize) -> Self {
622+
vec![0 as u8; len]
623+
}
624+
}
625+
626+
/// Data decrypted from a payment's onion payload.
584627
pub(crate) enum Hop {
585628
/// This onion payload was for us, not for forwarding to a next-hop. Contains information for
586629
/// verifying the incoming payment.
@@ -592,11 +635,12 @@ pub(crate) enum Hop {
592635
/// HMAC of the next hop's onion packet.
593636
next_hop_hmac: [u8; 32],
594637
/// Bytes of the onion packet we're forwarding.
595-
new_packet_bytes: [u8; 20*65],
638+
new_packet_bytes: [u8; ONION_DATA_LEN],
596639
},
597640
}
598641

599642
/// Error returned when we fail to decode the onion packet.
643+
#[derive(Debug)]
600644
pub(crate) enum OnionDecodeErr {
601645
/// The HMAC of the onion packet did not match the hop data.
602646
Malformed {
@@ -610,11 +654,27 @@ pub(crate) enum OnionDecodeErr {
610654
},
611655
}
612656

613-
pub(crate) fn decode_next_hop(shared_secret: [u8; 32], hop_data: &[u8], hmac_bytes: [u8; 32], payment_hash: PaymentHash) -> Result<Hop, OnionDecodeErr> {
657+
pub(crate) fn decode_next_payment_hop(shared_secret: [u8; 32], hop_data: &[u8], hmac_bytes: [u8; 32], payment_hash: PaymentHash) -> Result<Hop, OnionDecodeErr> {
658+
match decode_next_hop(shared_secret, hop_data, hmac_bytes, payment_hash) {
659+
Ok((next_hop_data, None)) => Ok(Hop::Receive(next_hop_data)),
660+
Ok((next_hop_data, Some((next_hop_hmac, FixedSizeOnionPacket(new_packet_bytes))))) => {
661+
Ok(Hop::Forward {
662+
next_hop_data,
663+
next_hop_hmac,
664+
new_packet_bytes
665+
})
666+
},
667+
Err(e) => Err(e),
668+
}
669+
}
670+
671+
pub(crate) fn decode_next_hop<D: DecodeInput, R: ReadableArgs<D::Arg>, N: NextPacketBytes>(shared_secret: [u8; 32], hop_data: &[u8], hmac_bytes: [u8; 32], decode_input: D) -> Result<(R, Option<([u8; 32], N)>), OnionDecodeErr> {
614672
let (rho, mu) = gen_rho_mu_from_shared_secret(&shared_secret);
615673
let mut hmac = HmacEngine::<Sha256>::new(&mu);
616674
hmac.input(hop_data);
617-
hmac.input(&payment_hash.0[..]);
675+
if let Some(payment_hash) = decode_input.payment_hash() {
676+
hmac.input(&payment_hash.0[..]);
677+
}
618678
if !fixed_time_eq(&Hmac::from_engine(hmac).into_inner(), &hmac_bytes) {
619679
return Err(OnionDecodeErr::Malformed {
620680
err_msg: "HMAC Check failed",
@@ -624,7 +684,7 @@ pub(crate) fn decode_next_hop(shared_secret: [u8; 32], hop_data: &[u8], hmac_byt
624684

625685
let mut chacha = ChaCha20::new(&rho, &[0u8; 8]);
626686
let mut chacha_stream = ChaChaReader { chacha: &mut chacha, read: Cursor::new(&hop_data[..]) };
627-
match <msgs::OnionHopData as Readable>::read(&mut chacha_stream) {
687+
match R::read(&mut chacha_stream, decode_input.read_arg()) {
628688
Err(err) => {
629689
let error_code = match err {
630690
msgs::DecodeError::UnknownVersion => 0x4000 | 1, // unknown realm byte
@@ -662,10 +722,10 @@ pub(crate) fn decode_next_hop(shared_secret: [u8; 32], hop_data: &[u8], hmac_byt
662722
chacha_stream.read_exact(&mut next_bytes).unwrap();
663723
assert_ne!(next_bytes[..], [0; 32][..]);
664724
}
665-
return Ok(Hop::Receive(msg));
725+
return Ok((msg, None)); // We are the final destination for this packet
666726
} else {
667-
let mut new_packet_bytes = [0; 20*65];
668-
let read_pos = chacha_stream.read(&mut new_packet_bytes).unwrap();
727+
let mut new_packet_bytes = N::new(hop_data.len());
728+
let read_pos = chacha_stream.read(new_packet_bytes.as_mut()).unwrap();
669729
#[cfg(debug_assertions)]
670730
{
671731
// Check two things:
@@ -677,12 +737,8 @@ pub(crate) fn decode_next_hop(shared_secret: [u8; 32], hop_data: &[u8], hmac_byt
677737
}
678738
// Once we've emptied the set of bytes our peer gave us, encrypt 0 bytes until we
679739
// fill the onion hop data we'll forward to our next-hop peer.
680-
chacha_stream.chacha.process_in_place(&mut new_packet_bytes[read_pos..]);
681-
return Ok(Hop::Forward {
682-
next_hop_data: msg,
683-
next_hop_hmac: hmac,
684-
new_packet_bytes,
685-
})
740+
chacha_stream.chacha.process_in_place(&mut new_packet_bytes.as_mut()[read_pos..]);
741+
return Ok((msg, Some((hmac, new_packet_bytes)))) // This packet needs forwarding
686742
}
687743
},
688744
}

lightning/src/onion_message/messenger.rs

Lines changed: 95 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,12 @@
1010
//! LDK sends, receives, and forwards onion messages via the [`OnionMessenger`]. See its docs for
1111
//! more information.
1212
13+
use bitcoin::hashes::{Hash, HashEngine};
14+
use bitcoin::hashes::hmac::{Hmac, HmacEngine};
15+
use bitcoin::hashes::sha256::Hash as Sha256;
1316
use bitcoin::secp256k1::{self, PublicKey, Secp256k1, SecretKey};
1417

15-
use chain::keysinterface::{InMemorySigner, KeysInterface, KeysManager, Sign};
18+
use chain::keysinterface::{InMemorySigner, KeysInterface, KeysManager, Recipient, Sign};
1619
use ln::msgs;
1720
use ln::onion_utils;
1821
use super::blinded_route::{BlindedRoute, ForwardTlvs, ReceiveTlvs};
@@ -104,6 +107,97 @@ impl<Signer: Sign, K: Deref, L: Deref> OnionMessenger<Signer, K, L>
104107
);
105108
Ok(())
106109
}
110+
111+
/// Handle an incoming onion message. Currently, if a message was destined for us we will log, but
112+
/// soon we'll delegate the onion message to a handler that can generate invoices or send
113+
/// payments.
114+
pub fn handle_onion_message(&self, _peer_node_id: &PublicKey, msg: &msgs::OnionMessage) {
115+
let control_tlvs_ss = match self.keys_manager.ecdh(Recipient::Node, &msg.blinding_point, None) {
116+
Ok(ss) => ss,
117+
Err(e) => {
118+
log_error!(self.logger, "Failed to retrieve node secret: {:?}", e);
119+
return
120+
}
121+
};
122+
let onion_decode_ss = {
123+
let blinding_factor = {
124+
let mut hmac = HmacEngine::<Sha256>::new(b"blinded_node_id");
125+
hmac.input(control_tlvs_ss.as_ref());
126+
Hmac::from_engine(hmac).into_inner()
127+
};
128+
match self.keys_manager.ecdh(Recipient::Node, &msg.onion_routing_packet.public_key,
129+
Some(&blinding_factor))
130+
{
131+
Ok(ss) => ss.secret_bytes(),
132+
Err(()) => {
133+
log_trace!(self.logger, "Failed to compute onion packet shared secret");
134+
return
135+
}
136+
}
137+
};
138+
match onion_utils::decode_next_hop(onion_decode_ss, &msg.onion_routing_packet.hop_data[..],
139+
msg.onion_routing_packet.hmac, control_tlvs_ss)
140+
{
141+
Ok((Payload::Receive {
142+
control_tlvs: ReceiveControlTlvs::Unblinded(ReceiveTlvs { path_id })
143+
}, None)) => {
144+
log_info!(self.logger, "Received an onion message with path_id: {:02x?}", path_id);
145+
},
146+
Ok((Payload::Forward(ForwardControlTlvs::Unblinded(ForwardTlvs {
147+
next_node_id, next_blinding_override
148+
})), Some((next_hop_hmac, new_packet_bytes)))) => {
149+
// TODO: we need to check whether `next_node_id` is our node, in which case this is a dummy
150+
// blinded hop and this onion message is destined for us. In this situation, we should keep
151+
// unwrapping the onion layers to get to the final payload. Since we don't have the option
152+
// of creating blinded routes with dummy hops currently, we should be ok to not handle this
153+
// for now.
154+
let new_pubkey = match onion_utils::next_hop_packet_pubkey(&self.secp_ctx, msg.onion_routing_packet.public_key, &onion_decode_ss) {
155+
Ok(pk) => pk,
156+
Err(e) => {
157+
log_trace!(self.logger, "Failed to compute next hop packet pubkey: {}", e);
158+
return
159+
}
160+
};
161+
let outgoing_packet = Packet {
162+
version: 0,
163+
public_key: new_pubkey,
164+
hop_data: new_packet_bytes,
165+
hmac: next_hop_hmac,
166+
};
167+
168+
let mut pending_per_peer_msgs = self.pending_messages.lock().unwrap();
169+
let pending_msgs = pending_per_peer_msgs.entry(next_node_id).or_insert(Vec::new());
170+
pending_msgs.push(
171+
msgs::OnionMessage {
172+
blinding_point: match next_blinding_override {
173+
Some(blinding_point) => blinding_point,
174+
None => {
175+
let blinding_factor = {
176+
let mut sha = Sha256::engine();
177+
sha.input(&msg.blinding_point.serialize()[..]);
178+
sha.input(control_tlvs_ss.as_ref());
179+
Sha256::from_engine(sha).into_inner()
180+
};
181+
let mut next_blinding_point = msg.blinding_point;
182+
if let Err(e) = next_blinding_point.mul_assign(&self.secp_ctx, &blinding_factor[..]) {
183+
log_trace!(self.logger, "Failed to compute next blinding point: {}", e);
184+
return
185+
}
186+
next_blinding_point
187+
},
188+
},
189+
onion_routing_packet: outgoing_packet,
190+
},
191+
);
192+
},
193+
Err(e) => {
194+
log_trace!(self.logger, "Errored decoding onion message packet: {:?}", e);
195+
},
196+
_ => {
197+
log_trace!(self.logger, "Received bogus onion message packet, either the sender encoded a final hop as a forwarding hop or vice versa");
198+
},
199+
};
200+
}
107201
}
108202

109203
// TODO: parameterize the below Simple* types with OnionMessenger and handle the messages it

0 commit comments

Comments
 (0)