|
13 | 13 | use bitcoin::hashes::hmac::{Hmac, HmacEngine};
|
14 | 14 | use bitcoin::hashes::sha256::Hash as Sha256;
|
15 | 15 | use bitcoin::hashes::{Hash, HashEngine};
|
| 16 | +use bitcoin::secp256k1::ecdh::SharedSecret; |
16 | 17 | use bitcoin::secp256k1::{self, PublicKey, Scalar, Secp256k1, SecretKey};
|
17 | 18 |
|
18 | 19 | #[cfg(async_payments)]
|
19 | 20 | use super::async_payments::AsyncPaymentsMessage;
|
20 | 21 | use super::async_payments::AsyncPaymentsMessageHandler;
|
21 | 22 | use super::dns_resolution::{DNSResolverMessage, DNSResolverMessageHandler};
|
22 | 23 | use super::offers::{OffersMessage, OffersMessageHandler};
|
23 |
| -use super::packet::OnionMessageContents; |
24 | 24 | use super::packet::ParsedOnionMessageContents;
|
| 25 | +use super::packet::{DummyControlTlvs, OnionMessageContents}; |
25 | 26 | use super::packet::{
|
26 | 27 | ForwardControlTlvs, Packet, Payload, ReceiveControlTlvs, BIG_PACKET_HOP_DATA_LEN,
|
27 | 28 | SMALL_PACKET_HOP_DATA_LEN,
|
28 | 29 | };
|
29 | 30 | #[cfg(async_payments)]
|
30 | 31 | use crate::blinded_path::message::AsyncPaymentsContext;
|
31 | 32 | use crate::blinded_path::message::{
|
32 |
| - BlindedMessagePath, DNSResolverContext, ForwardTlvs, MessageContext, MessageForwardNode, |
33 |
| - NextMessageHop, OffersContext, ReceiveTlvs, |
| 33 | + BlindedMessagePath, DNSResolverContext, DummyTlv, ForwardTlvs, MessageContext, |
| 34 | + MessageForwardNode, NextMessageHop, OffersContext, PrimaryDummyTlv, ReceiveTlvs, |
34 | 35 | };
|
35 | 36 | use crate::blinded_path::utils;
|
36 | 37 | use crate::blinded_path::{IntroductionNode, NodeIdLookUp};
|
37 | 38 | use crate::events::{Event, EventHandler, EventsProvider, ReplayEvent};
|
| 39 | +use crate::ln::channelmanager::Verification; |
38 | 40 | use crate::ln::msgs::{
|
39 | 41 | self, BaseMessageHandler, MessageSendEvent, OnionMessage, OnionMessageHandler, SocketAddress,
|
40 | 42 | };
|
@@ -1045,44 +1047,46 @@ where
|
1045 | 1047 | L::Target: Logger,
|
1046 | 1048 | CMH::Target: CustomOnionMessageHandler,
|
1047 | 1049 | {
|
1048 |
| - let control_tlvs_ss = match node_signer.ecdh(Recipient::Node, &msg.blinding_point, None) { |
1049 |
| - Ok(ss) => ss, |
1050 |
| - Err(e) => { |
1051 |
| - log_error!(logger, "Failed to retrieve node secret: {:?}", e); |
1052 |
| - return Err(()); |
1053 |
| - }, |
1054 |
| - }; |
1055 |
| - let onion_decode_ss = { |
1056 |
| - let blinding_factor = { |
1057 |
| - let mut hmac = HmacEngine::<Sha256>::new(b"blinded_node_id"); |
1058 |
| - hmac.input(control_tlvs_ss.as_ref()); |
1059 |
| - let hmac = Hmac::from_engine(hmac).to_byte_array(); |
1060 |
| - Scalar::from_be_bytes(hmac).unwrap() |
1061 |
| - }; |
1062 |
| - let packet_pubkey = &msg.onion_routing_packet.public_key; |
1063 |
| - match node_signer.ecdh(Recipient::Node, packet_pubkey, Some(&blinding_factor)) { |
1064 |
| - Ok(ss) => ss.secret_bytes(), |
1065 |
| - Err(()) => { |
1066 |
| - log_trace!(logger, "Failed to compute onion packet shared secret"); |
| 1050 | + // Helper function to compute shared secrets for onion decoding |
| 1051 | + let compute_shared_secrets = |blinding_point: &PublicKey, |
| 1052 | + packet_pubkey: &PublicKey| |
| 1053 | + -> Result<(SharedSecret, [u8; 32]), ()> { |
| 1054 | + let control_tlvs_ss = match node_signer.ecdh(Recipient::Node, blinding_point, None) { |
| 1055 | + Ok(ss) => ss, |
| 1056 | + Err(e) => { |
| 1057 | + log_error!(logger, "Failed to retrieve node secret: {:?}", e); |
1067 | 1058 | return Err(());
|
1068 | 1059 | },
|
1069 |
| - } |
| 1060 | + }; |
| 1061 | + |
| 1062 | + let onion_decode_ss = { |
| 1063 | + let blinding_factor = { |
| 1064 | + let mut hmac = HmacEngine::<Sha256>::new(b"blinded_node_id"); |
| 1065 | + hmac.input(control_tlvs_ss.as_ref()); |
| 1066 | + let hmac = Hmac::from_engine(hmac).to_byte_array(); |
| 1067 | + Scalar::from_be_bytes(hmac).unwrap() |
| 1068 | + }; |
| 1069 | + match node_signer.ecdh(Recipient::Node, packet_pubkey, Some(&blinding_factor)) { |
| 1070 | + Ok(ss) => ss.secret_bytes(), |
| 1071 | + Err(()) => { |
| 1072 | + log_trace!(logger, "Failed to compute onion packet shared secret"); |
| 1073 | + return Err(()); |
| 1074 | + }, |
| 1075 | + } |
| 1076 | + }; |
| 1077 | + |
| 1078 | + Ok((control_tlvs_ss, onion_decode_ss)) |
1070 | 1079 | };
|
1071 |
| - let next_hop = onion_utils::decode_next_untagged_hop( |
1072 |
| - onion_decode_ss, |
1073 |
| - &msg.onion_routing_packet.hop_data[..], |
1074 |
| - msg.onion_routing_packet.hmac, |
1075 |
| - (control_tlvs_ss, custom_handler.deref(), logger.deref()), |
1076 |
| - ); |
1077 |
| - match next_hop { |
1078 |
| - Ok(( |
1079 |
| - Payload::Receive { |
1080 |
| - message, |
1081 |
| - control_tlvs: ReceiveControlTlvs::Unblinded(ReceiveTlvs { context }), |
1082 |
| - reply_path, |
1083 |
| - }, |
1084 |
| - None, |
1085 |
| - )) => match (message, context) { |
| 1080 | + |
| 1081 | + // Helper function to process receive payloads |
| 1082 | + let process_receive_payload = |message, |
| 1083 | + context, |
| 1084 | + reply_path| |
| 1085 | + -> Result< |
| 1086 | + PeeledOnion<<<CMH>::Target as CustomOnionMessageHandler>::CustomMessage>, |
| 1087 | + (), |
| 1088 | + > { |
| 1089 | + match (message, context) { |
1086 | 1090 | (ParsedOnionMessageContents::Offers(msg), Some(MessageContext::Offers(ctx))) => {
|
1087 | 1091 | Ok(PeeledOnion::Offers(msg, Some(ctx), reply_path))
|
1088 | 1092 | },
|
@@ -1114,55 +1118,201 @@ where
|
1114 | 1118 | );
|
1115 | 1119 | Err(())
|
1116 | 1120 | },
|
1117 |
| - }, |
1118 |
| - Ok(( |
1119 |
| - Payload::Forward(ForwardControlTlvs::Unblinded(ForwardTlvs { |
1120 |
| - next_hop, |
1121 |
| - next_blinding_override, |
1122 |
| - })), |
1123 |
| - Some((next_hop_hmac, new_packet_bytes)), |
1124 |
| - )) => { |
1125 |
| - // TODO: we need to check whether `next_hop` is our node, in which case this is a dummy |
1126 |
| - // blinded hop and this onion message is destined for us. In this situation, we should keep |
1127 |
| - // unwrapping the onion layers to get to the final payload. Since we don't have the option |
1128 |
| - // of creating blinded paths with dummy hops currently, we should be ok to not handle this |
1129 |
| - // for now. |
1130 |
| - let packet_pubkey = msg.onion_routing_packet.public_key; |
1131 |
| - let new_pubkey_opt = |
1132 |
| - onion_utils::next_hop_pubkey(&secp_ctx, packet_pubkey, &onion_decode_ss); |
1133 |
| - let new_pubkey = match new_pubkey_opt { |
1134 |
| - Ok(pk) => pk, |
| 1121 | + } |
| 1122 | + }; |
| 1123 | + |
| 1124 | + // Constructs the next onion message using packet data and blinding logic. |
| 1125 | + let compute_onion_message = |packet_pubkey: PublicKey, |
| 1126 | + next_hop_hmac: [u8; 32], |
| 1127 | + new_packet_bytes: Vec<u8>, |
| 1128 | + current_blinding_point: PublicKey, |
| 1129 | + current_control_tlvs_ss: SharedSecret, |
| 1130 | + current_onion_decode_ss: [u8; 32], |
| 1131 | + blinding_point_opt: Option<PublicKey>| |
| 1132 | + -> Result<OnionMessage, ()> { |
| 1133 | + let new_pubkey = match onion_utils::next_hop_pubkey( |
| 1134 | + &secp_ctx, |
| 1135 | + packet_pubkey, |
| 1136 | + ¤t_onion_decode_ss, |
| 1137 | + ) { |
| 1138 | + Ok(pk) => pk, |
| 1139 | + Err(e) => { |
| 1140 | + log_trace!(logger, "Failed to compute next hop packet pubkey: {}", e); |
| 1141 | + return Err(()); |
| 1142 | + }, |
| 1143 | + }; |
| 1144 | + let outgoing_packet = Packet { |
| 1145 | + version: 0, |
| 1146 | + public_key: new_pubkey, |
| 1147 | + hop_data: new_packet_bytes, |
| 1148 | + hmac: next_hop_hmac, |
| 1149 | + }; |
| 1150 | + let blinding_point = match blinding_point_opt { |
| 1151 | + Some(bp) => bp, |
| 1152 | + None => match onion_utils::next_hop_pubkey( |
| 1153 | + &secp_ctx, |
| 1154 | + current_blinding_point, |
| 1155 | + current_control_tlvs_ss.as_ref(), |
| 1156 | + ) { |
| 1157 | + Ok(bp) => bp, |
1135 | 1158 | Err(e) => {
|
1136 |
| - log_trace!(logger, "Failed to compute next hop packet pubkey: {}", e); |
| 1159 | + log_trace!(logger, "Failed to compute next blinding point: {}", e); |
1137 | 1160 | return Err(());
|
1138 | 1161 | },
|
1139 |
| - }; |
1140 |
| - let outgoing_packet = Packet { |
1141 |
| - version: 0, |
1142 |
| - public_key: new_pubkey, |
1143 |
| - hop_data: new_packet_bytes, |
1144 |
| - hmac: next_hop_hmac, |
1145 |
| - }; |
1146 |
| - let onion_message = OnionMessage { |
1147 |
| - blinding_point: match next_blinding_override { |
1148 |
| - Some(blinding_point) => blinding_point, |
1149 |
| - None => { |
1150 |
| - match onion_utils::next_hop_pubkey( |
| 1162 | + }, |
| 1163 | + }; |
| 1164 | + Ok(OnionMessage { blinding_point, onion_routing_packet: outgoing_packet }) |
| 1165 | + }; |
| 1166 | + |
| 1167 | + let (control_tlvs_ss, onion_decode_ss) = |
| 1168 | + compute_shared_secrets(&msg.blinding_point, &msg.onion_routing_packet.public_key)?; |
| 1169 | + |
| 1170 | + let next_hop = onion_utils::decode_next_untagged_hop( |
| 1171 | + onion_decode_ss, |
| 1172 | + &msg.onion_routing_packet.hop_data[..], |
| 1173 | + msg.onion_routing_packet.hmac, |
| 1174 | + (control_tlvs_ss, custom_handler.deref(), logger.deref()), |
| 1175 | + ); |
| 1176 | + |
| 1177 | + match next_hop { |
| 1178 | + Ok(( |
| 1179 | + Payload::Receive { |
| 1180 | + message, |
| 1181 | + control_tlvs: ReceiveControlTlvs::Unblinded(ReceiveTlvs { context }), |
| 1182 | + reply_path, |
| 1183 | + }, |
| 1184 | + None, |
| 1185 | + )) => process_receive_payload(message, context, reply_path), |
| 1186 | + Ok(( |
| 1187 | + Payload::Dummy(DummyControlTlvs::Unblinded(DummyTlv::Primary(PrimaryDummyTlv { |
| 1188 | + dummy_tlv, |
| 1189 | + authentication, |
| 1190 | + }))), |
| 1191 | + Some((next_hop_hmac, new_packet_bytes)), |
| 1192 | + )) => { |
| 1193 | + let expanded_key = node_signer.get_expanded_key(); |
| 1194 | + dummy_tlv.verify_data(authentication.0, authentication.1, &expanded_key)?; |
| 1195 | + |
| 1196 | + // Start iterative processing of the dummy chain |
| 1197 | + let mut current_message = compute_onion_message( |
| 1198 | + msg.onion_routing_packet.public_key, |
| 1199 | + next_hop_hmac, |
| 1200 | + new_packet_bytes, |
| 1201 | + msg.blinding_point, |
| 1202 | + control_tlvs_ss, |
| 1203 | + onion_decode_ss, |
| 1204 | + None, |
| 1205 | + )?; |
| 1206 | + |
| 1207 | + // Process subsequent dummy hops iteratively |
| 1208 | + loop { |
| 1209 | + let (current_control_tlvs_ss, current_onion_decode_ss) = compute_shared_secrets( |
| 1210 | + ¤t_message.blinding_point, |
| 1211 | + ¤t_message.onion_routing_packet.public_key, |
| 1212 | + )?; |
| 1213 | + |
| 1214 | + let current_next_hop = onion_utils::decode_next_untagged_hop( |
| 1215 | + current_onion_decode_ss, |
| 1216 | + ¤t_message.onion_routing_packet.hop_data[..], |
| 1217 | + current_message.onion_routing_packet.hmac, |
| 1218 | + (current_control_tlvs_ss, custom_handler.deref(), logger.deref()), |
| 1219 | + ); |
| 1220 | + |
| 1221 | + match current_next_hop { |
| 1222 | + Ok(( |
| 1223 | + Payload::Dummy(DummyControlTlvs::Unblinded(DummyTlv::Subsequent)), |
| 1224 | + Some((next_hop_hmac, new_packet_bytes)), |
| 1225 | + )) => { |
| 1226 | + // Valid: Subsequent dummy after Primary dummy, continue processing |
| 1227 | + let new_pubkey = match onion_utils::next_hop_pubkey( |
| 1228 | + &secp_ctx, |
| 1229 | + current_message.onion_routing_packet.public_key, |
| 1230 | + ¤t_onion_decode_ss, |
| 1231 | + ) { |
| 1232 | + Ok(pk) => pk, |
| 1233 | + Err(e) => { |
| 1234 | + log_trace!( |
| 1235 | + logger, |
| 1236 | + "Failed to compute next hop packet pubkey: {}", |
| 1237 | + e |
| 1238 | + ); |
| 1239 | + return Err(()); |
| 1240 | + }, |
| 1241 | + }; |
| 1242 | + let outgoing_packet = Packet { |
| 1243 | + version: 0, |
| 1244 | + public_key: new_pubkey, |
| 1245 | + hop_data: new_packet_bytes, |
| 1246 | + hmac: next_hop_hmac, |
| 1247 | + }; |
| 1248 | + let blinding_point = match onion_utils::next_hop_pubkey( |
1151 | 1249 | &secp_ctx,
|
1152 |
| - msg.blinding_point, |
1153 |
| - control_tlvs_ss.as_ref(), |
| 1250 | + current_message.blinding_point, |
| 1251 | + current_control_tlvs_ss.as_ref(), |
1154 | 1252 | ) {
|
1155 | 1253 | Ok(bp) => bp,
|
1156 | 1254 | Err(e) => {
|
1157 | 1255 | log_trace!(logger, "Failed to compute next blinding point: {}", e);
|
1158 | 1256 | return Err(());
|
1159 | 1257 | },
|
1160 |
| - } |
| 1258 | + }; |
| 1259 | + current_message = |
| 1260 | + OnionMessage { blinding_point, onion_routing_packet: outgoing_packet }; |
1161 | 1261 | },
|
1162 |
| - }, |
1163 |
| - onion_routing_packet: outgoing_packet, |
1164 |
| - }; |
1165 |
| - |
| 1262 | + Ok(( |
| 1263 | + Payload::Receive { |
| 1264 | + message, |
| 1265 | + control_tlvs: ReceiveControlTlvs::Unblinded(ReceiveTlvs { context }), |
| 1266 | + reply_path, |
| 1267 | + }, |
| 1268 | + None, |
| 1269 | + )) => { |
| 1270 | + // End of dummy chain, process the final receive payload |
| 1271 | + return process_receive_payload(message, context, reply_path); |
| 1272 | + }, |
| 1273 | + Err(e) => { |
| 1274 | + log_trace!( |
| 1275 | + logger, |
| 1276 | + "Errored decoding onion message packet in dummy chain: {:?}", |
| 1277 | + e |
| 1278 | + ); |
| 1279 | + return Err(()); |
| 1280 | + }, |
| 1281 | + _ => { |
| 1282 | + // Invalid: anything else breaks the expected structure |
| 1283 | + log_trace!( |
| 1284 | + logger, |
| 1285 | + "Invalid payload type in dummy chain. Expected Subsequent dummy or Receive payload." |
| 1286 | + ); |
| 1287 | + return Err(()); |
| 1288 | + }, |
| 1289 | + } |
| 1290 | + } |
| 1291 | + }, |
| 1292 | + Ok((Payload::Dummy(DummyControlTlvs::Unblinded(DummyTlv::Subsequent)), _)) => { |
| 1293 | + // ERROR: Subsequent dummy without preceding Primary dummy |
| 1294 | + log_trace!( |
| 1295 | + logger, |
| 1296 | + "Received Subsequent dummy without preceding Primary dummy - invalid structure" |
| 1297 | + ); |
| 1298 | + Err(()) |
| 1299 | + }, |
| 1300 | + Ok(( |
| 1301 | + Payload::Forward(ForwardControlTlvs::Unblinded(ForwardTlvs { |
| 1302 | + next_hop, |
| 1303 | + next_blinding_override, |
| 1304 | + })), |
| 1305 | + Some((next_hop_hmac, new_packet_bytes)), |
| 1306 | + )) => { |
| 1307 | + let onion_message = compute_onion_message( |
| 1308 | + msg.onion_routing_packet.public_key, |
| 1309 | + next_hop_hmac, |
| 1310 | + new_packet_bytes, |
| 1311 | + msg.blinding_point, |
| 1312 | + control_tlvs_ss, |
| 1313 | + onion_decode_ss, |
| 1314 | + next_blinding_override, |
| 1315 | + )?; |
1166 | 1316 | Ok(PeeledOnion::Forward(next_hop, onion_message))
|
1167 | 1317 | },
|
1168 | 1318 | Err(e) => {
|
|
0 commit comments