Skip to content

Commit 43d9164

Browse files
committed
Return a malformed HTLC message when ephemeral pubkey is garbage
This resolves a spec-compliance bug with BOLT 4 where we simply failed to deserialize the message and thus could never return an HTLC failure message. However, note that BOLT 4 incorrectly hints that a non-malformed message should be used ("...MUST report a route failure to the origin node") which we cannot do as we cannot derive a SharedSecret to encrypt a regular update_fail_htlc message
1 parent 4ca5bcf commit 43d9164

File tree

6 files changed

+47
-35
lines changed

6 files changed

+47
-35
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_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 & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,14 @@ use lightning::util::reset_rng_state;
88

99
use lightning::ln::msgs::{MsgEncodable, MsgDecodable};
1010

11-
mod utils;
12-
1311
#[inline]
1412
pub fn do_test(data: &[u8]) {
1513
reset_rng_state();
16-
test_msg!(msgs::UpdateAddHTLC, data);
14+
if let Ok(msg) = msgs::UpdateAddHTLC::decode(data){
15+
let enc = msg.encode();
16+
assert_eq!(&data[0..86], &enc[0..86]);
17+
assert_eq!(&data[86+33..enc.len()], &enc[86+33..]);
18+
}
1719
}
1820

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

src/ln/channelmanager.rs

Lines changed: 26 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -625,7 +625,7 @@ impl ChannelManager {
625625

626626
Ok(msgs::OnionPacket{
627627
version: 0,
628-
public_key: onion_keys.first().unwrap().ephemeral_pubkey,
628+
public_key: Ok(onion_keys.first().unwrap().ephemeral_pubkey),
629629
hop_data: packet_data,
630630
hmac: hmac_res,
631631
})
@@ -681,10 +681,7 @@ impl ChannelManager {
681681
ChannelManager::encrypt_failure_packet(shared_secret, &failure_packet.encode()[..])
682682
}
683683

684-
fn decode_update_add_htlc_onion(&self, msg: &msgs::UpdateAddHTLC) -> (PendingHTLCStatus, SharedSecret, MutexGuard<ChannelHolder>) {
685-
let shared_secret = SharedSecret::new(&self.secp_ctx, &msg.onion_routing_packet.public_key, &self.our_network_key);
686-
let (rho, mu) = ChannelManager::gen_rho_mu_from_shared_secret(&shared_secret);
687-
684+
fn decode_update_add_htlc_onion(&self, msg: &msgs::UpdateAddHTLC) -> (PendingHTLCStatus, Option<SharedSecret>, MutexGuard<ChannelHolder>) {
688685
macro_rules! get_onion_hash {
689686
() => {
690687
{
@@ -697,6 +694,19 @@ impl ChannelManager {
697694
}
698695
}
699696

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+
700710
let mut channel_state = None;
701711
macro_rules! return_err {
702712
($msg: expr, $err_code: expr, $data: expr) => {
@@ -709,7 +719,7 @@ impl ChannelManager {
709719
channel_id: msg.channel_id,
710720
htlc_id: msg.htlc_id,
711721
reason: ChannelManager::build_first_hop_failure_packet(&shared_secret, $err_code, $data),
712-
})), shared_secret, channel_state.unwrap());
722+
})), Some(shared_secret), channel_state.unwrap());
713723
}
714724
}
715725
}
@@ -776,7 +786,7 @@ impl ChannelManager {
776786
chacha.process(&msg.onion_routing_packet.hop_data[65..], &mut new_packet_data[0..19*65]);
777787
chacha.process(&ChannelManager::ZERO[0..65], &mut new_packet_data[19*65..]);
778788

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

781791
let blinding_factor = {
782792
let mut sha = Sha256::new();
@@ -786,26 +796,19 @@ impl ChannelManager {
786796
sha.result(&mut res);
787797
match SecretKey::from_slice(&self.secp_ctx, &res) {
788798
Err(_) => {
789-
// Return temporary node failure as its technically our issue, not the
790-
// channel's issue.
791-
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!());
792800
},
793801
Ok(key) => key
794802
}
795803
};
796804

797-
match new_pubkey.mul_assign(&self.secp_ctx, &blinding_factor) {
798-
Err(_) => {
799-
// Return temporary node failure as its technically our issue, not the
800-
// channel's issue.
801-
return_err!("New blinding factor is an invalid private key", 0x2000 | 2, &[0;0]);
802-
},
803-
Ok(_) => {}
804-
};
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+
}
805808

806809
let outgoing_packet = msgs::OnionPacket {
807810
version: 0,
808-
public_key: new_pubkey,
811+
public_key: Ok(new_pubkey),
809812
hop_data: new_packet_data,
810813
hmac: next_hop_data.hmac.clone(),
811814
};
@@ -852,7 +855,7 @@ impl ChannelManager {
852855
}
853856
}
854857

855-
(pending_forward_info, shared_secret, channel_state.unwrap())
858+
(pending_forward_info, Some(shared_secret), channel_state.unwrap())
856859
}
857860

858861
/// only fails if the channel does not yet have an assigned short_id
@@ -1735,7 +1738,7 @@ impl ChannelMessageHandler for ChannelManager {
17351738
pending_forward_info = PendingHTLCStatus::Fail(HTLCFailureMsg::Relay(msgs::UpdateFailHTLC {
17361739
channel_id: msg.channel_id,
17371740
htlc_id: msg.htlc_id,
1738-
reason: ChannelManager::build_first_hop_failure_packet(&shared_secret, 0x4000 | 0x2000 | 2, &[0;0]),
1741+
reason: ChannelManager::build_first_hop_failure_packet(&shared_secret.unwrap(), 0x4000 | 0x2000 | 2, &[0;0]),
17391742
}));
17401743
} else {
17411744
will_forward = true;
@@ -1774,15 +1777,15 @@ impl ChannelMessageHandler for ChannelManager {
17741777
};
17751778
*outbound_route = PendingOutboundHTLC::CycledRoute {
17761779
source_short_channel_id,
1777-
incoming_packet_shared_secret: shared_secret,
1780+
incoming_packet_shared_secret: shared_secret.unwrap(),
17781781
route,
17791782
session_priv,
17801783
};
17811784
},
17821785
hash_map::Entry::Vacant(e) => {
17831786
e.insert(PendingOutboundHTLC::IntermediaryHopData {
17841787
source_short_channel_id,
1785-
incoming_packet_shared_secret: shared_secret,
1788+
incoming_packet_shared_secret: shared_secret.unwrap(),
17861789
});
17871790
}
17881791
}

src/ln/msgs.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use secp256k1::key::PublicKey;
22
use secp256k1::{Secp256k1, Signature};
3+
use secp256k1;
34
use bitcoin::util::hash::Sha256dHash;
45
use bitcoin::network::serialize::{deserialize,serialize};
56
use bitcoin::blockdata::script::Script;
@@ -476,7 +477,10 @@ unsafe impl internal_traits::NoDealloc for OnionHopData{}
476477
#[derive(Clone)]
477478
pub struct OnionPacket {
478479
pub version: u8,
479-
pub public_key: PublicKey,
480+
/// In order to ensure we always return an error on Onion decode in compliance with BOLT 4, we
481+
/// have to deserialize OnionPackets contained in UpdateAddHTLCs even if the ephemeral public
482+
/// key (here) is bogus, so we hold a Result instead of a PublicKey as we'd like.
483+
pub public_key: Result<PublicKey, secp256k1::Error>,
480484
pub hop_data: [u8; 20*65],
481485
pub hmac: [u8; 32],
482486
}
@@ -1538,7 +1542,7 @@ impl MsgDecodable for OnionPacket {
15381542
let secp_ctx = Secp256k1::without_caps();
15391543
Ok(Self {
15401544
version: v[0],
1541-
public_key: secp_pubkey!(&secp_ctx, &v[1..34]),
1545+
public_key: PublicKey::from_slice(&secp_ctx, &v[1..34]),
15421546
hop_data,
15431547
hmac,
15441548
})
@@ -1548,7 +1552,10 @@ impl MsgEncodable for OnionPacket {
15481552
fn encode(&self) -> Vec<u8> {
15491553
let mut res = Vec::with_capacity(1 + 33 + 20*65 + 32);
15501554
res.push(self.version);
1551-
res.extend_from_slice(&self.public_key.serialize());
1555+
match self.public_key {
1556+
Ok(pubkey) => res.extend_from_slice(&pubkey.serialize()),
1557+
Err(_) => res.extend_from_slice(&[0; 33]),
1558+
}
15521559
res.extend_from_slice(&self.hop_data);
15531560
res.extend_from_slice(&self.hmac);
15541561
res

0 commit comments

Comments
 (0)