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)]
}
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,
}
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
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)?;
});
},
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)),
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
}
(format, amt, cltv_value)
};
+ if amt > MAX_VALUE_MSAT {
+ return Err(DecodeError::InvalidValue);
+ }
Ok(OnionHopData {
format,
amt_to_forward: amt,
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;
#[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 {