d4894a86aa1a9069abdc9b2e073cd0a31c789048
[rust-lightning] / lightning / src / blinded_path / utils.rs
1 // This file is Copyright its original authors, visible in version control
2 // history.
3 //
4 // This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
5 // or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
7 // You may not use this file except in accordance with one or both of these
8 // licenses.
9
10 //! Onion message utility methods live here.
11
12 use bitcoin::hashes::{Hash, HashEngine};
13 use bitcoin::hashes::hmac::{Hmac, HmacEngine};
14 use bitcoin::hashes::sha256::Hash as Sha256;
15 use bitcoin::secp256k1::{self, PublicKey, Secp256k1, SecretKey, Scalar};
16 use bitcoin::secp256k1::ecdh::SharedSecret;
17
18 use super::{BlindedHop, BlindedPath};
19 use crate::ln::msgs::DecodeError;
20 use crate::ln::onion_utils;
21 use crate::onion_message::messenger::Destination;
22 use crate::crypto::streams::ChaChaPolyWriteAdapter;
23 use crate::util::ser::{Readable, Writeable};
24
25 use crate::io;
26 use crate::prelude::*;
27
28 // TODO: DRY with onion_utils::construct_onion_keys_callback
29 #[inline]
30 pub(crate) fn construct_keys_callback<'a, T, I, F>(
31         secp_ctx: &Secp256k1<T>, unblinded_path: I, destination: Option<Destination>,
32         session_priv: &SecretKey, mut callback: F
33 ) -> Result<(), secp256k1::Error>
34 where
35         T: secp256k1::Signing + secp256k1::Verification,
36         I: Iterator<Item=&'a PublicKey>,
37         F: FnMut(PublicKey, SharedSecret, PublicKey, [u8; 32], Option<PublicKey>, Option<Vec<u8>>),
38 {
39         let mut msg_blinding_point_priv = session_priv.clone();
40         let mut msg_blinding_point = PublicKey::from_secret_key(secp_ctx, &msg_blinding_point_priv);
41         let mut onion_packet_pubkey_priv = msg_blinding_point_priv.clone();
42         let mut onion_packet_pubkey = msg_blinding_point.clone();
43
44         macro_rules! build_keys {
45                 ($pk: expr, $blinded: expr, $encrypted_payload: expr) => {{
46                         let encrypted_data_ss = SharedSecret::new(&$pk, &msg_blinding_point_priv);
47
48                         let blinded_hop_pk = if $blinded { $pk } else {
49                                 let hop_pk_blinding_factor = {
50                                         let mut hmac = HmacEngine::<Sha256>::new(b"blinded_node_id");
51                                         hmac.input(encrypted_data_ss.as_ref());
52                                         Hmac::from_engine(hmac).to_byte_array()
53                                 };
54                                 $pk.mul_tweak(secp_ctx, &Scalar::from_be_bytes(hop_pk_blinding_factor).unwrap())?
55                         };
56                         let onion_packet_ss = SharedSecret::new(&blinded_hop_pk, &onion_packet_pubkey_priv);
57
58                         let rho = onion_utils::gen_rho_from_shared_secret(encrypted_data_ss.as_ref());
59                         let unblinded_pk_opt = if $blinded { None } else { Some($pk) };
60                         callback(blinded_hop_pk, onion_packet_ss, onion_packet_pubkey, rho, unblinded_pk_opt, $encrypted_payload);
61                         (encrypted_data_ss, onion_packet_ss)
62                 }}
63         }
64
65         macro_rules! build_keys_in_loop {
66                 ($pk: expr, $blinded: expr, $encrypted_payload: expr) => {
67                         let (encrypted_data_ss, onion_packet_ss) = build_keys!($pk, $blinded, $encrypted_payload);
68
69                         let msg_blinding_point_blinding_factor = {
70                                 let mut sha = Sha256::engine();
71                                 sha.input(&msg_blinding_point.serialize()[..]);
72                                 sha.input(encrypted_data_ss.as_ref());
73                                 Sha256::from_engine(sha).to_byte_array()
74                         };
75
76                         msg_blinding_point_priv = msg_blinding_point_priv.mul_tweak(&Scalar::from_be_bytes(msg_blinding_point_blinding_factor).unwrap())?;
77                         msg_blinding_point = PublicKey::from_secret_key(secp_ctx, &msg_blinding_point_priv);
78
79                         let onion_packet_pubkey_blinding_factor = {
80                                 let mut sha = Sha256::engine();
81                                 sha.input(&onion_packet_pubkey.serialize()[..]);
82                                 sha.input(onion_packet_ss.as_ref());
83                                 Sha256::from_engine(sha).to_byte_array()
84                         };
85                         onion_packet_pubkey_priv = onion_packet_pubkey_priv.mul_tweak(&Scalar::from_be_bytes(onion_packet_pubkey_blinding_factor).unwrap())?;
86                         onion_packet_pubkey = PublicKey::from_secret_key(secp_ctx, &onion_packet_pubkey_priv);
87                 };
88         }
89
90         for pk in unblinded_path {
91                 build_keys_in_loop!(*pk, false, None);
92         }
93         if let Some(dest) = destination {
94                 match dest {
95                         Destination::Node(pk) => {
96                                 build_keys!(pk, false, None);
97                         },
98                         Destination::BlindedPath(BlindedPath { blinded_hops, .. }) => {
99                                 for hop in blinded_hops {
100                                         build_keys_in_loop!(hop.blinded_node_id, true, Some(hop.encrypted_payload));
101                                 }
102                         },
103                 }
104         }
105         Ok(())
106 }
107
108 // Panics if `unblinded_tlvs` length is less than `unblinded_pks` length
109 pub(super) fn construct_blinded_hops<'a, T, I1, I2>(
110         secp_ctx: &Secp256k1<T>, unblinded_pks: I1, mut unblinded_tlvs: I2, session_priv: &SecretKey
111 ) -> Result<Vec<BlindedHop>, secp256k1::Error>
112 where
113         T: secp256k1::Signing + secp256k1::Verification,
114         I1: Iterator<Item=&'a PublicKey>,
115         I2: Iterator,
116         I2::Item: Writeable
117 {
118         let mut blinded_hops = Vec::with_capacity(unblinded_pks.size_hint().0);
119         construct_keys_callback(
120                 secp_ctx, unblinded_pks, None, session_priv,
121                 |blinded_node_id, _, _, encrypted_payload_rho, _, _| {
122                         blinded_hops.push(BlindedHop {
123                                 blinded_node_id,
124                                 encrypted_payload: encrypt_payload(unblinded_tlvs.next().unwrap(), encrypted_payload_rho),
125                         });
126                 })?;
127         Ok(blinded_hops)
128 }
129
130 /// Encrypt TLV payload to be used as a [`crate::blinded_path::BlindedHop::encrypted_payload`].
131 fn encrypt_payload<P: Writeable>(payload: P, encrypted_tlvs_rho: [u8; 32]) -> Vec<u8> {
132         let write_adapter = ChaChaPolyWriteAdapter::new(encrypted_tlvs_rho, &payload);
133         write_adapter.encode()
134 }
135
136 /// Blinded path encrypted payloads may be padded to ensure they are equal length.
137 ///
138 /// Reads padding to the end, ignoring what's read.
139 pub(crate) struct Padding {}
140 impl Readable for Padding {
141         #[inline]
142         fn read<R: io::Read>(reader: &mut R) -> Result<Self, DecodeError> {
143                 loop {
144                         let mut buf = [0; 8192];
145                         if reader.read(&mut buf[..])? == 0 { break; }
146                 }
147                 Ok(Self {})
148         }
149 }