Skip to content

Commit 7659877

Browse files
authored
Merge pull request #132 from TheBlueMatt/2018-08-bolt-4-spec-return-fail
Return a malformed HTLC message when ephemeral pubkey is garbage
2 parents b83443f + a34e80f commit 7659877

9 files changed

+88
-55
lines changed

fuzz/Cargo.toml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@ path = "fuzz_targets/msg_pong_target.rs"
6464
name = "msg_error_message_target"
6565
path = "fuzz_targets/msg_error_message_target.rs"
6666

67+
[[bin]]
68+
name = "msg_update_add_htlc_target"
69+
path = "fuzz_targets/msg_update_add_htlc_target.rs"
70+
6771
[[bin]]
6872
name = "msg_accept_channel_target"
6973
path = "fuzz_targets/msg_targets/msg_accept_channel_target.rs"
@@ -100,10 +104,6 @@ path = "fuzz_targets/msg_targets/msg_revoke_and_ack_target.rs"
100104
name = "msg_shutdown_target"
101105
path = "fuzz_targets/msg_targets/msg_shutdown_target.rs"
102106

103-
[[bin]]
104-
name = "msg_update_add_htlc_target"
105-
path = "fuzz_targets/msg_targets/msg_update_add_htlc_target.rs"
106-
107107
[[bin]]
108108
name = "msg_update_fail_malformed_htlc_target"
109109
path = "fuzz_targets/msg_targets/msg_update_fail_malformed_htlc_target.rs"

fuzz/fuzz_targets/channel_target.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ pub fn do_test(data: &[u8]) {
269269
0 => {
270270
test_err!(channel.send_htlc(slice_to_be64(get_slice!(8)), [42; 32], slice_to_be32(get_slice!(4)), msgs::OnionPacket {
271271
version: get_slice!(1)[0],
272-
public_key: get_pubkey!(),
272+
public_key: PublicKey::from_slice(&secp_ctx, get_slice!(33)),
273273
hop_data: [0; 20*65],
274274
hmac: [0; 32],
275275
}));

fuzz/fuzz_targets/msg_error_message_target.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
// This file is auto-generated by gen_target.sh based on msg_target_template.txt
2-
// To modify it, modify msg_target_template.txt and run gen_target.sh instead.
3-
41
extern crate lightning;
52

63
use lightning::ln::msgs;

fuzz/fuzz_targets/msg_targets/gen_target.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
for target in CommitmentSigned FundingCreated FundingLocked FundingSigned OpenChannel RevokeAndACK Shutdown UpdateAddHTLC UpdateFailHTLC UpdateFailMalformedHTLC UpdateFee UpdateFulfillHTLC AcceptChannel ClosingSigned ChannelReestablish; do
1+
for target in CommitmentSigned FundingCreated FundingLocked FundingSigned OpenChannel RevokeAndACK Shutdown UpdateFailHTLC UpdateFailMalformedHTLC UpdateFee UpdateFulfillHTLC AcceptChannel ClosingSigned ChannelReestablish; do
22
tn=$(echo $target | sed 's/\([a-z0-9]\)\([A-Z]\)/\1_\2/g')
33
fn=msg_$(echo $tn | tr '[:upper:]' '[:lower:]')_target.rs
44
cat msg_target_template.txt | sed s/MSG_TARGET/$target/ > $fn

fuzz/fuzz_targets/msg_targets/msg_update_add_htlc_target.rs renamed to fuzz/fuzz_targets/msg_update_add_htlc_target.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,18 @@
1-
// This file is auto-generated by gen_target.sh based on msg_target_template.txt
2-
// To modify it, modify msg_target_template.txt and run gen_target.sh instead.
3-
41
extern crate lightning;
52

63
use lightning::ln::msgs;
74
use lightning::util::reset_rng_state;
85

96
use lightning::ln::msgs::{MsgEncodable, MsgDecodable};
107

11-
mod utils;
12-
138
#[inline]
149
pub fn do_test(data: &[u8]) {
1510
reset_rng_state();
16-
test_msg!(msgs::UpdateAddHTLC, data);
11+
if let Ok(msg) = msgs::UpdateAddHTLC::decode(data){
12+
let enc = msg.encode();
13+
assert_eq!(&data[0..85], &enc[0..85]);
14+
assert_eq!(&data[85+33..enc.len()], &enc[85+33..]);
15+
}
1716
}
1817

1918
#[cfg(feature = "afl")]

src/ln/channel.rs

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use crypto::hkdf::{hkdf_extract,hkdf_expand};
1616
use ln::msgs;
1717
use ln::msgs::{ErrorAction, HandleError, MsgEncodable};
1818
use ln::channelmonitor::ChannelMonitor;
19-
use ln::channelmanager::{PendingHTLCStatus, PendingForwardHTLCInfo, HTLCFailReason};
19+
use ln::channelmanager::{PendingHTLCStatus, PendingForwardHTLCInfo, HTLCFailReason, HTLCFailureMsg};
2020
use ln::chan_utils::{TxCreationKeys,HTLCOutputInCommitment,HTLC_SUCCESS_TX_WEIGHT,HTLC_TIMEOUT_TX_WEIGHT};
2121
use ln::chan_utils;
2222
use chain::chaininterface::{FeeEstimator,ConfirmationTarget};
@@ -1643,6 +1643,7 @@ impl Channel {
16431643
update_add_htlcs,
16441644
update_fulfill_htlcs,
16451645
update_fail_htlcs,
1646+
update_fail_malformed_htlcs: Vec::new(),
16461647
commitment_signed,
16471648
}, monitor_update)))
16481649
},
@@ -1680,7 +1681,8 @@ impl Channel {
16801681

16811682
let mut to_forward_infos = Vec::new();
16821683
let mut revoked_htlcs = Vec::new();
1683-
let mut failed_htlcs = Vec::new();
1684+
let mut update_fail_htlcs = Vec::new();
1685+
let mut update_fail_malformed_htlcs = Vec::new();
16841686
let mut require_commitment = false;
16851687
let mut value_to_self_msat_diff: i64 = 0;
16861688
// We really shouldnt have two passes here, but retain gives a non-mutable ref (Rust bug)
@@ -1708,7 +1710,10 @@ impl Channel {
17081710
PendingHTLCStatus::Fail(fail_msg) => {
17091711
htlc.state = HTLCState::LocalRemoved;
17101712
require_commitment = true;
1711-
failed_htlcs.push(fail_msg);
1713+
match fail_msg {
1714+
HTLCFailureMsg::Relay(msg) => update_fail_htlcs.push(msg),
1715+
HTLCFailureMsg::Malformed(msg) => update_fail_malformed_htlcs.push(msg),
1716+
}
17121717
},
17131718
PendingHTLCStatus::Forward(forward_info) => {
17141719
to_forward_infos.push(forward_info);
@@ -1727,10 +1732,14 @@ impl Channel {
17271732

17281733
match self.free_holding_cell_htlcs()? {
17291734
Some(mut commitment_update) => {
1730-
commitment_update.0.update_fail_htlcs.reserve(failed_htlcs.len());
1731-
for fail_msg in failed_htlcs.drain(..) {
1735+
commitment_update.0.update_fail_htlcs.reserve(update_fail_htlcs.len());
1736+
for fail_msg in update_fail_htlcs.drain(..) {
17321737
commitment_update.0.update_fail_htlcs.push(fail_msg);
17331738
}
1739+
commitment_update.0.update_fail_malformed_htlcs.reserve(update_fail_malformed_htlcs.len());
1740+
for fail_msg in update_fail_malformed_htlcs.drain(..) {
1741+
commitment_update.0.update_fail_malformed_htlcs.push(fail_msg);
1742+
}
17341743
Ok((Some(commitment_update.0), to_forward_infos, revoked_htlcs, commitment_update.1))
17351744
},
17361745
None => {
@@ -1739,7 +1748,8 @@ impl Channel {
17391748
Ok((Some(msgs::CommitmentUpdate {
17401749
update_add_htlcs: Vec::new(),
17411750
update_fulfill_htlcs: Vec::new(),
1742-
update_fail_htlcs: failed_htlcs,
1751+
update_fail_htlcs,
1752+
update_fail_malformed_htlcs,
17431753
commitment_signed
17441754
}), to_forward_infos, revoked_htlcs, monitor_update))
17451755
} else {

src/ln/channelmanager.rs

Lines changed: 46 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,17 @@ mod channel_held_info {
5050
pub(super) outgoing_cltv_value: u32,
5151
}
5252

53+
#[derive(Clone)] // See Channel::revoke_and_ack for why, tl;dr: Rust bug
54+
pub enum HTLCFailureMsg {
55+
Relay(msgs::UpdateFailHTLC),
56+
Malformed(msgs::UpdateFailMalformedHTLC),
57+
}
58+
5359
/// Stores whether we can't forward an HTLC or relevant forwarding info
5460
#[derive(Clone)] // See Channel::revoke_and_ack for why, tl;dr: Rust bug
5561
pub enum PendingHTLCStatus {
5662
Forward(PendingForwardHTLCInfo),
57-
Fail(msgs::UpdateFailHTLC),
63+
Fail(HTLCFailureMsg),
5864
}
5965

6066
#[cfg(feature = "fuzztarget")]
@@ -619,7 +625,7 @@ impl ChannelManager {
619625

620626
Ok(msgs::OnionPacket{
621627
version: 0,
622-
public_key: onion_keys.first().unwrap().ephemeral_pubkey,
628+
public_key: Ok(onion_keys.first().unwrap().ephemeral_pubkey),
623629
hop_data: packet_data,
624630
hmac: hmac_res,
625631
})
@@ -675,10 +681,7 @@ impl ChannelManager {
675681
ChannelManager::encrypt_failure_packet(shared_secret, &failure_packet.encode()[..])
676682
}
677683

678-
fn decode_update_add_htlc_onion(&self, msg: &msgs::UpdateAddHTLC) -> (PendingHTLCStatus, SharedSecret, MutexGuard<ChannelHolder>) {
679-
let shared_secret = SharedSecret::new(&self.secp_ctx, &msg.onion_routing_packet.public_key, &self.our_network_key);
680-
let (rho, mu) = ChannelManager::gen_rho_mu_from_shared_secret(&shared_secret);
681-
684+
fn decode_update_add_htlc_onion(&self, msg: &msgs::UpdateAddHTLC) -> (PendingHTLCStatus, Option<SharedSecret>, MutexGuard<ChannelHolder>) {
682685
macro_rules! get_onion_hash {
683686
() => {
684687
{
@@ -691,6 +694,19 @@ impl ChannelManager {
691694
}
692695
}
693696

697+
if let Err(_) = msg.onion_routing_packet.public_key {
698+
log_info!(self, "Failed to accept/forward incoming HTLC with invalid ephemeral pubkey");
699+
return (PendingHTLCStatus::Fail(HTLCFailureMsg::Malformed(msgs::UpdateFailMalformedHTLC {
700+
channel_id: msg.channel_id,
701+
htlc_id: msg.htlc_id,
702+
sha256_of_onion: get_onion_hash!(),
703+
failure_code: 0x8000 | 0x4000 | 6,
704+
})), None, self.channel_state.lock().unwrap());
705+
}
706+
707+
let shared_secret = SharedSecret::new(&self.secp_ctx, &msg.onion_routing_packet.public_key.unwrap(), &self.our_network_key);
708+
let (rho, mu) = ChannelManager::gen_rho_mu_from_shared_secret(&shared_secret);
709+
694710
let mut channel_state = None;
695711
macro_rules! return_err {
696712
($msg: expr, $err_code: expr, $data: expr) => {
@@ -699,11 +715,11 @@ impl ChannelManager {
699715
if channel_state.is_none() {
700716
channel_state = Some(self.channel_state.lock().unwrap());
701717
}
702-
return (PendingHTLCStatus::Fail(msgs::UpdateFailHTLC {
718+
return (PendingHTLCStatus::Fail(HTLCFailureMsg::Relay(msgs::UpdateFailHTLC {
703719
channel_id: msg.channel_id,
704720
htlc_id: msg.htlc_id,
705721
reason: ChannelManager::build_first_hop_failure_packet(&shared_secret, $err_code, $data),
706-
}), shared_secret, channel_state.unwrap());
722+
})), Some(shared_secret), channel_state.unwrap());
707723
}
708724
}
709725
}
@@ -770,7 +786,7 @@ impl ChannelManager {
770786
chacha.process(&msg.onion_routing_packet.hop_data[65..], &mut new_packet_data[0..19*65]);
771787
chacha.process(&ChannelManager::ZERO[0..65], &mut new_packet_data[19*65..]);
772788

773-
let mut new_pubkey = msg.onion_routing_packet.public_key.clone();
789+
let mut new_pubkey = msg.onion_routing_packet.public_key.unwrap();
774790

775791
let blinding_factor = {
776792
let mut sha = Sha256::new();
@@ -780,26 +796,19 @@ impl ChannelManager {
780796
sha.result(&mut res);
781797
match SecretKey::from_slice(&self.secp_ctx, &res) {
782798
Err(_) => {
783-
// Return temporary node failure as its technically our issue, not the
784-
// channel's issue.
785-
return_err!("Blinding factor is an invalid private key", 0x2000 | 2, &[0;0]);
799+
return_err!("Blinding factor is an invalid private key", 0x8000 | 0x4000 | 6, &get_onion_hash!());
786800
},
787801
Ok(key) => key
788802
}
789803
};
790804

791-
match new_pubkey.mul_assign(&self.secp_ctx, &blinding_factor) {
792-
Err(_) => {
793-
// Return temporary node failure as its technically our issue, not the
794-
// channel's issue.
795-
return_err!("New blinding factor is an invalid private key", 0x2000 | 2, &[0;0]);
796-
},
797-
Ok(_) => {}
798-
};
805+
if let Err(_) = new_pubkey.mul_assign(&self.secp_ctx, &blinding_factor) {
806+
return_err!("New blinding factor is an invalid private key", 0x8000 | 0x4000 | 6, &get_onion_hash!());
807+
}
799808

800809
let outgoing_packet = msgs::OnionPacket {
801810
version: 0,
802-
public_key: new_pubkey,
811+
public_key: Ok(new_pubkey),
803812
hop_data: new_packet_data,
804813
hmac: next_hop_data.hmac.clone(),
805814
};
@@ -846,7 +855,7 @@ impl ChannelManager {
846855
}
847856
}
848857

849-
(pending_forward_info, shared_secret, channel_state.unwrap())
858+
(pending_forward_info, Some(shared_secret), channel_state.unwrap())
850859
}
851860

852861
/// only fails if the channel does not yet have an assigned short_id
@@ -958,6 +967,7 @@ impl ChannelManager {
958967
update_add_htlcs: vec![update_add],
959968
update_fulfill_htlcs: Vec::new(),
960969
update_fail_htlcs: Vec::new(),
970+
update_fail_malformed_htlcs: Vec::new(),
961971
commitment_signed,
962972
},
963973
});
@@ -1102,6 +1112,7 @@ impl ChannelManager {
11021112
update_add_htlcs: add_htlc_msgs,
11031113
update_fulfill_htlcs: Vec::new(),
11041114
update_fail_htlcs: Vec::new(),
1115+
update_fail_malformed_htlcs: Vec::new(),
11051116
commitment_signed: commitment_msg,
11061117
},
11071118
}));
@@ -1225,6 +1236,7 @@ impl ChannelManager {
12251236
update_add_htlcs: Vec::new(),
12261237
update_fulfill_htlcs: Vec::new(),
12271238
update_fail_htlcs: vec![msg],
1239+
update_fail_malformed_htlcs: Vec::new(),
12281240
commitment_signed: commitment_msg,
12291241
},
12301242
});
@@ -1324,6 +1336,7 @@ impl ChannelManager {
13241336
update_add_htlcs: Vec::new(),
13251337
update_fulfill_htlcs: vec![msg],
13261338
update_fail_htlcs: Vec::new(),
1339+
update_fail_malformed_htlcs: Vec::new(),
13271340
commitment_signed: commitment_msg,
13281341
}
13291342
});
@@ -1722,11 +1735,11 @@ impl ChannelMessageHandler for ChannelManager {
17221735
}
17231736
if !acceptable_cycle {
17241737
log_info!(self, "Failed to accept incoming HTLC: Payment looped through us twice");
1725-
pending_forward_info = PendingHTLCStatus::Fail(msgs::UpdateFailHTLC {
1738+
pending_forward_info = PendingHTLCStatus::Fail(HTLCFailureMsg::Relay(msgs::UpdateFailHTLC {
17261739
channel_id: msg.channel_id,
17271740
htlc_id: msg.htlc_id,
1728-
reason: ChannelManager::build_first_hop_failure_packet(&shared_secret, 0x4000 | 0x2000 | 2, &[0;0]),
1729-
});
1741+
reason: ChannelManager::build_first_hop_failure_packet(&shared_secret.unwrap(), 0x4000 | 0x2000 | 2, &[0;0]),
1742+
}));
17301743
} else {
17311744
will_forward = true;
17321745
}
@@ -1764,15 +1777,15 @@ impl ChannelMessageHandler for ChannelManager {
17641777
};
17651778
*outbound_route = PendingOutboundHTLC::CycledRoute {
17661779
source_short_channel_id,
1767-
incoming_packet_shared_secret: shared_secret,
1780+
incoming_packet_shared_secret: shared_secret.unwrap(),
17681781
route,
17691782
session_priv,
17701783
};
17711784
},
17721785
hash_map::Entry::Vacant(e) => {
17731786
e.insert(PendingOutboundHTLC::IntermediaryHopData {
17741787
source_short_channel_id,
1775-
incoming_packet_shared_secret: shared_secret,
1788+
incoming_packet_shared_secret: shared_secret.unwrap(),
17761789
});
17771790
}
17781791
}
@@ -2487,9 +2500,10 @@ mod tests {
24872500
impl SendEvent {
24882501
fn from_event(event: Event) -> SendEvent {
24892502
match event {
2490-
Event::UpdateHTLCs { node_id, updates: msgs::CommitmentUpdate { update_add_htlcs, update_fulfill_htlcs, update_fail_htlcs, commitment_signed } } => {
2503+
Event::UpdateHTLCs { node_id, updates: msgs::CommitmentUpdate { update_add_htlcs, update_fulfill_htlcs, update_fail_htlcs, update_fail_malformed_htlcs, commitment_signed } } => {
24912504
assert!(update_fulfill_htlcs.is_empty());
24922505
assert!(update_fail_htlcs.is_empty());
2506+
assert!(update_fail_malformed_htlcs.is_empty());
24932507
SendEvent { node_id: node_id, msgs: update_add_htlcs, commitment_msg: commitment_signed }
24942508
},
24952509
_ => panic!("Unexpected event type!"),
@@ -2646,10 +2660,11 @@ mod tests {
26462660
let events = node.node.get_and_clear_pending_events();
26472661
assert_eq!(events.len(), 1);
26482662
match events[0] {
2649-
Event::UpdateHTLCs { ref node_id, updates: msgs::CommitmentUpdate { ref update_add_htlcs, ref update_fulfill_htlcs, ref update_fail_htlcs, ref commitment_signed } } => {
2663+
Event::UpdateHTLCs { ref node_id, updates: msgs::CommitmentUpdate { ref update_add_htlcs, ref update_fulfill_htlcs, ref update_fail_htlcs, ref update_fail_malformed_htlcs, ref commitment_signed } } => {
26502664
assert!(update_add_htlcs.is_empty());
26512665
assert_eq!(update_fulfill_htlcs.len(), 1);
26522666
assert!(update_fail_htlcs.is_empty());
2667+
assert!(update_fail_malformed_htlcs.is_empty());
26532668
expected_next_node = node_id.clone();
26542669
next_msgs = Some((update_fulfill_htlcs[0].clone(), commitment_signed.clone()));
26552670
},
@@ -2770,10 +2785,11 @@ mod tests {
27702785
let events = node.node.get_and_clear_pending_events();
27712786
assert_eq!(events.len(), 1);
27722787
match events[0] {
2773-
Event::UpdateHTLCs { ref node_id, updates: msgs::CommitmentUpdate { ref update_add_htlcs, ref update_fulfill_htlcs, ref update_fail_htlcs, ref commitment_signed } } => {
2788+
Event::UpdateHTLCs { ref node_id, updates: msgs::CommitmentUpdate { ref update_add_htlcs, ref update_fulfill_htlcs, ref update_fail_htlcs, ref update_fail_malformed_htlcs, ref commitment_signed } } => {
27742789
assert!(update_add_htlcs.is_empty());
27752790
assert!(update_fulfill_htlcs.is_empty());
27762791
assert_eq!(update_fail_htlcs.len(), 1);
2792+
assert!(update_fail_malformed_htlcs.is_empty());
27772793
expected_next_node = node_id.clone();
27782794
next_msgs = Some((update_fail_htlcs[0].clone(), commitment_signed.clone()));
27792795
},

0 commit comments

Comments
 (0)