]> git.bitcoin.ninja Git - rust-lightning/blobdiff - lightning/src/ln/msgs.rs
Merge pull request #441 from TheBlueMatt/2020-01-mpp
[rust-lightning] / lightning / src / ln / msgs.rs
index 294dbb965e781aebdfa90bcb00ca4e2fff081755..9698798c22ee0ae79ec24e002ef47220e616e2c4 100644 (file)
@@ -31,7 +31,10 @@ use std::result::Result;
 use util::events;
 use util::ser::{Readable, Writeable, Writer, FixedLengthReader, HighZeroBytesDroppedVarInt};
 
-use ln::channelmanager::{PaymentPreimage, PaymentHash};
+use ln::channelmanager::{PaymentPreimage, PaymentHash, PaymentSecret};
+
+/// 21 million * 10^8 * 1000
+pub(crate) const MAX_VALUE_MSAT: u64 = 21_000_000_0000_0000_000;
 
 /// An error in decoding a message or struct.
 #[derive(Debug)]
@@ -612,11 +615,15 @@ pub trait RoutingMessageHandler : Send + Sync {
 }
 
 mod fuzzy_internal_msgs {
+       use ln::channelmanager::PaymentSecret;
+
        // These types aren't intended to be pub, but are exposed for direct fuzzing (as we deserialize
        // them from untrusted input):
        #[derive(Clone)]
        pub(crate) struct FinalOnionHopData {
-               pub(crate) payment_secret: [u8; 32],
+               pub(crate) payment_secret: PaymentSecret,
+               /// The total value, in msat, of the payment as received by the ultimate recipient.
+               /// Message serialization may panic if this value is more than 21 million Bitcoin.
                pub(crate) total_msat: u64,
        }
 
@@ -634,6 +641,8 @@ mod fuzzy_internal_msgs {
 
        pub struct OnionHopData {
                pub(crate) format: OnionHopDataFormat,
+               /// The value, in msat, of the payment after this hop's fee is deducted.
+               /// Message serialization may panic if this value is more than 21 million Bitcoin.
                pub(crate) amt_to_forward: u64,
                pub(crate) outgoing_cltv_value: u32,
                // 12 bytes of 0-padding for Legacy format
@@ -975,22 +984,26 @@ impl_writeable!(UpdateAddHTLC, 32+8+8+32+4+1366, {
 impl Writeable for FinalOnionHopData {
        fn write<W: Writer>(&self, w: &mut W) -> Result<(), ::std::io::Error> {
                w.size_hint(32 + 8 - (self.total_msat.leading_zeros()/8) as usize);
-               self.payment_secret.write(w)?;
+               self.payment_secret.0.write(w)?;
                HighZeroBytesDroppedVarInt(self.total_msat).write(w)
        }
 }
 
 impl Readable for FinalOnionHopData {
        fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
-               let payment_secret = Readable::read(r)?;
+               let secret: [u8; 32] = Readable::read(r)?;
                let amt: HighZeroBytesDroppedVarInt<u64> = Readable::read(r)?;
-               Ok(Self { payment_secret, total_msat: amt.0 })
+               Ok(Self { payment_secret: PaymentSecret(secret), total_msat: amt.0 })
        }
 }
 
 impl Writeable for OnionHopData {
        fn write<W: Writer>(&self, w: &mut W) -> Result<(), ::std::io::Error> {
                w.size_hint(33);
+               // Note that this should never be reachable if Rust-Lightning generated the message, as we
+               // check values are sane long before we get here, though its possible in the future
+               // user-generated messages may hit this.
+               if self.amt_to_forward > MAX_VALUE_MSAT { panic!("We should never be sending infinite/overflow onion payments"); }
                match self.format {
                        OnionHopDataFormat::Legacy { short_channel_id } => {
                                0u8.write(w)?;
@@ -1007,6 +1020,7 @@ impl Writeable for OnionHopData {
                                });
                        },
                        OnionHopDataFormat::FinalNode { payment_data: Some(ref final_data) } => {
+                               if final_data.total_msat > MAX_VALUE_MSAT { panic!("We should never be sending infinite/overflow onion payments"); }
                                encode_varint_length_prefixed_tlv!(w, {
                                        (2, HighZeroBytesDroppedVarInt(self.amt_to_forward)),
                                        (4, HighZeroBytesDroppedVarInt(self.outgoing_cltv_value)),
@@ -1053,6 +1067,11 @@ impl Readable for OnionHopData {
                                        short_channel_id,
                                }
                        } else {
+                               if let &Some(ref data) = &payment_data {
+                                       if data.total_msat > MAX_VALUE_MSAT {
+                                               return Err(DecodeError::InvalidValue);
+                                       }
+                               }
                                OnionHopDataFormat::FinalNode {
                                        payment_data
                                }
@@ -1068,6 +1087,9 @@ impl Readable for OnionHopData {
                        (format, amt, cltv_value)
                };
 
+               if amt > MAX_VALUE_MSAT {
+                       return Err(DecodeError::InvalidValue);
+               }
                Ok(OnionHopData {
                        format,
                        amt_to_forward: amt,
@@ -1341,7 +1363,7 @@ mod tests {
        use hex;
        use ln::msgs;
        use ln::msgs::{ChannelFeatures, FinalOnionHopData, InitFeatures, NodeFeatures, OptionalField, OnionErrorPacket, OnionHopDataFormat};
-       use ln::channelmanager::{PaymentPreimage, PaymentHash};
+       use ln::channelmanager::{PaymentPreimage, PaymentHash, PaymentSecret};
        use util::ser::{Writeable, Readable};
 
        use bitcoin_hashes::sha256d::Hash as Sha256dHash;
@@ -2050,7 +2072,7 @@ mod tests {
 
        #[test]
        fn encoding_final_onion_hop_data_with_secret() {
-               let expected_payment_secret = [0x42u8; 32];
+               let expected_payment_secret = PaymentSecret([0x42u8; 32]);
                let mut msg = msgs::OnionHopData {
                        format: OnionHopDataFormat::FinalNode {
                                payment_data: Some(FinalOnionHopData {