Skip to content

Commit d74a26b

Browse files
Enable the construction of blinded routes
Blinded routes can be provided as destinations for onion messages, when the recipient prefers to remain anonymous. We also add supporting utilities for constructing blinded path keys, and a ControlTlvs struct representing blinded payloads prior to being encoded/encrypted. These utilities and struct will be re-used in upcoming commits for sending and receiving/forwarding onion messages.
1 parent 7eaa628 commit d74a26b

File tree

1 file changed

+251
-0
lines changed

1 file changed

+251
-0
lines changed

lightning/src/ln/onion_message.rs

Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,254 @@
88
// licenses.
99

1010
//! Onion Messages: sending, receiving, forwarding, and ancillary utilities live here
11+
use bitcoin::hashes::{Hash, HashEngine};
12+
use bitcoin::hashes::hmac::{Hmac, HmacEngine};
13+
use bitcoin::hashes::sha256::Hash as Sha256;
14+
use bitcoin::secp256k1::{self, PublicKey, Secp256k1, SecretKey};
15+
use bitcoin::secp256k1::ecdh::SharedSecret;
16+
17+
use chain::keysinterface::{KeysInterface, Sign};
18+
use ln::msgs::DecodeError;
19+
use ln::onion_utils;
20+
use util::chacha20poly1305rfc::{ChaCha20Poly1305RFC, ChaChaPoly1305Writer};
21+
use util::ser::{Readable, VecWriter, Writeable, Writer};
22+
23+
use core::ops::Deref;
24+
use io::{self, Read};
25+
use prelude::*;
26+
27+
/// Onion messages have "control" TLVs and "data" TLVs. Control TLVs are used to control the
28+
/// direction and routing of an onion message from hop to hop, whereas data TLVs contain the onion
29+
/// message content itself.
30+
pub(crate) enum ControlTlvs {
31+
/// Control TLVs for the final recipient of an onion message.
32+
Receive {
33+
/// If `path_id` is `Some`, it is used to identify the blinded route that this onion message is
34+
/// sending to. This is useful for receivers to check that said blinded route is being used in
35+
/// the right context.
36+
path_id: Option<[u8; 32]>
37+
},
38+
/// Control TLVs for an intermediate forwarder of an onion message.
39+
Forward {
40+
/// The node id of the next hop in the onion message's path.
41+
next_node_id: PublicKey,
42+
/// Senders of onion messages have the option of specifying an overriding [`blinding_point`]
43+
/// for forwarding nodes along the path. If this field is absent, forwarding nodes will
44+
/// calculate the next hop's blinding point by multiplying the blinding point that they
45+
/// received by a blinding factor.
46+
///
47+
/// [`blinding_point`]: crate::ln::msgs::OnionMessage::blinding_point
48+
next_blinding_override: Option<PublicKey>,
49+
}
50+
}
51+
52+
impl Writeable for ControlTlvs {
53+
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
54+
match self {
55+
ControlTlvs::Receive { path_id } => {
56+
encode_tlv_stream!(writer, {
57+
(6, path_id, option)
58+
});
59+
},
60+
ControlTlvs::Forward { next_node_id, next_blinding_override } => {
61+
encode_tlv_stream!(writer, {
62+
(4, next_node_id, required),
63+
(8, next_blinding_override, option)
64+
});
65+
},
66+
}
67+
Ok(())
68+
}
69+
}
70+
71+
impl Readable for ControlTlvs {
72+
fn read<R: Read>(mut r: &mut R) -> Result<Self, DecodeError> {
73+
let mut _padding: Option<Vec<u8>> = Some(Vec::new());
74+
let mut _short_channel_id: Option<u64> = None;
75+
let mut next_node_id: Option<PublicKey> = None;
76+
let mut path_id: Option<[u8; 32]> = None;
77+
let mut next_blinding_override: Option<PublicKey> = None;
78+
decode_tlv_stream!(&mut r, {
79+
(1, _padding, vec_type),
80+
(2, _short_channel_id, option),
81+
(4, next_node_id, option),
82+
(6, path_id, option),
83+
(8, next_blinding_override, option),
84+
});
85+
86+
let valid_fwd_fmt = next_node_id.is_some() && path_id.is_none();
87+
let valid_recv_fmt = next_node_id.is_none() && next_blinding_override.is_none();
88+
89+
let payload_fmt = if valid_fwd_fmt {
90+
ControlTlvs::Forward {
91+
next_node_id: next_node_id.unwrap(),
92+
next_blinding_override,
93+
}
94+
} else if valid_recv_fmt {
95+
ControlTlvs::Receive {
96+
path_id,
97+
}
98+
} else {
99+
return Err(DecodeError::InvalidValue)
100+
};
101+
Ok(payload_fmt)
102+
}
103+
}
104+
105+
/// Used to construct the blinded hops portion of a blinded route. These hops cannot be identified
106+
/// by outside observers and thus can be used to hide the identity of the recipient.
107+
pub struct BlindedNode {
108+
/// The blinded node id of this hop in a blinded route.
109+
pub blinded_node_id: PublicKey,
110+
/// The encrypted payload intended for this hop in a blinded route.
111+
// If we're sending to this blinded route, this payload will later be encoded into the
112+
// [`EncryptedTlvs`] for the hop when constructing the onion packet for sending.
113+
//
114+
// [`EncryptedTlvs`]: EncryptedTlvs
115+
pub encrypted_payload: Vec<u8>,
116+
}
117+
118+
/// Onion messages can be sent and received to blinded routes, which serve to hide the identity of
119+
/// the recipient.
120+
pub struct BlindedRoute {
121+
/// To send to a blinded route, the sender first finds a route to the unblinded
122+
/// `introduction_node_id`, which can unblind its [`encrypted_payload`] to find out the onion
123+
/// message's next hop and forward it along.
124+
///
125+
/// [`encrypted_payload`]: BlindedNode::encrypted_payload
126+
pub introduction_node_id: PublicKey,
127+
/// Creators of blinded routes supply the introduction node id's `blinding_point`, which the
128+
/// introduction node will use in decrypting its [`encrypted_payload`] to forward the onion
129+
/// message.
130+
///
131+
/// [`encrypted_payload`]: BlindedNode::encrypted_payload
132+
pub blinding_point: PublicKey,
133+
/// The blinded hops of the blinded route.
134+
pub blinded_hops: Vec<BlindedNode>,
135+
}
136+
137+
impl BlindedRoute {
138+
/// Create a blinded route to be forwarded along `hops`. The last node pubkey in `node_pks` will
139+
/// be the destination node.
140+
pub fn new<Signer: Sign, K: Deref>(node_pks: Vec<PublicKey>, keys_manager: &K) -> Result<Self, ()>
141+
where K::Target: KeysInterface<Signer = Signer>,
142+
{
143+
if node_pks.len() <= 1 { return Err(()) }
144+
let secp_ctx = Secp256k1::new();
145+
let blinding_secret_bytes = keys_manager.get_secure_random_bytes();
146+
let blinding_secret = SecretKey::from_slice(&blinding_secret_bytes[..]).expect("RNG is busted");
147+
let (mut encrypted_data_keys, mut blinded_node_pks) = construct_blinded_route_keys(&secp_ctx, &node_pks, &blinding_secret).map_err(|_| ())?;
148+
let mut blinded_hops = Vec::with_capacity(node_pks.len());
149+
debug_assert_eq!(encrypted_data_keys.len(), blinded_node_pks.len());
150+
let mut enc_tlvs_keys = encrypted_data_keys.drain(..);
151+
let mut blinded_pks = blinded_node_pks.drain(..);
152+
153+
for pk in node_pks.iter().skip(1) {
154+
let encrypted_tlvs = ControlTlvs::Forward {
155+
next_node_id: pk.clone(),
156+
next_blinding_override: None,
157+
};
158+
blinded_hops.push(BlindedNode {
159+
blinded_node_id: blinded_pks.next().unwrap(),
160+
encrypted_payload: Self::encrypt_payload(encrypted_tlvs, enc_tlvs_keys.next().unwrap()),
161+
});
162+
}
163+
164+
// Add the recipient final payload.
165+
let encrypted_tlvs = ControlTlvs::Receive { path_id: Some([42; 32]) };
166+
blinded_hops.push(BlindedNode {
167+
blinded_node_id: blinded_pks.next().unwrap(),
168+
encrypted_payload: Self::encrypt_payload(encrypted_tlvs, enc_tlvs_keys.next().unwrap()),
169+
});
170+
171+
Ok(BlindedRoute {
172+
introduction_node_id: node_pks[0].clone(),
173+
blinding_point: PublicKey::from_secret_key(&secp_ctx, &blinding_secret),
174+
blinded_hops,
175+
})
176+
}
177+
178+
fn encrypt_payload(payload: ControlTlvs, encrypted_tlvs_ss: SharedSecret) -> Vec<u8> {
179+
let mut enc_tlvs_blob = VecWriter(Vec::new());
180+
let (rho, _) = onion_utils::gen_rho_mu_from_shared_secret(encrypted_tlvs_ss.as_ref());
181+
let mut chacha = ChaCha20Poly1305RFC::new(&rho, &[0; 12], &[]);
182+
let mut chacha_stream = ChaChaPoly1305Writer { chacha: &mut chacha, write: &mut enc_tlvs_blob };
183+
payload.write(&mut chacha_stream).expect("In-memory writes cannot fail");
184+
let mut tag = [0 as u8; 16];
185+
chacha.finish_and_get_tag(&mut tag);
186+
tag.write(&mut enc_tlvs_blob).expect("In-memory writes cannot fail");
187+
enc_tlvs_blob.0
188+
}
189+
}
190+
191+
#[allow(unused_assignments)]
192+
#[inline]
193+
fn construct_keys_callback<T: secp256k1::Signing + secp256k1::Verification, FType: FnMut(PublicKey, SharedSecret, [u8; 32], PublicKey, SharedSecret)> (secp_ctx: &Secp256k1<T>, unblinded_path: &Vec<PublicKey>, session_priv: &SecretKey, mut callback: FType) -> Result<(), secp256k1::Error> {
194+
let mut msg_blinding_point_priv = session_priv.clone();
195+
let mut msg_blinding_point = PublicKey::from_secret_key(secp_ctx, &msg_blinding_point_priv);
196+
let mut onion_packet_pubkey_priv = msg_blinding_point_priv.clone();
197+
let mut onion_packet_pubkey = msg_blinding_point.clone();
198+
199+
macro_rules! build_keys {
200+
($pk: expr, $blinded: expr) => {
201+
let encrypted_data_ss = SharedSecret::new(&$pk, &msg_blinding_point_priv);
202+
203+
let hop_pk_blinding_factor = {
204+
let mut hmac = HmacEngine::<Sha256>::new(b"blinded_node_id");
205+
hmac.input(encrypted_data_ss.as_ref());
206+
Hmac::from_engine(hmac).into_inner()
207+
};
208+
let blinded_hop_pk = if $blinded { $pk.clone() } else {
209+
let mut unblinded_pk = $pk.clone();
210+
unblinded_pk.mul_assign(secp_ctx, &hop_pk_blinding_factor)?;
211+
unblinded_pk
212+
};
213+
let onion_packet_ss = SharedSecret::new(&blinded_hop_pk, &onion_packet_pubkey_priv);
214+
215+
callback(blinded_hop_pk, onion_packet_ss, hop_pk_blinding_factor, onion_packet_pubkey, encrypted_data_ss);
216+
217+
let msg_blinding_point_blinding_factor = {
218+
let mut sha = Sha256::engine();
219+
sha.input(&msg_blinding_point.serialize()[..]);
220+
sha.input(encrypted_data_ss.as_ref());
221+
Sha256::from_engine(sha).into_inner()
222+
};
223+
224+
msg_blinding_point_priv.mul_assign(&msg_blinding_point_blinding_factor)?;
225+
msg_blinding_point = PublicKey::from_secret_key(secp_ctx, &msg_blinding_point_priv);
226+
227+
let onion_packet_pubkey_blinding_factor = {
228+
let mut sha = Sha256::engine();
229+
sha.input(&onion_packet_pubkey.serialize()[..]);
230+
sha.input(onion_packet_ss.as_ref());
231+
Sha256::from_engine(sha).into_inner()
232+
};
233+
onion_packet_pubkey.mul_assign(secp_ctx, &onion_packet_pubkey_blinding_factor)?;
234+
onion_packet_pubkey_priv.mul_assign(&onion_packet_pubkey_blinding_factor)?;
235+
};
236+
}
237+
238+
for pk in unblinded_path {
239+
build_keys!(pk, false);
240+
}
241+
Ok(())
242+
}
243+
244+
/// Construct keys for constructing a blinded route along the given `unblinded_path`.
245+
///
246+
/// Returns: `(encrypted_tlvs_keys, blinded_node_ids)`
247+
/// where the encrypted tlvs keys are used to encrypt the blinded route's blinded payloads and the
248+
/// blinded node ids are used to set the [`blinded_node_id`]s of the [`BlindedRoute`].
249+
fn construct_blinded_route_keys<T: secp256k1::Signing + secp256k1::Verification>(
250+
secp_ctx: &Secp256k1<T>, unblinded_path: &Vec<PublicKey>, session_priv: &SecretKey
251+
) -> Result<(Vec<SharedSecret>, Vec<PublicKey>), secp256k1::Error> {
252+
let mut encrypted_data_keys = Vec::with_capacity(unblinded_path.len());
253+
let mut blinded_node_pks = Vec::with_capacity(unblinded_path.len());
254+
255+
construct_keys_callback(secp_ctx, unblinded_path, session_priv, |blinded_hop_pubkey, _, _, _, encrypted_data_ss| {
256+
blinded_node_pks.push(blinded_hop_pubkey);
257+
encrypted_data_keys.push(encrypted_data_ss);
258+
})?;
259+
260+
Ok((encrypted_data_keys, blinded_node_pks))
261+
}

0 commit comments

Comments
 (0)