Skip to content

Commit 644eda0

Browse files
committed
Req+check payment secrets for inbound payments pre-PaymentReceived
Our current PaymentReceived API is incredibly easy to mis-use - the "obvious" way to implement a client is to always call `ChannelManager::claim_funds` in response to a `PaymentReceived` event. However, users are *required* to check the payment secret and value against the expected values before claiming in order to avoid a number of potentially funds-losing attacks. Instead, if we rely on payment secrets being pre-registered with the ChannelManager before we receive HTLCs for a payment we can simply check the payment secrets and never generate `PaymentReceived` events if they do not match. Further, when the user knows the value to expect in advance, we can have them register it as well, allowing us to check it for them. Other implementations already require payment secrets for inbound payments, so this shouldn't materially lose compatibility.
1 parent 35eeeb2 commit 644eda0

File tree

1 file changed

+94
-68
lines changed

1 file changed

+94
-68
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 94 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ enum PendingHTLCRouting {
9595
short_channel_id: u64, // This should be NonZero<u64> eventually when we bump MSRV
9696
},
9797
Receive {
98-
payment_data: Option<msgs::FinalOnionHopData>,
98+
payment_data: msgs::FinalOnionHopData,
9999
incoming_cltv_expiry: u32, // Used to track when we should expire pending HTLCs that go unclaimed
100100
},
101101
}
@@ -159,7 +159,7 @@ struct ClaimableHTLC {
159159
/// total_msat (which may differ from value if this is a Multi-Path Payment) and a
160160
/// payment_secret which prevents path-probing attacks and can associate different HTLCs which
161161
/// are part of the same payment.
162-
payment_data: Option<msgs::FinalOnionHopData>,
162+
payment_data: msgs::FinalOnionHopData,
163163
cltv_expiry: u32,
164164
}
165165

@@ -331,7 +331,7 @@ pub(super) struct ChannelHolder<Signer: Sign> {
331331
/// Note that while this is held in the same mutex as the channels themselves, no consistency
332332
/// guarantees are made about the channels given here actually existing anymore by the time you
333333
/// go to read them!
334-
claimable_htlcs: HashMap<(PaymentHash, Option<PaymentSecret>), Vec<ClaimableHTLC>>,
334+
claimable_htlcs: HashMap<PaymentHash, Vec<ClaimableHTLC>>,
335335
/// Messages to send to peers - pushed to in the same lock that they are generated in (except
336336
/// for broadcast messages, where ordering isn't as strict).
337337
pub(super) pending_msg_events: Vec<MessageSendEvent>,
@@ -1232,14 +1232,18 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
12321232
msgs::OnionHopDataFormat::FinalNode { payment_data } => payment_data,
12331233
};
12341234

1235+
if payment_data.is_none() {
1236+
return_err!("We require payment_secrets", 0x4000|0x2000|3, &[0;0]);
1237+
}
1238+
12351239
// Note that we could obviously respond immediately with an update_fulfill_htlc
12361240
// message, however that would leak that we are the recipient of this payment, so
12371241
// instead we stay symmetric with the forwarding case, only responding (after a
12381242
// delay) once they've send us a commitment_signed!
12391243

12401244
PendingHTLCStatus::Forward(PendingHTLCInfo {
12411245
routing: PendingHTLCRouting::Receive {
1242-
payment_data,
1246+
payment_data: payment_data.unwrap(),
12431247
incoming_cltv_expiry: msg.cltv_expiry,
12441248
},
12451249
payment_hash: msg.payment_hash.clone(),
@@ -1918,60 +1922,74 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
19181922
routing: PendingHTLCRouting::Receive { payment_data, incoming_cltv_expiry },
19191923
incoming_shared_secret, payment_hash, amt_to_forward, .. },
19201924
prev_funding_outpoint } => {
1921-
let prev_hop = HTLCPreviousHopData {
1922-
short_channel_id: prev_short_channel_id,
1923-
outpoint: prev_funding_outpoint,
1924-
htlc_id: prev_htlc_id,
1925-
incoming_packet_shared_secret: incoming_shared_secret,
1926-
};
1927-
1928-
let mut total_value = 0;
1929-
let payment_secret_opt =
1930-
if let &Some(ref data) = &payment_data { Some(data.payment_secret.clone()) } else { None };
1931-
let htlcs = channel_state.claimable_htlcs.entry((payment_hash, payment_secret_opt))
1932-
.or_insert(Vec::new());
1933-
htlcs.push(ClaimableHTLC {
1934-
prev_hop,
1925+
let claimable_htlc = ClaimableHTLC {
1926+
prev_hop: HTLCPreviousHopData {
1927+
short_channel_id: prev_short_channel_id,
1928+
outpoint: prev_funding_outpoint,
1929+
htlc_id: prev_htlc_id,
1930+
incoming_packet_shared_secret: incoming_shared_secret,
1931+
},
19351932
value: amt_to_forward,
19361933
payment_data: payment_data.clone(),
19371934
cltv_expiry: incoming_cltv_expiry,
1938-
});
1939-
if let &Some(ref data) = &payment_data {
1935+
};
1936+
1937+
macro_rules! fail_htlc {
1938+
($htlc: expr) => {
1939+
let mut htlc_msat_height_data = byte_utils::be64_to_array($htlc.value).to_vec();
1940+
htlc_msat_height_data.extend_from_slice(
1941+
&byte_utils::be32_to_array(self.best_block.read().unwrap().height()),
1942+
);
1943+
failed_forwards.push((HTLCSource::PreviousHopData(HTLCPreviousHopData {
1944+
short_channel_id: $htlc.prev_hop.short_channel_id,
1945+
outpoint: prev_funding_outpoint,
1946+
htlc_id: $htlc.prev_hop.htlc_id,
1947+
incoming_packet_shared_secret: $htlc.prev_hop.incoming_packet_shared_secret,
1948+
}), payment_hash,
1949+
HTLCFailReason::Reason { failure_code: 0x4000 | 15, data: htlc_msat_height_data }
1950+
));
1951+
}
1952+
}
1953+
1954+
// Check that the payment hash and secret are known. Note that we
1955+
// MUST take care to handle the "unknown payment hash" and
1956+
// "incorrect payment secret" cases here identically or we'd expose
1957+
// that we are the ultimately recipient of the given payment hash.
1958+
// Further, we must not expose whether we have any other HTLCs
1959+
// associated with the same payment_hash pending or not.
1960+
if {
1961+
let payment_secrets = self.payment_secrets.lock().unwrap();
1962+
if let Some(inbound_payment) = payment_secrets.get(&payment_hash) {
1963+
inbound_payment.payment_secret != payment_data.payment_secret ||
1964+
(inbound_payment.value_msat.is_some() && inbound_payment.value_msat.unwrap() > payment_data.total_msat)
1965+
} else { true }
1966+
} {
1967+
log_trace!(self.logger, "Failing new HTLC with payment_hash {} as it didn't match our expected payment secret.", log_bytes!(payment_hash.0));
1968+
fail_htlc!(claimable_htlc);
1969+
} else {
1970+
let mut total_value = 0;
1971+
let htlcs = channel_state.claimable_htlcs.entry(payment_hash)
1972+
.or_insert(Vec::new());
1973+
htlcs.push(claimable_htlc);
19401974
for htlc in htlcs.iter() {
19411975
total_value += htlc.value;
1942-
if htlc.payment_data.as_ref().unwrap().total_msat != data.total_msat {
1976+
if htlc.payment_data.total_msat != payment_data.total_msat {
19431977
total_value = msgs::MAX_VALUE_MSAT;
19441978
}
19451979
if total_value >= msgs::MAX_VALUE_MSAT { break; }
19461980
}
1947-
if total_value >= msgs::MAX_VALUE_MSAT || total_value > data.total_msat {
1981+
if total_value >= msgs::MAX_VALUE_MSAT || total_value > payment_data.total_msat {
1982+
log_trace!(self.logger, "Failing HTLCs with payment_hash {} as the total value {} ran over expected value {}", log_bytes!(payment_hash.0), total_value, payment_data.total_msat);
19481983
for htlc in htlcs.iter() {
1949-
let mut htlc_msat_height_data = byte_utils::be64_to_array(htlc.value).to_vec();
1950-
htlc_msat_height_data.extend_from_slice(
1951-
&byte_utils::be32_to_array(self.best_block.read().unwrap().height()),
1952-
);
1953-
failed_forwards.push((HTLCSource::PreviousHopData(HTLCPreviousHopData {
1954-
short_channel_id: htlc.prev_hop.short_channel_id,
1955-
outpoint: prev_funding_outpoint,
1956-
htlc_id: htlc.prev_hop.htlc_id,
1957-
incoming_packet_shared_secret: htlc.prev_hop.incoming_packet_shared_secret,
1958-
}), payment_hash,
1959-
HTLCFailReason::Reason { failure_code: 0x4000 | 15, data: htlc_msat_height_data }
1960-
));
1984+
fail_htlc!(htlc);
19611985
}
1962-
} else if total_value == data.total_msat {
1986+
} else if total_value == payment_data.total_msat {
19631987
new_events.push(events::Event::PaymentReceived {
19641988
payment_hash,
1965-
payment_secret: Some(data.payment_secret),
1989+
payment_secret: Some(payment_data.payment_secret),
19661990
amt: total_value,
19671991
});
19681992
}
1969-
} else {
1970-
new_events.push(events::Event::PaymentReceived {
1971-
payment_hash,
1972-
payment_secret: None,
1973-
amt: amt_to_forward,
1974-
});
19751993
}
19761994
},
19771995
HTLCForwardInfo::AddHTLC { .. } => {
@@ -2062,11 +2080,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
20622080
let _persistence_guard = PersistenceNotifierGuard::new(&self.total_consistency_lock, &self.persistence_notifier);
20632081

20642082
let mut channel_state = Some(self.channel_state.lock().unwrap());
2065-
let payment_secrets = self.payment_secrets.lock().unwrap();
2066-
let payment_secret = if let Some(secret) = payment_secrets.get(&payment_hash) {
2067-
Some(secret.payment_secret)
2068-
} else { return false; };
2069-
let removed_source = channel_state.as_mut().unwrap().claimable_htlcs.remove(&(*payment_hash, payment_secret));
2083+
let removed_source = channel_state.as_mut().unwrap().claimable_htlcs.remove(payment_hash);
20702084
if let Some(mut sources) = removed_source {
20712085
for htlc in sources.drain(..) {
20722086
if channel_state.is_none() { channel_state = Some(self.channel_state.lock().unwrap()); }
@@ -2248,11 +2262,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
22482262
let _persistence_guard = PersistenceNotifierGuard::new(&self.total_consistency_lock, &self.persistence_notifier);
22492263

22502264
let mut channel_state = Some(self.channel_state.lock().unwrap());
2251-
let payment_secrets = self.payment_secrets.lock().unwrap();
2252-
let payment_secret = if let Some(secret) = payment_secrets.get(&payment_hash) {
2253-
Some(secret.payment_secret)
2254-
} else { return false; };
2255-
let removed_source = channel_state.as_mut().unwrap().claimable_htlcs.remove(&(payment_hash, payment_secret));
2265+
let removed_source = channel_state.as_mut().unwrap().claimable_htlcs.remove(&payment_hash);
22562266
if let Some(mut sources) = removed_source {
22572267
assert!(!sources.is_empty());
22582268

@@ -2268,13 +2278,8 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
22682278
// provide the preimage, so worrying too much about the optimal handling isn't worth
22692279
// it.
22702280

2271-
let (is_mpp, mut valid_mpp) = if let &Some(ref data) = &sources[0].payment_data {
2272-
assert!(payment_secret.is_some());
2273-
(true, data.total_msat >= expected_amount)
2274-
} else {
2275-
assert!(payment_secret.is_none());
2276-
(false, false)
2277-
};
2281+
let is_mpp = true;
2282+
let mut valid_mpp = sources[0].payment_data.total_msat >= expected_amount;
22782283

22792284
for htlc in sources.iter() {
22802285
if !is_mpp || !valid_mpp { break; }
@@ -3541,7 +3546,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
35413546
});
35423547

35433548
if let Some(height) = height_opt {
3544-
channel_state.claimable_htlcs.retain(|&(ref payment_hash, _), htlcs| {
3549+
channel_state.claimable_htlcs.retain(|payment_hash, htlcs| {
35453550
htlcs.retain(|htlc| {
35463551
// If height is approaching the number of blocks we think it takes us to get
35473552
// our commitment transaction confirmed before the HTLC expires, plus the
@@ -4040,7 +4045,8 @@ impl Writeable for PendingHTLCInfo {
40404045
},
40414046
&PendingHTLCRouting::Receive { ref payment_data, ref incoming_cltv_expiry } => {
40424047
1u8.write(writer)?;
4043-
payment_data.write(writer)?;
4048+
payment_data.payment_secret.write(writer)?;
4049+
payment_data.total_msat.write(writer)?;
40444050
incoming_cltv_expiry.write(writer)?;
40454051
},
40464052
}
@@ -4061,7 +4067,10 @@ impl Readable for PendingHTLCInfo {
40614067
short_channel_id: Readable::read(reader)?,
40624068
},
40634069
1u8 => PendingHTLCRouting::Receive {
4064-
payment_data: Readable::read(reader)?,
4070+
payment_data: msgs::FinalOnionHopData {
4071+
payment_secret: Readable::read(reader)?,
4072+
total_msat: Readable::read(reader)?,
4073+
},
40654074
incoming_cltv_expiry: Readable::read(reader)?,
40664075
},
40674076
_ => return Err(DecodeError::InvalidValue),
@@ -4133,12 +4142,29 @@ impl_writeable!(HTLCPreviousHopData, 0, {
41334142
incoming_packet_shared_secret
41344143
});
41354144

4136-
impl_writeable!(ClaimableHTLC, 0, {
4137-
prev_hop,
4138-
value,
4139-
payment_data,
4140-
cltv_expiry
4141-
});
4145+
impl Writeable for ClaimableHTLC {
4146+
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ::std::io::Error> {
4147+
self.prev_hop.write(writer)?;
4148+
self.value.write(writer)?;
4149+
self.payment_data.payment_secret.write(writer)?;
4150+
self.payment_data.total_msat.write(writer)?;
4151+
self.cltv_expiry.write(writer)
4152+
}
4153+
}
4154+
4155+
impl Readable for ClaimableHTLC {
4156+
fn read<R: ::std::io::Read>(reader: &mut R) -> Result<Self, DecodeError> {
4157+
Ok(ClaimableHTLC {
4158+
prev_hop: Readable::read(reader)?,
4159+
value: Readable::read(reader)?,
4160+
payment_data: msgs::FinalOnionHopData {
4161+
payment_secret: Readable::read(reader)?,
4162+
total_msat: Readable::read(reader)?,
4163+
},
4164+
cltv_expiry: Readable::read(reader)?,
4165+
})
4166+
}
4167+
}
41424168

41434169
impl Writeable for HTLCSource {
41444170
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ::std::io::Error> {

0 commit comments

Comments
 (0)