use utils::test_persister::TestPersister;
use bitcoin::secp256k1::{PublicKey,SecretKey};
+use bitcoin::secp256k1::ecdh::SharedSecret;
use bitcoin::secp256k1::ecdsa::RecoverableSignature;
use bitcoin::secp256k1::Secp256k1;
Ok(SecretKey::from_slice(&[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, self.node_id]).unwrap())
}
+ fn ecdh(&self, recipient: Recipient, other_key: &PublicKey, tweak: Option<&[u8; 32]>) -> Result<SharedSecret, ()> {
+ let mut node_secret = self.get_node_secret(recipient)?;
+ if let Some(tweak) = tweak {
+ node_secret.mul_assign(tweak).map_err(|_| ())?;
+ }
+ Ok(SharedSecret::new(other_key, &node_secret))
+ }
+
fn get_inbound_payment_key_material(&self) -> KeyMaterial {
KeyMaterial([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, self.node_id])
}
use utils::test_persister::TestPersister;
use bitcoin::secp256k1::{PublicKey,SecretKey};
+use bitcoin::secp256k1::ecdh::SharedSecret;
use bitcoin::secp256k1::ecdsa::RecoverableSignature;
use bitcoin::secp256k1::Secp256k1;
Ok(self.node_secret.clone())
}
+ fn ecdh(&self, recipient: Recipient, other_key: &PublicKey, tweak: Option<&[u8; 32]>) -> Result<SharedSecret, ()> {
+ let mut node_secret = self.get_node_secret(recipient)?;
+ if let Some(tweak) = tweak {
+ node_secret.mul_assign(tweak).map_err(|_| ())?;
+ }
+ Ok(SharedSecret::new(other_key, &node_secret))
+ }
+
fn get_inbound_payment_key_material(&self) -> KeyMaterial {
self.inbound_payment_key.clone()
}
use bitcoin::secp256k1::{SecretKey, PublicKey};
use bitcoin::secp256k1::{Secp256k1, ecdsa::Signature, Signing};
+use bitcoin::secp256k1::ecdh::SharedSecret;
use bitcoin::secp256k1::ecdsa::RecoverableSignature;
use bitcoin::{secp256k1, Witness};
/// This method must return the same value each time it is called with a given `Recipient`
/// parameter.
fn get_node_secret(&self, recipient: Recipient) -> Result<SecretKey, ()>;
+ /// Gets the ECDH shared secret of our [`node secret`] and `other_key`, multiplying by `tweak` if
+ /// one is provided. Note that this tweak can be applied to `other_key` instead of our node
+ /// secret, though this is less efficient.
+ ///
+ /// [`node secret`]: Self::get_node_secret
+ fn ecdh(&self, recipient: Recipient, other_key: &PublicKey, tweak: Option<&[u8; 32]>) -> Result<SharedSecret, ()>;
/// Get a script pubkey which we send funds to when claiming on-chain contestable outputs.
///
/// This method should return a different value each time it is called, to avoid linking
}
}
+ fn ecdh(&self, recipient: Recipient, other_key: &PublicKey, tweak: Option<&[u8; 32]>) -> Result<SharedSecret, ()> {
+ let mut node_secret = self.get_node_secret(recipient)?;
+ if let Some(tweak) = tweak {
+ node_secret.mul_assign(tweak).map_err(|_| ())?;
+ }
+ Ok(SharedSecret::new(other_key, &node_secret))
+ }
+
fn get_inbound_payment_key_material(&self) -> KeyMaterial {
self.inbound_payment_key.clone()
}
}
}
+ fn ecdh(&self, recipient: Recipient, other_key: &PublicKey, tweak: Option<&[u8; 32]>) -> Result<SharedSecret, ()> {
+ let mut node_secret = self.get_node_secret(recipient)?;
+ if let Some(tweak) = tweak {
+ node_secret.mul_assign(tweak).map_err(|_| ())?;
+ }
+ Ok(SharedSecret::new(other_key, &node_secret))
+ }
+
fn get_inbound_payment_key_material(&self) -> KeyMaterial {
self.inbound_payment_key.clone()
}
//! figure out how best to make networking happen/timers fire/things get written to disk/keys get
//! generated/etc. This makes it a good candidate for tight integration into an existing wallet
//! instead of having a rather-separate lightning appendage to a wallet.
-//!
+//!
//! `default` features are:
//!
//! * `std` - enables functionalities which require `std`, including `std::io` trait implementations and things which utilize time
pub mod chain;
pub mod ln;
pub mod routing;
+#[allow(unused)]
+mod onion_message; // To be exposed after sending/receiving OMs is supported in PeerManager.
#[cfg(feature = "std")]
/// Re-export of either `core2::io` or `std::io`, depending on the `std` feature flag.
use bitcoin::secp256k1::{Secp256k1, ecdsa::Signature};
use bitcoin::secp256k1::ffi::Signature as FFISignature;
use bitcoin::secp256k1::{SecretKey,PublicKey};
+ use bitcoin::secp256k1::ecdh::SharedSecret;
use bitcoin::secp256k1::ecdsa::RecoverableSignature;
use bitcoin::hashes::sha256::Hash as Sha256;
use bitcoin::hashes::Hash;
type Signer = InMemorySigner;
fn get_node_secret(&self, _recipient: Recipient) -> Result<SecretKey, ()> { panic!(); }
+ fn ecdh(&self, _recipient: Recipient, _other_key: &PublicKey, _tweak: Option<&[u8; 32]>) -> Result<SharedSecret, ()> { panic!(); }
fn get_inbound_payment_key_material(&self) -> KeyMaterial { panic!(); }
fn get_destination_script(&self) -> Script {
let secp_ctx = Secp256k1::signing_only();
}
}
- 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) {
+ 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) {
Ok(res) => res,
Err(onion_utils::OnionDecodeErr::Malformed { err_msg, err_code }) => {
return_malformed_err!(err_msg, err_code);
let phantom_secret_res = self.keys_manager.get_node_secret(Recipient::PhantomNode);
if phantom_secret_res.is_ok() && fake_scid::is_valid_phantom(&self.fake_scid_rand_bytes, short_chan_id) {
let phantom_shared_secret = SharedSecret::new(&onion_packet.public_key.unwrap(), &phantom_secret_res.unwrap()).secret_bytes();
- let next_hop = match onion_utils::decode_next_hop(phantom_shared_secret, &onion_packet.hop_data, onion_packet.hmac, payment_hash) {
+ let next_hop = match onion_utils::decode_next_payment_hop(phantom_shared_secret, &onion_packet.hop_data, onion_packet.hmac, payment_hash) {
Ok(res) => res,
Err(onion_utils::OnionDecodeErr::Malformed { err_msg, err_code }) => {
let sha256_of_onion = Sha256::hash(&onion_packet.hop_data).into_inner();
#[cfg(not(fuzzing))]
pub(crate) mod channel;
-mod onion_utils;
+pub(crate) mod onion_utils;
pub mod wire;
// Older rustc (which we support) refuses to let us call the get_payment_preimage_hash!() macro
use bitcoin::hash_types::{Txid, BlockHash};
use ln::features::{ChannelFeatures, ChannelTypeFeatures, InitFeatures, NodeFeatures};
+use ln::onion_utils;
+use onion_message;
use prelude::*;
use core::fmt;
use util::events::MessageSendEventsProvider;
use util::logger;
-use util::ser::{Readable, Writeable, Writer, FixedLengthReader, HighZeroBytesDroppedVarInt, Hostname};
+use util::ser::{LengthReadable, Readable, ReadableArgs, Writeable, Writer, FixedLengthReader, HighZeroBytesDroppedVarInt, Hostname};
use ln::{PaymentPreimage, PaymentHash, PaymentSecret};
pub(crate) onion_routing_packet: OnionPacket,
}
+ /// An onion message to be sent or received from a peer
+#[derive(Clone, Debug, PartialEq)]
+pub struct OnionMessage {
+ /// Used in decrypting the onion packet's payload.
+ pub blinding_point: PublicKey,
+ pub(crate) onion_routing_packet: onion_message::Packet,
+}
+
/// An update_fulfill_htlc message to be sent or received from a peer
#[derive(Clone, Debug, PartialEq)]
pub struct UpdateFulfillHTLC {
pub(crate) hmac: [u8; 32],
}
+impl onion_utils::Packet for OnionPacket {
+ type Data = onion_utils::FixedSizeOnionPacket;
+ fn new(pubkey: PublicKey, hop_data: onion_utils::FixedSizeOnionPacket, hmac: [u8; 32]) -> Self {
+ Self {
+ version: 0,
+ public_key: Ok(pubkey),
+ hop_data: hop_data.0,
+ hmac,
+ }
+ }
+}
+
impl PartialEq for OnionPacket {
fn eq(&self, other: &OnionPacket) -> bool {
for (i, j) in self.hop_data.iter().zip(other.hop_data.iter()) {
onion_routing_packet
}, {});
+impl Readable for OnionMessage {
+ fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
+ let blinding_point: PublicKey = Readable::read(r)?;
+ let len: u16 = Readable::read(r)?;
+ let mut packet_reader = FixedLengthReader::new(r, len as u64);
+ let onion_routing_packet: onion_message::Packet = <onion_message::Packet as LengthReadable>::read(&mut packet_reader)?;
+ Ok(Self {
+ blinding_point,
+ onion_routing_packet,
+ })
+ }
+}
+
+impl Writeable for OnionMessage {
+ fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
+ self.blinding_point.write(w)?;
+ let onion_packet_len = self.onion_routing_packet.serialized_length();
+ (onion_packet_len as u16).write(w)?;
+ self.onion_routing_packet.write(w)?;
+ Ok(())
+ }
+}
+
impl Writeable for FinalOnionHopData {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
self.payment_secret.0.write(w)?;
}
}
+// ReadableArgs because we need onion_utils::decode_next_hop to accommodate payment packets and
+// onion message packets.
+impl ReadableArgs<()> for OnionHopData {
+ fn read<R: Read>(r: &mut R, _arg: ()) -> Result<Self, DecodeError> {
+ <Self as Readable>::read(r)
+ }
+}
+
impl Readable for OnionHopData {
fn read<R: Read>(mut r: &mut R) -> Result<Self, DecodeError> {
use bitcoin::consensus::encode::{Decodable, Error, VarInt};
use routing::router::RouteHop;
use util::chacha20::{ChaCha20, ChaChaReader};
use util::errors::{self, APIError};
-use util::ser::{Readable, Writeable, LengthCalculatingWriter};
+use util::ser::{Readable, ReadableArgs, Writeable, LengthCalculatingWriter};
use util::logger::Logger;
use bitcoin::hashes::{Hash, HashEngine};
use prelude::*;
use io::{Cursor, Read};
-use core::convert::TryInto;
+use core::convert::{AsMut, TryInto};
use core::ops::Deref;
-pub(super) struct OnionKeys {
+pub(crate) struct OnionKeys {
#[cfg(test)]
- pub(super) shared_secret: SharedSecret,
+ pub(crate) shared_secret: SharedSecret,
#[cfg(test)]
- pub(super) blinding_factor: [u8; 32],
- pub(super) ephemeral_pubkey: PublicKey,
- pub(super) rho: [u8; 32],
- pub(super) mu: [u8; 32],
+ pub(crate) blinding_factor: [u8; 32],
+ pub(crate) ephemeral_pubkey: PublicKey,
+ pub(crate) rho: [u8; 32],
+ pub(crate) mu: [u8; 32],
}
#[inline]
-pub(super) fn gen_rho_mu_from_shared_secret(shared_secret: &[u8]) -> ([u8; 32], [u8; 32]) {
+pub(crate) fn gen_rho_from_shared_secret(shared_secret: &[u8]) -> [u8; 32] {
+ assert_eq!(shared_secret.len(), 32);
+ let mut hmac = HmacEngine::<Sha256>::new(&[0x72, 0x68, 0x6f]); // rho
+ hmac.input(&shared_secret);
+ Hmac::from_engine(hmac).into_inner()
+}
+
+#[inline]
+pub(crate) fn gen_rho_mu_from_shared_secret(shared_secret: &[u8]) -> ([u8; 32], [u8; 32]) {
assert_eq!(shared_secret.len(), 32);
({
let mut hmac = HmacEngine::<Sha256>::new(&[0x72, 0x68, 0x6f]); // rho
Hmac::from_engine(hmac).into_inner()
}
-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> {
+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> {
let blinding_factor = {
let mut sha = Sha256::engine();
sha.input(&packet_pubkey.serialize()[..]);
pub(crate) const ONION_DATA_LEN: usize = 20*65;
#[inline]
-fn shift_arr_right(arr: &mut [u8; ONION_DATA_LEN], amt: usize) {
- for i in (amt..ONION_DATA_LEN).rev() {
+fn shift_slice_right(arr: &mut [u8], amt: usize) {
+ for i in (amt..arr.len()).rev() {
arr[i] = arr[i-amt];
}
for i in 0..amt {
false
}
-/// panics if route_size_insane(paylods)
+/// panics if route_size_insane(payloads)
pub(super) fn construct_onion_packet(payloads: Vec<msgs::OnionHopData>, onion_keys: Vec<OnionKeys>, prng_seed: [u8; 32], associated_data: &PaymentHash) -> msgs::OnionPacket {
let mut packet_data = [0; ONION_DATA_LEN];
let mut chacha = ChaCha20::new(&prng_seed, &[0; 8]);
chacha.process(&[0; ONION_DATA_LEN], &mut packet_data);
- construct_onion_packet_with_init_noise(payloads, onion_keys, packet_data, associated_data)
+ construct_onion_packet_with_init_noise::<_, _>(
+ payloads, onion_keys, FixedSizeOnionPacket(packet_data), Some(associated_data))
}
#[cfg(test)]
let mut chacha = ChaCha20::new(&prng_seed, &[0; 8]);
chacha.process(&[0; ONION_DATA_LEN], &mut packet_data);
- construct_onion_packet_with_init_noise(payloads, onion_keys, packet_data, associated_data)
+ construct_onion_packet_with_init_noise::<_, _>(
+ payloads, onion_keys, FixedSizeOnionPacket(packet_data), Some(associated_data))
+}
+
+/// Since onion message packets and onion payment packets have different lengths but are otherwise
+/// identical, we use this trait to allow `construct_onion_packet_with_init_noise` to return either
+/// type.
+pub(crate) trait Packet {
+ type Data: AsMut<[u8]>;
+ fn new(pubkey: PublicKey, hop_data: Self::Data, hmac: [u8; 32]) -> Self;
+}
+
+// Needed for rustc versions older than 1.47 to avoid E0277: "arrays only have std trait
+// implementations for lengths 0..=32".
+pub(crate) struct FixedSizeOnionPacket(pub(crate) [u8; ONION_DATA_LEN]);
+
+impl AsMut<[u8]> for FixedSizeOnionPacket {
+ fn as_mut(&mut self) -> &mut [u8] {
+ &mut self.0
+ }
+}
+
+pub(crate) fn payloads_serialized_length<HD: Writeable>(payloads: &Vec<HD>) -> usize {
+ payloads.iter().map(|p| p.serialized_length() + 32 /* HMAC */).sum()
}
-/// panics if route_size_insane(paylods)
-fn construct_onion_packet_with_init_noise<HD: Writeable>(mut payloads: Vec<HD>, onion_keys: Vec<OnionKeys>, mut packet_data: [u8; ONION_DATA_LEN], associated_data: &PaymentHash) -> msgs::OnionPacket {
+/// panics if payloads_serialized_length(payloads) > packet_data_len
+pub(crate) fn construct_onion_message_packet<HD: Writeable, P: Packet<Data = Vec<u8>>>(
+ payloads: Vec<HD>, onion_keys: Vec<OnionKeys>, prng_seed: [u8; 32], packet_data_len: usize) -> P
+{
+ let mut packet_data = vec![0; packet_data_len];
+
+ let mut chacha = ChaCha20::new(&prng_seed, &[0; 8]);
+ chacha.process_in_place(&mut packet_data);
+
+ construct_onion_packet_with_init_noise::<_, _>(payloads, onion_keys, packet_data, None)
+}
+
+/// panics if payloads_serialized_length(payloads) > packet_data.len()
+fn construct_onion_packet_with_init_noise<HD: Writeable, P: Packet>(
+ mut payloads: Vec<HD>, onion_keys: Vec<OnionKeys>, mut packet_data: P::Data, associated_data: Option<&PaymentHash>) -> P
+{
let filler = {
+ let packet_data = packet_data.as_mut();
const ONION_HOP_DATA_LEN: usize = 65; // We may decrease this eventually after TLV is common
let mut res = Vec::with_capacity(ONION_HOP_DATA_LEN * (payloads.len() - 1));
if i == payloads.len() - 1 { break; }
let mut chacha = ChaCha20::new(&keys.rho, &[0u8; 8]);
- for _ in 0..(ONION_DATA_LEN - pos) { // TODO: Batch this.
+ for _ in 0..(packet_data.len() - pos) { // TODO: Batch this.
let mut dummy = [0; 1];
chacha.process_in_place(&mut dummy); // We don't have a seek function :(
}
let mut payload_len = LengthCalculatingWriter(0);
payload.write(&mut payload_len).expect("Failed to calculate length");
pos += payload_len.0 + 32;
- assert!(pos <= ONION_DATA_LEN);
+ assert!(pos <= packet_data.len());
res.resize(pos, 0u8);
chacha.process_in_place(&mut res);
for (i, (payload, keys)) in payloads.iter_mut().zip(onion_keys.iter()).rev().enumerate() {
let mut payload_len = LengthCalculatingWriter(0);
payload.write(&mut payload_len).expect("Failed to calculate length");
- shift_arr_right(&mut packet_data, payload_len.0 + 32);
+
+ let packet_data = packet_data.as_mut();
+ shift_slice_right(packet_data, payload_len.0 + 32);
packet_data[0..payload_len.0].copy_from_slice(&payload.encode()[..]);
packet_data[payload_len.0..(payload_len.0 + 32)].copy_from_slice(&hmac_res);
let mut chacha = ChaCha20::new(&keys.rho, &[0u8; 8]);
- chacha.process_in_place(&mut packet_data);
+ chacha.process_in_place(packet_data);
if i == 0 {
packet_data[ONION_DATA_LEN - filler.len()..ONION_DATA_LEN].copy_from_slice(&filler[..]);
}
let mut hmac = HmacEngine::<Sha256>::new(&keys.mu);
- hmac.input(&packet_data);
- hmac.input(&associated_data.0[..]);
+ hmac.input(packet_data);
+ if let Some(associated_data) = associated_data {
+ hmac.input(&associated_data.0[..]);
+ }
hmac_res = Hmac::from_engine(hmac).into_inner();
}
- msgs::OnionPacket {
- version: 0,
- public_key: Ok(onion_keys.first().unwrap().ephemeral_pubkey),
- hop_data: packet_data,
- hmac: hmac_res,
- }
+ P::new(onion_keys.first().unwrap().ephemeral_pubkey, packet_data, hmac_res)
}
/// Encrypts a failure packet. raw_packet can either be a
} else { unreachable!(); }
}
-/// Data decrypted from the onion payload.
+/// An input used when decoding an onion packet.
+pub(crate) trait DecodeInput {
+ type Arg;
+ /// If Some, this is the input when checking the hmac of the onion packet.
+ fn payment_hash(&self) -> Option<&PaymentHash>;
+ /// Read argument when decrypting our hop payload.
+ fn read_arg(self) -> Self::Arg;
+}
+
+impl DecodeInput for PaymentHash {
+ type Arg = ();
+ fn payment_hash(&self) -> Option<&PaymentHash> {
+ Some(self)
+ }
+ fn read_arg(self) -> Self::Arg { () }
+}
+
+impl DecodeInput for SharedSecret {
+ type Arg = SharedSecret;
+ fn payment_hash(&self) -> Option<&PaymentHash> {
+ None
+ }
+ fn read_arg(self) -> Self::Arg { self }
+}
+
+/// Allows `decode_next_hop` to return the next hop packet bytes for either payments or onion
+/// message forwards.
+pub(crate) trait NextPacketBytes: AsMut<[u8]> {
+ fn new(len: usize) -> Self;
+}
+
+impl NextPacketBytes for FixedSizeOnionPacket {
+ fn new(_len: usize) -> Self {
+ Self([0 as u8; ONION_DATA_LEN])
+ }
+}
+
+impl NextPacketBytes for Vec<u8> {
+ fn new(len: usize) -> Self {
+ vec![0 as u8; len]
+ }
+}
+
+/// Data decrypted from a payment's onion payload.
pub(crate) enum Hop {
/// This onion payload was for us, not for forwarding to a next-hop. Contains information for
/// verifying the incoming payment.
/// HMAC of the next hop's onion packet.
next_hop_hmac: [u8; 32],
/// Bytes of the onion packet we're forwarding.
- new_packet_bytes: [u8; 20*65],
+ new_packet_bytes: [u8; ONION_DATA_LEN],
},
}
/// Error returned when we fail to decode the onion packet.
+#[derive(Debug)]
pub(crate) enum OnionDecodeErr {
/// The HMAC of the onion packet did not match the hop data.
Malformed {
},
}
-pub(crate) fn decode_next_hop(shared_secret: [u8; 32], hop_data: &[u8], hmac_bytes: [u8; 32], payment_hash: PaymentHash) -> Result<Hop, OnionDecodeErr> {
+pub(crate) fn decode_next_payment_hop(shared_secret: [u8; 32], hop_data: &[u8], hmac_bytes: [u8; 32], payment_hash: PaymentHash) -> Result<Hop, OnionDecodeErr> {
+ match decode_next_hop(shared_secret, hop_data, hmac_bytes, payment_hash) {
+ Ok((next_hop_data, None)) => Ok(Hop::Receive(next_hop_data)),
+ Ok((next_hop_data, Some((next_hop_hmac, FixedSizeOnionPacket(new_packet_bytes))))) => {
+ Ok(Hop::Forward {
+ next_hop_data,
+ next_hop_hmac,
+ new_packet_bytes
+ })
+ },
+ Err(e) => Err(e),
+ }
+}
+
+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> {
let (rho, mu) = gen_rho_mu_from_shared_secret(&shared_secret);
let mut hmac = HmacEngine::<Sha256>::new(&mu);
hmac.input(hop_data);
- hmac.input(&payment_hash.0[..]);
+ if let Some(payment_hash) = decode_input.payment_hash() {
+ hmac.input(&payment_hash.0[..]);
+ }
if !fixed_time_eq(&Hmac::from_engine(hmac).into_inner(), &hmac_bytes) {
return Err(OnionDecodeErr::Malformed {
err_msg: "HMAC Check failed",
let mut chacha = ChaCha20::new(&rho, &[0u8; 8]);
let mut chacha_stream = ChaChaReader { chacha: &mut chacha, read: Cursor::new(&hop_data[..]) };
- match <msgs::OnionHopData as Readable>::read(&mut chacha_stream) {
+ match R::read(&mut chacha_stream, decode_input.read_arg()) {
Err(err) => {
let error_code = match err {
msgs::DecodeError::UnknownVersion => 0x4000 | 1, // unknown realm byte
chacha_stream.read_exact(&mut next_bytes).unwrap();
assert_ne!(next_bytes[..], [0; 32][..]);
}
- return Ok(Hop::Receive(msg));
+ return Ok((msg, None)); // We are the final destination for this packet
} else {
- let mut new_packet_bytes = [0; 20*65];
- let read_pos = chacha_stream.read(&mut new_packet_bytes).unwrap();
+ let mut new_packet_bytes = N::new(hop_data.len());
+ let read_pos = hop_data.len() - chacha_stream.read.position() as usize;
+ chacha_stream.read_exact(&mut new_packet_bytes.as_mut()[..read_pos]).unwrap();
#[cfg(debug_assertions)]
{
// Check two things:
}
// Once we've emptied the set of bytes our peer gave us, encrypt 0 bytes until we
// fill the onion hop data we'll forward to our next-hop peer.
- chacha_stream.chacha.process_in_place(&mut new_packet_bytes[read_pos..]);
- return Ok(Hop::Forward {
- next_hop_data: msg,
- next_hop_hmac: hmac,
- new_packet_bytes,
- })
+ chacha_stream.chacha.process_in_place(&mut new_packet_bytes.as_mut()[read_pos..]);
+ return Ok((msg, Some((hmac, new_packet_bytes)))) // This packet needs forwarding
}
},
}
},
);
- let packet = super::construct_onion_packet_with_init_noise(payloads, onion_keys, [0; super::ONION_DATA_LEN], &PaymentHash([0x42; 32]));
+ let packet: msgs::OnionPacket = super::construct_onion_packet_with_init_noise::<_, _>(payloads, onion_keys, super::FixedSizeOnionPacket([0; super::ONION_DATA_LEN]), Some(&PaymentHash([0x42; 32])));
// Just check the final packet encoding, as it includes all the per-hop vectors in it
// anyway...
assert_eq!(packet.encode(), hex::decode("0002eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619e5f14350c2a76fc232b5e46d421e9615471ab9e0bc887beff8c95fdb878f7b3a716a996c7845c93d90e4ecbb9bde4ece2f69425c99e4bc820e44485455f135edc0d10f7d61ab590531cf08000179a333a347f8b4072f216400406bdf3bf038659793d4a1fd7b246979e3150a0a4cb052c9ec69acf0f48c3d39cd55675fe717cb7d80ce721caad69320c3a469a202f1e468c67eaf7a7cd8226d0fd32f7b48084dca885d56047694762b67021713ca673929c163ec36e04e40ca8e1c6d17569419d3039d9a1ec866abe044a9ad635778b961fc0776dc832b3a451bd5d35072d2269cf9b040f6b7a7dad84fb114ed413b1426cb96ceaf83825665ed5a1d002c1687f92465b49ed4c7f0218ff8c6c7dd7221d589c65b3b9aaa71a41484b122846c7c7b57e02e679ea8469b70e14fe4f70fee4d87b910cf144be6fe48eef24da475c0b0bcc6565ae82cd3f4e3b24c76eaa5616c6111343306ab35c1fe5ca4a77c0e314ed7dba39d6f1e0de791719c241a939cc493bea2bae1c1e932679ea94d29084278513c77b899cc98059d06a27d171b0dbdf6bee13ddc4fc17a0c4d2827d488436b57baa167544138ca2e64a11b43ac8a06cd0c2fba2d4d900ed2d9205305e2d7383cc98dacb078133de5f6fb6bed2ef26ba92cea28aafc3b9948dd9ae5559e8bd6920b8cea462aa445ca6a95e0e7ba52961b181c79e73bd581821df2b10173727a810c92b83b5ba4a0403eb710d2ca10689a35bec6c3a708e9e92f7d78ff3c5d9989574b00c6736f84c199256e76e19e78f0c98a9d580b4a658c84fc8f2096c2fbea8f5f8c59d0fdacb3be2802ef802abbecb3aba4acaac69a0e965abd8981e9896b1f6ef9d60f7a164b371af869fd0e48073742825e9434fc54da837e120266d53302954843538ea7c6c3dbfb4ff3b2fdbe244437f2a153ccf7bdb4c92aa08102d4f3cff2ae5ef86fab4653595e6a5837fa2f3e29f27a9cde5966843fb847a4a61f1e76c281fe8bb2b0a181d096100db5a1a5ce7a910238251a43ca556712eaadea167fb4d7d75825e440f3ecd782036d7574df8bceacb397abefc5f5254d2722215c53ff54af8299aaaad642c6d72a14d27882d9bbd539e1cc7a527526ba89b8c037ad09120e98ab042d3e8652b31ae0e478516bfaf88efca9f3676ffe99d2819dcaeb7610a626695f53117665d267d3f7abebd6bbd6733f645c72c389f03855bdf1e4b8075b516569b118233a0f0971d24b83113c0b096f5216a207ca99a7cddc81c130923fe3d91e7508c9ac5f2e914ff5dccab9e558566fa14efb34ac98d878580814b94b73acbfde9072f30b881f7f0fff42d4045d1ace6322d86a97d164aa84d93a60498065cc7c20e636f5862dc81531a88c60305a2e59a985be327a6902e4bed986dbf4a0b50c217af0ea7fdf9ab37f9ea1a1aaa72f54cf40154ea9b269f1a7c09f9f43245109431a175d50e2db0132337baa0ef97eed0fcf20489da36b79a1172faccc2f7ded7c60e00694282d93359c4682135642bc81f433574aa8ef0c97b4ade7ca372c5ffc23c7eddd839bab4e0f14d6df15c9dbeab176bec8b5701cf054eb3072f6dadc98f88819042bf10c407516ee58bce33fbe3b3d86a54255e577db4598e30a135361528c101683a5fcde7e8ba53f3456254be8f45fe3a56120ae96ea3773631fcb3873aa3abd91bcff00bd38bd43697a2e789e00da6077482e7b1b1a677b5afae4c54e6cbdf7377b694eb7d7a5b913476a5be923322d3de06060fd5e819635232a2cf4f0731da13b8546d1d6d4f8d75b9fce6c2341a71b0ea6f780df54bfdb0dd5cd9855179f602f9172307c7268724c3618e6817abd793adc214a0dc0bc616816632f27ea336fb56dfd").unwrap());
}),
);
- let packet = super::construct_onion_packet_with_init_noise(payloads, onion_keys, [0; super::ONION_DATA_LEN], &PaymentHash([0x42; 32]));
+ let packet: msgs::OnionPacket = super::construct_onion_packet_with_init_noise::<_, _>(payloads, onion_keys, super::FixedSizeOnionPacket([0; super::ONION_DATA_LEN]), Some(&PaymentHash([0x42; 32])));
// Just check the final packet encoding, as it includes all the per-hop vectors in it
// anyway...
assert_eq!(packet.encode(), hex::decode("0002eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619e5f14350c2a76fc232b5e46d421e9615471ab9e0bc887beff8c95fdb878f7b3a71a060daf367132b378b3a3883c0e2c0e026b8900b2b5cdbc784e1a3bb913f88a9c50f7d61ab590531cf08000178a333a347f8b4072ed056f820f77774345e183a342ec4729f3d84accf515e88adddb85ecc08daba68404bae9a8e8d7178977d7094a1ae549f89338c0777551f874159eb42d3a59fb9285ad4e24883f27de23942ec966611e99bee1cee503455be9e8e642cef6cef7b9864130f692283f8a973d47a8f1c1726b6e59969385975c766e35737c8d76388b64f748ee7943ffb0e2ee45c57a1abc40762ae598723d21bd184e2b338f68ebff47219357bd19cd7e01e2337b806ef4d717888e129e59cd3dc31e6201ccb2fd6d7499836f37a993262468bcb3a4dcd03a22818aca49c6b7b9b8e9e870045631d8e039b066ff86e0d1b7291f71cefa7264c70404a8e538b566c17ccc5feab231401e6c08a01bd5edfc1aa8e3e533b96e82d1f91118d508924b923531929aea889fcdf050597c681185f336b1da63b0939aa2b7c50b21b5eb7b6ad66c81fab98a3cdf73f658149e7e9ced4edde5d38c9b8f92e16f6b4ab13d7fca6a0e4ecc9f9de611a90da6e99c39551094c56e3196f282c5dffd9fc4b2fc12f3bca8e6fe47eb45fbdd3be21a8a8d200797eae3c9a0497132f92410d804977408494dff49dd3d8bce248e0b74fd9e6f0f7102c25ddfa02bd9ad9f746abbfa337ef811d5345a9e16b60de1767b209645ba40bd1f9a5f75bc04feca9b27c5554be4fe83fac2cb83aa447a817bb85ae966c68b420063833fada375e2f515965e687a45699632902672c654d1d18d7bcbf55e8fa57f63f2da449f8e1e606e8722df081e5f193fc4179feb99ad22819afdeef211f7c54afdba92aeef0c00b7bc2b65a4813c01f907a8377585708f2d4c940a25328e585714c8ded0a9a4d7a6de1027c1cb7a0198cd3db68b58c0704dfd0cfbe624e9cd18cc0ae5d96697bb476708b9ee0403d211e64e0d5a7683a7a9a140c02f0ff1c6e67a302941b4052bdea8a63e70a3ad62c5b89c698f1fd3c7685cb49705096cad702d02d93bcb1c27a409f4c9bddec001205ca4a2740f19b50900be81c7e847f1a863deea8d35701f1355cad8db57b1d4eb2ab4e29587734785abfb46ddede71928213d7d089dfdeda052827f459f1688cc0935bd47e7bcec27427c8376dcce7e22699567c0d145f8a7db33f6758815f1f15f9f7a9760dec4f34ae095edda4c64e9735bdd029c4e32c2ee31ba47ec5e6bdb97813d52dbd15b4e0b7a2c7f790ae64104d99f38c127f0a093288fa34144adb16b8968d4fa7656fcec99de8503dd46d3b03620a71c7cd085364abd30dccf7fbda25a1cdc102600149c9af1c97aa0372cd2e1909f28ac5c686f432b310e79528c9b8b9e8f314c1e74621ce6308ad2278b81d460892e0d9dd38b7c76d58be6dfd10ae7583ee1e7ef5b3f6f78dc60af0950df1b00cc55b6d178ba2e476bea0eaeef49323b83f05804159e7aef4eed4cc60dd07be76f067dfd0bcfb0b806b69ba921336a20c43c832d0cab8fa3ddeb29e3bf07b0d98a112eb07802756235a49d44a8b82a950d84e95e01971f0e106ccb337f07384e21620e0ad39e16ed9edca123226cf55ac44f449eeb53e38a7f27d101806e4823e4efcc887414240ee6826c4a5cb1c6443ad36ebf905a435c1d9054e54173911b17b5b40f60b3d9fd5f12eac54ca1e20191f5f18544d5fd3d665e9bcef96fb44b76110aa64d9db4c86c9513cbdad546538e8aec521fbe83ceac5e74a15629f1ed0b870a1d0d1e5680b6d6100d1bd3f3b9043bd35b8919c4088f1949b8be89e4701eb870f8ed64fafa446c78df3ea").unwrap());
--- /dev/null
+// This file is Copyright its original authors, visible in version control
+// history.
+//
+// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
+// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
+// You may not use this file except in accordance with one or both of these
+// licenses.
+
+//! Creating blinded routes and related utilities live here.
+
+use bitcoin::secp256k1::{self, PublicKey, Secp256k1, SecretKey};
+
+use chain::keysinterface::{KeysInterface, Sign};
+use super::utils;
+use util::chacha20poly1305rfc::ChaChaPolyWriteAdapter;
+use util::ser::{VecWriter, Writeable, Writer};
+
+use core::iter::FromIterator;
+use io;
+use prelude::*;
+
+/// Onion messages can be sent and received to blinded routes, which serve to hide the identity of
+/// the recipient.
+pub struct BlindedRoute {
+ /// To send to a blinded route, the sender first finds a route to the unblinded
+ /// `introduction_node_id`, which can unblind its [`encrypted_payload`] to find out the onion
+ /// message's next hop and forward it along.
+ ///
+ /// [`encrypted_payload`]: BlindedHop::encrypted_payload
+ pub(super) introduction_node_id: PublicKey,
+ /// Used by the introduction node to decrypt its [`encrypted_payload`] to forward the onion
+ /// message.
+ ///
+ /// [`encrypted_payload`]: BlindedHop::encrypted_payload
+ pub(super) blinding_point: PublicKey,
+ /// The hops composing the blinded route.
+ pub(super) blinded_hops: Vec<BlindedHop>,
+}
+
+/// Used to construct the blinded hops portion of a blinded route. These hops cannot be identified
+/// by outside observers and thus can be used to hide the identity of the recipient.
+pub struct BlindedHop {
+ /// The blinded node id of this hop in a blinded route.
+ pub(super) blinded_node_id: PublicKey,
+ /// The encrypted payload intended for this hop in a blinded route.
+ // The node sending to this blinded route will later encode this payload into the onion packet for
+ // this hop.
+ pub(super) encrypted_payload: Vec<u8>,
+}
+
+impl BlindedRoute {
+ /// Create a blinded route to be forwarded along `node_pks`. The last node pubkey in `node_pks`
+ /// will be the destination node.
+ ///
+ /// Errors if less than two hops are provided or if `node_pk`(s) are invalid.
+ // TODO: make all payloads the same size with padding + add dummy hops
+ pub fn new<Signer: Sign, K: KeysInterface, T: secp256k1::Signing + secp256k1::Verification>
+ (node_pks: &[PublicKey], keys_manager: &K, secp_ctx: &Secp256k1<T>) -> Result<Self, ()>
+ {
+ if node_pks.len() < 2 { return Err(()) }
+ let blinding_secret_bytes = keys_manager.get_secure_random_bytes();
+ let blinding_secret = SecretKey::from_slice(&blinding_secret_bytes[..]).expect("RNG is busted");
+ let introduction_node_id = node_pks[0];
+
+ Ok(BlindedRoute {
+ introduction_node_id,
+ blinding_point: PublicKey::from_secret_key(secp_ctx, &blinding_secret),
+ blinded_hops: blinded_hops(secp_ctx, node_pks, &blinding_secret).map_err(|_| ())?,
+ })
+ }
+}
+
+/// Construct blinded hops for the given `unblinded_path`.
+fn blinded_hops<T: secp256k1::Signing + secp256k1::Verification>(
+ secp_ctx: &Secp256k1<T>, unblinded_path: &[PublicKey], session_priv: &SecretKey
+) -> Result<Vec<BlindedHop>, secp256k1::Error> {
+ let mut blinded_hops = Vec::with_capacity(unblinded_path.len());
+
+ let mut prev_ss_and_blinded_node_id = None;
+ utils::construct_keys_callback(secp_ctx, unblinded_path, None, session_priv, |blinded_node_id, _, _, encrypted_payload_ss, unblinded_pk, _| {
+ if let Some((prev_ss, prev_blinded_node_id)) = prev_ss_and_blinded_node_id {
+ if let Some(pk) = unblinded_pk {
+ let payload = ForwardTlvs {
+ next_node_id: pk,
+ next_blinding_override: None,
+ };
+ blinded_hops.push(BlindedHop {
+ blinded_node_id: prev_blinded_node_id,
+ encrypted_payload: encrypt_payload(payload, prev_ss),
+ });
+ } else { debug_assert!(false); }
+ }
+ prev_ss_and_blinded_node_id = Some((encrypted_payload_ss, blinded_node_id));
+ })?;
+
+ if let Some((final_ss, final_blinded_node_id)) = prev_ss_and_blinded_node_id {
+ let final_payload = ReceiveTlvs { path_id: None };
+ blinded_hops.push(BlindedHop {
+ blinded_node_id: final_blinded_node_id,
+ encrypted_payload: encrypt_payload(final_payload, final_ss),
+ });
+ } else { debug_assert!(false) }
+
+ Ok(blinded_hops)
+}
+
+/// Encrypt TLV payload to be used as a [`BlindedHop::encrypted_payload`].
+fn encrypt_payload<P: Writeable>(payload: P, encrypted_tlvs_ss: [u8; 32]) -> Vec<u8> {
+ let mut writer = VecWriter(Vec::new());
+ let write_adapter = ChaChaPolyWriteAdapter::new(encrypted_tlvs_ss, &payload);
+ write_adapter.write(&mut writer).expect("In-memory writes cannot fail");
+ writer.0
+}
+
+/// TLVs to encode in an intermediate onion message packet's hop data. When provided in a blinded
+/// route, they are encoded into [`BlindedHop::encrypted_payload`].
+pub(crate) struct ForwardTlvs {
+ /// The node id of the next hop in the onion message's path.
+ pub(super) next_node_id: PublicKey,
+ /// Senders to a blinded route use this value to concatenate the route they find to the
+ /// introduction node with the blinded route.
+ pub(super) next_blinding_override: Option<PublicKey>,
+}
+
+/// Similar to [`ForwardTlvs`], but these TLVs are for the final node.
+pub(crate) struct ReceiveTlvs {
+ /// If `path_id` is `Some`, it is used to identify the blinded route that this onion message is
+ /// sending to. This is useful for receivers to check that said blinded route is being used in
+ /// the right context.
+ pub(super) path_id: Option<[u8; 32]>,
+}
+
+impl Writeable for ForwardTlvs {
+ fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
+ // TODO: write padding
+ encode_tlv_stream!(writer, {
+ (4, self.next_node_id, required),
+ (8, self.next_blinding_override, option)
+ });
+ Ok(())
+ }
+}
+
+impl Writeable for ReceiveTlvs {
+ fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
+ // TODO: write padding
+ encode_tlv_stream!(writer, {
+ (6, self.path_id, option),
+ });
+ Ok(())
+ }
+}
--- /dev/null
+// This file is Copyright its original authors, visible in version control
+// history.
+//
+// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
+// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
+// You may not use this file except in accordance with one or both of these
+// licenses.
+
+//! Onion message testing and test utilities live here.
+
+use chain::keysinterface::{KeysInterface, Recipient};
+use super::{BlindedRoute, Destination, OnionMessenger, SendError};
+use util::enforcing_trait_impls::EnforcingSigner;
+use util::test_utils;
+
+use bitcoin::network::constants::Network;
+use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey};
+
+use sync::Arc;
+
+struct MessengerNode {
+ keys_manager: Arc<test_utils::TestKeysInterface>,
+ messenger: OnionMessenger<EnforcingSigner, Arc<test_utils::TestKeysInterface>, Arc<test_utils::TestLogger>>,
+ logger: Arc<test_utils::TestLogger>,
+}
+
+impl MessengerNode {
+ fn get_node_pk(&self) -> PublicKey {
+ let secp_ctx = Secp256k1::new();
+ PublicKey::from_secret_key(&secp_ctx, &self.keys_manager.get_node_secret(Recipient::Node).unwrap())
+ }
+}
+
+fn create_nodes(num_messengers: u8) -> Vec<MessengerNode> {
+ let mut res = Vec::new();
+ for i in 0..num_messengers {
+ let logger = Arc::new(test_utils::TestLogger::with_id(format!("node {}", i)));
+ let seed = [i as u8; 32];
+ let keys_manager = Arc::new(test_utils::TestKeysInterface::new(&seed, Network::Testnet));
+ res.push(MessengerNode {
+ keys_manager: keys_manager.clone(),
+ messenger: OnionMessenger::new(keys_manager, logger.clone()),
+ logger,
+ });
+ }
+ res
+}
+
+fn pass_along_path(mut path: Vec<MessengerNode>, expected_path_id: Option<[u8; 32]>) {
+ let mut prev_node = path.remove(0);
+ let num_nodes = path.len();
+ for (idx, node) in path.into_iter().enumerate() {
+ let events = prev_node.messenger.release_pending_msgs();
+ assert_eq!(events.len(), 1);
+ let onion_msg = {
+ let msgs = events.get(&node.get_node_pk()).unwrap();
+ assert_eq!(msgs.len(), 1);
+ msgs[0].clone()
+ };
+ node.messenger.handle_onion_message(&prev_node.get_node_pk(), &onion_msg);
+ if idx == num_nodes - 1 {
+ node.logger.assert_log_contains(
+ "lightning::onion_message::messenger".to_string(),
+ format!("Received an onion message with path_id: {:02x?}", expected_path_id).to_string(), 1);
+ }
+ prev_node = node;
+ }
+}
+
+#[test]
+fn one_hop() {
+ let nodes = create_nodes(2);
+
+ nodes[0].messenger.send_onion_message(&[], Destination::Node(nodes[1].get_node_pk())).unwrap();
+ pass_along_path(nodes, None);
+}
+
+#[test]
+fn two_unblinded_hops() {
+ let nodes = create_nodes(3);
+
+ nodes[0].messenger.send_onion_message(&[nodes[1].get_node_pk()], Destination::Node(nodes[2].get_node_pk())).unwrap();
+ pass_along_path(nodes, None);
+}
+
+#[test]
+fn two_unblinded_two_blinded() {
+ let nodes = create_nodes(5);
+
+ let secp_ctx = Secp256k1::new();
+ let blinded_route = BlindedRoute::new::<EnforcingSigner, _, _>(&[nodes[3].get_node_pk(), nodes[4].get_node_pk()], &*nodes[4].keys_manager, &secp_ctx).unwrap();
+
+ nodes[0].messenger.send_onion_message(&[nodes[1].get_node_pk(), nodes[2].get_node_pk()], Destination::BlindedRoute(blinded_route)).unwrap();
+ pass_along_path(nodes, None);
+}
+
+#[test]
+fn three_blinded_hops() {
+ let nodes = create_nodes(4);
+
+ let secp_ctx = Secp256k1::new();
+ let blinded_route = BlindedRoute::new::<EnforcingSigner, _, _>(&[nodes[1].get_node_pk(), nodes[2].get_node_pk(), nodes[3].get_node_pk()], &*nodes[3].keys_manager, &secp_ctx).unwrap();
+
+ nodes[0].messenger.send_onion_message(&[], Destination::BlindedRoute(blinded_route)).unwrap();
+ pass_along_path(nodes, None);
+}
+
+#[test]
+fn too_big_packet_error() {
+ // Make sure we error as expected if a packet is too big to send.
+ let nodes = create_nodes(1);
+
+ let hop_secret = SecretKey::from_slice(&hex::decode("0101010101010101010101010101010101010101010101010101010101010101").unwrap()[..]).unwrap();
+ let secp_ctx = Secp256k1::new();
+ let hop_node_id = PublicKey::from_secret_key(&secp_ctx, &hop_secret);
+
+ let hops = [hop_node_id; 400];
+ let err = nodes[0].messenger.send_onion_message(&hops, Destination::Node(hop_node_id)).unwrap_err();
+ assert_eq!(err, SendError::TooBigPacket);
+}
+
+#[test]
+fn invalid_blinded_route_error() {
+ // Make sure we error as expected if a provided blinded route has 0 or 1 hops.
+ let mut nodes = create_nodes(3);
+ let (node1, node2, node3) = (nodes.remove(0), nodes.remove(0), nodes.remove(0));
+
+ // 0 hops
+ let secp_ctx = Secp256k1::new();
+ let mut blinded_route = BlindedRoute::new::<EnforcingSigner, _, _>(&[node2.get_node_pk(), node3.get_node_pk()], &*node3.keys_manager, &secp_ctx).unwrap();
+ blinded_route.blinded_hops.clear();
+ let err = node1.messenger.send_onion_message(&[], Destination::BlindedRoute(blinded_route)).unwrap_err();
+ assert_eq!(err, SendError::TooFewBlindedHops);
+
+ // 1 hop
+ let mut blinded_route = BlindedRoute::new::<EnforcingSigner, _, _>(&[node2.get_node_pk(), node3.get_node_pk()], &*node3.keys_manager, &secp_ctx).unwrap();
+ blinded_route.blinded_hops.remove(0);
+ assert_eq!(blinded_route.blinded_hops.len(), 1);
+ let err = node1.messenger.send_onion_message(&[], Destination::BlindedRoute(blinded_route)).unwrap_err();
+ assert_eq!(err, SendError::TooFewBlindedHops);
+}
--- /dev/null
+// This file is Copyright its original authors, visible in version control
+// history.
+//
+// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
+// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
+// You may not use this file except in accordance with one or both of these
+// licenses.
+
+//! LDK sends, receives, and forwards onion messages via the [`OnionMessenger`]. See its docs for
+//! more information.
+
+use bitcoin::hashes::{Hash, HashEngine};
+use bitcoin::hashes::hmac::{Hmac, HmacEngine};
+use bitcoin::hashes::sha256::Hash as Sha256;
+use bitcoin::secp256k1::{self, PublicKey, Secp256k1, SecretKey};
+
+use chain::keysinterface::{InMemorySigner, KeysInterface, KeysManager, Recipient, Sign};
+use ln::msgs;
+use ln::onion_utils;
+use super::blinded_route::{BlindedRoute, ForwardTlvs, ReceiveTlvs};
+use super::packet::{BIG_PACKET_HOP_DATA_LEN, ForwardControlTlvs, Packet, Payload, ReceiveControlTlvs, SMALL_PACKET_HOP_DATA_LEN};
+use super::utils;
+use util::logger::Logger;
+
+use core::ops::Deref;
+use sync::{Arc, Mutex};
+use prelude::*;
+
+/// A sender, receiver and forwarder of onion messages. In upcoming releases, this object will be
+/// used to retrieve invoices and fulfill invoice requests from [offers]. Currently, only sending
+/// and receiving empty onion messages is supported.
+///
+/// # Example
+///
+// Needs to be `ignore` until the `onion_message` module is made public, otherwise this is a test
+// failure.
+/// ```ignore
+/// # extern crate bitcoin;
+/// # use bitcoin::hashes::_export::_core::time::Duration;
+/// # use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey};
+/// # use lightning::chain::keysinterface::{InMemorySigner, KeysManager, KeysInterface};
+/// # use lightning::onion_message::{BlindedRoute, Destination, OnionMessenger};
+/// # use lightning::util::logger::{Logger, Record};
+/// # use std::sync::Arc;
+/// # struct FakeLogger {};
+/// # impl Logger for FakeLogger {
+/// # fn log(&self, record: &Record) { unimplemented!() }
+/// # }
+/// # let seed = [42u8; 32];
+/// # let time = Duration::from_secs(123456);
+/// # let keys_manager = KeysManager::new(&seed, time.as_secs(), time.subsec_nanos());
+/// # let logger = Arc::new(FakeLogger {});
+/// # let node_secret = SecretKey::from_slice(&hex::decode("0101010101010101010101010101010101010101010101010101010101010101").unwrap()[..]).unwrap();
+/// # let secp_ctx = Secp256k1::new();
+/// # let hop_node_id1 = PublicKey::from_secret_key(&secp_ctx, &node_secret);
+/// # let (hop_node_id2, hop_node_id3, hop_node_id4) = (hop_node_id1, hop_node_id1,
+/// hop_node_id1);
+/// # let destination_node_id = hop_node_id1;
+/// #
+/// // Create the onion messenger. This must use the same `keys_manager` as is passed to your
+/// // ChannelManager.
+/// let onion_messenger = OnionMessenger::new(&keys_manager, logger);
+///
+/// // Send an empty onion message to a node id.
+/// let intermediate_hops = [hop_node_id1, hop_node_id2];
+/// onion_messenger.send_onion_message(&intermediate_hops, Destination::Node(destination_node_id));
+///
+/// // Create a blinded route to yourself, for someone to send an onion message to.
+/// # let your_node_id = hop_node_id1;
+/// let hops = [hop_node_id3, hop_node_id4, your_node_id];
+/// let blinded_route = BlindedRoute::new::<InMemorySigner, _, _>(&hops, &keys_manager, &secp_ctx).unwrap();
+///
+/// // Send an empty onion message to a blinded route.
+/// # let intermediate_hops = [hop_node_id1, hop_node_id2];
+/// onion_messenger.send_onion_message(&intermediate_hops, Destination::BlindedRoute(blinded_route));
+/// ```
+///
+/// [offers]: <https://github.com/lightning/bolts/pull/798>
+/// [`OnionMessenger`]: crate::onion_message::OnionMessenger
+pub struct OnionMessenger<Signer: Sign, K: Deref, L: Deref>
+ where K::Target: KeysInterface<Signer = Signer>,
+ L::Target: Logger,
+{
+ keys_manager: K,
+ logger: L,
+ pending_messages: Mutex<HashMap<PublicKey, Vec<msgs::OnionMessage>>>,
+ secp_ctx: Secp256k1<secp256k1::All>,
+ // Coming soon:
+ // invoice_handler: InvoiceHandler,
+ // custom_handler: CustomHandler, // handles custom onion messages
+}
+
+/// The destination of an onion message.
+pub enum Destination {
+ /// We're sending this onion message to a node.
+ Node(PublicKey),
+ /// We're sending this onion message to a blinded route.
+ BlindedRoute(BlindedRoute),
+}
+
+impl Destination {
+ pub(super) fn num_hops(&self) -> usize {
+ match self {
+ Destination::Node(_) => 1,
+ Destination::BlindedRoute(BlindedRoute { blinded_hops, .. }) => blinded_hops.len(),
+ }
+ }
+}
+
+/// Errors that may occur when [sending an onion message].
+///
+/// [sending an onion message]: OnionMessenger::send_onion_message
+#[derive(Debug, PartialEq)]
+pub enum SendError {
+ /// Errored computing onion message packet keys.
+ Secp256k1(secp256k1::Error),
+ /// Because implementations such as Eclair will drop onion messages where the message packet
+ /// exceeds 32834 bytes, we refuse to send messages where the packet exceeds this size.
+ TooBigPacket,
+ /// The provided [`Destination`] was an invalid [`BlindedRoute`], due to having fewer than two
+ /// blinded hops.
+ TooFewBlindedHops,
+}
+
+impl<Signer: Sign, K: Deref, L: Deref> OnionMessenger<Signer, K, L>
+ where K::Target: KeysInterface<Signer = Signer>,
+ L::Target: Logger,
+{
+ /// Constructs a new `OnionMessenger` to send, forward, and delegate received onion messages to
+ /// their respective handlers.
+ pub fn new(keys_manager: K, logger: L) -> Self {
+ let mut secp_ctx = Secp256k1::new();
+ secp_ctx.seeded_randomize(&keys_manager.get_secure_random_bytes());
+ OnionMessenger {
+ keys_manager,
+ pending_messages: Mutex::new(HashMap::new()),
+ secp_ctx,
+ logger,
+ }
+ }
+
+ /// Send an empty onion message to `destination`, routing it through `intermediate_nodes`.
+ /// See [`OnionMessenger`] for example usage.
+ pub fn send_onion_message(&self, intermediate_nodes: &[PublicKey], destination: Destination) -> Result<(), SendError> {
+ if let Destination::BlindedRoute(BlindedRoute { ref blinded_hops, .. }) = destination {
+ if blinded_hops.len() < 2 {
+ return Err(SendError::TooFewBlindedHops);
+ }
+ }
+ let blinding_secret_bytes = self.keys_manager.get_secure_random_bytes();
+ let blinding_secret = SecretKey::from_slice(&blinding_secret_bytes[..]).expect("RNG is busted");
+ let (introduction_node_id, blinding_point) = if intermediate_nodes.len() != 0 {
+ (intermediate_nodes[0], PublicKey::from_secret_key(&self.secp_ctx, &blinding_secret))
+ } else {
+ match destination {
+ Destination::Node(pk) => (pk, PublicKey::from_secret_key(&self.secp_ctx, &blinding_secret)),
+ Destination::BlindedRoute(BlindedRoute { introduction_node_id, blinding_point, .. }) =>
+ (introduction_node_id, blinding_point),
+ }
+ };
+ let (packet_payloads, packet_keys) = packet_payloads_and_keys(
+ &self.secp_ctx, intermediate_nodes, destination, &blinding_secret)
+ .map_err(|e| SendError::Secp256k1(e))?;
+
+ let prng_seed = self.keys_manager.get_secure_random_bytes();
+ let onion_packet = construct_onion_message_packet(
+ packet_payloads, packet_keys, prng_seed).map_err(|()| SendError::TooBigPacket)?;
+
+ let mut pending_per_peer_msgs = self.pending_messages.lock().unwrap();
+ let pending_msgs = pending_per_peer_msgs.entry(introduction_node_id).or_insert(Vec::new());
+ pending_msgs.push(
+ msgs::OnionMessage {
+ blinding_point,
+ onion_routing_packet: onion_packet,
+ }
+ );
+ Ok(())
+ }
+
+ /// Handle an incoming onion message. Currently, if a message was destined for us we will log, but
+ /// soon we'll delegate the onion message to a handler that can generate invoices or send
+ /// payments.
+ pub fn handle_onion_message(&self, _peer_node_id: &PublicKey, msg: &msgs::OnionMessage) {
+ let control_tlvs_ss = match self.keys_manager.ecdh(Recipient::Node, &msg.blinding_point, None) {
+ Ok(ss) => ss,
+ Err(e) => {
+ log_error!(self.logger, "Failed to retrieve node secret: {:?}", e);
+ return
+ }
+ };
+ let onion_decode_ss = {
+ let blinding_factor = {
+ let mut hmac = HmacEngine::<Sha256>::new(b"blinded_node_id");
+ hmac.input(control_tlvs_ss.as_ref());
+ Hmac::from_engine(hmac).into_inner()
+ };
+ match self.keys_manager.ecdh(Recipient::Node, &msg.onion_routing_packet.public_key,
+ Some(&blinding_factor))
+ {
+ Ok(ss) => ss.secret_bytes(),
+ Err(()) => {
+ log_trace!(self.logger, "Failed to compute onion packet shared secret");
+ return
+ }
+ }
+ };
+ match onion_utils::decode_next_hop(onion_decode_ss, &msg.onion_routing_packet.hop_data[..],
+ msg.onion_routing_packet.hmac, control_tlvs_ss)
+ {
+ Ok((Payload::Receive {
+ control_tlvs: ReceiveControlTlvs::Unblinded(ReceiveTlvs { path_id })
+ }, None)) => {
+ log_info!(self.logger, "Received an onion message with path_id: {:02x?}", path_id);
+ },
+ Ok((Payload::Forward(ForwardControlTlvs::Unblinded(ForwardTlvs {
+ next_node_id, next_blinding_override
+ })), Some((next_hop_hmac, new_packet_bytes)))) => {
+ // TODO: we need to check whether `next_node_id` is our node, in which case this is a dummy
+ // blinded hop and this onion message is destined for us. In this situation, we should keep
+ // unwrapping the onion layers to get to the final payload. Since we don't have the option
+ // of creating blinded routes with dummy hops currently, we should be ok to not handle this
+ // for now.
+ let new_pubkey = match onion_utils::next_hop_packet_pubkey(&self.secp_ctx, msg.onion_routing_packet.public_key, &onion_decode_ss) {
+ Ok(pk) => pk,
+ Err(e) => {
+ log_trace!(self.logger, "Failed to compute next hop packet pubkey: {}", e);
+ return
+ }
+ };
+ let outgoing_packet = Packet {
+ version: 0,
+ public_key: new_pubkey,
+ hop_data: new_packet_bytes,
+ hmac: next_hop_hmac,
+ };
+
+ let mut pending_per_peer_msgs = self.pending_messages.lock().unwrap();
+ let pending_msgs = pending_per_peer_msgs.entry(next_node_id).or_insert(Vec::new());
+ pending_msgs.push(
+ msgs::OnionMessage {
+ blinding_point: match next_blinding_override {
+ Some(blinding_point) => blinding_point,
+ None => {
+ let blinding_factor = {
+ let mut sha = Sha256::engine();
+ sha.input(&msg.blinding_point.serialize()[..]);
+ sha.input(control_tlvs_ss.as_ref());
+ Sha256::from_engine(sha).into_inner()
+ };
+ let mut next_blinding_point = msg.blinding_point;
+ if let Err(e) = next_blinding_point.mul_assign(&self.secp_ctx, &blinding_factor[..]) {
+ log_trace!(self.logger, "Failed to compute next blinding point: {}", e);
+ return
+ }
+ next_blinding_point
+ },
+ },
+ onion_routing_packet: outgoing_packet,
+ },
+ );
+ },
+ Err(e) => {
+ log_trace!(self.logger, "Errored decoding onion message packet: {:?}", e);
+ },
+ _ => {
+ log_trace!(self.logger, "Received bogus onion message packet, either the sender encoded a final hop as a forwarding hop or vice versa");
+ },
+ };
+ }
+
+ #[cfg(test)]
+ pub(super) fn release_pending_msgs(&self) -> HashMap<PublicKey, Vec<msgs::OnionMessage>> {
+ let mut pending_msgs = self.pending_messages.lock().unwrap();
+ let mut msgs = HashMap::new();
+ core::mem::swap(&mut *pending_msgs, &mut msgs);
+ msgs
+ }
+}
+
+// TODO: parameterize the below Simple* types with OnionMessenger and handle the messages it
+// produces
+/// Useful for simplifying the parameters of [`SimpleArcChannelManager`] and
+/// [`SimpleArcPeerManager`]. See their docs for more details.
+///
+///[`SimpleArcChannelManager`]: crate::ln::channelmanager::SimpleArcChannelManager
+///[`SimpleArcPeerManager`]: crate::ln::peer_handler::SimpleArcPeerManager
+pub type SimpleArcOnionMessenger<L> = OnionMessenger<InMemorySigner, Arc<KeysManager>, Arc<L>>;
+/// Useful for simplifying the parameters of [`SimpleRefChannelManager`] and
+/// [`SimpleRefPeerManager`]. See their docs for more details.
+///
+///[`SimpleRefChannelManager`]: crate::ln::channelmanager::SimpleRefChannelManager
+///[`SimpleRefPeerManager`]: crate::ln::peer_handler::SimpleRefPeerManager
+pub type SimpleRefOnionMessenger<'a, 'b, L> = OnionMessenger<InMemorySigner, &'a KeysManager, &'b L>;
+
+/// Construct onion packet payloads and keys for sending an onion message along the given
+/// `unblinded_path` to the given `destination`.
+fn packet_payloads_and_keys<T: secp256k1::Signing + secp256k1::Verification>(
+ secp_ctx: &Secp256k1<T>, unblinded_path: &[PublicKey], destination: Destination, session_priv: &SecretKey
+) -> Result<(Vec<(Payload, [u8; 32])>, Vec<onion_utils::OnionKeys>), secp256k1::Error> {
+ let num_hops = unblinded_path.len() + destination.num_hops();
+ let mut payloads = Vec::with_capacity(num_hops);
+ let mut onion_packet_keys = Vec::with_capacity(num_hops);
+
+ let (mut intro_node_id_blinding_pt, num_blinded_hops) = if let Destination::BlindedRoute(BlindedRoute {
+ introduction_node_id, blinding_point, blinded_hops }) = &destination {
+ (Some((*introduction_node_id, *blinding_point)), blinded_hops.len()) } else { (None, 0) };
+ let num_unblinded_hops = num_hops - num_blinded_hops;
+
+ let mut unblinded_path_idx = 0;
+ let mut blinded_path_idx = 0;
+ let mut prev_control_tlvs_ss = None;
+ utils::construct_keys_callback(secp_ctx, unblinded_path, Some(destination), session_priv, |_, onion_packet_ss, ephemeral_pubkey, control_tlvs_ss, unblinded_pk_opt, enc_payload_opt| {
+ if num_unblinded_hops != 0 && unblinded_path_idx < num_unblinded_hops {
+ if let Some(ss) = prev_control_tlvs_ss.take() {
+ payloads.push((Payload::Forward(ForwardControlTlvs::Unblinded(
+ ForwardTlvs {
+ next_node_id: unblinded_pk_opt.unwrap(),
+ next_blinding_override: None,
+ }
+ )), ss));
+ }
+ prev_control_tlvs_ss = Some(control_tlvs_ss);
+ unblinded_path_idx += 1;
+ } else if let Some((intro_node_id, blinding_pt)) = intro_node_id_blinding_pt.take() {
+ if let Some(control_tlvs_ss) = prev_control_tlvs_ss.take() {
+ payloads.push((Payload::Forward(ForwardControlTlvs::Unblinded(ForwardTlvs {
+ next_node_id: intro_node_id,
+ next_blinding_override: Some(blinding_pt),
+ })), control_tlvs_ss));
+ }
+ if let Some(encrypted_payload) = enc_payload_opt {
+ payloads.push((Payload::Forward(ForwardControlTlvs::Blinded(encrypted_payload)),
+ control_tlvs_ss));
+ } else { debug_assert!(false); }
+ blinded_path_idx += 1;
+ } else if blinded_path_idx < num_blinded_hops - 1 && enc_payload_opt.is_some() {
+ payloads.push((Payload::Forward(ForwardControlTlvs::Blinded(enc_payload_opt.unwrap())),
+ control_tlvs_ss));
+ blinded_path_idx += 1;
+ } else if let Some(encrypted_payload) = enc_payload_opt {
+ payloads.push((Payload::Receive {
+ control_tlvs: ReceiveControlTlvs::Blinded(encrypted_payload),
+ }, control_tlvs_ss));
+ }
+
+ let (rho, mu) = onion_utils::gen_rho_mu_from_shared_secret(onion_packet_ss.as_ref());
+ onion_packet_keys.push(onion_utils::OnionKeys {
+ #[cfg(test)]
+ shared_secret: onion_packet_ss,
+ #[cfg(test)]
+ blinding_factor: [0; 32],
+ ephemeral_pubkey,
+ rho,
+ mu,
+ });
+ })?;
+
+ if let Some(control_tlvs_ss) = prev_control_tlvs_ss {
+ payloads.push((Payload::Receive {
+ control_tlvs: ReceiveControlTlvs::Unblinded(ReceiveTlvs { path_id: None, })
+ }, control_tlvs_ss));
+ }
+
+ Ok((payloads, onion_packet_keys))
+}
+
+/// Errors if the serialized payload size exceeds onion_message::BIG_PACKET_HOP_DATA_LEN
+fn construct_onion_message_packet(payloads: Vec<(Payload, [u8; 32])>, onion_keys: Vec<onion_utils::OnionKeys>, prng_seed: [u8; 32]) -> Result<Packet, ()> {
+ // Spec rationale:
+ // "`len` allows larger messages to be sent than the standard 1300 bytes allowed for an HTLC
+ // onion, but this should be used sparingly as it is reduces anonymity set, hence the
+ // recommendation that it either look like an HTLC onion, or if larger, be a fixed size."
+ let payloads_ser_len = onion_utils::payloads_serialized_length(&payloads);
+ let hop_data_len = if payloads_ser_len <= SMALL_PACKET_HOP_DATA_LEN {
+ SMALL_PACKET_HOP_DATA_LEN
+ } else if payloads_ser_len <= BIG_PACKET_HOP_DATA_LEN {
+ BIG_PACKET_HOP_DATA_LEN
+ } else { return Err(()) };
+
+ Ok(onion_utils::construct_onion_message_packet::<_, _>(
+ payloads, onion_keys, prng_seed, hop_data_len))
+}
--- /dev/null
+// This file is Copyright its original authors, visible in version control
+// history.
+//
+// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
+// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
+// You may not use this file except in accordance with one or both of these
+// licenses.
+
+//! Onion Messages: sending, receiving, forwarding, and ancillary utilities live here
+//!
+//! Onion messages are multi-purpose messages sent between peers over the lightning network. In the
+//! near future, they will be used to communicate invoices for [offers], unlocking use cases such as
+//! static invoices, refunds and proof of payer. Further, you will be able to accept payments
+//! without revealing your node id through the use of [blinded routes].
+//!
+//! LDK sends and receives onion messages via the [`OnionMessenger`]. See its documentation for more
+//! information on its usage.
+//!
+//! [offers]: <https://github.com/lightning/bolts/pull/798>
+//! [blinded routes]: crate::onion_message::BlindedRoute
+
+mod blinded_route;
+mod messenger;
+mod packet;
+mod utils;
+#[cfg(test)]
+mod functional_tests;
+
+// Re-export structs so they can be imported with just the `onion_message::` module prefix.
+pub use self::blinded_route::{BlindedRoute, BlindedHop};
+pub use self::messenger::{Destination, OnionMessenger, SendError, SimpleArcOnionMessenger, SimpleRefOnionMessenger};
+pub(crate) use self::packet::Packet;
--- /dev/null
+// This file is Copyright its original authors, visible in version control
+// history.
+//
+// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
+// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
+// You may not use this file except in accordance with one or both of these
+// licenses.
+
+//! Structs and enums useful for constructing and reading an onion message packet.
+
+use bitcoin::secp256k1::PublicKey;
+use bitcoin::secp256k1::ecdh::SharedSecret;
+
+use ln::msgs::DecodeError;
+use ln::onion_utils;
+use super::blinded_route::{ForwardTlvs, ReceiveTlvs};
+use util::chacha20poly1305rfc::{ChaChaPolyReadAdapter, ChaChaPolyWriteAdapter};
+use util::ser::{FixedLengthReader, LengthRead, LengthReadable, LengthReadableArgs, Readable, ReadableArgs, Writeable, Writer};
+
+use core::cmp;
+use io::{self, Read};
+use prelude::*;
+
+// Per the spec, an onion message packet's `hop_data` field length should be
+// SMALL_PACKET_HOP_DATA_LEN if it fits, else BIG_PACKET_HOP_DATA_LEN if it fits.
+pub(super) const SMALL_PACKET_HOP_DATA_LEN: usize = 1300;
+pub(super) const BIG_PACKET_HOP_DATA_LEN: usize = 32768;
+
+#[derive(Clone, Debug, PartialEq)]
+pub(crate) struct Packet {
+ pub(super) version: u8,
+ pub(super) public_key: PublicKey,
+ // Unlike the onion packets used for payments, onion message packets can have payloads greater
+ // than 1300 bytes.
+ // TODO: if 1300 ends up being the most common size, optimize this to be:
+ // enum { ThirteenHundred([u8; 1300]), VarLen(Vec<u8>) }
+ pub(super) hop_data: Vec<u8>,
+ pub(super) hmac: [u8; 32],
+}
+
+impl onion_utils::Packet for Packet {
+ type Data = Vec<u8>;
+ fn new(public_key: PublicKey, hop_data: Vec<u8>, hmac: [u8; 32]) -> Packet {
+ Self {
+ version: 0,
+ public_key,
+ hop_data,
+ hmac,
+ }
+ }
+}
+
+impl Writeable for Packet {
+ fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
+ self.version.write(w)?;
+ self.public_key.write(w)?;
+ w.write_all(&self.hop_data)?;
+ self.hmac.write(w)?;
+ Ok(())
+ }
+}
+
+impl LengthReadable for Packet {
+ fn read<R: LengthRead>(r: &mut R) -> Result<Self, DecodeError> {
+ const READ_BUFFER_SIZE: usize = 4096;
+
+ let version = Readable::read(r)?;
+ let public_key = Readable::read(r)?;
+
+ let mut hop_data = Vec::new();
+ let hop_data_len = r.total_bytes() as usize - 66; // 1 (version) + 33 (pubkey) + 32 (HMAC) = 66
+ let mut read_idx = 0;
+ while read_idx < hop_data_len {
+ let mut read_buffer = [0; READ_BUFFER_SIZE];
+ let read_amt = cmp::min(hop_data_len - read_idx, READ_BUFFER_SIZE);
+ r.read_exact(&mut read_buffer[..read_amt]);
+ hop_data.extend_from_slice(&read_buffer[..read_amt]);
+ read_idx += read_amt;
+ }
+
+ let hmac = Readable::read(r)?;
+ Ok(Packet {
+ version,
+ public_key,
+ hop_data,
+ hmac,
+ })
+ }
+}
+
+/// Onion message payloads contain "control" TLVs and "data" TLVs. Control TLVs are used to route
+/// the onion message from hop to hop and for path verification, whereas data TLVs contain the onion
+/// message content itself, such as an invoice request.
+pub(super) enum Payload {
+ /// This payload is for an intermediate hop.
+ Forward(ForwardControlTlvs),
+ /// This payload is for the final hop.
+ Receive {
+ control_tlvs: ReceiveControlTlvs,
+ // Coming soon:
+ // reply_path: Option<BlindedRoute>,
+ // message: Message,
+ }
+}
+
+// Coming soon:
+// enum Message {
+// InvoiceRequest(InvoiceRequest),
+// Invoice(Invoice),
+// InvoiceError(InvoiceError),
+// CustomMessage<T>,
+// }
+
+/// Forward control TLVs in their blinded and unblinded form.
+pub(super) enum ForwardControlTlvs {
+ /// If we're sending to a blinded route, the node that constructed the blinded route has provided
+ /// this hop's control TLVs, already encrypted into bytes.
+ Blinded(Vec<u8>),
+ /// If we're constructing an onion message hop through an intermediate unblinded node, we'll need
+ /// to construct the intermediate hop's control TLVs in their unblinded state to avoid encoding
+ /// them into an intermediate Vec. See [`super::blinded_route::ForwardTlvs`] for more info.
+ Unblinded(ForwardTlvs),
+}
+
+/// Receive control TLVs in their blinded and unblinded form.
+pub(super) enum ReceiveControlTlvs {
+ /// See [`ForwardControlTlvs::Blinded`].
+ Blinded(Vec<u8>),
+ /// See [`ForwardControlTlvs::Unblinded`] and [`super::blinded_route::ReceiveTlvs`].
+ Unblinded(ReceiveTlvs),
+}
+
+// Uses the provided secret to simultaneously encode and encrypt the unblinded control TLVs.
+impl Writeable for (Payload, [u8; 32]) {
+ fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
+ match &self.0 {
+ Payload::Forward(ForwardControlTlvs::Blinded(encrypted_bytes)) |
+ Payload::Receive { control_tlvs: ReceiveControlTlvs::Blinded(encrypted_bytes)} => {
+ encode_varint_length_prefixed_tlv!(w, {
+ (4, encrypted_bytes, vec_type)
+ })
+ },
+ Payload::Forward(ForwardControlTlvs::Unblinded(control_tlvs)) => {
+ let write_adapter = ChaChaPolyWriteAdapter::new(self.1, &control_tlvs);
+ encode_varint_length_prefixed_tlv!(w, {
+ (4, write_adapter, required)
+ })
+ },
+ Payload::Receive { control_tlvs: ReceiveControlTlvs::Unblinded(control_tlvs)} => {
+ let write_adapter = ChaChaPolyWriteAdapter::new(self.1, &control_tlvs);
+ encode_varint_length_prefixed_tlv!(w, {
+ (4, write_adapter, required)
+ })
+ },
+ }
+ Ok(())
+ }
+}
+
+// Uses the provided secret to simultaneously decode and decrypt the control TLVs.
+impl ReadableArgs<SharedSecret> for Payload {
+ fn read<R: Read>(mut r: &mut R, encrypted_tlvs_ss: SharedSecret) -> Result<Self, DecodeError> {
+ use bitcoin::consensus::encode::{Decodable, Error, VarInt};
+ let v: VarInt = Decodable::consensus_decode(&mut r)
+ .map_err(|e| match e {
+ Error::Io(ioe) => DecodeError::from(ioe),
+ _ => DecodeError::InvalidValue
+ })?;
+
+ let mut rd = FixedLengthReader::new(r, v.0);
+ // TODO: support reply paths
+ let mut _reply_path_bytes: Option<Vec<u8>> = Some(Vec::new());
+ let mut read_adapter: Option<ChaChaPolyReadAdapter<ControlTlvs>> = None;
+ let rho = onion_utils::gen_rho_from_shared_secret(&encrypted_tlvs_ss.secret_bytes());
+ decode_tlv_stream!(&mut rd, {
+ (2, _reply_path_bytes, vec_type),
+ (4, read_adapter, (option: LengthReadableArgs, rho))
+ });
+ rd.eat_remaining().map_err(|_| DecodeError::ShortRead)?;
+
+ match read_adapter {
+ None => return Err(DecodeError::InvalidValue),
+ Some(ChaChaPolyReadAdapter { readable: ControlTlvs::Forward(tlvs)}) => {
+ Ok(Payload::Forward(ForwardControlTlvs::Unblinded(tlvs)))
+ },
+ Some(ChaChaPolyReadAdapter { readable: ControlTlvs::Receive(tlvs)}) => {
+ Ok(Payload::Receive { control_tlvs: ReceiveControlTlvs::Unblinded(tlvs)})
+ },
+ }
+ }
+}
+
+/// When reading a packet off the wire, we don't know a priori whether the packet is to be forwarded
+/// or received. Thus we read a ControlTlvs rather than reading a ForwardControlTlvs or
+/// ReceiveControlTlvs directly.
+pub(super) enum ControlTlvs {
+ /// This onion message is intended to be forwarded.
+ Forward(ForwardTlvs),
+ /// This onion message is intended to be received.
+ Receive(ReceiveTlvs),
+}
+
+impl Readable for ControlTlvs {
+ fn read<R: Read>(mut r: &mut R) -> Result<Self, DecodeError> {
+ let mut _padding: Option<Padding> = None;
+ let mut _short_channel_id: Option<u64> = None;
+ let mut next_node_id: Option<PublicKey> = None;
+ let mut path_id: Option<[u8; 32]> = None;
+ let mut next_blinding_override: Option<PublicKey> = None;
+ decode_tlv_stream!(&mut r, {
+ (1, _padding, option),
+ (2, _short_channel_id, option),
+ (4, next_node_id, option),
+ (6, path_id, option),
+ (8, next_blinding_override, option),
+ });
+
+ let valid_fwd_fmt = next_node_id.is_some() && path_id.is_none();
+ let valid_recv_fmt = next_node_id.is_none() && next_blinding_override.is_none();
+
+ let payload_fmt = if valid_fwd_fmt {
+ ControlTlvs::Forward(ForwardTlvs {
+ next_node_id: next_node_id.unwrap(),
+ next_blinding_override,
+ })
+ } else if valid_recv_fmt {
+ ControlTlvs::Receive(ReceiveTlvs {
+ path_id,
+ })
+ } else {
+ return Err(DecodeError::InvalidValue)
+ };
+
+ Ok(payload_fmt)
+ }
+}
+
+/// Reads padding to the end, ignoring what's read.
+pub(crate) struct Padding {}
+impl Readable for Padding {
+ #[inline]
+ fn read<R: Read>(reader: &mut R) -> Result<Self, DecodeError> {
+ loop {
+ let mut buf = [0; 8192];
+ if reader.read(&mut buf[..])? == 0 { break; }
+ }
+ Ok(Self {})
+ }
+}
--- /dev/null
+// This file is Copyright its original authors, visible in version control
+// history.
+//
+// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
+// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
+// You may not use this file except in accordance with one or both of these
+// licenses.
+
+//! Onion message utility methods live here.
+
+use bitcoin::hashes::{Hash, HashEngine};
+use bitcoin::hashes::hmac::{Hmac, HmacEngine};
+use bitcoin::hashes::sha256::Hash as Sha256;
+use bitcoin::secp256k1::{self, PublicKey, Secp256k1, SecretKey};
+use bitcoin::secp256k1::ecdh::SharedSecret;
+
+use ln::onion_utils;
+use super::blinded_route::BlindedRoute;
+use super::messenger::Destination;
+
+use prelude::*;
+
+// TODO: DRY with onion_utils::construct_onion_keys_callback
+#[inline]
+pub(super) fn construct_keys_callback<T: secp256k1::Signing + secp256k1::Verification,
+ FType: FnMut(PublicKey, SharedSecret, PublicKey, [u8; 32], Option<PublicKey>, Option<Vec<u8>>)>(
+ secp_ctx: &Secp256k1<T>, unblinded_path: &[PublicKey], destination: Option<Destination>,
+ session_priv: &SecretKey, mut callback: FType
+) -> Result<(), secp256k1::Error> {
+ let mut msg_blinding_point_priv = session_priv.clone();
+ let mut msg_blinding_point = PublicKey::from_secret_key(secp_ctx, &msg_blinding_point_priv);
+ let mut onion_packet_pubkey_priv = msg_blinding_point_priv.clone();
+ let mut onion_packet_pubkey = msg_blinding_point.clone();
+
+ macro_rules! build_keys {
+ ($pk: expr, $blinded: expr, $encrypted_payload: expr) => {{
+ let encrypted_data_ss = SharedSecret::new(&$pk, &msg_blinding_point_priv);
+
+ let blinded_hop_pk = if $blinded { $pk } else {
+ let hop_pk_blinding_factor = {
+ let mut hmac = HmacEngine::<Sha256>::new(b"blinded_node_id");
+ hmac.input(encrypted_data_ss.as_ref());
+ Hmac::from_engine(hmac).into_inner()
+ };
+ let mut unblinded_pk = $pk;
+ unblinded_pk.mul_assign(secp_ctx, &hop_pk_blinding_factor)?;
+ unblinded_pk
+ };
+ let onion_packet_ss = SharedSecret::new(&blinded_hop_pk, &onion_packet_pubkey_priv);
+
+ let rho = onion_utils::gen_rho_from_shared_secret(encrypted_data_ss.as_ref());
+ let unblinded_pk_opt = if $blinded { None } else { Some($pk) };
+ callback(blinded_hop_pk, onion_packet_ss, onion_packet_pubkey, rho, unblinded_pk_opt, $encrypted_payload);
+ (encrypted_data_ss, onion_packet_ss)
+ }}
+ }
+
+ macro_rules! build_keys_in_loop {
+ ($pk: expr, $blinded: expr, $encrypted_payload: expr) => {
+ let (encrypted_data_ss, onion_packet_ss) = build_keys!($pk, $blinded, $encrypted_payload);
+
+ let msg_blinding_point_blinding_factor = {
+ let mut sha = Sha256::engine();
+ sha.input(&msg_blinding_point.serialize()[..]);
+ sha.input(encrypted_data_ss.as_ref());
+ Sha256::from_engine(sha).into_inner()
+ };
+
+ msg_blinding_point_priv.mul_assign(&msg_blinding_point_blinding_factor)?;
+ msg_blinding_point = PublicKey::from_secret_key(secp_ctx, &msg_blinding_point_priv);
+
+ let onion_packet_pubkey_blinding_factor = {
+ let mut sha = Sha256::engine();
+ sha.input(&onion_packet_pubkey.serialize()[..]);
+ sha.input(onion_packet_ss.as_ref());
+ Sha256::from_engine(sha).into_inner()
+ };
+ onion_packet_pubkey_priv.mul_assign(&onion_packet_pubkey_blinding_factor)?;
+ onion_packet_pubkey = PublicKey::from_secret_key(secp_ctx, &onion_packet_pubkey_priv);
+ };
+ }
+
+ for pk in unblinded_path {
+ build_keys_in_loop!(*pk, false, None);
+ }
+ if let Some(dest) = destination {
+ match dest {
+ Destination::Node(pk) => {
+ build_keys!(pk, false, None);
+ },
+ Destination::BlindedRoute(BlindedRoute { blinded_hops, .. }) => {
+ for hop in blinded_hops {
+ build_keys_in_loop!(hop.blinded_node_id, true, Some(hop.encrypted_payload));
+ }
+ },
+ }
+ }
+ Ok(())
+}
fn read<R: LengthRead>(reader: &mut R, params: P) -> Result<Self, DecodeError>;
}
+/// A trait that various higher-level rust-lightning types implement allowing them to be read in
+/// from a Read, requiring the implementer to provide the total length of the read.
+pub(crate) trait LengthReadable where Self: Sized
+{
+ /// Reads a Self in from the given LengthRead
+ fn read<R: LengthRead>(reader: &mut R) -> Result<Self, DecodeError>;
+}
+
/// A trait that various rust-lightning types implement allowing them to (maybe) be read in from a Read
///
/// (C-not exported) as we only export serialization to/from byte arrays instead
use bitcoin::hash_types::{BlockHash, Txid};
use bitcoin::secp256k1::{SecretKey, PublicKey, Secp256k1, ecdsa::Signature};
+use bitcoin::secp256k1::ecdh::SharedSecret;
use bitcoin::secp256k1::ecdsa::RecoverableSignature;
use regex;
type Signer = EnforcingSigner;
fn get_node_secret(&self, _recipient: Recipient) -> Result<SecretKey, ()> { unreachable!(); }
+ fn ecdh(&self, _recipient: Recipient, _other_key: &PublicKey, _tweak: Option<&[u8; 32]>) -> Result<SharedSecret, ()> { unreachable!(); }
fn get_inbound_payment_key_material(&self) -> KeyMaterial { unreachable!(); }
fn get_destination_script(&self) -> Script { unreachable!(); }
fn get_shutdown_scriptpubkey(&self) -> ShutdownScript { unreachable!(); }
fn get_node_secret(&self, recipient: Recipient) -> Result<SecretKey, ()> {
self.backing.get_node_secret(recipient)
}
+ fn ecdh(&self, recipient: Recipient, other_key: &PublicKey, tweak: Option<&[u8; 32]>) -> Result<SharedSecret, ()> {
+ self.backing.ecdh(recipient, other_key, tweak)
+ }
fn get_inbound_payment_key_material(&self) -> keysinterface::KeyMaterial {
self.backing.get_inbound_payment_key_material()
}