X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Fln%2Fonion_utils.rs;h=64a1e3fa3487da78dc4918d17f56b57f8c99b085;hb=fb9ad5686ef2ad3f9ef06ad19b9ef1c8b60f8c1f;hp=31f1d5064fe1fdba3dadf30b735c105a02efdf7f;hpb=9f5e574b0b0f0f87a48f79d3150e93570957965c;p=rust-lightning diff --git a/lightning/src/ln/onion_utils.rs b/lightning/src/ln/onion_utils.rs index 31f1d506..64a1e3fa 100644 --- a/lightning/src/ln/onion_utils.rs +++ b/lightning/src/ln/onion_utils.rs @@ -91,25 +91,39 @@ pub(super) fn gen_pad_from_shared_secret(shared_secret: &[u8]) -> [u8; 32] { Hmac::from_engine(hmac).into_inner() } -pub(crate) fn next_hop_packet_pubkey(secp_ctx: &Secp256k1, packet_pubkey: PublicKey, packet_shared_secret: &[u8; 32]) -> Result { +/// Calculates a pubkey for the next hop, such as the next hop's packet pubkey or blinding point. +pub(crate) fn next_hop_pubkey( + secp_ctx: &Secp256k1, curr_pubkey: PublicKey, shared_secret: &[u8] +) -> Result { let blinding_factor = { let mut sha = Sha256::engine(); - sha.input(&packet_pubkey.serialize()[..]); - sha.input(packet_shared_secret); + sha.input(&curr_pubkey.serialize()[..]); + sha.input(shared_secret); Sha256::from_engine(sha).into_inner() }; - packet_pubkey.mul_tweak(secp_ctx, &Scalar::from_be_bytes(blinding_factor).unwrap()) + curr_pubkey.mul_tweak(secp_ctx, &Scalar::from_be_bytes(blinding_factor).unwrap()) } // can only fail if an intermediary hop has an invalid public key or session_priv is invalid #[inline] -pub(super) fn construct_onion_keys_callback (secp_ctx: &Secp256k1, path: &Vec, session_priv: &SecretKey, mut callback: FType) -> Result<(), secp256k1::Error> { +pub(super) fn construct_onion_keys_callback( + secp_ctx: &Secp256k1, path: &Path, session_priv: &SecretKey, mut callback: FType +) -> Result<(), secp256k1::Error> +where + T: secp256k1::Signing, + FType: FnMut(SharedSecret, [u8; 32], PublicKey, Option<&RouteHop>, usize) +{ let mut blinded_priv = session_priv.clone(); let mut blinded_pub = PublicKey::from_secret_key(secp_ctx, &blinded_priv); - for (idx, hop) in path.iter().enumerate() { - let shared_secret = SharedSecret::new(&hop.pubkey, &blinded_priv); + let unblinded_hops_iter = path.hops.iter().map(|h| (&h.pubkey, Some(h))); + let blinded_pks_iter = path.blinded_tail.as_ref() + .map(|t| t.hops.iter()).unwrap_or([].iter()) + .skip(1) // Skip the intro node because it's included in the unblinded hops + .map(|h| (&h.blinded_node_id, None)); + for (idx, (pubkey, route_hop_opt)) in unblinded_hops_iter.chain(blinded_pks_iter).enumerate() { + let shared_secret = SharedSecret::new(pubkey, &blinded_priv); let mut sha = Sha256::engine(); sha.input(&blinded_pub.serialize()[..]); @@ -121,7 +135,7 @@ pub(super) fn construct_onion_keys_callback(secp_ctx: &Secp256k1, path: &Path, session_priv: &SecretKey) -> Result, secp256k1::Error> { let mut res = Vec::with_capacity(path.hops.len()); - construct_onion_keys_callback(secp_ctx, &path.hops, session_priv, |shared_secret, _blinding_factor, ephemeral_pubkey, _, _| { + construct_onion_keys_callback(secp_ctx, &path, session_priv, + |shared_secret, _blinding_factor, ephemeral_pubkey, _, _| + { let (rho, mu) = gen_rho_mu_from_shared_secret(shared_secret.as_ref()); res.push(OnionKeys { @@ -400,10 +416,28 @@ where L::Target: Logger { let mut error_packet_ret = None; let mut is_from_final_node = false; + const BADONION: u16 = 0x8000; + const PERM: u16 = 0x4000; + const NODE: u16 = 0x2000; + const UPDATE: u16 = 0x1000; + // Handle packed channel/node updates for passing back for the route handler - construct_onion_keys_callback(secp_ctx, &path.hops, session_priv, |shared_secret, _, _, route_hop, route_hop_idx| { + construct_onion_keys_callback(secp_ctx, &path, session_priv, + |shared_secret, _, _, route_hop_opt, route_hop_idx| + { if res.is_some() { return; } + let route_hop = match route_hop_opt { + Some(hop) => hop, + None => { + // Got an error from within a blinded route. + error_code_ret = Some(BADONION | PERM | 24); // invalid_onion_blinding + error_packet_ret = Some(vec![0; 32]); + is_from_final_node = false; + return + }, + }; + let amt_to_forward = htlc_msat - route_hop.fee_msat; htlc_msat = amt_to_forward; @@ -415,8 +449,8 @@ where L::Target: Logger { chacha.process(&packet_decrypted, &mut decryption_tmp[..]); packet_decrypted = decryption_tmp; - // The failing hop includes either the inbound channel to the recipient or the outbound - // channel from the current hop (i.e., the next hop's inbound channel). + // The failing hop includes either the inbound channel to the recipient or the outbound channel + // from the current hop (i.e., the next hop's inbound channel). is_from_final_node = route_hop_idx + 1 == path.hops.len(); let failing_route_hop = if is_from_final_node { route_hop } else { &path.hops[route_hop_idx + 1] }; @@ -432,8 +466,8 @@ where L::Target: Logger { let error_code_slice = match err_packet.failuremsg.get(0..2) { Some(s) => s, None => { - // Useless packet that we can't use but it passed HMAC, so it - // definitely came from the peer in question + // Useless packet that we can't use but it passed HMAC, so it definitely came from the peer + // in question let network_update = Some(NetworkUpdate::NodeFailure { node_id: route_hop.pubkey, is_permanent: true, @@ -443,10 +477,6 @@ where L::Target: Logger { return } }; - const BADONION: u16 = 0x8000; - const PERM: u16 = 0x4000; - const NODE: u16 = 0x2000; - const UPDATE: u16 = 0x1000; let error_code = u16::from_be_bytes(error_code_slice.try_into().expect("len is 2")); error_code_ret = Some(error_code); @@ -454,8 +484,7 @@ where L::Target: Logger { let (debug_field, debug_field_size) = errors::get_onion_debug_field(error_code); - // indicate that payment parameter has failed and no need to - // update Route object + // indicate that payment parameter has failed and no need to update Route object let payment_failed = match error_code & 0xff { 15|16|17|18|19|23 => true, _ => false, @@ -465,14 +494,13 @@ where L::Target: Logger { let mut short_channel_id = None; if error_code & BADONION == BADONION { - // If the error code has the BADONION bit set, always blame the channel - // from the node "originating" the error to its next hop. The - // "originator" is ultimately actually claiming that its counterparty - // is the one who is failing the HTLC. - // If the "originator" here isn't lying we should really mark the - // next-hop node as failed entirely, but we can't be confident in that, - // as it would allow any node to get us to completely ban one of its - // counterparties. Instead, we simply remove the channel in question. + // If the error code has the BADONION bit set, always blame the channel from the node + // "originating" the error to its next hop. The "originator" is ultimately actually claiming + // that its counterparty is the one who is failing the HTLC. + // If the "originator" here isn't lying we should really mark the next-hop node as failed + // entirely, but we can't be confident in that, as it would allow any node to get us to + // completely ban one of its counterparties. Instead, we simply remove the channel in + // question. network_update = Some(NetworkUpdate::ChannelFailure { short_channel_id: failing_route_hop.short_channel_id, is_permanent: true, @@ -551,6 +579,8 @@ where L::Target: Logger { msg: chan_update, }) } else { + // The node in question intentionally encoded a 0-length channel update. This is + // likely due to https://github.com/ElementsProject/lightning/issues/6200. network_update = Some(NetworkUpdate::ChannelFailure { short_channel_id: route_hop.short_channel_id, is_permanent: false, @@ -579,16 +609,15 @@ where L::Target: Logger { short_channel_id = Some(route_hop.short_channel_id); } } else if payment_failed { - // Only blame the hop when a value in the HTLC doesn't match the - // corresponding value in the onion. + // Only blame the hop when a value in the HTLC doesn't match the corresponding value in the + // onion. short_channel_id = match error_code & 0xff { 18|19 => Some(route_hop.short_channel_id), _ => None, }; } else { - // We can't understand their error messages and they failed to - // forward...they probably can't understand our forwards so its - // really not worth trying any further. + // We can't understand their error messages and they failed to forward...they probably can't + // understand our forwards so it's really not worth trying any further. network_update = Some(NetworkUpdate::NodeFailure { node_id: route_hop.pubkey, is_permanent: true,