X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Fln%2Fonion_payment.rs;h=5966dce91039db227155264deb0557a2ed2012ff;hb=c80d9a74b5437dd7c82fab40806adeb995219fc2;hp=5ea07cfab30db4325bc71415f1b3f331ca1a37dc;hpb=339f3fccf80c8ccfc8de936df8de65481586f833;p=rust-lightning diff --git a/lightning/src/ln/onion_payment.rs b/lightning/src/ln/onion_payment.rs index 5ea07cfa..5966dce9 100644 --- a/lightning/src/ln/onion_payment.rs +++ b/lightning/src/ln/onion_payment.rs @@ -24,6 +24,7 @@ use crate::prelude::*; use core::ops::Deref; /// Invalid inbound onion payment. +#[derive(Debug)] pub struct InboundOnionErr { /// BOLT 4 error code. pub err_code: u16, @@ -33,6 +34,15 @@ pub struct InboundOnionErr { pub msg: &'static str, } +fn check_blinded_payment_constraints( + amt_msat: u64, cltv_expiry: u32, constraints: &PaymentConstraints +) -> Result<(), ()> { + if amt_msat < constraints.htlc_minimum_msat || + cltv_expiry > constraints.max_cltv_expiry + { return Err(()) } + Ok(()) +} + fn check_blinded_forward( inbound_amt_msat: u64, inbound_cltv_expiry: u32, payment_relay: &PaymentRelay, payment_constraints: &PaymentConstraints, features: &BlindedHopFeatures @@ -43,9 +53,8 @@ fn check_blinded_forward( let outgoing_cltv_value = inbound_cltv_expiry.checked_sub( payment_relay.cltv_expiry_delta as u32 ).ok_or(())?; - if inbound_amt_msat < payment_constraints.htlc_minimum_msat || - outgoing_cltv_value > payment_constraints.max_cltv_expiry - { return Err(()) } + check_blinded_payment_constraints(inbound_amt_msat, outgoing_cltv_value, payment_constraints)?; + if features.requires_unknown_bits_from(&BlindedHopFeatures::empty()) { return Err(()) } Ok((amt_to_forward, outgoing_cltv_value)) } @@ -122,8 +131,17 @@ pub(super) fn create_recv_pending_htlc_info( (payment_data, keysend_preimage, custom_tlvs, amt_msat, outgoing_cltv_value, payment_metadata, false), msgs::InboundOnionPayload::BlindedReceive { - amt_msat, total_msat, outgoing_cltv_value, payment_secret, intro_node_blinding_point, .. + amt_msat, total_msat, outgoing_cltv_value, payment_secret, intro_node_blinding_point, + payment_constraints, .. } => { + check_blinded_payment_constraints(amt_msat, cltv_expiry, &payment_constraints) + .map_err(|()| { + InboundOnionErr { + err_code: INVALID_ONION_BLINDING, + err_data: vec![0; 32], + msg: "Amount or cltv_expiry violated blinded payment constraints", + } + })?; let payment_data = msgs::FinalOnionHopData { payment_secret, total_msat }; (Some(payment_data), None, Vec::new(), amt_msat, outgoing_cltv_value, None, intro_node_blinding_point.is_none()) @@ -319,11 +337,16 @@ where ($msg: expr, $err_code: expr) => { { log_info!(logger, "Failed to accept/forward incoming HTLC: {}", $msg); + let (sha256_of_onion, failure_code) = if msg.blinding_point.is_some() { + ([0; 32], INVALID_ONION_BLINDING) + } else { + (Sha256::hash(&msg.onion_routing_packet.hop_data).to_byte_array(), $err_code) + }; return Err(HTLCFailureMsg::Malformed(msgs::UpdateFailMalformedHTLC { channel_id: msg.channel_id, htlc_id: msg.htlc_id, - sha256_of_onion: Sha256::hash(&msg.onion_routing_packet.hop_data).to_byte_array(), - failure_code: $err_code, + sha256_of_onion, + failure_code, })); } } @@ -355,6 +378,10 @@ where macro_rules! return_err { ($msg: expr, $err_code: expr, $data: expr) => { { + if msg.blinding_point.is_some() { + return_malformed_err!($msg, INVALID_ONION_BLINDING) + } + log_info!(logger, "Failed to accept/forward incoming HTLC: {}", $msg); return Err(HTLCFailureMsg::Relay(msgs::UpdateFailHTLC { channel_id: msg.channel_id, @@ -461,7 +488,7 @@ pub(super) fn check_incoming_htlc_cltv( mod tests { use bitcoin::hashes::Hash; use bitcoin::hashes::sha256::Hash as Sha256; - use bitcoin::secp256k1::{PublicKey, SecretKey}; + use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey}; use crate::ln::{PaymentPreimage, PaymentHash, PaymentSecret}; use crate::ln::ChannelId; use crate::ln::channelmanager::RecipientOnionFields; @@ -471,6 +498,38 @@ mod tests { use crate::routing::router::{Path, RouteHop}; use crate::util::test_utils; + #[test] + fn fail_construct_onion_on_too_big_payloads() { + // Ensure that if we call `construct_onion_packet` and friends where payloads are too large for + // the allotted packet length, we'll fail to construct. Previously, senders would happily + // construct invalid packets by array-shifting the final node's HMAC out of the packet when + // adding an intermediate onion layer, causing the receiver to error with "final payload + // provided for us as an intermediate node." + let secp_ctx = Secp256k1::new(); + let bob = crate::sign::KeysManager::new(&[2; 32], 42, 42); + let bob_pk = PublicKey::from_secret_key(&secp_ctx, &bob.get_node_secret_key()); + let charlie = crate::sign::KeysManager::new(&[3; 32], 42, 42); + let charlie_pk = PublicKey::from_secret_key(&secp_ctx, &charlie.get_node_secret_key()); + + let ( + session_priv, total_amt_msat, cur_height, mut recipient_onion, keysend_preimage, payment_hash, + prng_seed, hops, .. + ) = payment_onion_args(bob_pk, charlie_pk); + + // Ensure the onion will not fit all the payloads by adding a large custom TLV. + recipient_onion.custom_tlvs.push((13377331, vec![0; 1156])); + + let path = Path { hops, blinded_tail: None, }; + let onion_keys = super::onion_utils::construct_onion_keys(&secp_ctx, &path, &session_priv).unwrap(); + let (onion_payloads, ..) = super::onion_utils::build_onion_payloads( + &path, total_amt_msat, recipient_onion, cur_height + 1, &Some(keysend_preimage) + ).unwrap(); + + assert!(super::onion_utils::construct_onion_packet( + onion_payloads, onion_keys, prng_seed, &payment_hash + ).is_err()); + } + #[test] fn test_peel_payment_onion() { use super::*;