From 244faa7a70af5325d405d3453712d207a07b7c93 Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Tue, 15 Sep 2020 10:01:27 -0700 Subject: [PATCH 01/72] Squash changes from 494 into a single commit This will make future changes that stabilize this work easier to review and merge. Co-authored-by: Arik Sosman Co-authored-by: Julian Knutsen --- fuzz/src/full_stack.rs | 20 +- lightning-net-tokio/src/lib.rs | 18 +- lightning/src/ln/mod.rs | 2 +- lightning/src/ln/peers/chacha.rs | 40 ++ lightning/src/ln/peers/conduit.rs | 297 ++++++++++++ .../ln/{peer_handler.rs => peers/handler.rs} | 458 ++++++++++-------- lightning/src/ln/peers/handshake/acts.rs | 42 ++ lightning/src/ln/peers/handshake/hash.rs | 25 + lightning/src/ln/peers/handshake/mod.rs | 394 +++++++++++++++ lightning/src/ln/peers/handshake/states.rs | 30 ++ lightning/src/ln/peers/handshake/tests.rs | 39 ++ lightning/src/ln/peers/hkdf.rs | 18 + lightning/src/ln/peers/mod.rs | 10 + 13 files changed, 1174 insertions(+), 219 deletions(-) create mode 100644 lightning/src/ln/peers/chacha.rs create mode 100644 lightning/src/ln/peers/conduit.rs rename lightning/src/ln/{peer_handler.rs => peers/handler.rs} (82%) create mode 100644 lightning/src/ln/peers/handshake/acts.rs create mode 100644 lightning/src/ln/peers/handshake/hash.rs create mode 100644 lightning/src/ln/peers/handshake/mod.rs create mode 100644 lightning/src/ln/peers/handshake/states.rs create mode 100644 lightning/src/ln/peers/handshake/tests.rs create mode 100644 lightning/src/ln/peers/hkdf.rs create mode 100644 lightning/src/ln/peers/mod.rs diff --git a/fuzz/src/full_stack.rs b/fuzz/src/full_stack.rs index 1ed17b9ea3f..f6a07f351a4 100644 --- a/fuzz/src/full_stack.rs +++ b/fuzz/src/full_stack.rs @@ -31,7 +31,7 @@ use lightning::chain::chainmonitor; use lightning::chain::transaction::OutPoint; use lightning::chain::keysinterface::{InMemoryChannelKeys, KeysInterface}; use lightning::ln::channelmanager::{ChannelManager, PaymentHash, PaymentPreimage, PaymentSecret}; -use lightning::ln::peer_handler::{MessageHandler,PeerManager,SocketDescriptor}; +use lightning::ln::peers::handler::{MessageHandler,PeerManager,SocketDescriptor}; use lightning::routing::router::get_route; use lightning::routing::network_graph::NetGraphMsgHandler; use lightning::util::events::{EventsProvider,Event}; @@ -891,15 +891,15 @@ mod tests { super::do_test(&::hex::decode("00000000000000000000000000000000000000000000000000000000000000000000000001000300000000000000000000000000000000000000000000000000000000000000000300320003000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000030012000a0300000000000000000000000000000003001a00100002200000022000030000000000000000000000000000000300120141030000000000000000000000000000000300fe00207500000000000000000000000000000000000000000000000000000000000000ff4f00f805273c1b203bb5ebf8436bfde57b3be8c2f5e95d9491dbb181909679000000000000c35000000000000000000000000000000222ffffffffffffffff00000000000002220000000000000000000000fd000601e3030000000000000000000000000000000000000000000000000000000000000001030000000000000000000000000000000000000000000000000000000000000002030000000000000000000000000000000000000000000000000000000000000003030000000000000000000000000000000000000000000000000000000000000004030053030000000000000000000000000000000000000000000000000000000000000005030000000000000000000000000000000000000000000000000000000000000000010300000000000000000000000000000000fd00fd00fd0300120084030000000000000000000000000000000300940022ff4f00f805273c1b203bb5ebf8436bfde57b3be8c2f5e95d9491dbb1819096793d00000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001030000000000000000000000000000000c005e020000000100000000000000000000000000000000000000000000000000000000000000000000000000ffffffff0150c3000000000000220020ae00000000000000000000000000000000000000000000000000000000000000000000000c00000c00000c00000c00000c00000c00000c00000c00000c00000c00000c00000c000003001200430300000000000000000000000000000003005300243d0000000000000000000000000000000000000000000000000000000000000003010000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000010301320003000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000030142000302000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000003000000000000000000000000000000030112000a0100000000000000000000000000000003011a0010000220000002200001000000000000000000000000000000050103020000000000000000000000000000000000000000000000000000000000000000c3500003e800fd00fd0301120110010000000000000000000000000000000301ff00210000000000000000000000000000000000000000000000000000000000000e02000000000000001a00000000004c4b4000000000000003e800000000000003e80000000203f00005030000000000000000000000000000000000000000000000000000000000000100030000000000000000000000000000000000000000000000000000000000000200030000000000000000000000000000000000000000000000000000000000000300030000000000000000000000000000000000000000000000000000000000000400030000000000000000000000000000000000000000000000000000000000000500030000000000000000000000000000000301210000000000000000000000000000000000010000000000000000000000000000000a03011200620100000000000000000000000000000003017200233900000000000000000000000000000000000000000000000000000000000000f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100010000000000000000000000000000000b030112004301000000000000000000000000000000030153002439000000000000000000000000000000000000000000000000000000000000000301000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000003001205ac030000000000000000000000000000000300ff00803d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003e80ff0000000000000000000000000000000000000000000000000000000000000000000121000300000000000000000000000000000000000000000000000000000000000005550000000e000001000000000000000003e80000007b0000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300c1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff95000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000fd03001200640300000000000000000000000000000003007400843d000000000000000000000000000000000000000000000000000000000000003100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000300000000000000000000000000000003001200630300000000000000000000000000000003007300853d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030200000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000703011200640100000000000000000000000000000003017400843900000000000000000000000000000000000000000000000000000000000000f100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000100000000000000000000000000000003011200630100000000000000000000000000000003017300853900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003020000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000030112004a0100000000000000000000000000000003015a008239000000000000000000000000000000000000000000000000000000000000000000000000000000ff008888888888888888888888888888888888888888888888888888888888880100000000000000000000000000000003011200640100000000000000000000000000000003017400843900000000000000000000000000000000000000000000000000000000000000fd0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000010000000000000000000000000000000301120063010000000000000000000000000000000301730085390000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000303000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000003001205ac030000000000000000000000000000000300ff00803d0000000000000000000000000000000000000000000000000000000000000000000000000000010000000000003e80ff0000000000000000000000000000000000000000000000000000000000000000000121000300000000000000000000000000000000000000000000000000000000000005550000000e000001000000000000000003e80000007b0000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300c1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff95000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000fd03001200630300000000000000000000000000000003007300853d0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000303000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000003001200640300000000000000000000000000000003007400843d00000000000000000000000000000000000000000000000000000000000000c200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000300000000000000000000000000000003001200630300000000000000000000000000000003007300853d000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000030400000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000703011200640100000000000000000000000000000003017400843900000000000000000000000000000000000000000000000000000000000000fc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000100000000000000000000000000000003011200630100000000000000000000000000000003017300853900000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003040000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000030112002c0100000000000000000000000000000003013c00833900000000000000000000000000000000000000000000000000000000000000000000000000000100000100000000000000000000000000000003011200640100000000000000000000000000000003017400843900000000000000000000000000000000000000000000000000000000000000fb000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000001000000000000000000000000000000030112006301000000000000000000000000000000030173008539000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000030500000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000703001200630300000000000000000000000000000003007300853d0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000305000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000003001200640300000000000000000000000000000003007400843d000000000000000000000000000000000000000000000000000000000000003300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000300000000000000000000000000000003001205ac030000000000000000000000000000000300ff00803d00000000000000000000000000000000000000000000000000000000000000000000000000000200000000000b0838ff0000000000000000000000000000000000000000000000000000000000000000000121000300000000000000000000000000000000000000000000000000000000000005550000000e0000010000000000000003e8000000007b0000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300c1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff95000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000fd03001200a4030000000000000000000000000000000300b400843d000000000000000000000000000000000000000000000000000000000000007b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010001c8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007f000000000000000300000000000000000000000000000003001200630300000000000000000000000000000003007300853d00000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000003060000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000070c007d02000000013900000000000000000000000000000000000000000000000000000000000000000000000000000080020001000000000000220020bb000000000000000000000000000000000000000000000000000000000000006cc10000000000001600142b000000000000000000000000000000000000000500002000fd00fd0c005e0200000001a100000000000000000000000000000000000000000000000000000000000000000000000000000000014f00000000000000220020f600000000000000000000000000000000000000000000000000000000000000000000000c00000c000000fd0c00000c00000c000007").unwrap(), &(Arc::clone(&logger) as Arc)); let log_entries = logger.lines.lock().unwrap(); - assert_eq!(log_entries.get(&("lightning::ln::peer_handler".to_string(), "Handling SendAcceptChannel event in peer_handler for node 030000000000000000000000000000000000000000000000000000000000000000 for channel ff4f00f805273c1b203bb5ebf8436bfde57b3be8c2f5e95d9491dbb181909679".to_string())), Some(&1)); // 1 - assert_eq!(log_entries.get(&("lightning::ln::peer_handler".to_string(), "Handling SendFundingSigned event in peer_handler for node 030000000000000000000000000000000000000000000000000000000000000000 for channel 3d00000000000000000000000000000000000000000000000000000000000000".to_string())), Some(&1)); // 2 - assert_eq!(log_entries.get(&("lightning::ln::peer_handler".to_string(), "Handling SendFundingLocked event in peer_handler for node 030000000000000000000000000000000000000000000000000000000000000000 for channel 3d00000000000000000000000000000000000000000000000000000000000000".to_string())), Some(&1)); // 3 - assert_eq!(log_entries.get(&("lightning::ln::peer_handler".to_string(), "Handling SendFundingLocked event in peer_handler for node 030200000000000000000000000000000000000000000000000000000000000000 for channel 3900000000000000000000000000000000000000000000000000000000000000".to_string())), Some(&1)); // 4 - assert_eq!(log_entries.get(&("lightning::ln::peer_handler".to_string(), "Handling SendRevokeAndACK event in peer_handler for node 030000000000000000000000000000000000000000000000000000000000000000 for channel 3d00000000000000000000000000000000000000000000000000000000000000".to_string())), Some(&4)); // 5 - assert_eq!(log_entries.get(&("lightning::ln::peer_handler".to_string(), "Handling UpdateHTLCs event in peer_handler for node 030000000000000000000000000000000000000000000000000000000000000000 with 0 adds, 0 fulfills, 0 fails for channel 3d00000000000000000000000000000000000000000000000000000000000000".to_string())), Some(&3)); // 6 - assert_eq!(log_entries.get(&("lightning::ln::peer_handler".to_string(), "Handling UpdateHTLCs event in peer_handler for node 030200000000000000000000000000000000000000000000000000000000000000 with 1 adds, 0 fulfills, 0 fails for channel 3900000000000000000000000000000000000000000000000000000000000000".to_string())), Some(&3)); // 7 - assert_eq!(log_entries.get(&("lightning::ln::peer_handler".to_string(), "Handling UpdateHTLCs event in peer_handler for node 030000000000000000000000000000000000000000000000000000000000000000 with 0 adds, 1 fulfills, 0 fails for channel 3d00000000000000000000000000000000000000000000000000000000000000".to_string())), Some(&1)); // 8 - assert_eq!(log_entries.get(&("lightning::ln::peer_handler".to_string(), "Handling UpdateHTLCs event in peer_handler for node 030000000000000000000000000000000000000000000000000000000000000000 with 0 adds, 0 fulfills, 1 fails for channel 3d00000000000000000000000000000000000000000000000000000000000000".to_string())), Some(&2)); // 9 + assert_eq!(log_entries.get(&("lightning::ln::peers::handler".to_string(), "Handling SendAcceptChannel event in peer_handler for node 030000000000000000000000000000000000000000000000000000000000000000 for channel ff4f00f805273c1b203bb5ebf8436bfde57b3be8c2f5e95d9491dbb181909679".to_string())), Some(&1)); // 1 + assert_eq!(log_entries.get(&("lightning::ln::peers::handler".to_string(), "Handling SendFundingSigned event in peer_handler for node 030000000000000000000000000000000000000000000000000000000000000000 for channel 3d00000000000000000000000000000000000000000000000000000000000000".to_string())), Some(&1)); // 2 + assert_eq!(log_entries.get(&("lightning::ln::peers::handler".to_string(), "Handling SendFundingLocked event in peer_handler for node 030000000000000000000000000000000000000000000000000000000000000000 for channel 3d00000000000000000000000000000000000000000000000000000000000000".to_string())), Some(&1)); // 3 + assert_eq!(log_entries.get(&("lightning::ln::peers::handler".to_string(), "Handling SendFundingLocked event in peer_handler for node 030200000000000000000000000000000000000000000000000000000000000000 for channel 3900000000000000000000000000000000000000000000000000000000000000".to_string())), Some(&1)); // 4 + assert_eq!(log_entries.get(&("lightning::ln::peers::handler".to_string(), "Handling SendRevokeAndACK event in peer_handler for node 030000000000000000000000000000000000000000000000000000000000000000 for channel 3d00000000000000000000000000000000000000000000000000000000000000".to_string())), Some(&4)); // 5 + assert_eq!(log_entries.get(&("lightning::ln::peers::handler".to_string(), "Handling UpdateHTLCs event in peer_handler for node 030000000000000000000000000000000000000000000000000000000000000000 with 0 adds, 0 fulfills, 0 fails for channel 3d00000000000000000000000000000000000000000000000000000000000000".to_string())), Some(&3)); // 6 + assert_eq!(log_entries.get(&("lightning::ln::peers::handler".to_string(), "Handling UpdateHTLCs event in peer_handler for node 030200000000000000000000000000000000000000000000000000000000000000 with 1 adds, 0 fulfills, 0 fails for channel 3900000000000000000000000000000000000000000000000000000000000000".to_string())), Some(&3)); // 7 + assert_eq!(log_entries.get(&("lightning::ln::peers::handler".to_string(), "Handling UpdateHTLCs event in peer_handler for node 030000000000000000000000000000000000000000000000000000000000000000 with 0 adds, 1 fulfills, 0 fails for channel 3d00000000000000000000000000000000000000000000000000000000000000".to_string())), Some(&1)); // 8 + assert_eq!(log_entries.get(&("lightning::ln::peers::handler".to_string(), "Handling UpdateHTLCs event in peer_handler for node 030000000000000000000000000000000000000000000000000000000000000000 with 0 adds, 0 fulfills, 1 fails for channel 3d00000000000000000000000000000000000000000000000000000000000000".to_string())), Some(&2)); // 9 assert_eq!(log_entries.get(&("lightning::chain::channelmonitor".to_string(), "Input spending counterparty commitment tx (00000000000000000000000000000000000000000000000000000000000000a1:0) in 0000000000000000000000000000000000000000000000000000000000000018 resolves outbound HTLC with payment hash ff00000000000000000000000000000000000000000000000000000000000000 with timeout".to_string())), Some(&1)); // 10 } } diff --git a/lightning-net-tokio/src/lib.rs b/lightning-net-tokio/src/lib.rs index e84ee76229f..8e02b11e719 100644 --- a/lightning-net-tokio/src/lib.rs +++ b/lightning-net-tokio/src/lib.rs @@ -38,7 +38,7 @@ //! type ChainFilter = dyn lightning::chain::Filter; //! type ChainMonitor = lightning::chain::chainmonitor::ChainMonitor, Arc, Arc, Arc>; //! type ChannelManager = lightning::ln::channelmanager::SimpleArcChannelManager; -//! type PeerManager = lightning::ln::peer_handler::SimpleArcPeerManager; +//! type PeerManager = lightning::ln::peers::handler::SimpleArcPeerManager; //! //! // Connect to node with pubkey their_node_id at addr: //! async fn connect_to_node(peer_manager: PeerManager, chain_monitor: Arc, channel_manager: ChannelManager, their_node_id: PublicKey, addr: SocketAddr) { @@ -78,8 +78,8 @@ use tokio::{io, time}; use tokio::sync::mpsc; use tokio::io::{AsyncReadExt, AsyncWrite, AsyncWriteExt}; -use lightning::ln::peer_handler; -use lightning::ln::peer_handler::SocketDescriptor as LnSocketTrait; +use lightning::ln::peers::handler; +use lightning::ln::peers::handler::SocketDescriptor as LnSocketTrait; use lightning::ln::msgs::{ChannelMessageHandler, RoutingMessageHandler}; use lightning::util::logger::Logger; @@ -134,7 +134,7 @@ impl Connection { _ => panic!() } } - async fn schedule_read(peer_manager: Arc, Arc, Arc>>, us: Arc>, mut reader: io::ReadHalf, mut read_wake_receiver: mpsc::Receiver<()>, mut write_avail_receiver: mpsc::Receiver<()>) where + async fn schedule_read(peer_manager: Arc, Arc, Arc>>, us: Arc>, mut reader: io::ReadHalf, mut read_wake_receiver: mpsc::Receiver<()>, mut write_avail_receiver: mpsc::Receiver<()>) where CMH: ChannelMessageHandler + 'static, RMH: RoutingMessageHandler + 'static, L: Logger + 'static + ?Sized { @@ -247,7 +247,7 @@ impl Connection { /// not need to poll the provided future in order to make progress. /// /// See the module-level documentation for how to handle the event_notify mpsc::Sender. -pub fn setup_inbound(peer_manager: Arc, Arc, Arc>>, event_notify: mpsc::Sender<()>, stream: TcpStream) -> impl std::future::Future where +pub fn setup_inbound(peer_manager: Arc, Arc, Arc>>, event_notify: mpsc::Sender<()>, stream: TcpStream) -> impl std::future::Future where CMH: ChannelMessageHandler + 'static, RMH: RoutingMessageHandler + 'static, L: Logger + 'static + ?Sized { @@ -289,7 +289,7 @@ pub fn setup_inbound(peer_manager: Arc(peer_manager: Arc, Arc, Arc>>, event_notify: mpsc::Sender<()>, their_node_id: PublicKey, stream: TcpStream) -> impl std::future::Future where +pub fn setup_outbound(peer_manager: Arc, Arc, Arc>>, event_notify: mpsc::Sender<()>, their_node_id: PublicKey, stream: TcpStream) -> impl std::future::Future where CMH: ChannelMessageHandler + 'static, RMH: RoutingMessageHandler + 'static, L: Logger + 'static + ?Sized { @@ -361,7 +361,7 @@ pub fn setup_outbound(peer_manager: Arc(peer_manager: Arc, Arc, Arc>>, event_notify: mpsc::Sender<()>, their_node_id: PublicKey, addr: SocketAddr) -> Option> where +pub async fn connect_outbound(peer_manager: Arc, Arc, Arc>>, event_notify: mpsc::Sender<()>, their_node_id: PublicKey, addr: SocketAddr) -> Option> where CMH: ChannelMessageHandler + 'static, RMH: RoutingMessageHandler + 'static, L: Logger + 'static + ?Sized { @@ -412,7 +412,7 @@ impl SocketDescriptor { Self { conn, id } } } -impl peer_handler::SocketDescriptor for SocketDescriptor { +impl handler::SocketDescriptor for SocketDescriptor { fn send_data(&mut self, data: &[u8], resume_read: bool) -> usize { // To send data, we take a lock on our Connection to access the WriteHalf of the TcpStream, // writing to it if there's room in the kernel buffer, or otherwise create a new Waker with @@ -504,7 +504,7 @@ impl Hash for SocketDescriptor { mod tests { use lightning::ln::features::*; use lightning::ln::msgs::*; - use lightning::ln::peer_handler::{MessageHandler, PeerManager}; + use lightning::ln::peers::handler::{MessageHandler, PeerManager}; use lightning::util::events::*; use bitcoin::secp256k1::{Secp256k1, SecretKey, PublicKey}; diff --git a/lightning/src/ln/mod.rs b/lightning/src/ln/mod.rs index cd959a74dc5..14e9cb86f48 100644 --- a/lightning/src/ln/mod.rs +++ b/lightning/src/ln/mod.rs @@ -20,7 +20,7 @@ pub mod channelmanager; pub mod msgs; -pub mod peer_handler; +pub mod peers; pub mod chan_utils; pub mod features; pub(crate) mod onchaintx; diff --git a/lightning/src/ln/peers/chacha.rs b/lightning/src/ln/peers/chacha.rs new file mode 100644 index 00000000000..4e8d948706f --- /dev/null +++ b/lightning/src/ln/peers/chacha.rs @@ -0,0 +1,40 @@ +use util::byte_utils; +use util::chacha20poly1305rfc::ChaCha20Poly1305RFC; + +pub const TAG_SIZE: usize = 16; + +pub fn encrypt(key: &[u8], nonce: u64, associated_data: &[u8], plaintext: &[u8]) -> Vec { + let mut nonce_bytes = [0; 12]; + nonce_bytes[4..].copy_from_slice(&byte_utils::le64_to_array(nonce)); + + let mut chacha = ChaCha20Poly1305RFC::new(key, &nonce_bytes, associated_data); + let mut ciphertext = vec![0u8; plaintext.len()]; + let mut authentication_tag = [0u8; 16]; + chacha.encrypt(plaintext, &mut ciphertext, &mut authentication_tag); + + let mut tagged_ciphertext = ciphertext; + tagged_ciphertext.extend_from_slice(&authentication_tag); + tagged_ciphertext +} + +pub fn decrypt(key: &[u8], nonce: u64, associated_data: &[u8], tagged_ciphertext: &[u8]) -> Result, String> { + let mut nonce_bytes = [0; 12]; + nonce_bytes[4..].copy_from_slice(&byte_utils::le64_to_array(nonce)); + + let length = tagged_ciphertext.len(); + if length < 16 { + return Err("ciphertext cannot be shorter than tag length of 16 bytes".to_string()); + } + let end_index = length - 16; + let ciphertext = &tagged_ciphertext[0..end_index]; + let authentication_tag = &tagged_ciphertext[end_index..length]; + + let mut chacha = ChaCha20Poly1305RFC::new(key, &nonce_bytes, associated_data); + let mut plaintext = vec![0u8; length - 16]; + let success = chacha.decrypt(ciphertext, &mut plaintext, authentication_tag); + if success { + Ok(plaintext.to_vec()) + } else { + Err("invalid hmac".to_string()) + } +} diff --git a/lightning/src/ln/peers/conduit.rs b/lightning/src/ln/peers/conduit.rs new file mode 100644 index 00000000000..74e420b192a --- /dev/null +++ b/lightning/src/ln/peers/conduit.rs @@ -0,0 +1,297 @@ +//! Handles all over the wire message encryption and decryption upon handshake completion. + +use ln::peers::{chacha, hkdf}; +use util::byte_utils; + +pub(super) type SymmetricKey = [u8; 32]; + +const MESSAGE_LENGTH_HEADER_SIZE: usize = 2; +const TAGGED_MESSAGE_LENGTH_HEADER_SIZE: usize = MESSAGE_LENGTH_HEADER_SIZE + chacha::TAG_SIZE; + +const KEY_ROTATION_INDEX: u32 = 1000; + +/// Returned after a successful handshake to encrypt and decrypt communication with peer nodes. +/// It should not normally be manually instantiated. +/// Automatically handles key rotation. +/// For decryption, it is recommended to call `decrypt_message_stream` for automatic buffering. +pub struct Conduit { + pub(super) encryptor: Encryptor, + pub(super) decryptor: Decryptor + +} + +pub(super) struct Encryptor { + sending_key: SymmetricKey, + sending_chaining_key: SymmetricKey, + sending_nonce: u32, +} + +pub(super) struct Decryptor { + receiving_key: SymmetricKey, + receiving_chaining_key: SymmetricKey, + receiving_nonce: u32, + + pending_message_length: Option, + read_buffer: Option>, +} + +impl Iterator for Decryptor { + type Item = Vec; + + fn next(&mut self) -> Option { + self.decrypt_single_message(None) + } +} + +impl Conduit { + /// Instantiate a new Conduit with specified sending and receiving keys + pub fn new(sending_key: SymmetricKey, receiving_key: SymmetricKey, chaining_key: SymmetricKey) -> Self { + Conduit { + encryptor: Encryptor { + sending_key, + sending_chaining_key: chaining_key, + sending_nonce: 0 + }, + decryptor: Decryptor { + receiving_key, + receiving_chaining_key: chaining_key, + receiving_nonce: 0, + read_buffer: None, + pending_message_length: None + } + } + } + + /// Encrypt data to be sent to peer + pub fn encrypt(&mut self, buffer: &[u8]) -> Vec { + self.encryptor.encrypt(buffer) + } + + pub(super) fn read(&mut self, data: &[u8]) { + self.decryptor.read(data) + } + + /// Decrypt a single message. If data containing more than one message has been received, + /// only the first message will be returned, and the rest stored in the internal buffer. + /// If a message pending in the buffer still hasn't been decrypted, that message will be + /// returned in lieu of anything new, even if new data is provided. + pub fn decrypt_single_message(&mut self, new_data: Option<&[u8]>) -> Option> { + self.decryptor.decrypt_single_message(new_data) + } + + /// Decrypt a message from the beginning of the provided buffer. Returns the consumed number of bytes. + fn decrypt(&mut self, buffer: &[u8]) -> (Option>, usize) { + self.decryptor.decrypt(buffer) + } + + fn increment_sending_nonce(&mut self) { + self.encryptor.increment_nonce() + } + + fn increment_receiving_nonce(&mut self) { + self.decryptor.increment_nonce() + } + + fn increment_nonce(nonce: &mut u32, chaining_key: &mut SymmetricKey, key: &mut SymmetricKey) { + *nonce += 1; + if *nonce == KEY_ROTATION_INDEX { + Self::rotate_key(chaining_key, key); + *nonce = 0; + } + } + + fn rotate_key(chaining_key: &mut SymmetricKey, key: &mut SymmetricKey) { + let (new_chaining_key, new_key) = hkdf::derive(chaining_key, key); + chaining_key.copy_from_slice(&new_chaining_key); + key.copy_from_slice(&new_key); + } +} + +impl Encryptor { + pub(super) fn encrypt(&mut self, buffer: &[u8]) -> Vec { + let length = buffer.len() as u16; + let length_bytes = byte_utils::be16_to_array(length); + + let mut ciphertext = vec![0u8; TAGGED_MESSAGE_LENGTH_HEADER_SIZE + length as usize + chacha::TAG_SIZE]; + + ciphertext[0..TAGGED_MESSAGE_LENGTH_HEADER_SIZE].copy_from_slice(&chacha::encrypt(&self.sending_key, self.sending_nonce as u64, &[0; 0], &length_bytes)); + self.increment_nonce(); + + ciphertext[TAGGED_MESSAGE_LENGTH_HEADER_SIZE..].copy_from_slice(&chacha::encrypt(&self.sending_key, self.sending_nonce as u64, &[0; 0], buffer)); + self.increment_nonce(); + + ciphertext + } + + fn increment_nonce(&mut self) { + Conduit::increment_nonce(&mut self.sending_nonce, &mut self.sending_chaining_key, &mut self.sending_key); + } +} + +impl Decryptor { + pub(super) fn read(&mut self, data: &[u8]) { + let read_buffer = self.read_buffer.get_or_insert(Vec::new()); + read_buffer.extend_from_slice(data); + } + + /// Decrypt a single message. If data containing more than one message has been received, + /// only the first message will be returned, and the rest stored in the internal buffer. + /// If a message pending in the buffer still hasn't been decrypted, that message will be + /// returned in lieu of anything new, even if new data is provided. + pub fn decrypt_single_message(&mut self, new_data: Option<&[u8]>) -> Option> { + let mut read_buffer = if let Some(buffer) = self.read_buffer.take() { + buffer + } else { + Vec::new() + }; + + if let Some(data) = new_data { + read_buffer.extend_from_slice(data); + } + + let (current_message, offset) = self.decrypt(&read_buffer[..]); + read_buffer.drain(..offset); // drain the read buffer + self.read_buffer = Some(read_buffer); // assign the new value to the built-in buffer + current_message + } + + fn decrypt(&mut self, buffer: &[u8]) -> (Option>, usize) { + let message_length = if let Some(length) = self.pending_message_length { + // we have already decrypted the header + length + } else { + if buffer.len() < TAGGED_MESSAGE_LENGTH_HEADER_SIZE { + // A message must be at least 18 bytes (2 for encrypted length, 16 for the tag) + return (None, 0); + } + + let encrypted_length = &buffer[0..TAGGED_MESSAGE_LENGTH_HEADER_SIZE]; + let mut length_bytes = [0u8; MESSAGE_LENGTH_HEADER_SIZE]; + length_bytes.copy_from_slice(&chacha::decrypt(&self.receiving_key, self.receiving_nonce as u64, &[0; 0], encrypted_length).unwrap()); + + self.increment_nonce(); + + // the message length + byte_utils::slice_to_be16(&length_bytes) as usize + }; + + let message_end_index = TAGGED_MESSAGE_LENGTH_HEADER_SIZE + message_length + chacha::TAG_SIZE; + + if buffer.len() < message_end_index { + self.pending_message_length = Some(message_length); + return (None, 0); + } + + self.pending_message_length = None; + + let encrypted_message = &buffer[TAGGED_MESSAGE_LENGTH_HEADER_SIZE..message_end_index]; + + let message = chacha::decrypt(&self.receiving_key, self.receiving_nonce as u64, &[0; 0], encrypted_message).unwrap(); + + self.increment_nonce(); + + (Some(message), message_end_index) + } + + fn increment_nonce(&mut self) { + Conduit::increment_nonce(&mut self.receiving_nonce, &mut self.receiving_chaining_key, &mut self.receiving_key); + } +} + +#[cfg(test)] +mod tests { + use hex; + + use ln::peers::conduit::Conduit; + + fn setup_peers() -> (Conduit, Conduit) { + let chaining_key_vec = hex::decode("919219dbb2920afa8db80f9a51787a840bcf111ed8d588caf9ab4be716e42b01").unwrap(); + let mut chaining_key = [0u8; 32]; + chaining_key.copy_from_slice(&chaining_key_vec); + + let sending_key_vec = hex::decode("969ab31b4d288cedf6218839b27a3e2140827047f2c0f01bf5c04435d43511a9").unwrap(); + let mut sending_key = [0u8; 32]; + sending_key.copy_from_slice(&sending_key_vec); + + let receiving_key_vec = hex::decode("bb9020b8965f4df047e07f955f3c4b88418984aadc5cdb35096b9ea8fa5c3442").unwrap(); + let mut receiving_key = [0u8; 32]; + receiving_key.copy_from_slice(&receiving_key_vec); + + let connected_peer = Conduit::new(sending_key, receiving_key, chaining_key); + let remote_peer = Conduit::new(receiving_key, sending_key, chaining_key); + + (connected_peer, remote_peer) + } + + #[test] + fn test_empty_message() { + let (mut connected_peer, mut remote_peer) = setup_peers(); + + let message: Vec = vec![]; + let encrypted_message = connected_peer.encrypt(&message); + assert_eq!(encrypted_message.len(), 2 + 16 + 16); + + let decrypted_message = remote_peer.decrypt_single_message(Some(&encrypted_message)).unwrap(); + assert_eq!(decrypted_message, vec![]); + } + + #[test] + fn test_nonce_chaining() { + let (mut connected_peer, mut remote_peer) = setup_peers(); + let message = hex::decode("68656c6c6f").unwrap(); + + let encrypted_message = connected_peer.encrypt(&message); + assert_eq!(encrypted_message, hex::decode("cf2b30ddf0cf3f80e7c35a6e6730b59fe802473180f396d88a8fb0db8cbcf25d2f214cf9ea1d95").unwrap()); + + // the second time the same message is encrypted, the ciphertext should be different + let encrypted_message = connected_peer.encrypt(&message); + assert_eq!(encrypted_message, hex::decode("72887022101f0b6753e0c7de21657d35a4cb2a1f5cde2650528bbc8f837d0f0d7ad833b1a256a1").unwrap()); + } + + #[test] + /// Based on RFC test vectors: https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#message-encryption-tests + fn test_key_rotation() { + let (mut connected_peer, mut remote_peer) = setup_peers(); + + let message = hex::decode("68656c6c6f").unwrap(); + let mut encrypted_messages: Vec> = Vec::new(); + + for _ in 0..1002 { + let encrypted_message = connected_peer.encrypt(&message); + encrypted_messages.push(encrypted_message); + } + + assert_eq!(encrypted_messages[500], hex::decode("178cb9d7387190fa34db9c2d50027d21793c9bc2d40b1e14dcf30ebeeeb220f48364f7a4c68bf8").unwrap()); + assert_eq!(encrypted_messages[501], hex::decode("1b186c57d44eb6de4c057c49940d79bb838a145cb528d6e8fd26dbe50a60ca2c104b56b60e45bd").unwrap()); + assert_eq!(encrypted_messages[1000], hex::decode("4a2f3cc3b5e78ddb83dcb426d9863d9d9a723b0337c89dd0b005d89f8d3c05c52b76b29b740f09").unwrap()); + assert_eq!(encrypted_messages[1001], hex::decode("2ecd8c8a5629d0d02ab457a0fdd0f7b90a192cd46be5ecb6ca570bfc5e268338b1a16cf4ef2d36").unwrap()); + } + + #[test] + fn test_decryption_buffering() { + let (mut connected_peer, mut remote_peer) = setup_peers(); + + let message = hex::decode("68656c6c6f").unwrap(); + let mut encrypted_messages: Vec> = Vec::new(); + + for _ in 0..1002 { + let encrypted_message = connected_peer.encrypt(&message); + encrypted_messages.push(encrypted_message); + } + + for _ in 0..501 { + // read two messages at once, filling buffer + let mut current_encrypted_message = encrypted_messages.remove(0); + let mut next_encrypted_message = encrypted_messages.remove(0); + current_encrypted_message.extend_from_slice(&next_encrypted_message); + let decrypted_message = remote_peer.decrypt_single_message(Some(¤t_encrypted_message)).unwrap(); + assert_eq!(decrypted_message, message); + } + + for _ in 0..501 { + // decrypt messages directly from buffer without adding to it + let decrypted_message = remote_peer.decrypt_single_message(None).unwrap(); + assert_eq!(decrypted_message, message); + } + } +} \ No newline at end of file diff --git a/lightning/src/ln/peer_handler.rs b/lightning/src/ln/peers/handler.rs similarity index 82% rename from lightning/src/ln/peer_handler.rs rename to lightning/src/ln/peers/handler.rs index edb2084c716..e9c2becd0fb 100644 --- a/lightning/src/ln/peer_handler.rs +++ b/lightning/src/ln/peers/handler.rs @@ -39,6 +39,9 @@ use std::ops::Deref; use bitcoin::hashes::sha256::Hash as Sha256; use bitcoin::hashes::sha256::HashEngine as Sha256Engine; use bitcoin::hashes::{HashEngine, Hash}; +use ln::peers::handshake::PeerHandshake; +use ln::peers::conduit::Conduit; +use ln::peers::handshake::acts::Act; /// Provides references to trait impls which handle different types of messages. pub struct MessageHandler where @@ -119,8 +122,66 @@ enum InitSyncTracker{ NodesSyncing(PublicKey), } +enum PeerState { + Authenticating(PeerHandshake), + Connected(Conduit), +} + +enum PeerDataProcessingDecision { + CompleteHandshake(bool, Option), + Continue, + Disconnect(PeerHandleError) +} + +impl PeerState { + fn is_ready_for_encryption(&self) -> bool { + match self { + &PeerState::Connected(_) => true, + _ => false + } + } + + fn process_peer_data(&mut self, data: &[u8], mutable_response_buffer: &mut LinkedList>) -> PeerDataProcessingDecision { + let mut conduit_option = None; + let mut decision_option = None; + + match self { + &mut PeerState::Authenticating(ref mut handshake) => { + let (next_act, conduit) = match handshake.process_act(data) { + Ok(act_result) => act_result, + Err(e) => { + return PeerDataProcessingDecision::Disconnect(PeerHandleError { no_connection_possible: false }); + } + }; + + let requires_response = next_act.is_some(); + if let Some(act) = next_act { + mutable_response_buffer.push_back(act.serialize()); + } + + let remote_pubkey_option = handshake.get_remote_pubkey(); + if let Some(conduit) = conduit { + conduit_option = Some(conduit); + decision_option = Some(PeerDataProcessingDecision::CompleteHandshake(requires_response, remote_pubkey_option)); + } + } + + &mut PeerState::Connected(ref mut conduit) => { + conduit.read(data); + } + }; + + if let (Some(conduit), Some(decision)) = (conduit_option, decision_option) { + *self = PeerState::Connected(conduit); + return decision; + } + + PeerDataProcessingDecision::Continue + } +} + struct Peer { - channel_encryptor: PeerChannelEncryptor, + encryptor: PeerState, outbound: bool, their_node_id: Option, their_features: Option, @@ -129,10 +190,6 @@ struct Peer { pending_outbound_buffer_first_msg_offset: usize, awaiting_write_event: bool, - pending_read_buffer: Vec, - pending_read_buffer_pos: usize, - pending_read_is_header: bool, - sync_status: InitSyncTracker, awaiting_pong: bool, @@ -279,7 +336,7 @@ impl PeerManager Vec { let peers = self.peers.lock().unwrap(); peers.peers.values().filter_map(|p| { - if !p.channel_encryptor.is_ready_for_encryption() || p.their_features.is_none() { + if !p.encryptor.is_ready_for_encryption() || p.their_features.is_none() { return None; } p.their_node_id @@ -308,25 +365,21 @@ impl PeerManager Result, PeerHandleError> { - let mut peer_encryptor = PeerChannelEncryptor::new_outbound(their_node_id.clone(), self.get_ephemeral_key()); - let res = peer_encryptor.get_act_one().to_vec(); - let pending_read_buffer = [0; 50].to_vec(); // Noise act two is 50 bytes + let mut handshake = PeerHandshake::new_outbound(&self.our_node_secret, &their_node_id, &self.get_ephemeral_key()); + let (act, ..) = handshake.process_act(&[]).unwrap(); + let res = act.unwrap().serialize(); let mut peers = self.peers.lock().unwrap(); if peers.peers.insert(descriptor, Peer { - channel_encryptor: peer_encryptor, + encryptor: PeerState::Authenticating(handshake), outbound: true, - their_node_id: None, + their_node_id: Some(their_node_id.clone()), their_features: None, pending_outbound_buffer: LinkedList::new(), pending_outbound_buffer_first_msg_offset: 0, awaiting_write_event: false, - pending_read_buffer: pending_read_buffer, - pending_read_buffer_pos: 0, - pending_read_is_header: false, - sync_status: InitSyncTracker::NoSyncRequested, awaiting_pong: false, @@ -346,12 +399,11 @@ impl PeerManager Result<(), PeerHandleError> { - let peer_encryptor = PeerChannelEncryptor::new_inbound(&self.our_node_secret); - let pending_read_buffer = [0; 50].to_vec(); // Noise act one is 50 bytes + let handshake = PeerHandshake::new_inbound(&self.our_node_secret, &self.get_ephemeral_key()); let mut peers = self.peers.lock().unwrap(); if peers.peers.insert(descriptor, Peer { - channel_encryptor: peer_encryptor, + encryptor: PeerState::Authenticating(handshake), outbound: false, their_node_id: None, their_features: None, @@ -360,10 +412,6 @@ impl PeerManager PeerManager { { log_trace!(self.logger, "Encoding and sending sync update message of type {} to {}", $msg.type_id(), log_pubkey!(peer.their_node_id.unwrap())); - peer.pending_outbound_buffer.push_back(peer.channel_encryptor.encrypt_message(&encode_msg!($msg)[..])); + match peer.encryptor { + PeerState::Connected(ref mut conduit) => peer.pending_outbound_buffer.push_back(conduit.encrypt(&encode_msg!($msg)[..])), + _ => panic!("peer must be connected!") + } } } } @@ -501,7 +552,10 @@ impl PeerManager peer.pending_outbound_buffer.push_back(conduit.encrypt(&encoded_message[..])), + _ => panic!("peer must be connected!") + } peers_needing_send.insert(descriptor); } @@ -512,145 +566,118 @@ impl PeerManager panic!("Descriptor for read_event is not already known to PeerManager"), Some(peer) => { - assert!(peer.pending_read_buffer.len() > 0); - assert!(peer.pending_read_buffer.len() > peer.pending_read_buffer_pos); - let mut read_pos = 0; - while read_pos < data.len() { - { - let data_to_copy = cmp::min(peer.pending_read_buffer.len() - peer.pending_read_buffer_pos, data.len() - read_pos); - peer.pending_read_buffer[peer.pending_read_buffer_pos..peer.pending_read_buffer_pos + data_to_copy].copy_from_slice(&data[read_pos..read_pos + data_to_copy]); - read_pos += data_to_copy; - peer.pending_read_buffer_pos += data_to_copy; + let mut send_init_message = false; + + let data_processing_decision = peer.encryptor.process_peer_data(data, &mut peer.pending_outbound_buffer); + match data_processing_decision { + PeerDataProcessingDecision::Disconnect(e) => { + log_trace!(self.logger, "Invalid act message; disconnecting: {}", e); + return Err(e); } - if peer.pending_read_buffer_pos == peer.pending_read_buffer.len() { - peer.pending_read_buffer_pos = 0; - - macro_rules! try_potential_handleerror { - ($thing: expr) => { - match $thing { - Ok(x) => x, - Err(e) => { - match e.action { - msgs::ErrorAction::DisconnectPeer { msg: _ } => { - //TODO: Try to push msg - log_trace!(self.logger, "Got Err handling message, disconnecting peer because {}", e.err); - return Err(PeerHandleError{ no_connection_possible: false }); - }, - msgs::ErrorAction::IgnoreError => { - log_trace!(self.logger, "Got Err handling message, ignoring because {}", e.err); - continue; - }, - msgs::ErrorAction::SendErrorMessage { msg } => { - log_trace!(self.logger, "Got Err handling message, sending Error message because {}", e.err); - self.enqueue_message(&mut peers.peers_needing_send, peer, peer_descriptor.clone(), &msg); - continue; - }, - } - } - }; - } + PeerDataProcessingDecision::CompleteHandshake(needs_to_send_init_message, remote_pubkey_option) => { + send_init_message = needs_to_send_init_message; + + if let Some(key) = remote_pubkey_option { + peer.their_node_id = Some(key); } - macro_rules! insert_node_id { - () => { - match peers.node_id_to_descriptor.entry(peer.their_node_id.unwrap()) { - hash_map::Entry::Occupied(_) => { - log_trace!(self.logger, "Got second connection with {}, closing", log_pubkey!(peer.their_node_id.unwrap())); - peer.their_node_id = None; // Unset so that we don't generate a peer_disconnected event - return Err(PeerHandleError{ no_connection_possible: false }) - }, - hash_map::Entry::Vacant(entry) => { - log_trace!(self.logger, "Finished noise handshake for connection with {}", log_pubkey!(peer.their_node_id.unwrap())); - entry.insert(peer_descriptor.clone()) - }, - }; + // insert node id + match peers.node_id_to_descriptor.entry(peer.their_node_id.unwrap()) { + hash_map::Entry::Occupied(_) => { + log_trace!(self.logger, "Got second connection with {}, closing", log_pubkey!(peer.their_node_id.unwrap())); + peer.their_node_id = None; // Unset so that we don't generate a peer_disconnected event + return Err(PeerHandleError { no_connection_possible: false }); } - } + hash_map::Entry::Vacant(entry) => { + log_trace!(self.logger, "Finished noise handshake for connection with {}", log_pubkey!(peer.their_node_id.unwrap())); + entry.insert(peer_descriptor.clone()) + } + }; + } + _ => {} + }; - let next_step = peer.channel_encryptor.get_noise_step(); - match next_step { - NextNoiseStep::ActOne => { - let act_two = try_potential_handleerror!(peer.channel_encryptor.process_act_one_with_keys(&peer.pending_read_buffer[..], &self.our_node_secret, self.get_ephemeral_key())).to_vec(); - peer.pending_outbound_buffer.push_back(act_two); - peer.pending_read_buffer = [0; 66].to_vec(); // act three is 66 bytes long - }, - NextNoiseStep::ActTwo => { - let (act_three, their_node_id) = try_potential_handleerror!(peer.channel_encryptor.process_act_two(&peer.pending_read_buffer[..], &self.our_node_secret)); - peer.pending_outbound_buffer.push_back(act_three.to_vec()); - peer.pending_read_buffer = [0; 18].to_vec(); // Message length header is 18 bytes - peer.pending_read_is_header = true; - - peer.their_node_id = Some(their_node_id); - insert_node_id!(); - let mut features = InitFeatures::known(); - if !self.message_handler.route_handler.should_request_full_sync(&peer.their_node_id.unwrap()) { - features.clear_initial_routing_sync(); - } + if send_init_message { + let mut features = InitFeatures::known(); + if !self.message_handler.route_handler.should_request_full_sync(&peer.their_node_id.unwrap()) { + features.clear_initial_routing_sync(); + } - let resp = msgs::Init { features }; - self.enqueue_message(&mut peers.peers_needing_send, peer, peer_descriptor.clone(), &resp); - }, - NextNoiseStep::ActThree => { - let their_node_id = try_potential_handleerror!(peer.channel_encryptor.process_act_three(&peer.pending_read_buffer[..])); - peer.pending_read_buffer = [0; 18].to_vec(); // Message length header is 18 bytes - peer.pending_read_is_header = true; - peer.their_node_id = Some(their_node_id); - insert_node_id!(); - }, - NextNoiseStep::NoiseComplete => { - if peer.pending_read_is_header { - let msg_len = try_potential_handleerror!(peer.channel_encryptor.decrypt_length_header(&peer.pending_read_buffer[..])); - peer.pending_read_buffer = Vec::with_capacity(msg_len as usize + 16); - peer.pending_read_buffer.resize(msg_len as usize + 16, 0); - if msg_len < 2 { // Need at least the message type tag - return Err(PeerHandleError{ no_connection_possible: false }); + let resp = msgs::Init { features }; + self.enqueue_message(&mut peers.peers_needing_send, peer, peer_descriptor.clone(), &resp); + send_init_message = false + } + + let mut received_messages = vec![]; + if let &mut PeerState::Connected(ref mut conduit) = &mut peer.encryptor { + let encryptor = &mut conduit.encryptor; + let decryptor = &mut conduit.decryptor; + + for msg_data in decryptor { + let mut reader = ::std::io::Cursor::new(&msg_data[..]); + let message_result = wire::read(&mut reader); + let message = match message_result { + Ok(x) => x, + Err(e) => { + match e { + msgs::DecodeError::UnknownVersion => return Err(PeerHandleError { no_connection_possible: false }), + msgs::DecodeError::UnknownRequiredFeature => { + log_debug!(self.logger, "Got a channel/node announcement with an known required feature flag, you may want to update!"); + continue; } - peer.pending_read_is_header = false; - } else { - let msg_data = try_potential_handleerror!(peer.channel_encryptor.decrypt_message(&peer.pending_read_buffer[..])); - assert!(msg_data.len() >= 2); - - // Reset read buffer - peer.pending_read_buffer = [0; 18].to_vec(); - peer.pending_read_is_header = true; - - let mut reader = ::std::io::Cursor::new(&msg_data[..]); - let message_result = wire::read(&mut reader); - let message = match message_result { - Ok(x) => x, - Err(e) => { - match e { - msgs::DecodeError::UnknownVersion => return Err(PeerHandleError { no_connection_possible: false }), - msgs::DecodeError::UnknownRequiredFeature => { - log_debug!(self.logger, "Got a channel/node announcement with an known required feature flag, you may want to update!"); - continue; - } - msgs::DecodeError::InvalidValue => { - log_debug!(self.logger, "Got an invalid value while deserializing message"); - return Err(PeerHandleError { no_connection_possible: false }); - } - msgs::DecodeError::ShortRead => { - log_debug!(self.logger, "Deserialization failed due to shortness of message"); - return Err(PeerHandleError { no_connection_possible: false }); - } - msgs::DecodeError::BadLengthDescriptor => return Err(PeerHandleError { no_connection_possible: false }), - msgs::DecodeError::Io(_) => return Err(PeerHandleError { no_connection_possible: false }), - } - } - }; - - if let Err(handling_error) = self.handle_message(&mut peers.peers_needing_send, peer, peer_descriptor.clone(), message){ - match handling_error { - MessageHandlingError::PeerHandleError(e) => { return Err(e) }, - MessageHandlingError::LightningError(e) => { - try_potential_handleerror!(Err(e)); - }, - } + msgs::DecodeError::InvalidValue => { + log_debug!(self.logger, "Got an invalid value while deserializing message"); + return Err(PeerHandleError { no_connection_possible: false }); + } + msgs::DecodeError::ShortRead => { + log_debug!(self.logger, "Deserialization failed due to shortness of message"); + return Err(PeerHandleError { no_connection_possible: false }); } + msgs::DecodeError::BadLengthDescriptor => return Err(PeerHandleError { no_connection_possible: false }), + msgs::DecodeError::Io(_) => return Err(PeerHandleError { no_connection_possible: false }), } } + }; + + received_messages.push(message); + } + } + + for message in received_messages { + macro_rules! try_potential_handleerror { + ($thing: expr) => { + match $thing { + Ok(x) => x, + Err(e) => { + match e.action { + msgs::ErrorAction::DisconnectPeer { msg: _ } => { + //TODO: Try to push msg + log_trace!(self.logger, "Got Err handling message, disconnecting peer because {}", e.err); + return Err(PeerHandleError{ no_connection_possible: false }); + }, + msgs::ErrorAction::IgnoreError => { + log_trace!(self.logger, "Got Err handling message, ignoring because {}", e.err); + continue; + }, + msgs::ErrorAction::SendErrorMessage { msg } => { + log_trace!(self.logger, "Got Err handling message, sending Error message because {}", e.err); + self.enqueue_message(&mut peers.peers_needing_send, peer, peer_descriptor.clone(), &msg); + continue; + }, + } + } + }; + } + } + + if let Err(handling_error) = self.handle_message(&mut peers.peers_needing_send, peer, peer_descriptor.clone(), message){ + match handling_error { + MessageHandlingError::PeerHandleError(e) => { return Err(e) }, + MessageHandlingError::LightningError(e) => { + try_potential_handleerror!(Err(e)); + }, } } } @@ -898,7 +925,9 @@ impl PeerManager { @@ -908,7 +937,9 @@ impl PeerManager { @@ -920,7 +951,9 @@ impl PeerManager { @@ -931,7 +964,9 @@ impl PeerManager { @@ -941,7 +976,9 @@ impl PeerManager { @@ -952,7 +989,9 @@ impl PeerManager { @@ -965,22 +1004,24 @@ impl PeerManager { @@ -990,7 +1031,9 @@ impl PeerManager { @@ -1000,7 +1043,9 @@ impl PeerManager { @@ -1010,7 +1055,9 @@ impl PeerManager { @@ -1020,7 +1067,9 @@ impl PeerManager { @@ -1030,8 +1079,8 @@ impl PeerManager PeerManager PeerManager PeerManager PeerManager PeerManager PeerManager PeerManager Vec { + match self { + &Act::One(ref act) => { + act.0.to_vec() + } + &Act::Two(ref act) => { + act.0.to_vec() + } + &Act::Three(ref act) => { + act.0.to_vec() + } + } + } +} \ No newline at end of file diff --git a/lightning/src/ln/peers/handshake/hash.rs b/lightning/src/ln/peers/handshake/hash.rs new file mode 100644 index 00000000000..e7704b37e34 --- /dev/null +++ b/lightning/src/ln/peers/handshake/hash.rs @@ -0,0 +1,25 @@ +use bitcoin::hashes::{Hash, HashEngine}; +use bitcoin::hashes::sha256::Hash as Sha256; + +pub(crate) struct HandshakeHash { + pub(super) value: [u8; 32] +} + +impl HandshakeHash { + pub(super) fn new(first_input: &[u8]) -> Self { + let mut hash = Self { + value: [0; 32] + }; + let mut sha = Sha256::engine(); + sha.input(first_input); + hash.value = Sha256::from_engine(sha).into_inner(); + hash + } + + pub(super) fn update(&mut self, input: &[u8]) { + let mut sha = Sha256::engine(); + sha.input(self.value.as_ref()); + sha.input(input); + self.value = Sha256::from_engine(sha).into_inner(); + } +} diff --git a/lightning/src/ln/peers/handshake/mod.rs b/lightning/src/ln/peers/handshake/mod.rs new file mode 100644 index 00000000000..32efc969a22 --- /dev/null +++ b/lightning/src/ln/peers/handshake/mod.rs @@ -0,0 +1,394 @@ +//! Execute handshakes for peer-to-peer connection establishment. +//! Handshake states can be advanced automatically, or by manually calling the appropriate step. +//! Once complete, returns an instance of Conduit. + +use bitcoin::secp256k1; + +use bitcoin::hashes::{Hash, HashEngine}; +use bitcoin::hashes::sha256::Hash as Sha256; +use bitcoin::secp256k1::{PublicKey, SecretKey}; + +use ln::peers::{chacha, hkdf}; +use ln::peers::conduit::{Conduit, SymmetricKey}; +use ln::peers::handshake::acts::{ActOne, ActThree, ActTwo, ACT_ONE_LENGTH, ACT_TWO_LENGTH, ACT_THREE_LENGTH, Act}; +use ln::peers::handshake::hash::HandshakeHash; +use ln::peers::handshake::states::{ActOneExpectation, ActThreeExpectation, ActTwoExpectation, HandshakeState}; + +pub(crate) mod acts; +mod hash; +mod states; +mod tests; + +/// Object for managing handshakes. +/// Currently requires explicit ephemeral private key specification. +pub struct PeerHandshake { + state: Option, + private_key: SecretKey, + remote_public_key: Option, + ephemeral_private_key: SecretKey, + + read_buffer: Vec, +} + +impl PeerHandshake { + /// Instantiate a new handshake with a node identity secret key and an ephemeral private key + pub fn new_outbound(private_key: &SecretKey, remote_public_key: &PublicKey, ephemeral_private_key: &SecretKey) -> Self { + Self { + state: Some(HandshakeState::Uninitiated), + private_key: (*private_key).clone(), + remote_public_key: Some(remote_public_key.clone()), + ephemeral_private_key: (*ephemeral_private_key).clone(), + read_buffer: Vec::new(), + } + } + + /// Instantiate a new handshake in anticipation of a peer's first handshake act + pub fn new_inbound(private_key: &SecretKey, ephemeral_private_key: &SecretKey) -> Self { + let mut handshake = Self { + state: Some(HandshakeState::Uninitiated), + private_key: (*private_key).clone(), + remote_public_key: None, + ephemeral_private_key: (*ephemeral_private_key).clone(), + read_buffer: Vec::new(), + }; + let public_key = Self::private_key_to_public_key(&private_key); + let (hash, chaining_key) = Self::initialize_state(&public_key); + handshake.state = Some(HandshakeState::AwaitingActOne(ActOneExpectation { + hash, + chaining_key, + })); + handshake + } + + /// Return the remote public key once it has been extracted from the third act. + /// Potentially useful for inbound connections + pub fn get_remote_pubkey(&self) -> Option { + self.remote_public_key + } + + fn initialize_state(public_key: &PublicKey) -> (HandshakeHash, [u8; 32]) { + let protocol_name = b"Noise_XK_secp256k1_ChaChaPoly_SHA256"; + let prologue = b"lightning"; + + let mut sha = Sha256::engine(); + sha.input(protocol_name); + let chaining_key = Sha256::from_engine(sha).into_inner(); + + let mut initial_hash_preimage = chaining_key.to_vec(); + initial_hash_preimage.extend_from_slice(prologue.as_ref()); + + let mut hash = HandshakeHash::new(initial_hash_preimage.as_slice()); + hash.update(&public_key.serialize()); + + (hash, chaining_key) + } + + /// Process act dynamically + /// # Arguments + /// `input`: Byte slice received from peer as part of the handshake protocol + /// + /// # Return values + /// Returns a tuple with the following components: + /// `.0`: Byte vector containing the next act to send back to the peer per the handshake protocol + /// `.1`: Conduit option if the handshake was just processed to completion and messages can now be encrypted and decrypted + pub fn process_act(&mut self, input: &[u8]) -> Result<(Option, Option), String> { + let mut response = None; + let mut connected_peer = None; + + self.read_buffer.extend_from_slice(input); + let read_buffer_length = self.read_buffer.len(); + + match &self.state { + &Some(HandshakeState::Uninitiated) => { + let remote_public_key = &self.remote_public_key.ok_or("outbound connections must be initialized with new_outbound")?; + let act_one = self.initiate(&remote_public_key)?; + response = Some(Act::One(act_one)); + } + &Some(HandshakeState::AwaitingActOne(_)) => { + if read_buffer_length < ACT_ONE_LENGTH { + return Err("need at least 50 bytes".to_string()); + } + + let mut act_one_buffer = [0u8; ACT_ONE_LENGTH]; + act_one_buffer.copy_from_slice(&self.read_buffer[..ACT_ONE_LENGTH]); + self.read_buffer.drain(..ACT_ONE_LENGTH); + + let act_two = self.process_act_one(ActOne(act_one_buffer))?; + response = Some(Act::Two(act_two)); + } + &Some(HandshakeState::AwaitingActTwo(_)) => { + if read_buffer_length < ACT_TWO_LENGTH { + return Err("need at least 50 bytes".to_string()); + } + + let mut act_two_buffer = [0u8; ACT_TWO_LENGTH]; + act_two_buffer.copy_from_slice(&self.read_buffer[..ACT_TWO_LENGTH]); + self.read_buffer.drain(..ACT_TWO_LENGTH); + + let (act_three, mut conduit) = self.process_act_two(ActTwo(act_two_buffer))?; + + if self.read_buffer.len() > 0 { // have we received more data still? + conduit.read(&self.read_buffer[..]); + self.read_buffer.drain(..); + } + + response = Some(Act::Three(act_three)); + connected_peer = Some(conduit); + } + &Some(HandshakeState::AwaitingActThree(_)) => { + if read_buffer_length < ACT_THREE_LENGTH { + return Err("need at least 66 bytes".to_string()); + } + + let mut act_three_buffer = [0u8; ACT_THREE_LENGTH]; + act_three_buffer.copy_from_slice(&self.read_buffer[..ACT_THREE_LENGTH]); + self.read_buffer.drain(..ACT_THREE_LENGTH); + + let (public_key, mut conduit) = self.process_act_three(ActThree(act_three_buffer))?; + + if self.read_buffer.len() > 0 { // have we received more data still? + conduit.read(&self.read_buffer[..]); + self.read_buffer.drain(..); + } + + connected_peer = Some(conduit); + self.remote_public_key = Some(public_key); + } + _ => { + panic!("no acts left to process"); + } + }; + Ok((response, connected_peer)) + } + + /// Initiate the handshake with a peer and return the first act + pub fn initiate(&mut self, remote_public_key: &PublicKey) -> Result { + if let &Some(HandshakeState::Uninitiated) = &self.state {} else { + return Err("Handshakes can only be initiated from the uninitiated state".to_string()); + } + + let (mut hash, chaining_key) = Self::initialize_state(&remote_public_key); + + // serialize act one + let (act_one, chaining_key, temporary_key) = Self::calculate_act_message( + &self.ephemeral_private_key, + remote_public_key, + chaining_key, + &mut hash, + ); + + self.state = Some(HandshakeState::AwaitingActTwo(ActTwoExpectation { + hash, + chaining_key, + temporary_key, + ephemeral_private_key: (*&self.ephemeral_private_key).clone(), + })); + + Ok(ActOne(act_one)) + } + + /// Process a peer's incoming first act and return the second act + pub(crate) fn process_act_one(&mut self, act: ActOne) -> Result { + let state = self.state.take(); + let act_one_expectation = match state { + Some(HandshakeState::AwaitingActOne(act_state)) => act_state, + Some(HandshakeState::Uninitiated) => { + let public_key = Self::private_key_to_public_key(&self.private_key); + let (hash, chaining_key) = Self::initialize_state(&public_key); + ActOneExpectation { + hash, + chaining_key, + } + } + _ => { + self.state = state; + panic!("unexpected state"); + } + }; + + let mut hash = act_one_expectation.hash; + let (remote_ephemeral_public_key, chaining_key, _) = Self::process_act_message( + act.0, + &self.private_key, + act_one_expectation.chaining_key, + &mut hash, + )?; + + let ephemeral_private_key = (*&self.ephemeral_private_key).clone(); + + let (act_two, chaining_key, temporary_key) = Self::calculate_act_message( + &ephemeral_private_key, + &remote_ephemeral_public_key, + chaining_key, + &mut hash, + ); + + self.state = Some(HandshakeState::AwaitingActThree(ActThreeExpectation { + hash, + chaining_key, + temporary_key, + ephemeral_private_key, + remote_ephemeral_public_key, + })); + + Ok(ActTwo(act_two)) + } + + /// Process a peer's incoming second act and return the third act alongside a Conduit instance + pub(crate) fn process_act_two(&mut self, act: ActTwo) -> Result<(ActThree, Conduit), String> { + let state = self.state.take(); + let act_two_expectation = match state { + Some(HandshakeState::AwaitingActTwo(act_state)) => act_state, + _ => { + self.state = state; + panic!("unexpected state".to_string()); + } + }; + + let mut hash = act_two_expectation.hash; + let (remote_ephemeral_public_key, chaining_key, temporary_key) = Self::process_act_message( + act.0, + &act_two_expectation.ephemeral_private_key, + act_two_expectation.chaining_key, + &mut hash, + )?; + + self.state = Some(HandshakeState::Complete); + + // start serializing act three + + let static_public_key = Self::private_key_to_public_key(&self.private_key); + let tagged_encrypted_pubkey = chacha::encrypt(&temporary_key, 1, &hash.value, &static_public_key.serialize()); + hash.update(&tagged_encrypted_pubkey); + + let ecdh = Self::ecdh(&self.private_key, &remote_ephemeral_public_key); + let (chaining_key, temporary_key) = hkdf::derive(&chaining_key, &ecdh); + let authentication_tag = chacha::encrypt(&temporary_key, 0, &hash.value, &[0; 0]); + let (sending_key, receiving_key) = hkdf::derive(&chaining_key, &[0; 0]); + + let mut act_three = [0u8; ACT_THREE_LENGTH]; + act_three[1..50].copy_from_slice(&tagged_encrypted_pubkey); + act_three[50..].copy_from_slice(authentication_tag.as_slice()); + + let connected_peer = Conduit::new(sending_key, receiving_key, chaining_key); + Ok((ActThree(act_three), connected_peer)) + } + + /// Process a peer's incoming third act and return a Conduit instance + pub(crate) fn process_act_three(&mut self, act: ActThree) -> Result<(PublicKey, Conduit), String> { + let state = self.state.take(); + let act_three_expectation = match state { + Some(HandshakeState::AwaitingActThree(act_state)) => act_state, + _ => { + self.state = state; + panic!("unexpected state".to_string()); + } + }; + + let version = act.0[0]; + if version != 0 { + // this should not crash the process, hence no panic + return Err("unexpected version".to_string()); + } + + let mut tagged_encrypted_pubkey = [0u8; 49]; + tagged_encrypted_pubkey.copy_from_slice(&act.0[1..50]); + + let mut chacha_tag = [0u8; 16]; + chacha_tag.copy_from_slice(&act.0[50..66]); + + let mut hash = act_three_expectation.hash; + + let remote_pubkey_vec = chacha::decrypt(&act_three_expectation.temporary_key, 1, &hash.value, &tagged_encrypted_pubkey)?; + let mut remote_pubkey_bytes = [0u8; 33]; + remote_pubkey_bytes.copy_from_slice(remote_pubkey_vec.as_slice()); + let remote_pubkey = if let Ok(public_key) = PublicKey::from_slice(&remote_pubkey_bytes) { + public_key + } else { + return Err("invalid remote public key".to_string()); + }; + + hash.update(&tagged_encrypted_pubkey); + + let ecdh = Self::ecdh(&act_three_expectation.ephemeral_private_key, &remote_pubkey); + let (chaining_key, temporary_key) = hkdf::derive(&act_three_expectation.chaining_key, &ecdh); + let _tag_check = chacha::decrypt(&temporary_key, 0, &hash.value, &chacha_tag)?; + let (receiving_key, sending_key) = hkdf::derive(&chaining_key, &[0; 0]); + + let connected_peer = Conduit::new(sending_key, receiving_key, chaining_key); + Ok((remote_pubkey, connected_peer)) + } + + fn calculate_act_message(local_private_key: &SecretKey, remote_public_key: &PublicKey, chaining_key: [u8; 32], hash: &mut HandshakeHash) -> ([u8; 50], SymmetricKey, SymmetricKey) { + let local_public_key = Self::private_key_to_public_key(local_private_key); + + hash.update(&local_public_key.serialize()); + + let ecdh = Self::ecdh(local_private_key, &remote_public_key); + let (chaining_key, temporary_key) = hkdf::derive(&chaining_key, &ecdh); + let tagged_ciphertext = chacha::encrypt(&temporary_key, 0, &hash.value, &[0; 0]); + + hash.update(&tagged_ciphertext); + + let mut act = [0u8; 50]; + act[1..34].copy_from_slice(&local_public_key.serialize()); + act[34..].copy_from_slice(tagged_ciphertext.as_slice()); + + (act, chaining_key, temporary_key) + } + + // Due to the very high similarity of acts 1 and 2, this method is used to process both + fn process_act_message(act_bytes: [u8; 50], local_private_key: &SecretKey, chaining_key: SymmetricKey, hash: &mut HandshakeHash) -> Result<(PublicKey, SymmetricKey, SymmetricKey), String> { + let version = act_bytes[0]; + if version != 0 { + // this should not crash the process, hence no panic + return Err("unexpected version".to_string()); + } + + let mut ephemeral_public_key_bytes = [0u8; 33]; + ephemeral_public_key_bytes.copy_from_slice(&act_bytes[1..34]); + let ephemeral_public_key = if let Ok(public_key) = PublicKey::from_slice(&ephemeral_public_key_bytes) { + public_key + } else { + return Err("invalid remote ephemeral public key".to_string()); + }; + + let mut chacha_tag = [0u8; 16]; + chacha_tag.copy_from_slice(&act_bytes[34..50]); + + // process the act message + + // update hash with partner's pubkey + hash.update(&ephemeral_public_key.serialize()); + + // calculate ECDH with partner's pubkey and local privkey + let ecdh = Self::ecdh(local_private_key, &ephemeral_public_key); + + // HKDF(chaining key, ECDH) -> chaining key' + next temporary key + let (chaining_key, temporary_key) = hkdf::derive(&chaining_key, &ecdh); + + // Validate chacha tag (temporary key, 0, hash, chacha_tag) + let _tag_check = chacha::decrypt(&temporary_key, 0, &hash.value, &chacha_tag)?; + + hash.update(&chacha_tag); + + Ok((ephemeral_public_key, chaining_key, temporary_key)) + } + + fn private_key_to_public_key(private_key: &SecretKey) -> PublicKey { + let curve = secp256k1::Secp256k1::new(); + let pk_object = PublicKey::from_secret_key(&curve, &private_key); + pk_object + } + + fn ecdh(private_key: &SecretKey, public_key: &PublicKey) -> SymmetricKey { + let curve = secp256k1::Secp256k1::new(); + let mut pk_object = public_key.clone(); + pk_object.mul_assign(&curve, &private_key[..]).expect("invalid multiplication"); + + let preimage = pk_object.serialize(); + let mut sha = Sha256::engine(); + sha.input(preimage.as_ref()); + Sha256::from_engine(sha).into_inner() + } +} diff --git a/lightning/src/ln/peers/handshake/states.rs b/lightning/src/ln/peers/handshake/states.rs new file mode 100644 index 00000000000..2a7401f5e9b --- /dev/null +++ b/lightning/src/ln/peers/handshake/states.rs @@ -0,0 +1,30 @@ +use ln::peers::handshake::hash::HandshakeHash; +use bitcoin::secp256k1::{SecretKey, PublicKey}; + +pub enum HandshakeState { + Uninitiated, + AwaitingActOne(ActOneExpectation), + AwaitingActTwo(ActTwoExpectation), + AwaitingActThree(ActThreeExpectation), + Complete, +} + +pub struct ActOneExpectation { + pub(super) hash: HandshakeHash, + pub(super) chaining_key: [u8; 32], +} + +pub struct ActTwoExpectation { + pub(super) hash: HandshakeHash, + pub(super) chaining_key: [u8; 32], + pub(super) temporary_key: [u8; 32], + pub(super) ephemeral_private_key: SecretKey, +} + +pub struct ActThreeExpectation { + pub(super) hash: HandshakeHash, + pub(super) chaining_key: [u8; 32], + pub(super) temporary_key: [u8; 32], + pub(super) ephemeral_private_key: SecretKey, + pub(super) remote_ephemeral_public_key: PublicKey, +} diff --git a/lightning/src/ln/peers/handshake/tests.rs b/lightning/src/ln/peers/handshake/tests.rs new file mode 100644 index 00000000000..e99500d1399 --- /dev/null +++ b/lightning/src/ln/peers/handshake/tests.rs @@ -0,0 +1,39 @@ +#![cfg(test)] + +use hex; +use bitcoin::secp256k1; + +use bitcoin::secp256k1::key::{PublicKey, SecretKey}; + +use ln::peers::handshake::PeerHandshake; + +#[test] +fn test_exchange() { + let curve = secp256k1::Secp256k1::new(); + + let local_private_key = SecretKey::from_slice(&[0x_11_u8; 32]).unwrap(); + let remote_private_key = SecretKey::from_slice(&[0x_21_u8; 32]).unwrap(); + + let local_ephemeral_private_key = SecretKey::from_slice(&[0x_12_u8; 32]).unwrap(); + let remote_ephemeral_private_key = SecretKey::from_slice(&[0x_22_u8; 32]).unwrap(); + + let remote_public_key = PublicKey::from_secret_key(&curve, &remote_private_key); + + let mut local_handshake = PeerHandshake::new_outbound(&local_private_key, &remote_public_key, &local_ephemeral_private_key); + let mut remote_handshake = PeerHandshake::new_inbound(&remote_private_key, &remote_ephemeral_private_key); + + let act_1 = local_handshake.initiate(&remote_public_key).unwrap(); + let act_1_hex = hex::encode(&act_1.0.to_vec()); + assert_eq!(act_1_hex, "00036360e856310ce5d294e8be33fc807077dc56ac80d95d9cd4ddbd21325eff73f70df6086551151f58b8afe6c195782c6a"); + + let act_2 = remote_handshake.process_act_one(act_1).unwrap(); + let act_2_hex = hex::encode(&act_2.0.to_vec()); + assert_eq!(act_2_hex, "0002466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f276e2470b93aac583c9ef6eafca3f730ae"); + + let act_2_result = local_handshake.process_act_two(act_2).unwrap(); + let act_3 = act_2_result.0; + let act_3_hex = hex::encode(&act_3.0.to_vec()); + assert_eq!(act_3_hex, "00b9e3a702e93e3a9948c2ed6e5fd7590a6e1c3a0344cfc9d5b57357049aa22355361aa02e55a8fc28fef5bd6d71ad0c38228dc68b1c466263b47fdf31e560e139ba"); + + remote_handshake.process_act_three(act_3).unwrap(); +} diff --git a/lightning/src/ln/peers/hkdf.rs b/lightning/src/ln/peers/hkdf.rs new file mode 100644 index 00000000000..48415594114 --- /dev/null +++ b/lightning/src/ln/peers/hkdf.rs @@ -0,0 +1,18 @@ +use bitcoin::hashes::{Hash, HashEngine, Hmac, HmacEngine}; +use bitcoin::hashes::sha256::Hash as Sha256; + +pub fn derive(salt: &[u8], master: &[u8]) -> ([u8; 32], [u8; 32]) { + let mut hmac = HmacEngine::::new(salt); + hmac.input(master); + let prk = Hmac::from_engine(hmac).into_inner(); // prk = sha256(master) + + let mut hmac = HmacEngine::::new(&prk[..]); + hmac.input(&[1; 1]); + let t1 = Hmac::from_engine(hmac).into_inner(); // t1 = sha256(prk | 1) + + let mut hmac = HmacEngine::::new(&prk[..]); + hmac.input(&t1); + hmac.input(&[2; 1]); + // sha256(prk | t1 | 2) = sha256(sha256(master) | sha256(sha256(sha256(master) | 1) | 2) + (t1, Hmac::from_engine(hmac).into_inner()) +} diff --git a/lightning/src/ln/peers/mod.rs b/lightning/src/ln/peers/mod.rs new file mode 100644 index 00000000000..f8082c50f10 --- /dev/null +++ b/lightning/src/ln/peers/mod.rs @@ -0,0 +1,10 @@ +//! Everything that has to do with over-the-wire peer communication. +//! The handshake module exposes mechanisms to conduct inbound and outbound handshakes. +//! When a handshake completes, it returns an instance of Conduit. +//! Conduit enables message encryption and decryption, and automatically handles key rotation. + +mod chacha; +pub mod conduit; +pub mod handshake; +pub mod handler; +mod hkdf; From ba37213a8e560bf35af0a3d583351d14365716e5 Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Tue, 15 Sep 2020 11:02:58 -0700 Subject: [PATCH 02/72] Fix compilation bug in 494. Lots of build warnings still, but it builds and tests pass. --- lightning/src/ln/peers/conduit.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lightning/src/ln/peers/conduit.rs b/lightning/src/ln/peers/conduit.rs index 74e420b192a..ea3b7a17afe 100644 --- a/lightning/src/ln/peers/conduit.rs +++ b/lightning/src/ln/peers/conduit.rs @@ -232,7 +232,7 @@ mod tests { assert_eq!(encrypted_message.len(), 2 + 16 + 16); let decrypted_message = remote_peer.decrypt_single_message(Some(&encrypted_message)).unwrap(); - assert_eq!(decrypted_message, vec![]); + assert_eq!(decrypted_message, Vec::::new()); } #[test] From 98bc0da44231c83e4bb0a35e17c5aa639dd76c1c Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Sun, 16 Aug 2020 15:39:59 -0700 Subject: [PATCH 03/72] tests: Add unit tests to handshake/mod.rs Add unit tests against the PeerHandshake public interface prior to changing any of the implementation details. This is a solid stand-alone patch, even if the subsequent refactor and design changes are not merged. A future patch uses the test vectors from the RFC to minimize the code under test. --- lightning/src/ln/peers/conduit.rs | 10 + lightning/src/ln/peers/handshake/mod.rs | 371 ++++++++++++++++++++++++ 2 files changed, 381 insertions(+) diff --git a/lightning/src/ln/peers/conduit.rs b/lightning/src/ln/peers/conduit.rs index ea3b7a17afe..57f191a8c0c 100644 --- a/lightning/src/ln/peers/conduit.rs +++ b/lightning/src/ln/peers/conduit.rs @@ -196,6 +196,16 @@ impl Decryptor { fn increment_nonce(&mut self) { Conduit::increment_nonce(&mut self.receiving_nonce, &mut self.receiving_chaining_key, &mut self.receiving_key); } + + // Used in tests to determine whether or not excess bytes entered the conduit without needing to bring up + // infrastructure to properly encode it + #[cfg(test)] + pub fn read_buffer_length(&self) -> usize { + match &self.read_buffer { + &Some(ref vec) => { vec.len() } + &None => 0 + } + } } #[cfg(test)] diff --git a/lightning/src/ln/peers/handshake/mod.rs b/lightning/src/ln/peers/handshake/mod.rs index 32efc969a22..2d95998aa2e 100644 --- a/lightning/src/ln/peers/handshake/mod.rs +++ b/lightning/src/ln/peers/handshake/mod.rs @@ -392,3 +392,374 @@ impl PeerHandshake { Sha256::from_engine(sha).into_inner() } } + +#[cfg(test)] +mod test { + use hex; + + use bitcoin::secp256k1; + use bitcoin::secp256k1::key::{PublicKey, SecretKey}; + + use ln::peers::handshake::PeerHandshake; + use ln::peers::handshake::acts::Act; + use ln::peers::handshake::states::HandshakeState; + + struct TestCtx { + outbound_handshake: PeerHandshake, + outbound_public_key: PublicKey, + inbound_handshake: PeerHandshake, + inbound_public_key: PublicKey + } + + impl TestCtx { + fn new() -> TestCtx { + let curve = secp256k1::Secp256k1::new(); + + let outbound_private_key = SecretKey::from_slice(&[0x_11_u8; 32]).unwrap(); + let outbound_public_key = PublicKey::from_secret_key(&curve, &outbound_private_key); + let outbound_ephemeral_key = SecretKey::from_slice(&[0x_12_u8; 32]).unwrap(); + + let inbound_private_key = SecretKey::from_slice(&[0x_21_u8; 32]).unwrap(); + let inbound_public_key = PublicKey::from_secret_key(&curve, &inbound_private_key); + let inbound_ephemeral_key = SecretKey::from_slice(&[0x_22_u8; 32]).unwrap(); + + let outbound_handshake = PeerHandshake::new_outbound(&outbound_private_key, &inbound_public_key, &outbound_ephemeral_key); + let inbound_handshake = PeerHandshake::new_inbound(&inbound_private_key, &inbound_ephemeral_key); + + TestCtx { + outbound_handshake, + outbound_public_key, + inbound_handshake, + inbound_public_key + } + } + } + + macro_rules! assert_matches { + ($e:expr, $state_match:pat) => { + match $e { + $state_match => (), + _ => panic!() + } + } + } + + macro_rules! do_process_act_or_panic { + ($handshake:expr, $input:expr) => { + $handshake.process_act($input).unwrap().0.unwrap().serialize() + } + } + + // Default Outbound::Uninitiated + #[test] + fn peer_handshake_new_outbound() { + let test_ctx = TestCtx::new(); + + assert_matches!(test_ctx.outbound_handshake.state, Some(HandshakeState::Uninitiated)); + assert_eq!(test_ctx.outbound_handshake.get_remote_pubkey(), Some(test_ctx.inbound_public_key)); + } + + // Default Inbound::AwaitingActOne + #[test] + fn peer_handshake_new_inbound() { + let test_ctx = TestCtx::new(); + + assert_matches!(test_ctx.inbound_handshake.state, Some(HandshakeState::AwaitingActOne(_))); + assert!(test_ctx.inbound_handshake.get_remote_pubkey().is_none()); + } + + /* + * PeerHandshake::process_act() tests + */ + + // Outbound::Uninitiated -> AwaitingActTwo + #[test] + fn peer_handshake_outbound_uninitiated_to_awaiting_act_two() { + let mut test_ctx = TestCtx::new(); + + assert_matches!(test_ctx.outbound_handshake.process_act(&[]).unwrap(), (Some(Act::One(_)), None)); + assert_matches!(test_ctx.outbound_handshake.state, Some(HandshakeState::AwaitingActTwo(_))); + assert_eq!(test_ctx.outbound_handshake.get_remote_pubkey(), Some(test_ctx.inbound_public_key)); + } + + // Outbound::Uninitiated -> AwaitingActTwo (extra bytes in argument) + #[test] + fn peer_handshake_outbound_uninitiated_to_awaiting_act_two_nonempty_input() { + let mut test_ctx = TestCtx::new(); + + // TODO: process_act() should error if state does not use vec, but it is non-empty + assert_matches!(test_ctx.outbound_handshake.process_act(&[1]).unwrap(), (Some(Act::One(_)), None)); + assert_matches!(test_ctx.outbound_handshake.state, Some(HandshakeState::AwaitingActTwo(_))); + assert_eq!(test_ctx.outbound_handshake.get_remote_pubkey(), Some(test_ctx.inbound_public_key)); + } + + // Inbound::AwaitingActOne -> Error (input too small) + #[test] + fn peer_handshake_new_inbound_awaiting_act_one_input_too_small() { + let mut test_ctx = TestCtx::new(); + + assert_eq!(test_ctx.inbound_handshake.process_act(&[]).err(), Some(String::from("need at least 50 bytes"))); + } + + // Inbound::AwaitingActOne -> AwaitingActThree (excess bytes) + // TODO: This should error early if we receive act3 data prior to sending act2 + #[test] + fn peer_handshake_new_inbound_awaiting_act_one_input_too_large() { + let mut test_ctx = TestCtx::new(); + let mut act1 = do_process_act_or_panic!(test_ctx.outbound_handshake, &[]); + act1.extend_from_slice(&[1]); + + assert_matches!(test_ctx.inbound_handshake.process_act(&act1).unwrap(), (Some(Act::Two(_)), None)); + assert_matches!(test_ctx.inbound_handshake.state, Some(HandshakeState::AwaitingActThree(_))); + assert!(test_ctx.inbound_handshake.get_remote_pubkey().is_none()); + } + + // Inbound::AwaitingActOne -> Error (bad version byte) + #[test] + fn peer_handshake_new_inbound_awaiting_act_one_bad_version() { + let mut test_ctx = TestCtx::new(); + let mut act1 = do_process_act_or_panic!(test_ctx.outbound_handshake, &[]); + // Set bad version byte + act1[0] = 1; + + assert_eq!(test_ctx.inbound_handshake.process_act(&act1).err(), Some(String::from("unexpected version"))); + } + + // Inbound::AwaitingActOne -> Error (invalid hmac) + #[test] + fn peer_handshake_new_inbound_awaiting_act_invalid_hmac() { + let mut test_ctx = TestCtx::new(); + let mut act1 = do_process_act_or_panic!(test_ctx.outbound_handshake, &[]); + // corrupt the ciphertext + act1[34] = 0; + + assert_eq!(test_ctx.inbound_handshake.process_act(&act1).err(), Some(String::from("invalid hmac"))); + } + + // Inbound::AwaitingActOne -> Error (invalid remote ephemeral key) + #[test] + fn peer_handshake_new_inbound_awaiting_act_invalid_remote_ephemeral_key() { + let mut test_ctx = TestCtx::new(); + let mut act1 = do_process_act_or_panic!(test_ctx.outbound_handshake, &[]); + // corrupt the ephemeral public key + act1[1] = 0; + + assert_eq!(test_ctx.inbound_handshake.process_act(&act1).err(), Some(String::from("invalid remote ephemeral public key"))); + } + + // Inbound::AwaitingActOne -> AwaitingActThree + #[test] + fn peer_handshake_new_inbound_awaiting_act_one_to_awaiting_act_three() { + let mut test_ctx = TestCtx::new(); + let act1 = do_process_act_or_panic!(test_ctx.outbound_handshake, &[]); + + assert_matches!(test_ctx.inbound_handshake.process_act(&act1).unwrap(), (Some(Act::Two(_)), None)); + assert_matches!(test_ctx.inbound_handshake.state, Some(HandshakeState::AwaitingActThree(_))); + assert_eq!(test_ctx.inbound_handshake.get_remote_pubkey(), None); + } + + // Outbound::AwaitingActTwo -> Complete (valid conduit) + #[test] + fn peer_handshake_outbound_awaiting_act_two_process() { + let mut test_ctx = TestCtx::new(); + let act1 = do_process_act_or_panic!(test_ctx.outbound_handshake, &[]); + let act2 = do_process_act_or_panic!(test_ctx.inbound_handshake, &act1); + + assert_matches!(test_ctx.outbound_handshake.process_act(&act2).unwrap(), (Some(Act::Three(_)), Some(_))); + assert_matches!(test_ctx.outbound_handshake.state, Some(HandshakeState::Complete)); + assert_eq!(test_ctx.outbound_handshake.get_remote_pubkey(), Some(test_ctx.inbound_public_key)); + } + + + // Outbound::AwaitingActTwo -> Complete (with extra data) + // Ensures that any remaining data in the read buffer is transferred to the conduit once + // the handshake is complete + // TODO: Is this valid? Don't we expect peers to need ActThree before sending additional data? + #[test] + fn peer_handshake_new_outbound_excess_bytes_after_complete_are_in_conduit() { + let mut test_ctx = TestCtx::new(); + let act1 = do_process_act_or_panic!(test_ctx.outbound_handshake, &[]); + let mut act2 = do_process_act_or_panic!(test_ctx.inbound_handshake, &act1); + act2.extend_from_slice(&[1; 100]); + + let conduit = if let (_, Some(conduit)) = test_ctx.outbound_handshake.process_act(&act2).unwrap() { + conduit + } else { + panic!(); + }; + + assert_eq!(100, conduit.decryptor.read_buffer_length()); + assert_eq!(test_ctx.outbound_handshake.get_remote_pubkey(), Some(test_ctx.inbound_public_key)); + } + + // Outbound::AwaitingActTwo -> Error (input too small) + #[test] + fn peer_handshake_outbound_awaiting_act_two_input_too_small() { + let mut test_ctx = TestCtx::new(); + let _act1 = do_process_act_or_panic!(test_ctx.outbound_handshake, &[]); + + assert_eq!(test_ctx.outbound_handshake.process_act(&[1]).err(), Some(String::from("need at least 50 bytes"))); + } + + // Outbound::AwaitingActTwo -> Error (bad version byte) + #[test] + fn peer_handshake_outbound_awaiting_act_two_bad_version() { + let mut test_ctx = TestCtx::new(); + let act1 = do_process_act_or_panic!(test_ctx.outbound_handshake, &[]); + let mut act2 = do_process_act_or_panic!(test_ctx.inbound_handshake, &act1); + // Set version byte to 1 + act2[0] = 1; + + assert_eq!(test_ctx.outbound_handshake.process_act(&act2).err(), Some(String::from("unexpected version"))); + } + + // Outbound::AwaitingActTwo -> Error (invalid hmac) + #[test] + fn peer_handshake_outbound_awaiting_act_two_invalid_hmac() { + let mut test_ctx = TestCtx::new(); + let act1 = do_process_act_or_panic!(test_ctx.outbound_handshake, &[]); + let mut act2 = do_process_act_or_panic!(test_ctx.inbound_handshake, &act1); + // corrupt the ciphertext + act2[34] = 1; + + assert_eq!(test_ctx.outbound_handshake.process_act(&act2).err(), Some(String::from("invalid hmac"))); + } + + // Outbound::AwaitingActTwo -> Error (invalid remote ephemeral key) + #[test] + fn peer_handshake_outbound_awaiting_act_two_invalid_remote_ephemeral_key() { + let mut test_ctx = TestCtx::new(); + let act1 = do_process_act_or_panic!(test_ctx.outbound_handshake, &[]); + let mut act2 = do_process_act_or_panic!(test_ctx.inbound_handshake, &act1); + // corrupt the ephemeral public key + act2[1] = 1; + + assert_eq!(test_ctx.outbound_handshake.process_act(&act2).err(), Some(String::from("invalid remote ephemeral public key"))); + } + + // Inbound::AwaitingActThree -> None + // TODO: should this transition to Complete instead of None? + #[test] + fn peer_handshake_new_inbound_awaiting_act_three_to_awaiting_act_three() { + let mut test_ctx = TestCtx::new(); + let act1 = do_process_act_or_panic!(test_ctx.outbound_handshake, &[]); + let act2 = do_process_act_or_panic!(test_ctx.inbound_handshake, &act1); + let act3 = do_process_act_or_panic!(test_ctx.outbound_handshake, &act2); + + assert_matches!(test_ctx.inbound_handshake.process_act(&act3).unwrap(), (None, Some(_))); + assert_eq!(test_ctx.inbound_handshake.get_remote_pubkey(), Some(test_ctx.outbound_public_key)); + } + + // Inbound::AwaitingActThree -> None (with extra bytes) + // Ensures that any remaining data in the read buffer is transferred to the conduit once + // the handshake is complete + #[test] + fn peer_handshake_new_inbound_excess_bytes_after_complete_are_in_conduit() { + let mut test_ctx = TestCtx::new(); + let act1 = do_process_act_or_panic!(test_ctx.outbound_handshake, &[]); + let act2 = do_process_act_or_panic!(test_ctx.inbound_handshake, &act1); + let mut act3 = do_process_act_or_panic!(test_ctx.outbound_handshake, &act2); + act3.extend_from_slice(&[2; 100]); + + let conduit = if let (None, Some(conduit)) = test_ctx.inbound_handshake.process_act(&act3).unwrap() { + conduit + } else { + panic!(); + }; + + assert_eq!(100, conduit.decryptor.read_buffer_length()); + } + + // Inbound::AwaitingActThree -> Error (input too small) + #[test] + fn peer_handshake_new_inbound_awaiting_act_three_input_too_small() { + let mut test_ctx = TestCtx::new(); + let act1 = do_process_act_or_panic!(test_ctx.outbound_handshake, &[]); + let act2 = do_process_act_or_panic!(test_ctx.inbound_handshake, &act1); + let act3 = do_process_act_or_panic!(test_ctx.outbound_handshake, &act2); + + assert_eq!(test_ctx.inbound_handshake.process_act(&act3[..65]).err(), Some(String::from("need at least 66 bytes"))); + } + + // Inbound::AwaitingActThree -> Error (bad version byte) + #[test] + fn peer_handshake_new_inbound_awaiting_act_three_bad_version() { + let mut test_ctx = TestCtx::new(); + let act1 = do_process_act_or_panic!(test_ctx.outbound_handshake, &[]); + let act2 = do_process_act_or_panic!(test_ctx.inbound_handshake, &act1); + let mut act3 = do_process_act_or_panic!(test_ctx.outbound_handshake, &act2); + // set version byte to 1 + act3[0] = 1; + + assert_eq!(test_ctx.inbound_handshake.process_act(&act3).err(), Some(String::from("unexpected version"))); + } + + // Inbound::AwaitingActThree -> Error (invalid hmac) + #[test] + fn peer_handshake_new_inbound_awaiting_act_three_invalid_hmac() { + let mut test_ctx = TestCtx::new(); + let act1 = do_process_act_or_panic!(test_ctx.outbound_handshake, &[]); + let act2 = do_process_act_or_panic!(test_ctx.inbound_handshake, &act1); + let mut act3 = do_process_act_or_panic!(test_ctx.outbound_handshake, &act2); + // trigger decryption error by corrupting byte 1 + act3[1] = 0; + + assert_eq!(test_ctx.inbound_handshake.process_act(&act3).err(), Some(String::from("invalid hmac"))); + } + + // Inbound::AwaitingActThree -> Error (invalid tag hmac) + #[test] + fn peer_handshake_new_inbound_awaiting_act_three_invalid_tag_hmac() { + let mut test_ctx = TestCtx::new(); + let act1 = do_process_act_or_panic!(test_ctx.outbound_handshake, &[]); + let act2 = do_process_act_or_panic!(test_ctx.inbound_handshake, &act1); + let mut act3 = do_process_act_or_panic!(test_ctx.outbound_handshake, &act2); + // trigger tag decryption error by corrupting byte 50 + act3[50] = 0; + + assert_eq!(test_ctx.inbound_handshake.process_act(&act3).err(), Some(String::from("invalid hmac"))); + } + + // Inbound::Complete -> Panic + #[test] + #[should_panic(expected = "no acts left to process")] + fn peer_handshake_new_inbound_complete_then_process_act() { + let mut test_ctx = TestCtx::new(); + let act1 = do_process_act_or_panic!(test_ctx.outbound_handshake, &[]); + let act2 = do_process_act_or_panic!(test_ctx.inbound_handshake, &act1); + let act3 = do_process_act_or_panic!(test_ctx.outbound_handshake, &act2); + test_ctx.inbound_handshake.process_act(&act3).unwrap(); + + do_process_act_or_panic!(test_ctx.inbound_handshake, &[]); + } + + // Outbound::None -> Panic + #[test] + #[should_panic(expected = "no acts left to process")] + fn peer_handshake_new_outbound_complete_then_process_act() { + let mut test_ctx = TestCtx::new(); + let act1 = do_process_act_or_panic!(test_ctx.outbound_handshake, &[]); + let act2 = do_process_act_or_panic!(test_ctx.inbound_handshake, &act1); + let act3 = do_process_act_or_panic!(test_ctx.outbound_handshake, &act2); + test_ctx.inbound_handshake.process_act(&act3).unwrap(); + + do_process_act_or_panic!(test_ctx.outbound_handshake, &[]); + } + + // Test the Act byte generation against known good hard-coded values in case the implementation + // changes in a symmetric way that makes the other tests useless + #[test] + fn peer_handshake_external_spec() { + let mut test_ctx = TestCtx::new(); + let act1 = do_process_act_or_panic!(test_ctx.outbound_handshake, &[]); + let act2 = do_process_act_or_panic!(test_ctx.inbound_handshake, &act1); + let act3 = do_process_act_or_panic!(test_ctx.outbound_handshake, &act2); + + assert_eq!(hex::encode(&act1), + "00036360e856310ce5d294e8be33fc807077dc56ac80d95d9cd4ddbd21325eff73f70df6086551151f58b8afe6c195782c6a"); + assert_eq!(hex::encode(&act2), + "0002466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f276e2470b93aac583c9ef6eafca3f730ae"); + assert_eq!(hex::encode(&act3), + "00b9e3a702e93e3a9948c2ed6e5fd7590a6e1c3a0344cfc9d5b57357049aa22355361aa02e55a8fc28fef5bd6d71ad0c38228dc68b1c466263b47fdf31e560e139ba"); + } +} From 52da509797261e787ec37f0e5942f79ff8ea8978 Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Sun, 16 Aug 2020 21:13:48 -0700 Subject: [PATCH 04/72] delete: Remove duplicate tests --- lightning/src/ln/peers/handler.rs | 1 - lightning/src/ln/peers/handshake/mod.rs | 11 +++---- lightning/src/ln/peers/handshake/tests.rs | 39 ----------------------- 3 files changed, 5 insertions(+), 46 deletions(-) delete mode 100644 lightning/src/ln/peers/handshake/tests.rs diff --git a/lightning/src/ln/peers/handler.rs b/lightning/src/ln/peers/handler.rs index e9c2becd0fb..903f4ccfaa3 100644 --- a/lightning/src/ln/peers/handler.rs +++ b/lightning/src/ln/peers/handler.rs @@ -41,7 +41,6 @@ use bitcoin::hashes::sha256::HashEngine as Sha256Engine; use bitcoin::hashes::{HashEngine, Hash}; use ln::peers::handshake::PeerHandshake; use ln::peers::conduit::Conduit; -use ln::peers::handshake::acts::Act; /// Provides references to trait impls which handle different types of messages. pub struct MessageHandler where diff --git a/lightning/src/ln/peers/handshake/mod.rs b/lightning/src/ln/peers/handshake/mod.rs index 2d95998aa2e..babdb32129b 100644 --- a/lightning/src/ln/peers/handshake/mod.rs +++ b/lightning/src/ln/peers/handshake/mod.rs @@ -14,10 +14,9 @@ use ln::peers::handshake::acts::{ActOne, ActThree, ActTwo, ACT_ONE_LENGTH, ACT_T use ln::peers::handshake::hash::HandshakeHash; use ln::peers::handshake::states::{ActOneExpectation, ActThreeExpectation, ActTwoExpectation, HandshakeState}; -pub(crate) mod acts; +mod acts; mod hash; mod states; -mod tests; /// Object for managing handshakes. /// Currently requires explicit ephemeral private key specification. @@ -162,7 +161,7 @@ impl PeerHandshake { } /// Initiate the handshake with a peer and return the first act - pub fn initiate(&mut self, remote_public_key: &PublicKey) -> Result { + fn initiate(&mut self, remote_public_key: &PublicKey) -> Result { if let &Some(HandshakeState::Uninitiated) = &self.state {} else { return Err("Handshakes can only be initiated from the uninitiated state".to_string()); } @@ -188,7 +187,7 @@ impl PeerHandshake { } /// Process a peer's incoming first act and return the second act - pub(crate) fn process_act_one(&mut self, act: ActOne) -> Result { + fn process_act_one(&mut self, act: ActOne) -> Result { let state = self.state.take(); let act_one_expectation = match state { Some(HandshakeState::AwaitingActOne(act_state)) => act_state, @@ -235,7 +234,7 @@ impl PeerHandshake { } /// Process a peer's incoming second act and return the third act alongside a Conduit instance - pub(crate) fn process_act_two(&mut self, act: ActTwo) -> Result<(ActThree, Conduit), String> { + fn process_act_two(&mut self, act: ActTwo) -> Result<(ActThree, Conduit), String> { let state = self.state.take(); let act_two_expectation = match state { Some(HandshakeState::AwaitingActTwo(act_state)) => act_state, @@ -275,7 +274,7 @@ impl PeerHandshake { } /// Process a peer's incoming third act and return a Conduit instance - pub(crate) fn process_act_three(&mut self, act: ActThree) -> Result<(PublicKey, Conduit), String> { + fn process_act_three(&mut self, act: ActThree) -> Result<(PublicKey, Conduit), String> { let state = self.state.take(); let act_three_expectation = match state { Some(HandshakeState::AwaitingActThree(act_state)) => act_state, diff --git a/lightning/src/ln/peers/handshake/tests.rs b/lightning/src/ln/peers/handshake/tests.rs deleted file mode 100644 index e99500d1399..00000000000 --- a/lightning/src/ln/peers/handshake/tests.rs +++ /dev/null @@ -1,39 +0,0 @@ -#![cfg(test)] - -use hex; -use bitcoin::secp256k1; - -use bitcoin::secp256k1::key::{PublicKey, SecretKey}; - -use ln::peers::handshake::PeerHandshake; - -#[test] -fn test_exchange() { - let curve = secp256k1::Secp256k1::new(); - - let local_private_key = SecretKey::from_slice(&[0x_11_u8; 32]).unwrap(); - let remote_private_key = SecretKey::from_slice(&[0x_21_u8; 32]).unwrap(); - - let local_ephemeral_private_key = SecretKey::from_slice(&[0x_12_u8; 32]).unwrap(); - let remote_ephemeral_private_key = SecretKey::from_slice(&[0x_22_u8; 32]).unwrap(); - - let remote_public_key = PublicKey::from_secret_key(&curve, &remote_private_key); - - let mut local_handshake = PeerHandshake::new_outbound(&local_private_key, &remote_public_key, &local_ephemeral_private_key); - let mut remote_handshake = PeerHandshake::new_inbound(&remote_private_key, &remote_ephemeral_private_key); - - let act_1 = local_handshake.initiate(&remote_public_key).unwrap(); - let act_1_hex = hex::encode(&act_1.0.to_vec()); - assert_eq!(act_1_hex, "00036360e856310ce5d294e8be33fc807077dc56ac80d95d9cd4ddbd21325eff73f70df6086551151f58b8afe6c195782c6a"); - - let act_2 = remote_handshake.process_act_one(act_1).unwrap(); - let act_2_hex = hex::encode(&act_2.0.to_vec()); - assert_eq!(act_2_hex, "0002466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f276e2470b93aac583c9ef6eafca3f730ae"); - - let act_2_result = local_handshake.process_act_two(act_2).unwrap(); - let act_3 = act_2_result.0; - let act_3_hex = hex::encode(&act_3.0.to_vec()); - assert_eq!(act_3_hex, "00b9e3a702e93e3a9948c2ed6e5fd7590a6e1c3a0344cfc9d5b57357049aa22355361aa02e55a8fc28fef5bd6d71ad0c38228dc68b1c466263b47fdf31e560e139ba"); - - remote_handshake.process_act_three(act_3).unwrap(); -} From bfe6c4cb80b19cbf7f2026896aaa875e80e6b026 Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Mon, 17 Aug 2020 15:32:56 -0700 Subject: [PATCH 05/72] refactor: Create enum-dispatch NOISE state machine The implementation logic is duplicated from handshake/mod.rs so that it can be tested and refactored independent of the current implementation. This uses enum dispatch coupled with separately 'moved' state objects that allow for simple testing, easy state inspection, and understandable transition logic between states. This pattern also removes the need for match statements inside implementation logic in favor of the instance representing the current state and available data. Every state transition is implemented in a next() function and is a straight forward translation from an object representing the existing known data and an input act message (bytes). Example usage: let (act_data, next_state) = cur_state.next(input_data); This state machine will eventually be moved into place inside the PeerHandshake which will act as a simple wrapper that just maintains the state machine and marshals the data between the peer_handler and states. --- lightning/src/ln/peers/handshake/states.rs | 661 ++++++++++++++++++++- 1 file changed, 660 insertions(+), 1 deletion(-) diff --git a/lightning/src/ln/peers/handshake/states.rs b/lightning/src/ln/peers/handshake/states.rs index 2a7401f5e9b..3f2649bfdd8 100644 --- a/lightning/src/ln/peers/handshake/states.rs +++ b/lightning/src/ln/peers/handshake/states.rs @@ -1,6 +1,14 @@ -use ln::peers::handshake::hash::HandshakeHash; +use bitcoin::secp256k1; + use bitcoin::secp256k1::{SecretKey, PublicKey}; +use ln::peers::handshake::hash::HandshakeHash; +use ln::peers::handshake::acts::{Act, ActOne, ACT_ONE_LENGTH, ActTwo, ACT_TWO_LENGTH, ACT_THREE_LENGTH, ActThree}; +use ln::peers::handshake::states::HandshakeState2::{AwaitingActTwo2, AwaitingActThree2, Complete2}; +use ln::peers::handshake::PeerHandshake; +use ln::peers::{chacha, hkdf}; +use ln::peers::conduit::Conduit; + pub enum HandshakeState { Uninitiated, AwaitingActOne(ActOneExpectation), @@ -9,6 +17,298 @@ pub enum HandshakeState { Complete, } +pub enum HandshakeState2 { + Uninitiated2(UninitiatedHandshakeState), + AwaitingActOne2(AwaitingActOneHandshakeState), + AwaitingActTwo2(AwaitingActTwoHandshakeState), + AwaitingActThree2(AwaitingActThreeHandshakeState), + Complete2(Option<(Conduit, PublicKey)>), +} + +impl HandshakeState2 { + pub(crate) fn next(self, input: &[u8]) -> Result<(Option, HandshakeState2), String> { + match self { + HandshakeState2::Uninitiated2(state) => { state.next(input) }, + HandshakeState2::AwaitingActOne2(state) => { state.next(input) }, + HandshakeState2::AwaitingActTwo2(state) => { state.next(input) }, + HandshakeState2::AwaitingActThree2(state) => { state.next(input) }, + HandshakeState2::Complete2(_conduit) => { panic!("no acts left to process") } + } + } +} + +trait IHandshakeState { + fn next(self, input: &[u8]) -> Result<(Option, HandshakeState2), String>; +} + +pub struct UninitiatedHandshakeState { + initiator_private_key: SecretKey, + initiator_ephemeral_private_key: SecretKey, + responder_public_key: PublicKey, +} + +pub struct AwaitingActOneHandshakeState { + responder_private_key: SecretKey, + responder_ephemeral_private_key: SecretKey, + chaining_key: [u8; 32], + hash: HandshakeHash, + read_buffer: Vec +} + +pub struct AwaitingActTwoHandshakeState { + initiator_private_key: SecretKey, + initiator_ephemeral_private_key: SecretKey, + responder_public_key: PublicKey, + chaining_key: [u8; 32], + hash: HandshakeHash, + read_buffer: Vec +} + +pub struct AwaitingActThreeHandshakeState { + hash: HandshakeHash, + responder_ephemeral_private_key: SecretKey, + chaining_key: [u8; 32], + temporary_key: [u8; 32], + read_buffer: Vec +} + +impl UninitiatedHandshakeState { + pub(crate) fn new(initiator_private_key: SecretKey, initiator_ephemeral_private_key: SecretKey, responder_public_key: PublicKey) -> Self { + UninitiatedHandshakeState { + initiator_private_key, + initiator_ephemeral_private_key, + responder_public_key + } + } +} + +impl IHandshakeState for UninitiatedHandshakeState { + fn next(self, _input: &[u8]) -> Result<(Option, HandshakeState2), String> { + + let initiator_private_key = self.initiator_private_key; + let initiator_ephemeral_private_key = self.initiator_ephemeral_private_key; + let responder_public_key = self.responder_public_key; + + let (mut hash, chaining_key) = PeerHandshake::initialize_state(&responder_public_key); + + // serialize act one + let (act_one, chaining_key, _) = PeerHandshake::calculate_act_message( + &initiator_ephemeral_private_key, + &responder_public_key, + chaining_key, + &mut hash, + ); + + Ok(( + Some(Act::One(ActOne(act_one))), + AwaitingActTwo2(AwaitingActTwoHandshakeState::new(initiator_private_key, initiator_ephemeral_private_key, responder_public_key, chaining_key, hash)) + )) + } +} + +impl AwaitingActOneHandshakeState { + pub(crate) fn new(responder_private_key: SecretKey, responder_ephemeral_private_key: SecretKey) -> Self { + + let curve = secp256k1::Secp256k1::new(); + let responder_public_key = PublicKey::from_secret_key(&curve, &responder_private_key); + let (hash, chaining_key) = PeerHandshake::initialize_state(&responder_public_key); + + AwaitingActOneHandshakeState { + responder_private_key, + responder_ephemeral_private_key, + chaining_key, + hash, + read_buffer: Vec::new() + } + } +} + +impl IHandshakeState for AwaitingActOneHandshakeState { + fn next(self, input: &[u8]) -> Result<(Option, HandshakeState2), String> { + + let mut read_buffer = self.read_buffer; + read_buffer.extend_from_slice(input); + + if read_buffer.len() < ACT_ONE_LENGTH { + return Err("need at least 50 bytes".to_string()); + } + + let mut hash = self.hash; + let responder_private_key = self.responder_private_key; + let chaining_key = self.chaining_key; + let responder_ephemeral_private_key = self.responder_ephemeral_private_key; + + // common functions take in an array so drain here for now + let mut act_one_bytes = [0u8; ACT_ONE_LENGTH]; + act_one_bytes.copy_from_slice(&read_buffer[..ACT_ONE_LENGTH]); + read_buffer.drain(..ACT_ONE_LENGTH); + + let (initiator_ephemeral_public_key, chaining_key, _) = PeerHandshake::process_act_message( + act_one_bytes, + &responder_private_key, + chaining_key, + &mut hash, + )?; + + let (act_two, chaining_key, temporary_key) = PeerHandshake::calculate_act_message( + &responder_ephemeral_private_key, + &initiator_ephemeral_public_key, + chaining_key, + &mut hash, + ); + + Ok(( + Some(Act::Two(ActTwo(act_two))), + AwaitingActThree2( + AwaitingActThreeHandshakeState::new(hash, responder_ephemeral_private_key, chaining_key, temporary_key, read_buffer) + ) + )) + } +} + +impl IHandshakeState for AwaitingActTwoHandshakeState { + fn next(self, input: &[u8]) -> Result<(Option, HandshakeState2), String> { + + let mut read_buffer = self.read_buffer; + read_buffer.extend_from_slice(input); + + if read_buffer.len() < ACT_TWO_LENGTH { + return Err("need at least 50 bytes".to_string()); + } + + let initiator_private_key = self.initiator_private_key; + let initiator_ephemeral_private_key = self.initiator_ephemeral_private_key; + let responder_public_key = self.responder_public_key; + let mut hash = self.hash; + let chaining_key = self.chaining_key; + + // common functions take in an array so drain here for now + let mut act_two_bytes = [0u8; ACT_TWO_LENGTH]; + act_two_bytes.copy_from_slice(&read_buffer[..ACT_TWO_LENGTH]); + read_buffer.drain(..ACT_TWO_LENGTH); + + let (responder_ephemeral_public_key, chaining_key, temporary_key) = PeerHandshake::process_act_message( + act_two_bytes, + &initiator_ephemeral_private_key, + chaining_key, + &mut hash, + )?; + + // start serializing act three + + let curve = secp256k1::Secp256k1::new(); + let initiator_public_key = PublicKey::from_secret_key(&curve, &initiator_private_key); + let tagged_encrypted_pubkey = chacha::encrypt(&temporary_key, 1, &hash.value, &initiator_public_key.serialize()); + hash.update(&tagged_encrypted_pubkey); + + let ecdh = PeerHandshake::ecdh(&initiator_private_key, &responder_ephemeral_public_key); + let (chaining_key, temporary_key) = hkdf::derive(&chaining_key, &ecdh); + let authentication_tag = chacha::encrypt(&temporary_key, 0, &hash.value, &[0; 0]); + let (sending_key, receiving_key) = hkdf::derive(&chaining_key, &[0; 0]); + + let mut act_three = [0u8; ACT_THREE_LENGTH]; + act_three[1..50].copy_from_slice(&tagged_encrypted_pubkey); + act_three[50..].copy_from_slice(authentication_tag.as_slice()); + + let mut conduit = Conduit::new(sending_key, receiving_key, chaining_key); + + if read_buffer.len() > 0 { // have we received more data still? + conduit.read(&read_buffer[..]); + read_buffer.drain(..); + } + + Ok(( + Some(Act::Three(ActThree(act_three))), + Complete2(Some((conduit, responder_public_key))) + )) + } +} + +impl AwaitingActTwoHandshakeState { + fn new(initiator_private_key: SecretKey, initiator_ephemeral_private_key: SecretKey, responder_public_key: PublicKey, chaining_key: [u8;32], hash: HandshakeHash) -> Self { + AwaitingActTwoHandshakeState { + initiator_private_key, + initiator_ephemeral_private_key, + responder_public_key, + chaining_key, + hash, + read_buffer: Vec::new() + } + } +} + +impl IHandshakeState for AwaitingActThreeHandshakeState { + fn next(self, input: &[u8]) -> Result<(Option, HandshakeState2), String> { + let mut read_buffer = self.read_buffer; + read_buffer.extend_from_slice(input); + + if read_buffer.len() < ACT_THREE_LENGTH { + return Err("need at least 66 bytes".to_string()); + } + + let mut hash = self.hash; + let temporary_key = self.temporary_key; + let responder_ephemeral_private_key = self.responder_ephemeral_private_key; + let chaining_key = self.chaining_key; + + let mut act_three_bytes = [0u8; ACT_THREE_LENGTH]; + act_three_bytes.copy_from_slice(&read_buffer[..ACT_THREE_LENGTH]); + read_buffer.drain(..ACT_THREE_LENGTH); + + let version = act_three_bytes[0]; + if version != 0 { + // this should not crash the process, hence no panic + return Err("unexpected version".to_string()); + } + + let mut tagged_encrypted_pubkey = [0u8; 49]; + tagged_encrypted_pubkey.copy_from_slice(&act_three_bytes[1..50]); + + let mut chacha_tag = [0u8; 16]; + chacha_tag.copy_from_slice(&act_three_bytes[50..66]); + + let remote_pubkey_vec = chacha::decrypt(&temporary_key, 1, &hash.value, &tagged_encrypted_pubkey)?; + let mut initiator_pubkey_bytes = [0u8; 33]; + initiator_pubkey_bytes.copy_from_slice(remote_pubkey_vec.as_slice()); + let initiator_pubkey = if let Ok(public_key) = PublicKey::from_slice(&initiator_pubkey_bytes) { + public_key + } else { + return Err("invalid remote public key".to_string()); + }; + + hash.update(&tagged_encrypted_pubkey); + + let ecdh = PeerHandshake::ecdh(&responder_ephemeral_private_key, &initiator_pubkey); + let (chaining_key, temporary_key) = hkdf::derive(&chaining_key, &ecdh); + let _tag_check = chacha::decrypt(&temporary_key, 0, &hash.value, &chacha_tag)?; + let (receiving_key, sending_key) = hkdf::derive(&chaining_key, &[0; 0]); + + let mut conduit = Conduit::new(sending_key, receiving_key, chaining_key); + + if read_buffer.len() > 0 { // have we received more data still? + conduit.read(&read_buffer[..]); + read_buffer.drain(..); + } + + Ok(( + None, + Complete2(Some((conduit, initiator_pubkey))) + )) + } +} + +impl AwaitingActThreeHandshakeState { + fn new(hash: HandshakeHash, responder_ephemeral_private_key: SecretKey, chaining_key: [u8; 32], temporary_key: [u8; 32], read_buffer: Vec) -> Self { + AwaitingActThreeHandshakeState { + hash, + responder_ephemeral_private_key, + chaining_key, + temporary_key, + read_buffer + } + } +} + pub struct ActOneExpectation { pub(super) hash: HandshakeHash, pub(super) chaining_key: [u8; 32], @@ -28,3 +328,362 @@ pub struct ActThreeExpectation { pub(super) ephemeral_private_key: SecretKey, pub(super) remote_ephemeral_public_key: PublicKey, } + +#[cfg(test)] +mod test { + use hex; + + use bitcoin::secp256k1; + use bitcoin::secp256k1::{PublicKey, SecretKey}; + + use ln::peers::handshake::acts::Act; + use ln::peers::handshake::states::{UninitiatedHandshakeState, AwaitingActOneHandshakeState, HandshakeState2}; + use ln::peers::handshake::states::HandshakeState2::{AwaitingActThree2, AwaitingActTwo2, Complete2}; + + struct TestCtx { + initiator: HandshakeState2, + initiator_public_key: PublicKey, + responder: HandshakeState2, + responder_public_key: PublicKey + } + + impl TestCtx { + fn new() -> Self { + let curve = secp256k1::Secp256k1::new(); + let initiator_private_key = SecretKey::from_slice(&[0x_11_u8; 32]).unwrap(); + let initiator_public_key = PublicKey::from_secret_key(&curve, &initiator_private_key); + let initiator_ephemeral_private_key = SecretKey::from_slice(&[0x_12_u8; 32]).unwrap(); + + let responder_private_key = SecretKey::from_slice(&[0x_21_u8; 32]).unwrap(); + let responder_public_key = PublicKey::from_secret_key(&curve, &responder_private_key); + let responder_ephemeral_private_key = SecretKey::from_slice(&[0x_22_u8; 32]).unwrap(); + + let initiator = UninitiatedHandshakeState::new(initiator_private_key, initiator_ephemeral_private_key, responder_public_key); + let responder = AwaitingActOneHandshakeState::new(responder_private_key, responder_ephemeral_private_key); + + TestCtx { + initiator: HandshakeState2::Uninitiated2(initiator), + initiator_public_key, + responder: HandshakeState2::AwaitingActOne2(responder), + responder_public_key, + } + } + } + + macro_rules! do_next_or_panic { + ($state:expr, $input:expr) => { + if let (Some(output_act), next_state) = $state.next($input).unwrap() { + (output_act.serialize(), next_state) + } else { + panic!(); + } + } + } + + macro_rules! assert_matches { + ($e:expr, $state_match:pat) => { + match $e { + $state_match => (), + _ => panic!() + } + } + } + + // Initiator::Uninitiated -> AwaitingActTwo + #[test] + fn uninitiated_to_awaiting_act_two() { + let test_ctx = TestCtx::new(); + + assert_matches!(test_ctx.initiator.next(&[]).unwrap(), (Some(Act::One(_)), AwaitingActTwo2(_))); + } + + // Initiator::Uninitiated -> AwaitingActTwo (extra bytes in argument) + #[test] + fn uninitiated_to_awaiting_act_two_extra_bytes() { + let test_ctx = TestCtx::new(); + + assert_matches!(test_ctx.initiator.next(&[1]).unwrap(), (Some(Act::One(_)), AwaitingActTwo2(_))); + } + + // Responder::AwaitingActOne -> Error (input too small) + #[test] + fn awaiting_act_one_to_awaiting_act_three_input_too_small() { + let test_ctx = TestCtx::new(); + assert_eq!(test_ctx.responder.next(&[]).err(), Some(String::from("need at least 50 bytes"))) + } + + // Responder::AwaitingActOne -> AwaitingActThree + // TODO: Should this fail since we don't expect data > ACT_ONE_LENGTH and likely indicates + // a bad peer? + // TODO: Should the behavior be changed to handle act1 data that is striped across multiple + // next() calls? + #[test] + fn awaiting_act_one_to_awaiting_act_three_input_extra_bytes() { + let test_ctx = TestCtx::new(); + let (mut act1, _) = do_next_or_panic!(test_ctx.initiator, &[]); + act1.extend_from_slice(&[1]); + + assert_matches!(test_ctx.responder.next(&act1).unwrap(), (Some(Act::Two(_)), AwaitingActThree2(_))); + } + + // Responder::AwaitingActOne -> Error (bad version byte) + #[test] + fn awaiting_act_one_to_awaiting_act_three_input_bad_version() { + let test_ctx = TestCtx::new(); + let (mut act1, _) = do_next_or_panic!(test_ctx.initiator, &[]); + // set version byte to 1 + act1[0] = 1; + + assert_eq!(test_ctx.responder.next(&act1).err(), Some(String::from("unexpected version"))); + } + + // Responder::AwaitingActOne -> Error (invalid hmac) + #[test] + fn awaiting_act_one_to_awaiting_act_three_invalid_hmac() { + let test_ctx = TestCtx::new(); + // Modify the initiator to point to a different responder + let (mut act1, _) = do_next_or_panic!(test_ctx.initiator, &[]); + // corrupt the ciphertext + act1[34] = 0; + + assert_eq!(test_ctx.responder.next(&act1).err(), Some(String::from("invalid hmac"))); + } + + // Responder::AwaitingActOne -> Error (invalid remote ephemeral key) + #[test] + fn awaiting_act_one_to_awaiting_act_three_invalid_remote_ephemeral_key() { + let test_ctx = TestCtx::new(); + // Modify the initiator to point to a different responder + let (mut act1, _) = do_next_or_panic!(test_ctx.initiator, &[]); + // corrupt the ephemeral public key + act1[1] = 0; + + assert_eq!(test_ctx.responder.next(&act1).err(), Some(String::from("invalid remote ephemeral public key"))); + } + + // Responder::AwaitingActOne -> AwaitingActThree + #[test] + fn awaiting_act_one_to_awaiting_act_three() { + let test_ctx = TestCtx::new(); + let (act1, _) = do_next_or_panic!(test_ctx.initiator, &[]); + + assert_matches!(test_ctx.responder.next(&act1).unwrap(), (Some(Act::Two(_)), AwaitingActThree2(_))); + } + + // Initiator::AwaitingActTwo -> Complete + #[test] + fn awaiting_act_two_to_complete() { + let test_ctx = TestCtx::new(); + let (act1, awaiting_act_two_state) = do_next_or_panic!(test_ctx.initiator, &[]); + let (act2, _awaiting_act_three_state) = do_next_or_panic!(test_ctx.responder, &act1); + + let remote_pubkey = if let (Some(Act::Three(_)), Complete2(Some((_, remote_pubkey)))) = awaiting_act_two_state.next(&act2).unwrap() { + remote_pubkey + } else { + panic!(); + }; + + assert_eq!(remote_pubkey, test_ctx.responder_public_key); + } + + // Initiator::AwaitingActTwo -> Complete (with extra data) + // Ensures that any remaining data in the read buffer is transferred to the conduit once + // the handshake is complete + // TODO: Is this valid? Don't we expect peers to need ActThree before sending additional data? + #[test] + fn awaiting_act_two_to_complete_excess_bytes_are_in_conduit() { + let test_ctx = TestCtx::new(); + let (act1, awaiting_act_two_state) = do_next_or_panic!(test_ctx.initiator, &[]); + let (mut act2, _awaiting_act_three_state) = do_next_or_panic!(test_ctx.responder, &act1); + act2.extend_from_slice(&[1; 100]); + + let (_act3, complete_state) = do_next_or_panic!(awaiting_act_two_state, &act2); + + let conduit = if let Complete2(Some((conduit, _))) = complete_state { + conduit + } else { + panic!(); + }; + + assert_eq!(100, conduit.decryptor.read_buffer_length()); + } + + // Initiator::AwaitingActTwo -> Error (input too small) + #[test] + fn awaiting_act_two_input_too_small() { + let test_ctx = TestCtx::new(); + let (_act1, awaiting_act_two_state) = do_next_or_panic!(test_ctx.initiator, &[]); + + assert_eq!(awaiting_act_two_state.next(&[]).err(), Some(String::from("need at least 50 bytes"))); + } + + // Initiator::AwaitingActTwo -> Error (bad version byte) + #[test] + fn awaiting_act_two_bad_version_byte() { + let test_ctx = TestCtx::new(); + let (act1, awaiting_act_two_state) = do_next_or_panic!(test_ctx.initiator, &[]); + let (mut act2, _awaiting_act_three_state) = do_next_or_panic!(test_ctx.responder, &act1); + // set invalid version byte + act2[0] = 1; + + assert_eq!(awaiting_act_two_state.next(&act2).err(), Some(String::from("unexpected version"))); + } + + // Initiator::AwaitingActTwo -> Error (invalid hmac) + #[test] + fn awaiting_act_two_invalid_hmac() { + let test_ctx = TestCtx::new(); + let (act1, awaiting_act_two_state) = do_next_or_panic!(test_ctx.initiator, &[]); + let (mut act2, _awaiting_act_three_state) = do_next_or_panic!(test_ctx.responder, &act1); + // corrupt the ciphertext + act2[34] = 0; + + assert_eq!(awaiting_act_two_state.next(&act2).err(), Some(String::from("invalid hmac"))); + } + + // Initiator::AwaitingActTwo -> Error (invalid ephemeral public key) + #[test] + fn awaiting_act_two_invalid_ephemeral_public_key() { + let test_ctx = TestCtx::new(); + let (act1, awaiting_act_two_state) = do_next_or_panic!(test_ctx.initiator, &[]); + let (mut act2, _awaiting_act_three_state) = do_next_or_panic!(test_ctx.responder, &act1); + // corrupt the ephemeral public key + act2[1] = 0; + + assert_eq!(awaiting_act_two_state.next(&act2).err(), Some(String::from("invalid remote ephemeral public key"))); + } + + // Responder::AwaitingActThree -> Complete + #[test] + fn awaiting_act_three_to_complete() { + let test_ctx = TestCtx::new(); + let (act1, awaiting_act_two_state) = do_next_or_panic!(test_ctx.initiator, &[]); + let (act2, awaiting_act_three_state) = do_next_or_panic!(test_ctx.responder, &act1); + let (act3, _complete_state) = do_next_or_panic!(awaiting_act_two_state, &act2); + + let remote_pubkey = if let (None, Complete2(Some((_, remote_pubkey)))) = awaiting_act_three_state.next(&act3).unwrap() { + remote_pubkey + } else { + panic!(); + }; + + assert_eq!(remote_pubkey, test_ctx.initiator_public_key); + } + + // Responder::AwaitingActThree -> None (with extra bytes) + // Ensures that any remaining data in the read buffer is transferred to the conduit once + // the handshake is complete + #[test] + fn awaiting_act_three_excess_bytes_after_complete_are_in_conduit() { + let test_ctx = TestCtx::new(); + let (act1, awaiting_act_two_state) = do_next_or_panic!(test_ctx.initiator, &[]); + let (act2, awaiting_act_three_state) = do_next_or_panic!(test_ctx.responder, &act1); + let (mut act3, _complete_state) = do_next_or_panic!(awaiting_act_two_state, &act2); + act3.extend_from_slice(&[2; 100]); + + let conduit = if let (_, Complete2(Some((conduit, _)))) = awaiting_act_three_state.next(&act3).unwrap() { + conduit + } else { + panic!(); + }; + + assert_eq!(100, conduit.decryptor.read_buffer_length()); + } + + // Responder::AwaitingActThree -> Error (input too small) + #[test] + fn awaiting_act_three_input_too_small() { + let test_ctx = TestCtx::new(); + let (act1, awaiting_act_two_state) = do_next_or_panic!(test_ctx.initiator, &[]); + let (act2, awaiting_act_three_state) = do_next_or_panic!(test_ctx.responder, &act1); + let (act3, _complete) = do_next_or_panic!(awaiting_act_two_state, &act2); + + assert_eq!(awaiting_act_three_state.next(&act3[..65]).err(), Some(String::from("need at least 66 bytes"))); + } + + // Responder::AwaitingActThree -> Error (bad version bytes) + #[test] + fn awaiting_act_three_bad_version_bytes() { + let test_ctx = TestCtx::new(); + let (act1, awaiting_act_two_state) = do_next_or_panic!(test_ctx.initiator, &[]); + let (act2, awaiting_act_three_state) = do_next_or_panic!(test_ctx.responder, &act1); + let (mut act3, _complete_state) = do_next_or_panic!(awaiting_act_two_state, &act2); + // set version byte to 1 + act3[0] = 1; + + assert_eq!(awaiting_act_three_state.next(&act3).err(), Some(String::from("unexpected version"))); + } + + // Responder::AwaitingActThree -> Error (invalid hmac) + #[test] + fn awaiting_act_three_invalid_hmac() { + let test_ctx = TestCtx::new(); + let (act1, awaiting_act_two_state) = do_next_or_panic!(test_ctx.initiator, &[]); + let (act2, awaiting_act_three_state) = do_next_or_panic!(test_ctx.responder, &act1); + let (mut act3, _complete_state) = do_next_or_panic!(awaiting_act_two_state, &act2); + // corrupt encrypted pubkey + act3[1] = 1; + + assert_eq!(awaiting_act_three_state.next(&act3).err(), Some(String::from("invalid hmac"))); + } + + // Responder::AwaitingActThree -> Error (invalid tag hmac) + #[test] + fn awaiting_act_three_invalid_tag_hmac() { + let test_ctx = TestCtx::new(); + let (act1, awaiting_act_two_state) = do_next_or_panic!(test_ctx.initiator, &[]); + let (act2, awaiting_act_three_state) = do_next_or_panic!(test_ctx.responder, &act1); + let (mut act3, _complete_state) = do_next_or_panic!(awaiting_act_two_state, &act2); + // corrupt tag + act3[50] = 1; + + assert_eq!(awaiting_act_three_state.next(&act3).err(), Some(String::from("invalid hmac"))); + } + + // Initiator::Complete -> Error + #[test] + #[should_panic(expected = "no acts left to process")] + fn initiator_complete_next_fail() { + let test_ctx = TestCtx::new(); + let (act1, awaiting_act_two_state) = do_next_or_panic!(test_ctx.initiator, &[]); + let (act2, _awaiting_act_three_state) = do_next_or_panic!(test_ctx.responder, &act1); + let (_act3, complete_state) = do_next_or_panic!(awaiting_act_two_state, &act2); + + complete_state.next(&[]).unwrap(); + } + + // Initiator::Complete -> Error + #[test] + #[should_panic(expected = "no acts left to process")] + fn responder_complete_next_fail() { + let test_ctx = TestCtx::new(); + let (act1, awaiting_act_two_state) = do_next_or_panic!(test_ctx.initiator, &[]); + let (act2, awaiting_act_three_state) = do_next_or_panic!(test_ctx.responder, &act1); + let (act3, _complete_state) = do_next_or_panic!(awaiting_act_two_state, &act2); + + let complete_state = if let (None, complete_state) = awaiting_act_three_state.next(&act3).unwrap() { + complete_state + } else { + panic!(); + }; + + complete_state.next(&[]).unwrap(); + } + + // Test the Act byte generation against known good hard-coded values in case the implementation + // changes in a symmetric way that makes the other tests useless + #[test] + fn test_acts_against_reference_bytes() { + let test_ctx = TestCtx::new(); + let (act1, awaiting_act_two_state) = do_next_or_panic!(test_ctx.initiator, &[]); + let (act2, _awaiting_act_three_state) = do_next_or_panic!(test_ctx.responder, &act1); + let (act3, _complete_state) = do_next_or_panic!(awaiting_act_two_state, &act2); + + assert_eq!(hex::encode(&act1), + "00036360e856310ce5d294e8be33fc807077dc56ac80d95d9cd4ddbd21325eff73f70df6086551151f58b8afe6c195782c6a"); + assert_eq!(hex::encode(&act2), + "0002466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f276e2470b93aac583c9ef6eafca3f730ae"); + assert_eq!(hex::encode(&act3), + "00b9e3a702e93e3a9948c2ed6e5fd7590a6e1c3a0344cfc9d5b57357049aa22355361aa02e55a8fc28fef5bd6d71ad0c38228dc68b1c466263b47fdf31e560e139ba"); + } +} From c154f09af83b49bb650ed0a6f3357fe8bfd4b4f8 Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Tue, 18 Aug 2020 20:13:39 -0700 Subject: [PATCH 06/72] refactor: Use new state machine in PeerHandshake PeerHandshake is now a simple object around the new state machine that simply marshals data between the callers and state machine states. For now, keep the APIs identical to make the patches easier to read. The only interesting piece to note here is the handling of get_remote_pubkey() to keep parity with the existing implementation. Future patches will remove this separate function in favor of callers taking ownership of it once the handshake is complete. --- lightning/src/ln/peers/handshake/mod.rs | 285 +++------------------ lightning/src/ln/peers/handshake/states.rs | 28 -- 2 files changed, 30 insertions(+), 283 deletions(-) diff --git a/lightning/src/ln/peers/handshake/mod.rs b/lightning/src/ln/peers/handshake/mod.rs index babdb32129b..987af5339af 100644 --- a/lightning/src/ln/peers/handshake/mod.rs +++ b/lightning/src/ln/peers/handshake/mod.rs @@ -10,9 +10,9 @@ use bitcoin::secp256k1::{PublicKey, SecretKey}; use ln::peers::{chacha, hkdf}; use ln::peers::conduit::{Conduit, SymmetricKey}; -use ln::peers::handshake::acts::{ActOne, ActThree, ActTwo, ACT_ONE_LENGTH, ACT_TWO_LENGTH, ACT_THREE_LENGTH, Act}; +use ln::peers::handshake::acts::Act; use ln::peers::handshake::hash::HandshakeHash; -use ln::peers::handshake::states::{ActOneExpectation, ActThreeExpectation, ActTwoExpectation, HandshakeState}; +use ln::peers::handshake::states::{HandshakeState2, UninitiatedHandshakeState, AwaitingActOneHandshakeState}; mod acts; mod hash; @@ -21,42 +21,25 @@ mod states; /// Object for managing handshakes. /// Currently requires explicit ephemeral private key specification. pub struct PeerHandshake { - state: Option, - private_key: SecretKey, + state: Option, remote_public_key: Option, - ephemeral_private_key: SecretKey, - - read_buffer: Vec, } impl PeerHandshake { /// Instantiate a new handshake with a node identity secret key and an ephemeral private key pub fn new_outbound(private_key: &SecretKey, remote_public_key: &PublicKey, ephemeral_private_key: &SecretKey) -> Self { Self { - state: Some(HandshakeState::Uninitiated), - private_key: (*private_key).clone(), + state: Some(HandshakeState2::Uninitiated2(UninitiatedHandshakeState::new(private_key.clone(), ephemeral_private_key.clone(), remote_public_key.clone()))), remote_public_key: Some(remote_public_key.clone()), - ephemeral_private_key: (*ephemeral_private_key).clone(), - read_buffer: Vec::new(), } } /// Instantiate a new handshake in anticipation of a peer's first handshake act pub fn new_inbound(private_key: &SecretKey, ephemeral_private_key: &SecretKey) -> Self { - let mut handshake = Self { - state: Some(HandshakeState::Uninitiated), - private_key: (*private_key).clone(), + Self { + state: Some(HandshakeState2::AwaitingActOne2(AwaitingActOneHandshakeState::new(private_key.clone(), ephemeral_private_key.clone()))), remote_public_key: None, - ephemeral_private_key: (*ephemeral_private_key).clone(), - read_buffer: Vec::new(), - }; - let public_key = Self::private_key_to_public_key(&private_key); - let (hash, chaining_key) = Self::initialize_state(&public_key); - handshake.state = Some(HandshakeState::AwaitingActOne(ActOneExpectation { - hash, - chaining_key, - })); - handshake + } } /// Return the remote public key once it has been extracted from the third act. @@ -91,231 +74,23 @@ impl PeerHandshake { /// `.0`: Byte vector containing the next act to send back to the peer per the handshake protocol /// `.1`: Conduit option if the handshake was just processed to completion and messages can now be encrypted and decrypted pub fn process_act(&mut self, input: &[u8]) -> Result<(Option, Option), String> { - let mut response = None; - let mut connected_peer = None; - - self.read_buffer.extend_from_slice(input); - let read_buffer_length = self.read_buffer.len(); - - match &self.state { - &Some(HandshakeState::Uninitiated) => { - let remote_public_key = &self.remote_public_key.ok_or("outbound connections must be initialized with new_outbound")?; - let act_one = self.initiate(&remote_public_key)?; - response = Some(Act::One(act_one)); - } - &Some(HandshakeState::AwaitingActOne(_)) => { - if read_buffer_length < ACT_ONE_LENGTH { - return Err("need at least 50 bytes".to_string()); - } - - let mut act_one_buffer = [0u8; ACT_ONE_LENGTH]; - act_one_buffer.copy_from_slice(&self.read_buffer[..ACT_ONE_LENGTH]); - self.read_buffer.drain(..ACT_ONE_LENGTH); - - let act_two = self.process_act_one(ActOne(act_one_buffer))?; - response = Some(Act::Two(act_two)); - } - &Some(HandshakeState::AwaitingActTwo(_)) => { - if read_buffer_length < ACT_TWO_LENGTH { - return Err("need at least 50 bytes".to_string()); - } - - let mut act_two_buffer = [0u8; ACT_TWO_LENGTH]; - act_two_buffer.copy_from_slice(&self.read_buffer[..ACT_TWO_LENGTH]); - self.read_buffer.drain(..ACT_TWO_LENGTH); - - let (act_three, mut conduit) = self.process_act_two(ActTwo(act_two_buffer))?; - - if self.read_buffer.len() > 0 { // have we received more data still? - conduit.read(&self.read_buffer[..]); - self.read_buffer.drain(..); - } - - response = Some(Act::Three(act_three)); - connected_peer = Some(conduit); - } - &Some(HandshakeState::AwaitingActThree(_)) => { - if read_buffer_length < ACT_THREE_LENGTH { - return Err("need at least 66 bytes".to_string()); - } - - let mut act_three_buffer = [0u8; ACT_THREE_LENGTH]; - act_three_buffer.copy_from_slice(&self.read_buffer[..ACT_THREE_LENGTH]); - self.read_buffer.drain(..ACT_THREE_LENGTH); - - let (public_key, mut conduit) = self.process_act_three(ActThree(act_three_buffer))?; - - if self.read_buffer.len() > 0 { // have we received more data still? - conduit.read(&self.read_buffer[..]); - self.read_buffer.drain(..); - } - - connected_peer = Some(conduit); - self.remote_public_key = Some(public_key); - } - _ => { - panic!("no acts left to process"); - } - }; - Ok((response, connected_peer)) - } - - /// Initiate the handshake with a peer and return the first act - fn initiate(&mut self, remote_public_key: &PublicKey) -> Result { - if let &Some(HandshakeState::Uninitiated) = &self.state {} else { - return Err("Handshakes can only be initiated from the uninitiated state".to_string()); - } - - let (mut hash, chaining_key) = Self::initialize_state(&remote_public_key); - - // serialize act one - let (act_one, chaining_key, temporary_key) = Self::calculate_act_message( - &self.ephemeral_private_key, - remote_public_key, - chaining_key, - &mut hash, - ); - - self.state = Some(HandshakeState::AwaitingActTwo(ActTwoExpectation { - hash, - chaining_key, - temporary_key, - ephemeral_private_key: (*&self.ephemeral_private_key).clone(), - })); - - Ok(ActOne(act_one)) - } - - /// Process a peer's incoming first act and return the second act - fn process_act_one(&mut self, act: ActOne) -> Result { - let state = self.state.take(); - let act_one_expectation = match state { - Some(HandshakeState::AwaitingActOne(act_state)) => act_state, - Some(HandshakeState::Uninitiated) => { - let public_key = Self::private_key_to_public_key(&self.private_key); - let (hash, chaining_key) = Self::initialize_state(&public_key); - ActOneExpectation { - hash, - chaining_key, - } - } - _ => { - self.state = state; - panic!("unexpected state"); - } - }; - - let mut hash = act_one_expectation.hash; - let (remote_ephemeral_public_key, chaining_key, _) = Self::process_act_message( - act.0, - &self.private_key, - act_one_expectation.chaining_key, - &mut hash, - )?; - - let ephemeral_private_key = (*&self.ephemeral_private_key).clone(); - - let (act_two, chaining_key, temporary_key) = Self::calculate_act_message( - &ephemeral_private_key, - &remote_ephemeral_public_key, - chaining_key, - &mut hash, - ); - - self.state = Some(HandshakeState::AwaitingActThree(ActThreeExpectation { - hash, - chaining_key, - temporary_key, - ephemeral_private_key, - remote_ephemeral_public_key, - })); - - Ok(ActTwo(act_two)) - } - - /// Process a peer's incoming second act and return the third act alongside a Conduit instance - fn process_act_two(&mut self, act: ActTwo) -> Result<(ActThree, Conduit), String> { - let state = self.state.take(); - let act_two_expectation = match state { - Some(HandshakeState::AwaitingActTwo(act_state)) => act_state, - _ => { - self.state = state; - panic!("unexpected state".to_string()); - } - }; - - let mut hash = act_two_expectation.hash; - let (remote_ephemeral_public_key, chaining_key, temporary_key) = Self::process_act_message( - act.0, - &act_two_expectation.ephemeral_private_key, - act_two_expectation.chaining_key, - &mut hash, - )?; - - self.state = Some(HandshakeState::Complete); - - // start serializing act three - - let static_public_key = Self::private_key_to_public_key(&self.private_key); - let tagged_encrypted_pubkey = chacha::encrypt(&temporary_key, 1, &hash.value, &static_public_key.serialize()); - hash.update(&tagged_encrypted_pubkey); - - let ecdh = Self::ecdh(&self.private_key, &remote_ephemeral_public_key); - let (chaining_key, temporary_key) = hkdf::derive(&chaining_key, &ecdh); - let authentication_tag = chacha::encrypt(&temporary_key, 0, &hash.value, &[0; 0]); - let (sending_key, receiving_key) = hkdf::derive(&chaining_key, &[0; 0]); - - let mut act_three = [0u8; ACT_THREE_LENGTH]; - act_three[1..50].copy_from_slice(&tagged_encrypted_pubkey); - act_three[50..].copy_from_slice(authentication_tag.as_slice()); - - let connected_peer = Conduit::new(sending_key, receiving_key, chaining_key); - Ok((ActThree(act_three), connected_peer)) - } - - /// Process a peer's incoming third act and return a Conduit instance - fn process_act_three(&mut self, act: ActThree) -> Result<(PublicKey, Conduit), String> { - let state = self.state.take(); - let act_three_expectation = match state { - Some(HandshakeState::AwaitingActThree(act_state)) => act_state, - _ => { - self.state = state; - panic!("unexpected state".to_string()); - } - }; - - let version = act.0[0]; - if version != 0 { - // this should not crash the process, hence no panic - return Err("unexpected version".to_string()); - } + let cur_state = self.state.take().unwrap(); - let mut tagged_encrypted_pubkey = [0u8; 49]; - tagged_encrypted_pubkey.copy_from_slice(&act.0[1..50]); + let (act_opt, mut next_state) = cur_state.next(input)?; - let mut chacha_tag = [0u8; 16]; - chacha_tag.copy_from_slice(&act.0[50..66]); - - let mut hash = act_three_expectation.hash; + let result = match next_state { + HandshakeState2::Complete2(ref mut conduit_and_pubkey) => { + let (conduit, remote_pubkey) = conduit_and_pubkey.take().unwrap(); + self.remote_public_key = Some(remote_pubkey); - let remote_pubkey_vec = chacha::decrypt(&act_three_expectation.temporary_key, 1, &hash.value, &tagged_encrypted_pubkey)?; - let mut remote_pubkey_bytes = [0u8; 33]; - remote_pubkey_bytes.copy_from_slice(remote_pubkey_vec.as_slice()); - let remote_pubkey = if let Ok(public_key) = PublicKey::from_slice(&remote_pubkey_bytes) { - public_key - } else { - return Err("invalid remote public key".to_string()); + Ok((act_opt, Some(conduit))) + }, + _ => { Ok((act_opt, None)) } }; - hash.update(&tagged_encrypted_pubkey); + self.state = Some(next_state); - let ecdh = Self::ecdh(&act_three_expectation.ephemeral_private_key, &remote_pubkey); - let (chaining_key, temporary_key) = hkdf::derive(&act_three_expectation.chaining_key, &ecdh); - let _tag_check = chacha::decrypt(&temporary_key, 0, &hash.value, &chacha_tag)?; - let (receiving_key, sending_key) = hkdf::derive(&chaining_key, &[0; 0]); - - let connected_peer = Conduit::new(sending_key, receiving_key, chaining_key); - Ok((remote_pubkey, connected_peer)) + result } fn calculate_act_message(local_private_key: &SecretKey, remote_public_key: &PublicKey, chaining_key: [u8; 32], hash: &mut HandshakeHash) -> ([u8; 50], SymmetricKey, SymmetricKey) { @@ -401,7 +176,7 @@ mod test { use ln::peers::handshake::PeerHandshake; use ln::peers::handshake::acts::Act; - use ln::peers::handshake::states::HandshakeState; + use ln::peers::handshake::states::HandshakeState2; struct TestCtx { outbound_handshake: PeerHandshake, @@ -454,7 +229,7 @@ mod test { fn peer_handshake_new_outbound() { let test_ctx = TestCtx::new(); - assert_matches!(test_ctx.outbound_handshake.state, Some(HandshakeState::Uninitiated)); + assert_matches!(test_ctx.outbound_handshake.state, Some(HandshakeState2::Uninitiated2(_))); assert_eq!(test_ctx.outbound_handshake.get_remote_pubkey(), Some(test_ctx.inbound_public_key)); } @@ -463,7 +238,7 @@ mod test { fn peer_handshake_new_inbound() { let test_ctx = TestCtx::new(); - assert_matches!(test_ctx.inbound_handshake.state, Some(HandshakeState::AwaitingActOne(_))); + assert_matches!(test_ctx.inbound_handshake.state, Some(HandshakeState2::AwaitingActOne2(_))); assert!(test_ctx.inbound_handshake.get_remote_pubkey().is_none()); } @@ -477,7 +252,7 @@ mod test { let mut test_ctx = TestCtx::new(); assert_matches!(test_ctx.outbound_handshake.process_act(&[]).unwrap(), (Some(Act::One(_)), None)); - assert_matches!(test_ctx.outbound_handshake.state, Some(HandshakeState::AwaitingActTwo(_))); + assert_matches!(test_ctx.outbound_handshake.state, Some(HandshakeState2::AwaitingActTwo2(_))); assert_eq!(test_ctx.outbound_handshake.get_remote_pubkey(), Some(test_ctx.inbound_public_key)); } @@ -488,7 +263,7 @@ mod test { // TODO: process_act() should error if state does not use vec, but it is non-empty assert_matches!(test_ctx.outbound_handshake.process_act(&[1]).unwrap(), (Some(Act::One(_)), None)); - assert_matches!(test_ctx.outbound_handshake.state, Some(HandshakeState::AwaitingActTwo(_))); + assert_matches!(test_ctx.outbound_handshake.state, Some(HandshakeState2::AwaitingActTwo2(_))); assert_eq!(test_ctx.outbound_handshake.get_remote_pubkey(), Some(test_ctx.inbound_public_key)); } @@ -509,7 +284,7 @@ mod test { act1.extend_from_slice(&[1]); assert_matches!(test_ctx.inbound_handshake.process_act(&act1).unwrap(), (Some(Act::Two(_)), None)); - assert_matches!(test_ctx.inbound_handshake.state, Some(HandshakeState::AwaitingActThree(_))); + assert_matches!(test_ctx.inbound_handshake.state, Some(HandshakeState2::AwaitingActThree2(_))); assert!(test_ctx.inbound_handshake.get_remote_pubkey().is_none()); } @@ -553,8 +328,8 @@ mod test { let act1 = do_process_act_or_panic!(test_ctx.outbound_handshake, &[]); assert_matches!(test_ctx.inbound_handshake.process_act(&act1).unwrap(), (Some(Act::Two(_)), None)); - assert_matches!(test_ctx.inbound_handshake.state, Some(HandshakeState::AwaitingActThree(_))); - assert_eq!(test_ctx.inbound_handshake.get_remote_pubkey(), None); + assert_matches!(test_ctx.inbound_handshake.state, Some(HandshakeState2::AwaitingActThree2(_))); + assert!(test_ctx.inbound_handshake.get_remote_pubkey().is_none()); } // Outbound::AwaitingActTwo -> Complete (valid conduit) @@ -565,7 +340,7 @@ mod test { let act2 = do_process_act_or_panic!(test_ctx.inbound_handshake, &act1); assert_matches!(test_ctx.outbound_handshake.process_act(&act2).unwrap(), (Some(Act::Three(_)), Some(_))); - assert_matches!(test_ctx.outbound_handshake.state, Some(HandshakeState::Complete)); + assert_matches!(test_ctx.outbound_handshake.state, Some(HandshakeState2::Complete2(_))); assert_eq!(test_ctx.outbound_handshake.get_remote_pubkey(), Some(test_ctx.inbound_public_key)); } @@ -636,8 +411,7 @@ mod test { assert_eq!(test_ctx.outbound_handshake.process_act(&act2).err(), Some(String::from("invalid remote ephemeral public key"))); } - // Inbound::AwaitingActThree -> None - // TODO: should this transition to Complete instead of None? + // Inbound::AwaitingActThree -> Complete #[test] fn peer_handshake_new_inbound_awaiting_act_three_to_awaiting_act_three() { let mut test_ctx = TestCtx::new(); @@ -646,10 +420,11 @@ mod test { let act3 = do_process_act_or_panic!(test_ctx.outbound_handshake, &act2); assert_matches!(test_ctx.inbound_handshake.process_act(&act3).unwrap(), (None, Some(_))); + assert_matches!(test_ctx.inbound_handshake.state, Some(HandshakeState2::Complete2(_))); assert_eq!(test_ctx.inbound_handshake.get_remote_pubkey(), Some(test_ctx.outbound_public_key)); } - // Inbound::AwaitingActThree -> None (with extra bytes) + // Inbound::AwaitingActThree -> Complete (with extra bytes) // Ensures that any remaining data in the read buffer is transferred to the conduit once // the handshake is complete #[test] diff --git a/lightning/src/ln/peers/handshake/states.rs b/lightning/src/ln/peers/handshake/states.rs index 3f2649bfdd8..b682d69930d 100644 --- a/lightning/src/ln/peers/handshake/states.rs +++ b/lightning/src/ln/peers/handshake/states.rs @@ -9,14 +9,6 @@ use ln::peers::handshake::PeerHandshake; use ln::peers::{chacha, hkdf}; use ln::peers::conduit::Conduit; -pub enum HandshakeState { - Uninitiated, - AwaitingActOne(ActOneExpectation), - AwaitingActTwo(ActTwoExpectation), - AwaitingActThree(ActThreeExpectation), - Complete, -} - pub enum HandshakeState2 { Uninitiated2(UninitiatedHandshakeState), AwaitingActOne2(AwaitingActOneHandshakeState), @@ -309,26 +301,6 @@ impl AwaitingActThreeHandshakeState { } } -pub struct ActOneExpectation { - pub(super) hash: HandshakeHash, - pub(super) chaining_key: [u8; 32], -} - -pub struct ActTwoExpectation { - pub(super) hash: HandshakeHash, - pub(super) chaining_key: [u8; 32], - pub(super) temporary_key: [u8; 32], - pub(super) ephemeral_private_key: SecretKey, -} - -pub struct ActThreeExpectation { - pub(super) hash: HandshakeHash, - pub(super) chaining_key: [u8; 32], - pub(super) temporary_key: [u8; 32], - pub(super) ephemeral_private_key: SecretKey, - pub(super) remote_ephemeral_public_key: PublicKey, -} - #[cfg(test)] mod test { use hex; From 9445feacde2b72b6116af70f896ef52f16e95253 Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Wed, 19 Aug 2020 13:07:03 -0700 Subject: [PATCH 07/72] refactor: Move helper functions to the states.rs --- lightning/src/ln/peers/handshake/mod.rs | 98 +----------------- lightning/src/ln/peers/handshake/states.rs | 111 +++++++++++++++++++-- 2 files changed, 102 insertions(+), 107 deletions(-) diff --git a/lightning/src/ln/peers/handshake/mod.rs b/lightning/src/ln/peers/handshake/mod.rs index 987af5339af..d2eb122c7c2 100644 --- a/lightning/src/ln/peers/handshake/mod.rs +++ b/lightning/src/ln/peers/handshake/mod.rs @@ -2,16 +2,10 @@ //! Handshake states can be advanced automatically, or by manually calling the appropriate step. //! Once complete, returns an instance of Conduit. -use bitcoin::secp256k1; - -use bitcoin::hashes::{Hash, HashEngine}; -use bitcoin::hashes::sha256::Hash as Sha256; use bitcoin::secp256k1::{PublicKey, SecretKey}; -use ln::peers::{chacha, hkdf}; -use ln::peers::conduit::{Conduit, SymmetricKey}; +use ln::peers::conduit::Conduit; use ln::peers::handshake::acts::Act; -use ln::peers::handshake::hash::HandshakeHash; use ln::peers::handshake::states::{HandshakeState2, UninitiatedHandshakeState, AwaitingActOneHandshakeState}; mod acts; @@ -48,23 +42,6 @@ impl PeerHandshake { self.remote_public_key } - fn initialize_state(public_key: &PublicKey) -> (HandshakeHash, [u8; 32]) { - let protocol_name = b"Noise_XK_secp256k1_ChaChaPoly_SHA256"; - let prologue = b"lightning"; - - let mut sha = Sha256::engine(); - sha.input(protocol_name); - let chaining_key = Sha256::from_engine(sha).into_inner(); - - let mut initial_hash_preimage = chaining_key.to_vec(); - initial_hash_preimage.extend_from_slice(prologue.as_ref()); - - let mut hash = HandshakeHash::new(initial_hash_preimage.as_slice()); - hash.update(&public_key.serialize()); - - (hash, chaining_key) - } - /// Process act dynamically /// # Arguments /// `input`: Byte slice received from peer as part of the handshake protocol @@ -92,79 +69,6 @@ impl PeerHandshake { result } - - fn calculate_act_message(local_private_key: &SecretKey, remote_public_key: &PublicKey, chaining_key: [u8; 32], hash: &mut HandshakeHash) -> ([u8; 50], SymmetricKey, SymmetricKey) { - let local_public_key = Self::private_key_to_public_key(local_private_key); - - hash.update(&local_public_key.serialize()); - - let ecdh = Self::ecdh(local_private_key, &remote_public_key); - let (chaining_key, temporary_key) = hkdf::derive(&chaining_key, &ecdh); - let tagged_ciphertext = chacha::encrypt(&temporary_key, 0, &hash.value, &[0; 0]); - - hash.update(&tagged_ciphertext); - - let mut act = [0u8; 50]; - act[1..34].copy_from_slice(&local_public_key.serialize()); - act[34..].copy_from_slice(tagged_ciphertext.as_slice()); - - (act, chaining_key, temporary_key) - } - - // Due to the very high similarity of acts 1 and 2, this method is used to process both - fn process_act_message(act_bytes: [u8; 50], local_private_key: &SecretKey, chaining_key: SymmetricKey, hash: &mut HandshakeHash) -> Result<(PublicKey, SymmetricKey, SymmetricKey), String> { - let version = act_bytes[0]; - if version != 0 { - // this should not crash the process, hence no panic - return Err("unexpected version".to_string()); - } - - let mut ephemeral_public_key_bytes = [0u8; 33]; - ephemeral_public_key_bytes.copy_from_slice(&act_bytes[1..34]); - let ephemeral_public_key = if let Ok(public_key) = PublicKey::from_slice(&ephemeral_public_key_bytes) { - public_key - } else { - return Err("invalid remote ephemeral public key".to_string()); - }; - - let mut chacha_tag = [0u8; 16]; - chacha_tag.copy_from_slice(&act_bytes[34..50]); - - // process the act message - - // update hash with partner's pubkey - hash.update(&ephemeral_public_key.serialize()); - - // calculate ECDH with partner's pubkey and local privkey - let ecdh = Self::ecdh(local_private_key, &ephemeral_public_key); - - // HKDF(chaining key, ECDH) -> chaining key' + next temporary key - let (chaining_key, temporary_key) = hkdf::derive(&chaining_key, &ecdh); - - // Validate chacha tag (temporary key, 0, hash, chacha_tag) - let _tag_check = chacha::decrypt(&temporary_key, 0, &hash.value, &chacha_tag)?; - - hash.update(&chacha_tag); - - Ok((ephemeral_public_key, chaining_key, temporary_key)) - } - - fn private_key_to_public_key(private_key: &SecretKey) -> PublicKey { - let curve = secp256k1::Secp256k1::new(); - let pk_object = PublicKey::from_secret_key(&curve, &private_key); - pk_object - } - - fn ecdh(private_key: &SecretKey, public_key: &PublicKey) -> SymmetricKey { - let curve = secp256k1::Secp256k1::new(); - let mut pk_object = public_key.clone(); - pk_object.mul_assign(&curve, &private_key[..]).expect("invalid multiplication"); - - let preimage = pk_object.serialize(); - let mut sha = Sha256::engine(); - sha.input(preimage.as_ref()); - Sha256::from_engine(sha).into_inner() - } } #[cfg(test)] diff --git a/lightning/src/ln/peers/handshake/states.rs b/lightning/src/ln/peers/handshake/states.rs index b682d69930d..de2a225cb15 100644 --- a/lightning/src/ln/peers/handshake/states.rs +++ b/lightning/src/ln/peers/handshake/states.rs @@ -1,13 +1,14 @@ use bitcoin::secp256k1; +use bitcoin::hashes::{Hash, HashEngine}; +use bitcoin::hashes::sha256::Hash as Sha256; use bitcoin::secp256k1::{SecretKey, PublicKey}; use ln::peers::handshake::hash::HandshakeHash; use ln::peers::handshake::acts::{Act, ActOne, ACT_ONE_LENGTH, ActTwo, ACT_TWO_LENGTH, ACT_THREE_LENGTH, ActThree}; use ln::peers::handshake::states::HandshakeState2::{AwaitingActTwo2, AwaitingActThree2, Complete2}; -use ln::peers::handshake::PeerHandshake; use ln::peers::{chacha, hkdf}; -use ln::peers::conduit::Conduit; +use ln::peers::conduit::{Conduit, SymmetricKey}; pub enum HandshakeState2 { Uninitiated2(UninitiatedHandshakeState), @@ -81,10 +82,10 @@ impl IHandshakeState for UninitiatedHandshakeState { let initiator_ephemeral_private_key = self.initiator_ephemeral_private_key; let responder_public_key = self.responder_public_key; - let (mut hash, chaining_key) = PeerHandshake::initialize_state(&responder_public_key); + let (mut hash, chaining_key) = initialize_state(&responder_public_key); // serialize act one - let (act_one, chaining_key, _) = PeerHandshake::calculate_act_message( + let (act_one, chaining_key, _) = calculate_act_message( &initiator_ephemeral_private_key, &responder_public_key, chaining_key, @@ -103,7 +104,7 @@ impl AwaitingActOneHandshakeState { let curve = secp256k1::Secp256k1::new(); let responder_public_key = PublicKey::from_secret_key(&curve, &responder_private_key); - let (hash, chaining_key) = PeerHandshake::initialize_state(&responder_public_key); + let (hash, chaining_key) = initialize_state(&responder_public_key); AwaitingActOneHandshakeState { responder_private_key, @@ -135,14 +136,14 @@ impl IHandshakeState for AwaitingActOneHandshakeState { act_one_bytes.copy_from_slice(&read_buffer[..ACT_ONE_LENGTH]); read_buffer.drain(..ACT_ONE_LENGTH); - let (initiator_ephemeral_public_key, chaining_key, _) = PeerHandshake::process_act_message( + let (initiator_ephemeral_public_key, chaining_key, _) = process_act_message( act_one_bytes, &responder_private_key, chaining_key, &mut hash, )?; - let (act_two, chaining_key, temporary_key) = PeerHandshake::calculate_act_message( + let (act_two, chaining_key, temporary_key) = calculate_act_message( &responder_ephemeral_private_key, &initiator_ephemeral_public_key, chaining_key, @@ -179,7 +180,7 @@ impl IHandshakeState for AwaitingActTwoHandshakeState { act_two_bytes.copy_from_slice(&read_buffer[..ACT_TWO_LENGTH]); read_buffer.drain(..ACT_TWO_LENGTH); - let (responder_ephemeral_public_key, chaining_key, temporary_key) = PeerHandshake::process_act_message( + let (responder_ephemeral_public_key, chaining_key, temporary_key) = process_act_message( act_two_bytes, &initiator_ephemeral_private_key, chaining_key, @@ -193,7 +194,7 @@ impl IHandshakeState for AwaitingActTwoHandshakeState { let tagged_encrypted_pubkey = chacha::encrypt(&temporary_key, 1, &hash.value, &initiator_public_key.serialize()); hash.update(&tagged_encrypted_pubkey); - let ecdh = PeerHandshake::ecdh(&initiator_private_key, &responder_ephemeral_public_key); + let ecdh = ecdh(&initiator_private_key, &responder_ephemeral_public_key); let (chaining_key, temporary_key) = hkdf::derive(&chaining_key, &ecdh); let authentication_tag = chacha::encrypt(&temporary_key, 0, &hash.value, &[0; 0]); let (sending_key, receiving_key) = hkdf::derive(&chaining_key, &[0; 0]); @@ -270,7 +271,7 @@ impl IHandshakeState for AwaitingActThreeHandshakeState { hash.update(&tagged_encrypted_pubkey); - let ecdh = PeerHandshake::ecdh(&responder_ephemeral_private_key, &initiator_pubkey); + let ecdh = ecdh(&responder_ephemeral_private_key, &initiator_pubkey); let (chaining_key, temporary_key) = hkdf::derive(&chaining_key, &ecdh); let _tag_check = chacha::decrypt(&temporary_key, 0, &hash.value, &chacha_tag)?; let (receiving_key, sending_key) = hkdf::derive(&chaining_key, &[0; 0]); @@ -301,6 +302,96 @@ impl AwaitingActThreeHandshakeState { } } +fn initialize_state(public_key: &PublicKey) -> (HandshakeHash, [u8; 32]) { + let protocol_name = b"Noise_XK_secp256k1_ChaChaPoly_SHA256"; + let prologue = b"lightning"; + + let mut sha = Sha256::engine(); + sha.input(protocol_name); + let chaining_key = Sha256::from_engine(sha).into_inner(); + + let mut initial_hash_preimage = chaining_key.to_vec(); + initial_hash_preimage.extend_from_slice(prologue.as_ref()); + + let mut hash = HandshakeHash::new(initial_hash_preimage.as_slice()); + hash.update(&public_key.serialize()); + + (hash, chaining_key) +} + +fn calculate_act_message(local_private_key: &SecretKey, remote_public_key: &PublicKey, chaining_key: [u8; 32], hash: &mut HandshakeHash) -> ([u8; 50], SymmetricKey, SymmetricKey) { + let local_public_key = private_key_to_public_key(local_private_key); + + hash.update(&local_public_key.serialize()); + + let ecdh = ecdh(local_private_key, &remote_public_key); + let (chaining_key, temporary_key) = hkdf::derive(&chaining_key, &ecdh); + let tagged_ciphertext = chacha::encrypt(&temporary_key, 0, &hash.value, &[0; 0]); + + hash.update(&tagged_ciphertext); + + let mut act = [0u8; 50]; + act[1..34].copy_from_slice(&local_public_key.serialize()); + act[34..].copy_from_slice(tagged_ciphertext.as_slice()); + + (act, chaining_key, temporary_key) +} + +// Due to the very high similarity of acts 1 and 2, this method is used to process both +fn process_act_message(act_bytes: [u8; 50], local_private_key: &SecretKey, chaining_key: SymmetricKey, hash: &mut HandshakeHash) -> Result<(PublicKey, SymmetricKey, SymmetricKey), String> { + let version = act_bytes[0]; + if version != 0 { + // this should not crash the process, hence no panic + return Err("unexpected version".to_string()); + } + + let mut ephemeral_public_key_bytes = [0u8; 33]; + ephemeral_public_key_bytes.copy_from_slice(&act_bytes[1..34]); + let ephemeral_public_key = if let Ok(public_key) = PublicKey::from_slice(&ephemeral_public_key_bytes) { + public_key + } else { + return Err("invalid remote ephemeral public key".to_string()); + }; + + let mut chacha_tag = [0u8; 16]; + chacha_tag.copy_from_slice(&act_bytes[34..50]); + + // process the act message + + // update hash with partner's pubkey + hash.update(&ephemeral_public_key.serialize()); + + // calculate ECDH with partner's pubkey and local privkey + let ecdh = ecdh(local_private_key, &ephemeral_public_key); + + // HKDF(chaining key, ECDH) -> chaining key' + next temporary key + let (chaining_key, temporary_key) = hkdf::derive(&chaining_key, &ecdh); + + // Validate chacha tag (temporary key, 0, hash, chacha_tag) + let _tag_check = chacha::decrypt(&temporary_key, 0, &hash.value, &chacha_tag)?; + + hash.update(&chacha_tag); + + Ok((ephemeral_public_key, chaining_key, temporary_key)) +} + +fn private_key_to_public_key(private_key: &SecretKey) -> PublicKey { + let curve = secp256k1::Secp256k1::new(); + let pk_object = PublicKey::from_secret_key(&curve, &private_key); + pk_object +} + +fn ecdh(private_key: &SecretKey, public_key: &PublicKey) -> SymmetricKey { + let curve = secp256k1::Secp256k1::new(); + let mut pk_object = public_key.clone(); + pk_object.mul_assign(&curve, &private_key[..]).expect("invalid multiplication"); + + let preimage = pk_object.serialize(); + let mut sha = Sha256::engine(); + sha.input(preimage.as_ref()); + Sha256::from_engine(sha).into_inner() +} + #[cfg(test)] mod test { use hex; From a0d6690322ea042dabd316bc082779429b16eed3 Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Wed, 19 Aug 2020 16:14:53 -0700 Subject: [PATCH 08/72] refactor: Structure initialize_state based on RFC Introduce the sha256!() and concat!() macros that are used to improve readability of the noise exchanges. --- lightning/src/ln/peers/handshake/states.rs | 45 +++++++++++++++++----- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/lightning/src/ln/peers/handshake/states.rs b/lightning/src/ln/peers/handshake/states.rs index de2a225cb15..1a19c2f34ad 100644 --- a/lightning/src/ln/peers/handshake/states.rs +++ b/lightning/src/ln/peers/handshake/states.rs @@ -82,7 +82,7 @@ impl IHandshakeState for UninitiatedHandshakeState { let initiator_ephemeral_private_key = self.initiator_ephemeral_private_key; let responder_public_key = self.responder_public_key; - let (mut hash, chaining_key) = initialize_state(&responder_public_key); + let (mut hash, chaining_key) = handshake_state_initialization(&responder_public_key); // serialize act one let (act_one, chaining_key, _) = calculate_act_message( @@ -104,7 +104,7 @@ impl AwaitingActOneHandshakeState { let curve = secp256k1::Secp256k1::new(); let responder_public_key = PublicKey::from_secret_key(&curve, &responder_private_key); - let (hash, chaining_key) = initialize_state(&responder_public_key); + let (hash, chaining_key) = handshake_state_initialization(&responder_public_key); AwaitingActOneHandshakeState { responder_private_key, @@ -302,19 +302,44 @@ impl AwaitingActThreeHandshakeState { } } -fn initialize_state(public_key: &PublicKey) -> (HandshakeHash, [u8; 32]) { +// Generate a SHA-256 hash from one or more elements +macro_rules! sha256 { + ( $( $x:expr ),+ ) => {{ + let mut sha = Sha256::engine(); + $( + sha.input($x.as_ref()); + )* + Sha256::from_engine(sha).into_inner() + }} +} + +// Concatenate two slices in a Vec +macro_rules! concat { + ($arg1:expr, $arg2:expr) => {{ + let mut result = $arg1.to_vec(); + result.extend_from_slice($arg2.as_ref()); + result + }} +} + +// https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#handshake-state-initialization +fn handshake_state_initialization(responder_public_key: &PublicKey) -> (HandshakeHash, [u8; 32]) { let protocol_name = b"Noise_XK_secp256k1_ChaChaPoly_SHA256"; let prologue = b"lightning"; - let mut sha = Sha256::engine(); - sha.input(protocol_name); - let chaining_key = Sha256::from_engine(sha).into_inner(); + // 1. h = SHA-256(protocolName) + // 2. ck = h + let chaining_key = sha256!(protocol_name); + + // 3. h = SHA-256(h || prologue) + let hash = sha256!(concat!(chaining_key, prologue)); - let mut initial_hash_preimage = chaining_key.to_vec(); - initial_hash_preimage.extend_from_slice(prologue.as_ref()); + // h = SHA-256(h || responderPublicKey) + let hash = sha256!(concat!(hash, responder_public_key.serialize())); - let mut hash = HandshakeHash::new(initial_hash_preimage.as_slice()); - hash.update(&public_key.serialize()); + let hash = HandshakeHash { + value: hash + }; (hash, chaining_key) } From a68af2dc72099e074e0a4c19c4733628df1fe7e0 Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Wed, 19 Aug 2020 16:49:21 -0700 Subject: [PATCH 09/72] refactor: Structure calculate_act_message based on RFC Small inconvenience with the HandshakeHash return value that will be taken care of by the end of this patch stack. --- lightning/src/ln/peers/handshake/states.rs | 33 ++++++++++++++++------ 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/lightning/src/ln/peers/handshake/states.rs b/lightning/src/ln/peers/handshake/states.rs index 1a19c2f34ad..61129d14791 100644 --- a/lightning/src/ln/peers/handshake/states.rs +++ b/lightning/src/ln/peers/handshake/states.rs @@ -344,19 +344,34 @@ fn handshake_state_initialization(responder_public_key: &PublicKey) -> (Handshak (hash, chaining_key) } -fn calculate_act_message(local_private_key: &SecretKey, remote_public_key: &PublicKey, chaining_key: [u8; 32], hash: &mut HandshakeHash) -> ([u8; 50], SymmetricKey, SymmetricKey) { - let local_public_key = private_key_to_public_key(local_private_key); - - hash.update(&local_public_key.serialize()); - - let ecdh = ecdh(local_private_key, &remote_public_key); +// https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#act-one (sender) +// https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#act-two (sender) +fn calculate_act_message(local_private_ephemeral_key: &SecretKey, remote_public_key: &PublicKey, chaining_key: [u8; 32], h_hash: &mut HandshakeHash) -> ([u8; 50], SymmetricKey, SymmetricKey) { + // 1. e = generateKey() (passed in) + // 2. h = SHA-256(h || e.pub.serializeCompressed()) + let serialized_local_public_key = private_key_to_public_key(local_private_ephemeral_key).serialize(); + let hash = sha256!(concat!(h_hash.value, serialized_local_public_key)); + + // 3. ACT1: es = ECDH(e.priv, rs) + // 3. ACT2: es = ECDH(e.priv, re) + let ecdh = ecdh(local_private_ephemeral_key, &remote_public_key); + + // 4. ACT1: ck, temp_k1 = HKDF(ck, es) + // 4. ACT2: ck, temp_k2 = HKDF(ck, ee) let (chaining_key, temporary_key) = hkdf::derive(&chaining_key, &ecdh); - let tagged_ciphertext = chacha::encrypt(&temporary_key, 0, &hash.value, &[0; 0]); - hash.update(&tagged_ciphertext); + // 5. ACT1: c = encryptWithAD(temp_k1, 0, h, zero) + // 5. ACT2: c = encryptWithAD(temp_k2, 0, h, zero) + let tagged_ciphertext = chacha::encrypt(&temporary_key, 0, &hash, &[0; 0]); + + // 6. h = SHA-256(h || c) + *h_hash = HandshakeHash { + value: sha256!(hash, tagged_ciphertext) + }; + // Send m = 0 || e.pub.serializeCompressed() || c let mut act = [0u8; 50]; - act[1..34].copy_from_slice(&local_public_key.serialize()); + act[1..34].copy_from_slice(&serialized_local_public_key); act[34..].copy_from_slice(tagged_ciphertext.as_slice()); (act, chaining_key, temporary_key) From abf924124af29e0eb97e906a227375ddd38c37ce Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Wed, 19 Aug 2020 17:54:15 -0700 Subject: [PATCH 10/72] refactor: Structure process_act_message based on RFC Fix inconsistent use of concat vs. Sha256::engine() input. These can be deduplicated at the end. --- lightning/src/ln/peers/handshake/states.rs | 124 +++++++++++---------- 1 file changed, 66 insertions(+), 58 deletions(-) diff --git a/lightning/src/ln/peers/handshake/states.rs b/lightning/src/ln/peers/handshake/states.rs index 61129d14791..80b2e89b19d 100644 --- a/lightning/src/ln/peers/handshake/states.rs +++ b/lightning/src/ln/peers/handshake/states.rs @@ -35,13 +35,13 @@ trait IHandshakeState { } pub struct UninitiatedHandshakeState { - initiator_private_key: SecretKey, + initiator_static_private_key: SecretKey, initiator_ephemeral_private_key: SecretKey, - responder_public_key: PublicKey, + responder_static_public_key: PublicKey, } pub struct AwaitingActOneHandshakeState { - responder_private_key: SecretKey, + responder_static_private_key: SecretKey, responder_ephemeral_private_key: SecretKey, chaining_key: [u8; 32], hash: HandshakeHash, @@ -49,9 +49,9 @@ pub struct AwaitingActOneHandshakeState { } pub struct AwaitingActTwoHandshakeState { - initiator_private_key: SecretKey, + initiator_static_private_key: SecretKey, initiator_ephemeral_private_key: SecretKey, - responder_public_key: PublicKey, + responder_static_public_key: PublicKey, chaining_key: [u8; 32], hash: HandshakeHash, read_buffer: Vec @@ -66,48 +66,46 @@ pub struct AwaitingActThreeHandshakeState { } impl UninitiatedHandshakeState { - pub(crate) fn new(initiator_private_key: SecretKey, initiator_ephemeral_private_key: SecretKey, responder_public_key: PublicKey) -> Self { + pub(crate) fn new(initiator_static_private_key: SecretKey, initiator_ephemeral_private_key: SecretKey, responder_static_public_key: PublicKey) -> Self { UninitiatedHandshakeState { - initiator_private_key, + initiator_static_private_key, initiator_ephemeral_private_key, - responder_public_key + responder_static_public_key } } } impl IHandshakeState for UninitiatedHandshakeState { + // https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#act-one (sender) fn next(self, _input: &[u8]) -> Result<(Option, HandshakeState2), String> { - - let initiator_private_key = self.initiator_private_key; + let initiator_static_private_key = self.initiator_static_private_key; let initiator_ephemeral_private_key = self.initiator_ephemeral_private_key; - let responder_public_key = self.responder_public_key; + let responder_static_public_key = self.responder_static_public_key; - let (mut hash, chaining_key) = handshake_state_initialization(&responder_public_key); + let (mut hash, chaining_key) = handshake_state_initialization(&responder_static_public_key); // serialize act one let (act_one, chaining_key, _) = calculate_act_message( &initiator_ephemeral_private_key, - &responder_public_key, + &responder_static_public_key, chaining_key, &mut hash, ); Ok(( Some(Act::One(ActOne(act_one))), - AwaitingActTwo2(AwaitingActTwoHandshakeState::new(initiator_private_key, initiator_ephemeral_private_key, responder_public_key, chaining_key, hash)) + AwaitingActTwo2(AwaitingActTwoHandshakeState::new(initiator_static_private_key, initiator_ephemeral_private_key, responder_static_public_key, chaining_key, hash)) )) } } impl AwaitingActOneHandshakeState { - pub(crate) fn new(responder_private_key: SecretKey, responder_ephemeral_private_key: SecretKey) -> Self { - - let curve = secp256k1::Secp256k1::new(); - let responder_public_key = PublicKey::from_secret_key(&curve, &responder_private_key); - let (hash, chaining_key) = handshake_state_initialization(&responder_public_key); + pub(crate) fn new(responder_static_private_key: SecretKey, responder_ephemeral_private_key: SecretKey) -> Self { + let responder_static_public_key = private_key_to_public_key(&responder_static_private_key); + let (hash, chaining_key) = handshake_state_initialization(&responder_static_public_key); AwaitingActOneHandshakeState { - responder_private_key, + responder_static_private_key, responder_ephemeral_private_key, chaining_key, hash, @@ -117,8 +115,9 @@ impl AwaitingActOneHandshakeState { } impl IHandshakeState for AwaitingActOneHandshakeState { + // https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#act-one (receiver) + // https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#act-two (sender) fn next(self, input: &[u8]) -> Result<(Option, HandshakeState2), String> { - let mut read_buffer = self.read_buffer; read_buffer.extend_from_slice(input); @@ -127,18 +126,18 @@ impl IHandshakeState for AwaitingActOneHandshakeState { } let mut hash = self.hash; - let responder_private_key = self.responder_private_key; + let responder_static_private_key = self.responder_static_private_key; let chaining_key = self.chaining_key; let responder_ephemeral_private_key = self.responder_ephemeral_private_key; - // common functions take in an array so drain here for now + // 1. Read exactly 50 bytes from the network buffer let mut act_one_bytes = [0u8; ACT_ONE_LENGTH]; act_one_bytes.copy_from_slice(&read_buffer[..ACT_ONE_LENGTH]); read_buffer.drain(..ACT_ONE_LENGTH); let (initiator_ephemeral_public_key, chaining_key, _) = process_act_message( act_one_bytes, - &responder_private_key, + &responder_static_private_key, chaining_key, &mut hash, )?; @@ -160,8 +159,9 @@ impl IHandshakeState for AwaitingActOneHandshakeState { } impl IHandshakeState for AwaitingActTwoHandshakeState { + // https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#act-two (receiver) + // https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#act-three (sender) fn next(self, input: &[u8]) -> Result<(Option, HandshakeState2), String> { - let mut read_buffer = self.read_buffer; read_buffer.extend_from_slice(input); @@ -169,13 +169,13 @@ impl IHandshakeState for AwaitingActTwoHandshakeState { return Err("need at least 50 bytes".to_string()); } - let initiator_private_key = self.initiator_private_key; + let initiator_static_private_key = self.initiator_static_private_key; let initiator_ephemeral_private_key = self.initiator_ephemeral_private_key; - let responder_public_key = self.responder_public_key; + let responder_static_public_key = self.responder_static_public_key; let mut hash = self.hash; let chaining_key = self.chaining_key; - // common functions take in an array so drain here for now + // 1. Read exactly 50 bytes from the network buffer let mut act_two_bytes = [0u8; ACT_TWO_LENGTH]; act_two_bytes.copy_from_slice(&read_buffer[..ACT_TWO_LENGTH]); read_buffer.drain(..ACT_TWO_LENGTH); @@ -190,11 +190,11 @@ impl IHandshakeState for AwaitingActTwoHandshakeState { // start serializing act three let curve = secp256k1::Secp256k1::new(); - let initiator_public_key = PublicKey::from_secret_key(&curve, &initiator_private_key); + let initiator_public_key = PublicKey::from_secret_key(&curve, &initiator_static_private_key); let tagged_encrypted_pubkey = chacha::encrypt(&temporary_key, 1, &hash.value, &initiator_public_key.serialize()); hash.update(&tagged_encrypted_pubkey); - let ecdh = ecdh(&initiator_private_key, &responder_ephemeral_public_key); + let ecdh = ecdh(&initiator_static_private_key, &responder_ephemeral_public_key); let (chaining_key, temporary_key) = hkdf::derive(&chaining_key, &ecdh); let authentication_tag = chacha::encrypt(&temporary_key, 0, &hash.value, &[0; 0]); let (sending_key, receiving_key) = hkdf::derive(&chaining_key, &[0; 0]); @@ -212,17 +212,17 @@ impl IHandshakeState for AwaitingActTwoHandshakeState { Ok(( Some(Act::Three(ActThree(act_three))), - Complete2(Some((conduit, responder_public_key))) + Complete2(Some((conduit, responder_static_public_key))) )) } } impl AwaitingActTwoHandshakeState { - fn new(initiator_private_key: SecretKey, initiator_ephemeral_private_key: SecretKey, responder_public_key: PublicKey, chaining_key: [u8;32], hash: HandshakeHash) -> Self { + fn new(initiator_static_private_key: SecretKey, initiator_ephemeral_private_key: SecretKey, responder_static_public_key: PublicKey, chaining_key: [u8;32], hash: HandshakeHash) -> Self { AwaitingActTwoHandshakeState { - initiator_private_key, + initiator_static_private_key, initiator_ephemeral_private_key, - responder_public_key, + responder_static_public_key, chaining_key, hash, read_buffer: Vec::new() @@ -323,7 +323,7 @@ macro_rules! concat { } // https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#handshake-state-initialization -fn handshake_state_initialization(responder_public_key: &PublicKey) -> (HandshakeHash, [u8; 32]) { +fn handshake_state_initialization(responder_static_public_key: &PublicKey) -> (HandshakeHash, [u8; 32]) { let protocol_name = b"Noise_XK_secp256k1_ChaChaPoly_SHA256"; let prologue = b"lightning"; @@ -335,7 +335,7 @@ fn handshake_state_initialization(responder_public_key: &PublicKey) -> (Handshak let hash = sha256!(concat!(chaining_key, prologue)); // h = SHA-256(h || responderPublicKey) - let hash = sha256!(concat!(hash, responder_public_key.serialize())); + let hash = sha256!(concat!(hash, responder_static_public_key.serialize())); let hash = HandshakeHash { value: hash @@ -378,12 +378,11 @@ fn calculate_act_message(local_private_ephemeral_key: &SecretKey, remote_public_ } // Due to the very high similarity of acts 1 and 2, this method is used to process both -fn process_act_message(act_bytes: [u8; 50], local_private_key: &SecretKey, chaining_key: SymmetricKey, hash: &mut HandshakeHash) -> Result<(PublicKey, SymmetricKey, SymmetricKey), String> { +// https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#act-one (receiver) +// https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#act-two (receiver) +fn process_act_message(act_bytes: [u8; 50], local_private_key: &SecretKey, chaining_key: SymmetricKey, h_hash: &mut HandshakeHash) -> Result<(PublicKey, SymmetricKey, SymmetricKey), String> { + // 2.Parse the read message (m) into v, re, and c let version = act_bytes[0]; - if version != 0 { - // this should not crash the process, hence no panic - return Err("unexpected version".to_string()); - } let mut ephemeral_public_key_bytes = [0u8; 33]; ephemeral_public_key_bytes.copy_from_slice(&act_bytes[1..34]); @@ -396,21 +395,30 @@ fn process_act_message(act_bytes: [u8; 50], local_private_key: &SecretKey, chain let mut chacha_tag = [0u8; 16]; chacha_tag.copy_from_slice(&act_bytes[34..50]); - // process the act message + // 3. If v is an unrecognized handshake version, then the responder MUST abort the connection attempt + if version != 0 { + // this should not crash the process, hence no panic + return Err("unexpected version".to_string()); + } - // update hash with partner's pubkey - hash.update(&ephemeral_public_key.serialize()); + // 4. h = SHA-256(h || re.serializeCompressed()) + let hash = sha256!(concat!(h_hash.value, ephemeral_public_key_bytes)); - // calculate ECDH with partner's pubkey and local privkey + // 5. Act1: es = ECDH(s.priv, re) + // 5. Act2: ee = ECDH(e.priv, ee) let ecdh = ecdh(local_private_key, &ephemeral_public_key); - // HKDF(chaining key, ECDH) -> chaining key' + next temporary key + // 6. Act1: ck, temp_k1 = HKDF(ck, es) + // 6. Act2: ck, temp_k2 = HKDF(ck, ee) let (chaining_key, temporary_key) = hkdf::derive(&chaining_key, &ecdh); - // Validate chacha tag (temporary key, 0, hash, chacha_tag) - let _tag_check = chacha::decrypt(&temporary_key, 0, &hash.value, &chacha_tag)?; + // 7. p = decryptWithAD(temp_k1, 0, h, c) + let _tag_check = chacha::decrypt(&temporary_key, 0, &hash, &chacha_tag)?; - hash.update(&chacha_tag); + // 8. h = SHA-256(h || c) + *h_hash = HandshakeHash { + value: sha256!(concat!(hash, chacha_tag)) + }; Ok((ephemeral_public_key, chaining_key, temporary_key)) } @@ -447,28 +455,28 @@ mod test { initiator: HandshakeState2, initiator_public_key: PublicKey, responder: HandshakeState2, - responder_public_key: PublicKey + responder_static_public_key: PublicKey } impl TestCtx { fn new() -> Self { let curve = secp256k1::Secp256k1::new(); - let initiator_private_key = SecretKey::from_slice(&[0x_11_u8; 32]).unwrap(); - let initiator_public_key = PublicKey::from_secret_key(&curve, &initiator_private_key); + let initiator_static_private_key = SecretKey::from_slice(&[0x_11_u8; 32]).unwrap(); + let initiator_public_key = PublicKey::from_secret_key(&curve, &initiator_static_private_key); let initiator_ephemeral_private_key = SecretKey::from_slice(&[0x_12_u8; 32]).unwrap(); - let responder_private_key = SecretKey::from_slice(&[0x_21_u8; 32]).unwrap(); - let responder_public_key = PublicKey::from_secret_key(&curve, &responder_private_key); + let responder_static_private_key = SecretKey::from_slice(&[0x_21_u8; 32]).unwrap(); + let responder_static_public_key = PublicKey::from_secret_key(&curve, &responder_static_private_key); let responder_ephemeral_private_key = SecretKey::from_slice(&[0x_22_u8; 32]).unwrap(); - let initiator = UninitiatedHandshakeState::new(initiator_private_key, initiator_ephemeral_private_key, responder_public_key); - let responder = AwaitingActOneHandshakeState::new(responder_private_key, responder_ephemeral_private_key); + let initiator = UninitiatedHandshakeState::new(initiator_static_private_key, initiator_ephemeral_private_key, responder_static_public_key); + let responder = AwaitingActOneHandshakeState::new(responder_static_private_key, responder_ephemeral_private_key); TestCtx { initiator: HandshakeState2::Uninitiated2(initiator), initiator_public_key, responder: HandshakeState2::AwaitingActOne2(responder), - responder_public_key, + responder_static_public_key, } } } @@ -586,7 +594,7 @@ mod test { panic!(); }; - assert_eq!(remote_pubkey, test_ctx.responder_public_key); + assert_eq!(remote_pubkey, test_ctx.responder_static_public_key); } // Initiator::AwaitingActTwo -> Complete (with extra data) From 3eb3146a473273d54243353f60f2f5a577ff5b50 Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Wed, 19 Aug 2020 18:07:19 -0700 Subject: [PATCH 11/72] refactor: Structure creation of Act3 based on RFC Identify potential issue regarding the invalid case where the read_buffer has data after completion. --- lightning/src/ln/peers/handshake/states.rs | 70 +++++++++++++--------- 1 file changed, 42 insertions(+), 28 deletions(-) diff --git a/lightning/src/ln/peers/handshake/states.rs b/lightning/src/ln/peers/handshake/states.rs index 80b2e89b19d..7f325b0d524 100644 --- a/lightning/src/ln/peers/handshake/states.rs +++ b/lightning/src/ln/peers/handshake/states.rs @@ -10,6 +10,26 @@ use ln::peers::handshake::states::HandshakeState2::{AwaitingActTwo2, AwaitingAct use ln::peers::{chacha, hkdf}; use ln::peers::conduit::{Conduit, SymmetricKey}; +// Generate a SHA-256 hash from one or more elements +macro_rules! sha256 { + ( $( $x:expr ),+ ) => {{ + let mut sha = Sha256::engine(); + $( + sha.input($x.as_ref()); + )+ + Sha256::from_engine(sha).into_inner() + }} +} + +// Concatenate two slices in a Vec +macro_rules! concat { + ($arg1:expr, $arg2:expr) => {{ + let mut result = $arg1.to_vec(); + result.extend_from_slice($arg2.as_ref()); + result + }} +} + pub enum HandshakeState2 { Uninitiated2(UninitiatedHandshakeState), AwaitingActOne2(AwaitingActOneHandshakeState), @@ -188,24 +208,38 @@ impl IHandshakeState for AwaitingActTwoHandshakeState { )?; // start serializing act three - - let curve = secp256k1::Secp256k1::new(); - let initiator_public_key = PublicKey::from_secret_key(&curve, &initiator_static_private_key); + // 1. c = encryptWithAD(temp_k2, 1, h, s.pub.serializeCompressed()) + let initiator_public_key = private_key_to_public_key(&initiator_static_private_key); let tagged_encrypted_pubkey = chacha::encrypt(&temporary_key, 1, &hash.value, &initiator_public_key.serialize()); - hash.update(&tagged_encrypted_pubkey); + // 2. h = SHA-256(h || c) + let hash = sha256!(concat!(hash.value, tagged_encrypted_pubkey)); + + // 3. se = ECDH(s.priv, re) let ecdh = ecdh(&initiator_static_private_key, &responder_ephemeral_public_key); + + // 4. ck, temp_k3 = HKDF(ck, se) let (chaining_key, temporary_key) = hkdf::derive(&chaining_key, &ecdh); - let authentication_tag = chacha::encrypt(&temporary_key, 0, &hash.value, &[0; 0]); + + // 5. t = encryptWithAD(temp_k3, 0, h, zero) + let authentication_tag = chacha::encrypt(&temporary_key, 0, &hash, &[0; 0]); + + // 6. sk, rk = HKDF(ck, zero) let (sending_key, receiving_key) = hkdf::derive(&chaining_key, &[0; 0]); + // 7. rn = 0, sn = 0 + // - done by Conduit + let mut conduit = Conduit::new(sending_key, receiving_key, chaining_key); + + // Send m = 0 || c || t over the network buffer let mut act_three = [0u8; ACT_THREE_LENGTH]; act_three[1..50].copy_from_slice(&tagged_encrypted_pubkey); act_three[50..].copy_from_slice(authentication_tag.as_slice()); - let mut conduit = Conduit::new(sending_key, receiving_key, chaining_key); - - if read_buffer.len() > 0 { // have we received more data still? + // Any remaining data in the read buffer would be encrypted, so transfer ownership + // to the Conduit for future use. In this case, it is unlikely that any valid data + // exists, since the responder doesn't have Act3 + if read_buffer.len() > 0 { conduit.read(&read_buffer[..]); read_buffer.drain(..); } @@ -302,26 +336,6 @@ impl AwaitingActThreeHandshakeState { } } -// Generate a SHA-256 hash from one or more elements -macro_rules! sha256 { - ( $( $x:expr ),+ ) => {{ - let mut sha = Sha256::engine(); - $( - sha.input($x.as_ref()); - )* - Sha256::from_engine(sha).into_inner() - }} -} - -// Concatenate two slices in a Vec -macro_rules! concat { - ($arg1:expr, $arg2:expr) => {{ - let mut result = $arg1.to_vec(); - result.extend_from_slice($arg2.as_ref()); - result - }} -} - // https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#handshake-state-initialization fn handshake_state_initialization(responder_static_public_key: &PublicKey) -> (HandshakeHash, [u8; 32]) { let protocol_name = b"Noise_XK_secp256k1_ChaChaPoly_SHA256"; From dbd1a2117e339a45b4331d9dcb1cb1a5e96fd7fd Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Wed, 19 Aug 2020 18:17:49 -0700 Subject: [PATCH 12/72] refactor: Structure receiving of Act3 data based on RFC --- lightning/src/ln/peers/handshake/states.rs | 30 +++++++++++++++++----- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/lightning/src/ln/peers/handshake/states.rs b/lightning/src/ln/peers/handshake/states.rs index 7f325b0d524..3c844d4a4b2 100644 --- a/lightning/src/ln/peers/handshake/states.rs +++ b/lightning/src/ln/peers/handshake/states.rs @@ -265,6 +265,7 @@ impl AwaitingActTwoHandshakeState { } impl IHandshakeState for AwaitingActThreeHandshakeState { + // https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#act-three (receiver) fn next(self, input: &[u8]) -> Result<(Option, HandshakeState2), String> { let mut read_buffer = self.read_buffer; read_buffer.extend_from_slice(input); @@ -278,15 +279,13 @@ impl IHandshakeState for AwaitingActThreeHandshakeState { let responder_ephemeral_private_key = self.responder_ephemeral_private_key; let chaining_key = self.chaining_key; + // 1. Read exactly 66 bytes from the network buffer let mut act_three_bytes = [0u8; ACT_THREE_LENGTH]; act_three_bytes.copy_from_slice(&read_buffer[..ACT_THREE_LENGTH]); read_buffer.drain(..ACT_THREE_LENGTH); + // 2. Parse the read message (m) into v, c, and t let version = act_three_bytes[0]; - if version != 0 { - // this should not crash the process, hence no panic - return Err("unexpected version".to_string()); - } let mut tagged_encrypted_pubkey = [0u8; 49]; tagged_encrypted_pubkey.copy_from_slice(&act_three_bytes[1..50]); @@ -294,6 +293,13 @@ impl IHandshakeState for AwaitingActThreeHandshakeState { let mut chacha_tag = [0u8; 16]; chacha_tag.copy_from_slice(&act_three_bytes[50..66]); + // 3. If v is an unrecognized handshake version, then the responder MUST abort the connection attempt. + if version != 0 { + // this should not crash the process, hence no panic + return Err("unexpected version".to_string()); + } + + // 4. rs = decryptWithAD(temp_k2, 1, h, c) let remote_pubkey_vec = chacha::decrypt(&temporary_key, 1, &hash.value, &tagged_encrypted_pubkey)?; let mut initiator_pubkey_bytes = [0u8; 33]; initiator_pubkey_bytes.copy_from_slice(remote_pubkey_vec.as_slice()); @@ -303,15 +309,27 @@ impl IHandshakeState for AwaitingActThreeHandshakeState { return Err("invalid remote public key".to_string()); }; - hash.update(&tagged_encrypted_pubkey); + // 5. h = SHA-256(h || c) + let hash = sha256!(concat!(hash.value, tagged_encrypted_pubkey)); + // 6. se = ECDH(e.priv, rs) let ecdh = ecdh(&responder_ephemeral_private_key, &initiator_pubkey); + + // 7. ck, temp_k3 = HKDF(ck, se) let (chaining_key, temporary_key) = hkdf::derive(&chaining_key, &ecdh); - let _tag_check = chacha::decrypt(&temporary_key, 0, &hash.value, &chacha_tag)?; + + // 8. p = decryptWithAD(temp_k3, 0, h, t) + let _tag_check = chacha::decrypt(&temporary_key, 0, &hash, &chacha_tag)?; + + // 9. rk, sk = HKDF(ck, zero) let (receiving_key, sending_key) = hkdf::derive(&chaining_key, &[0; 0]); + // 10. rn = 0, sn = 0 + // - done by Conduit let mut conduit = Conduit::new(sending_key, receiving_key, chaining_key); + // Any remaining data in the read buffer would be encrypted, so transfer ownership + // to the Conduit for future use. if read_buffer.len() > 0 { // have we received more data still? conduit.read(&read_buffer[..]); read_buffer.drain(..); From c88535af1fc39bf6d474a6c77a893f982170a383 Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Wed, 19 Aug 2020 18:27:29 -0700 Subject: [PATCH 13/72] refactor: Inline single use constructors --- lightning/src/ln/peers/handshake/states.rs | 44 ++++++++-------------- 1 file changed, 15 insertions(+), 29 deletions(-) diff --git a/lightning/src/ln/peers/handshake/states.rs b/lightning/src/ln/peers/handshake/states.rs index 3c844d4a4b2..4b4504956dd 100644 --- a/lightning/src/ln/peers/handshake/states.rs +++ b/lightning/src/ln/peers/handshake/states.rs @@ -114,7 +114,14 @@ impl IHandshakeState for UninitiatedHandshakeState { Ok(( Some(Act::One(ActOne(act_one))), - AwaitingActTwo2(AwaitingActTwoHandshakeState::new(initiator_static_private_key, initiator_ephemeral_private_key, responder_static_public_key, chaining_key, hash)) + AwaitingActTwo2(AwaitingActTwoHandshakeState { + initiator_static_private_key, + initiator_ephemeral_private_key, + responder_static_public_key, + chaining_key, + hash, + read_buffer: Vec::new() + }) )) } } @@ -171,9 +178,13 @@ impl IHandshakeState for AwaitingActOneHandshakeState { Ok(( Some(Act::Two(ActTwo(act_two))), - AwaitingActThree2( - AwaitingActThreeHandshakeState::new(hash, responder_ephemeral_private_key, chaining_key, temporary_key, read_buffer) - ) + AwaitingActThree2(AwaitingActThreeHandshakeState { + hash, + responder_ephemeral_private_key, + chaining_key, + temporary_key, + read_buffer + }) )) } } @@ -251,19 +262,6 @@ impl IHandshakeState for AwaitingActTwoHandshakeState { } } -impl AwaitingActTwoHandshakeState { - fn new(initiator_static_private_key: SecretKey, initiator_ephemeral_private_key: SecretKey, responder_static_public_key: PublicKey, chaining_key: [u8;32], hash: HandshakeHash) -> Self { - AwaitingActTwoHandshakeState { - initiator_static_private_key, - initiator_ephemeral_private_key, - responder_static_public_key, - chaining_key, - hash, - read_buffer: Vec::new() - } - } -} - impl IHandshakeState for AwaitingActThreeHandshakeState { // https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#act-three (receiver) fn next(self, input: &[u8]) -> Result<(Option, HandshakeState2), String> { @@ -342,18 +340,6 @@ impl IHandshakeState for AwaitingActThreeHandshakeState { } } -impl AwaitingActThreeHandshakeState { - fn new(hash: HandshakeHash, responder_ephemeral_private_key: SecretKey, chaining_key: [u8; 32], temporary_key: [u8; 32], read_buffer: Vec) -> Self { - AwaitingActThreeHandshakeState { - hash, - responder_ephemeral_private_key, - chaining_key, - temporary_key, - read_buffer - } - } -} - // https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#handshake-state-initialization fn handshake_state_initialization(responder_static_public_key: &PublicKey) -> (HandshakeHash, [u8; 32]) { let protocol_name = b"Noise_XK_secp256k1_ChaChaPoly_SHA256"; From 41a8ac01b47c06e1786aef1259e40e632e7d176a Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Wed, 19 Aug 2020 18:46:45 -0700 Subject: [PATCH 14/72] refactor: Pull public key generation to the initial states All needed public keys are calculated during the first state and passed through as needed. Limits the call sites of private_key_to_public and makes the initial states for the initiator and responder more symmetric. --- lightning/src/ln/peers/handshake/states.rs | 35 +++++++++++++++++----- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/lightning/src/ln/peers/handshake/states.rs b/lightning/src/ln/peers/handshake/states.rs index 4b4504956dd..bfc0f093b62 100644 --- a/lightning/src/ln/peers/handshake/states.rs +++ b/lightning/src/ln/peers/handshake/states.rs @@ -56,13 +56,18 @@ trait IHandshakeState { pub struct UninitiatedHandshakeState { initiator_static_private_key: SecretKey, + initiator_static_public_key: PublicKey, initiator_ephemeral_private_key: SecretKey, + initiator_ephemeral_public_key: PublicKey, responder_static_public_key: PublicKey, + chaining_key: [u8; 32], + hash: HandshakeHash, } pub struct AwaitingActOneHandshakeState { responder_static_private_key: SecretKey, responder_ephemeral_private_key: SecretKey, + responder_ephemeral_public_key: PublicKey, chaining_key: [u8; 32], hash: HandshakeHash, read_buffer: Vec @@ -70,6 +75,7 @@ pub struct AwaitingActOneHandshakeState { pub struct AwaitingActTwoHandshakeState { initiator_static_private_key: SecretKey, + initiator_static_public_key: PublicKey, initiator_ephemeral_private_key: SecretKey, responder_static_public_key: PublicKey, chaining_key: [u8; 32], @@ -87,10 +93,17 @@ pub struct AwaitingActThreeHandshakeState { impl UninitiatedHandshakeState { pub(crate) fn new(initiator_static_private_key: SecretKey, initiator_ephemeral_private_key: SecretKey, responder_static_public_key: PublicKey) -> Self { + let initiator_static_public_key = private_key_to_public_key(&initiator_static_private_key); + let (hash, chaining_key) = handshake_state_initialization(&responder_static_public_key); + let initiator_ephemeral_public_key = private_key_to_public_key(&initiator_ephemeral_private_key); UninitiatedHandshakeState { initiator_static_private_key, + initiator_static_public_key, initiator_ephemeral_private_key, - responder_static_public_key + initiator_ephemeral_public_key, + responder_static_public_key, + chaining_key, + hash } } } @@ -99,14 +112,17 @@ impl IHandshakeState for UninitiatedHandshakeState { // https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#act-one (sender) fn next(self, _input: &[u8]) -> Result<(Option, HandshakeState2), String> { let initiator_static_private_key = self.initiator_static_private_key; + let initiator_static_public_key = self.initiator_static_public_key; let initiator_ephemeral_private_key = self.initiator_ephemeral_private_key; + let initiator_ephemeral_public_key = self.initiator_ephemeral_public_key; let responder_static_public_key = self.responder_static_public_key; - - let (mut hash, chaining_key) = handshake_state_initialization(&responder_static_public_key); + let chaining_key = self.chaining_key; + let mut hash = self.hash; // serialize act one let (act_one, chaining_key, _) = calculate_act_message( &initiator_ephemeral_private_key, + &initiator_ephemeral_public_key, &responder_static_public_key, chaining_key, &mut hash, @@ -116,6 +132,7 @@ impl IHandshakeState for UninitiatedHandshakeState { Some(Act::One(ActOne(act_one))), AwaitingActTwo2(AwaitingActTwoHandshakeState { initiator_static_private_key, + initiator_static_public_key, initiator_ephemeral_private_key, responder_static_public_key, chaining_key, @@ -130,10 +147,12 @@ impl AwaitingActOneHandshakeState { pub(crate) fn new(responder_static_private_key: SecretKey, responder_ephemeral_private_key: SecretKey) -> Self { let responder_static_public_key = private_key_to_public_key(&responder_static_private_key); let (hash, chaining_key) = handshake_state_initialization(&responder_static_public_key); + let responder_ephemeral_public_key = private_key_to_public_key(&responder_ephemeral_private_key); AwaitingActOneHandshakeState { responder_static_private_key, responder_ephemeral_private_key, + responder_ephemeral_public_key, chaining_key, hash, read_buffer: Vec::new() @@ -156,6 +175,7 @@ impl IHandshakeState for AwaitingActOneHandshakeState { let responder_static_private_key = self.responder_static_private_key; let chaining_key = self.chaining_key; let responder_ephemeral_private_key = self.responder_ephemeral_private_key; + let responder_ephemeral_public_key = self.responder_ephemeral_public_key; // 1. Read exactly 50 bytes from the network buffer let mut act_one_bytes = [0u8; ACT_ONE_LENGTH]; @@ -171,6 +191,7 @@ impl IHandshakeState for AwaitingActOneHandshakeState { let (act_two, chaining_key, temporary_key) = calculate_act_message( &responder_ephemeral_private_key, + &responder_ephemeral_public_key, &initiator_ephemeral_public_key, chaining_key, &mut hash, @@ -201,6 +222,7 @@ impl IHandshakeState for AwaitingActTwoHandshakeState { } let initiator_static_private_key = self.initiator_static_private_key; + let initiator_static_public_key = self.initiator_static_public_key; let initiator_ephemeral_private_key = self.initiator_ephemeral_private_key; let responder_static_public_key = self.responder_static_public_key; let mut hash = self.hash; @@ -220,8 +242,7 @@ impl IHandshakeState for AwaitingActTwoHandshakeState { // start serializing act three // 1. c = encryptWithAD(temp_k2, 1, h, s.pub.serializeCompressed()) - let initiator_public_key = private_key_to_public_key(&initiator_static_private_key); - let tagged_encrypted_pubkey = chacha::encrypt(&temporary_key, 1, &hash.value, &initiator_public_key.serialize()); + let tagged_encrypted_pubkey = chacha::encrypt(&temporary_key, 1, &hash.value, &initiator_static_public_key.serialize()); // 2. h = SHA-256(h || c) let hash = sha256!(concat!(hash.value, tagged_encrypted_pubkey)); @@ -364,10 +385,10 @@ fn handshake_state_initialization(responder_static_public_key: &PublicKey) -> (H // https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#act-one (sender) // https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#act-two (sender) -fn calculate_act_message(local_private_ephemeral_key: &SecretKey, remote_public_key: &PublicKey, chaining_key: [u8; 32], h_hash: &mut HandshakeHash) -> ([u8; 50], SymmetricKey, SymmetricKey) { +fn calculate_act_message(local_private_ephemeral_key: &SecretKey, local_public_ephemeral_key: &PublicKey, remote_public_key: &PublicKey, chaining_key: [u8; 32], h_hash: &mut HandshakeHash) -> ([u8; 50], SymmetricKey, SymmetricKey) { // 1. e = generateKey() (passed in) // 2. h = SHA-256(h || e.pub.serializeCompressed()) - let serialized_local_public_key = private_key_to_public_key(local_private_ephemeral_key).serialize(); + let serialized_local_public_key = local_public_ephemeral_key.serialize(); let hash = sha256!(concat!(h_hash.value, serialized_local_public_key)); // 3. ACT1: es = ECDH(e.priv, rs) From 8f17345046f2f94004743a17ddd4da75339a6799 Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Wed, 19 Aug 2020 19:00:32 -0700 Subject: [PATCH 15/72] refactor: Remove HandshakeHash wrapper struct Create an alias type to make the code more readable and change all usages to moves. --- lightning/src/ln/peers/handshake/hash.rs | 25 ---------- lightning/src/ln/peers/handshake/mod.rs | 1 - lightning/src/ln/peers/handshake/states.rs | 56 ++++++++++------------ 3 files changed, 25 insertions(+), 57 deletions(-) delete mode 100644 lightning/src/ln/peers/handshake/hash.rs diff --git a/lightning/src/ln/peers/handshake/hash.rs b/lightning/src/ln/peers/handshake/hash.rs deleted file mode 100644 index e7704b37e34..00000000000 --- a/lightning/src/ln/peers/handshake/hash.rs +++ /dev/null @@ -1,25 +0,0 @@ -use bitcoin::hashes::{Hash, HashEngine}; -use bitcoin::hashes::sha256::Hash as Sha256; - -pub(crate) struct HandshakeHash { - pub(super) value: [u8; 32] -} - -impl HandshakeHash { - pub(super) fn new(first_input: &[u8]) -> Self { - let mut hash = Self { - value: [0; 32] - }; - let mut sha = Sha256::engine(); - sha.input(first_input); - hash.value = Sha256::from_engine(sha).into_inner(); - hash - } - - pub(super) fn update(&mut self, input: &[u8]) { - let mut sha = Sha256::engine(); - sha.input(self.value.as_ref()); - sha.input(input); - self.value = Sha256::from_engine(sha).into_inner(); - } -} diff --git a/lightning/src/ln/peers/handshake/mod.rs b/lightning/src/ln/peers/handshake/mod.rs index d2eb122c7c2..13eae16a380 100644 --- a/lightning/src/ln/peers/handshake/mod.rs +++ b/lightning/src/ln/peers/handshake/mod.rs @@ -9,7 +9,6 @@ use ln::peers::handshake::acts::Act; use ln::peers::handshake::states::{HandshakeState2, UninitiatedHandshakeState, AwaitingActOneHandshakeState}; mod acts; -mod hash; mod states; /// Object for managing handshakes. diff --git a/lightning/src/ln/peers/handshake/states.rs b/lightning/src/ln/peers/handshake/states.rs index bfc0f093b62..6ef7fe86dea 100644 --- a/lightning/src/ln/peers/handshake/states.rs +++ b/lightning/src/ln/peers/handshake/states.rs @@ -4,12 +4,14 @@ use bitcoin::hashes::{Hash, HashEngine}; use bitcoin::hashes::sha256::Hash as Sha256; use bitcoin::secp256k1::{SecretKey, PublicKey}; -use ln::peers::handshake::hash::HandshakeHash; use ln::peers::handshake::acts::{Act, ActOne, ACT_ONE_LENGTH, ActTwo, ACT_TWO_LENGTH, ACT_THREE_LENGTH, ActThree}; use ln::peers::handshake::states::HandshakeState2::{AwaitingActTwo2, AwaitingActThree2, Complete2}; use ln::peers::{chacha, hkdf}; use ln::peers::conduit::{Conduit, SymmetricKey}; +// Alias type to allow passing handshake hash easier +type HandshakeHash = [u8; 32]; + // Generate a SHA-256 hash from one or more elements macro_rules! sha256 { ( $( $x:expr ),+ ) => {{ @@ -120,12 +122,12 @@ impl IHandshakeState for UninitiatedHandshakeState { let mut hash = self.hash; // serialize act one - let (act_one, chaining_key, _) = calculate_act_message( + let (act_one, hash, chaining_key, _) = calculate_act_message( &initiator_ephemeral_private_key, &initiator_ephemeral_public_key, &responder_static_public_key, chaining_key, - &mut hash, + hash, ); Ok(( @@ -171,7 +173,7 @@ impl IHandshakeState for AwaitingActOneHandshakeState { return Err("need at least 50 bytes".to_string()); } - let mut hash = self.hash; + let hash = self.hash; let responder_static_private_key = self.responder_static_private_key; let chaining_key = self.chaining_key; let responder_ephemeral_private_key = self.responder_ephemeral_private_key; @@ -182,19 +184,19 @@ impl IHandshakeState for AwaitingActOneHandshakeState { act_one_bytes.copy_from_slice(&read_buffer[..ACT_ONE_LENGTH]); read_buffer.drain(..ACT_ONE_LENGTH); - let (initiator_ephemeral_public_key, chaining_key, _) = process_act_message( + let (initiator_ephemeral_public_key, hash, chaining_key, _) = process_act_message( act_one_bytes, &responder_static_private_key, chaining_key, - &mut hash, + hash, )?; - let (act_two, chaining_key, temporary_key) = calculate_act_message( + let (act_two, hash, chaining_key, temporary_key) = calculate_act_message( &responder_ephemeral_private_key, &responder_ephemeral_public_key, &initiator_ephemeral_public_key, chaining_key, - &mut hash, + hash, ); Ok(( @@ -225,7 +227,7 @@ impl IHandshakeState for AwaitingActTwoHandshakeState { let initiator_static_public_key = self.initiator_static_public_key; let initiator_ephemeral_private_key = self.initiator_ephemeral_private_key; let responder_static_public_key = self.responder_static_public_key; - let mut hash = self.hash; + let hash = self.hash; let chaining_key = self.chaining_key; // 1. Read exactly 50 bytes from the network buffer @@ -233,19 +235,19 @@ impl IHandshakeState for AwaitingActTwoHandshakeState { act_two_bytes.copy_from_slice(&read_buffer[..ACT_TWO_LENGTH]); read_buffer.drain(..ACT_TWO_LENGTH); - let (responder_ephemeral_public_key, chaining_key, temporary_key) = process_act_message( + let (responder_ephemeral_public_key, hash, chaining_key, temporary_key) = process_act_message( act_two_bytes, &initiator_ephemeral_private_key, chaining_key, - &mut hash, + hash, )?; // start serializing act three // 1. c = encryptWithAD(temp_k2, 1, h, s.pub.serializeCompressed()) - let tagged_encrypted_pubkey = chacha::encrypt(&temporary_key, 1, &hash.value, &initiator_static_public_key.serialize()); + let tagged_encrypted_pubkey = chacha::encrypt(&temporary_key, 1, &hash, &initiator_static_public_key.serialize()); // 2. h = SHA-256(h || c) - let hash = sha256!(concat!(hash.value, tagged_encrypted_pubkey)); + let hash = sha256!(concat!(hash, tagged_encrypted_pubkey)); // 3. se = ECDH(s.priv, re) let ecdh = ecdh(&initiator_static_private_key, &responder_ephemeral_public_key); @@ -319,7 +321,7 @@ impl IHandshakeState for AwaitingActThreeHandshakeState { } // 4. rs = decryptWithAD(temp_k2, 1, h, c) - let remote_pubkey_vec = chacha::decrypt(&temporary_key, 1, &hash.value, &tagged_encrypted_pubkey)?; + let remote_pubkey_vec = chacha::decrypt(&temporary_key, 1, &hash, &tagged_encrypted_pubkey)?; let mut initiator_pubkey_bytes = [0u8; 33]; initiator_pubkey_bytes.copy_from_slice(remote_pubkey_vec.as_slice()); let initiator_pubkey = if let Ok(public_key) = PublicKey::from_slice(&initiator_pubkey_bytes) { @@ -329,7 +331,7 @@ impl IHandshakeState for AwaitingActThreeHandshakeState { }; // 5. h = SHA-256(h || c) - let hash = sha256!(concat!(hash.value, tagged_encrypted_pubkey)); + let hash = sha256!(concat!(hash, tagged_encrypted_pubkey)); // 6. se = ECDH(e.priv, rs) let ecdh = ecdh(&responder_ephemeral_private_key, &initiator_pubkey); @@ -376,20 +378,16 @@ fn handshake_state_initialization(responder_static_public_key: &PublicKey) -> (H // h = SHA-256(h || responderPublicKey) let hash = sha256!(concat!(hash, responder_static_public_key.serialize())); - let hash = HandshakeHash { - value: hash - }; - (hash, chaining_key) } // https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#act-one (sender) // https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#act-two (sender) -fn calculate_act_message(local_private_ephemeral_key: &SecretKey, local_public_ephemeral_key: &PublicKey, remote_public_key: &PublicKey, chaining_key: [u8; 32], h_hash: &mut HandshakeHash) -> ([u8; 50], SymmetricKey, SymmetricKey) { +fn calculate_act_message(local_private_ephemeral_key: &SecretKey, local_public_ephemeral_key: &PublicKey, remote_public_key: &PublicKey, chaining_key: [u8; 32], hash: HandshakeHash) -> ([u8; 50], HandshakeHash, SymmetricKey, SymmetricKey) { // 1. e = generateKey() (passed in) // 2. h = SHA-256(h || e.pub.serializeCompressed()) let serialized_local_public_key = local_public_ephemeral_key.serialize(); - let hash = sha256!(concat!(h_hash.value, serialized_local_public_key)); + let hash = sha256!(concat!(hash, serialized_local_public_key)); // 3. ACT1: es = ECDH(e.priv, rs) // 3. ACT2: es = ECDH(e.priv, re) @@ -404,22 +402,20 @@ fn calculate_act_message(local_private_ephemeral_key: &SecretKey, local_public_e let tagged_ciphertext = chacha::encrypt(&temporary_key, 0, &hash, &[0; 0]); // 6. h = SHA-256(h || c) - *h_hash = HandshakeHash { - value: sha256!(hash, tagged_ciphertext) - }; + let hash = sha256!(hash, tagged_ciphertext); // Send m = 0 || e.pub.serializeCompressed() || c let mut act = [0u8; 50]; act[1..34].copy_from_slice(&serialized_local_public_key); act[34..].copy_from_slice(tagged_ciphertext.as_slice()); - (act, chaining_key, temporary_key) + (act, hash, chaining_key, temporary_key) } // Due to the very high similarity of acts 1 and 2, this method is used to process both // https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#act-one (receiver) // https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#act-two (receiver) -fn process_act_message(act_bytes: [u8; 50], local_private_key: &SecretKey, chaining_key: SymmetricKey, h_hash: &mut HandshakeHash) -> Result<(PublicKey, SymmetricKey, SymmetricKey), String> { +fn process_act_message(act_bytes: [u8; 50], local_private_key: &SecretKey, chaining_key: SymmetricKey, hash: HandshakeHash) -> Result<(PublicKey, HandshakeHash, SymmetricKey, SymmetricKey), String> { // 2.Parse the read message (m) into v, re, and c let version = act_bytes[0]; @@ -441,7 +437,7 @@ fn process_act_message(act_bytes: [u8; 50], local_private_key: &SecretKey, chain } // 4. h = SHA-256(h || re.serializeCompressed()) - let hash = sha256!(concat!(h_hash.value, ephemeral_public_key_bytes)); + let hash = sha256!(concat!(hash, ephemeral_public_key_bytes)); // 5. Act1: es = ECDH(s.priv, re) // 5. Act2: ee = ECDH(e.priv, ee) @@ -455,11 +451,9 @@ fn process_act_message(act_bytes: [u8; 50], local_private_key: &SecretKey, chain let _tag_check = chacha::decrypt(&temporary_key, 0, &hash, &chacha_tag)?; // 8. h = SHA-256(h || c) - *h_hash = HandshakeHash { - value: sha256!(concat!(hash, chacha_tag)) - }; + let hash = sha256!(concat!(hash, chacha_tag)); - Ok((ephemeral_public_key, chaining_key, temporary_key)) + Ok((ephemeral_public_key, hash, chaining_key, temporary_key)) } fn private_key_to_public_key(private_key: &SecretKey) -> PublicKey { From 988e767f4f851323aad4c1c06dd073a54d771f64 Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Wed, 19 Aug 2020 19:17:01 -0700 Subject: [PATCH 16/72] refactor: Combine duplicate code path for act1 and act2 Push buffer read into helper function Combines static length values of ActOne and ActTwo structs --- lightning/src/ln/peers/handshake/acts.rs | 7 ++-- lightning/src/ln/peers/handshake/states.rs | 37 ++++++++-------------- 2 files changed, 17 insertions(+), 27 deletions(-) diff --git a/lightning/src/ln/peers/handshake/acts.rs b/lightning/src/ln/peers/handshake/acts.rs index 4f29d0e4f8d..cddc2a1ac4b 100644 --- a/lightning/src/ln/peers/handshake/acts.rs +++ b/lightning/src/ln/peers/handshake/acts.rs @@ -1,15 +1,14 @@ -pub const ACT_ONE_LENGTH: usize = 50; -pub const ACT_TWO_LENGTH: usize = 50; +pub const ACT_ONE_TWO_LENGTH: usize = 50; pub const ACT_THREE_LENGTH: usize = 66; /// Wrapper for the first act message pub struct ActOne( - pub(super) [u8; ACT_ONE_LENGTH] + pub(super) [u8; ACT_ONE_TWO_LENGTH] ); /// Wrapper for the second act message pub struct ActTwo( - pub(super) [u8; ACT_TWO_LENGTH] + pub(super) [u8; ACT_ONE_TWO_LENGTH] ); /// Wrapper for the third act message diff --git a/lightning/src/ln/peers/handshake/states.rs b/lightning/src/ln/peers/handshake/states.rs index 6ef7fe86dea..d6c190250f7 100644 --- a/lightning/src/ln/peers/handshake/states.rs +++ b/lightning/src/ln/peers/handshake/states.rs @@ -4,7 +4,7 @@ use bitcoin::hashes::{Hash, HashEngine}; use bitcoin::hashes::sha256::Hash as Sha256; use bitcoin::secp256k1::{SecretKey, PublicKey}; -use ln::peers::handshake::acts::{Act, ActOne, ACT_ONE_LENGTH, ActTwo, ACT_TWO_LENGTH, ACT_THREE_LENGTH, ActThree}; +use ln::peers::handshake::acts::{Act, ActOne, ACT_ONE_TWO_LENGTH, ActTwo, ACT_THREE_LENGTH, ActThree}; use ln::peers::handshake::states::HandshakeState2::{AwaitingActTwo2, AwaitingActThree2, Complete2}; use ln::peers::{chacha, hkdf}; use ln::peers::conduit::{Conduit, SymmetricKey}; @@ -169,23 +169,14 @@ impl IHandshakeState for AwaitingActOneHandshakeState { let mut read_buffer = self.read_buffer; read_buffer.extend_from_slice(input); - if read_buffer.len() < ACT_ONE_LENGTH { - return Err("need at least 50 bytes".to_string()); - } - let hash = self.hash; let responder_static_private_key = self.responder_static_private_key; let chaining_key = self.chaining_key; let responder_ephemeral_private_key = self.responder_ephemeral_private_key; let responder_ephemeral_public_key = self.responder_ephemeral_public_key; - // 1. Read exactly 50 bytes from the network buffer - let mut act_one_bytes = [0u8; ACT_ONE_LENGTH]; - act_one_bytes.copy_from_slice(&read_buffer[..ACT_ONE_LENGTH]); - read_buffer.drain(..ACT_ONE_LENGTH); - let (initiator_ephemeral_public_key, hash, chaining_key, _) = process_act_message( - act_one_bytes, + &mut read_buffer, &responder_static_private_key, chaining_key, hash, @@ -219,10 +210,6 @@ impl IHandshakeState for AwaitingActTwoHandshakeState { let mut read_buffer = self.read_buffer; read_buffer.extend_from_slice(input); - if read_buffer.len() < ACT_TWO_LENGTH { - return Err("need at least 50 bytes".to_string()); - } - let initiator_static_private_key = self.initiator_static_private_key; let initiator_static_public_key = self.initiator_static_public_key; let initiator_ephemeral_private_key = self.initiator_ephemeral_private_key; @@ -230,13 +217,8 @@ impl IHandshakeState for AwaitingActTwoHandshakeState { let hash = self.hash; let chaining_key = self.chaining_key; - // 1. Read exactly 50 bytes from the network buffer - let mut act_two_bytes = [0u8; ACT_TWO_LENGTH]; - act_two_bytes.copy_from_slice(&read_buffer[..ACT_TWO_LENGTH]); - read_buffer.drain(..ACT_TWO_LENGTH); - let (responder_ephemeral_public_key, hash, chaining_key, temporary_key) = process_act_message( - act_two_bytes, + &mut read_buffer, &initiator_ephemeral_private_key, chaining_key, hash, @@ -415,7 +397,16 @@ fn calculate_act_message(local_private_ephemeral_key: &SecretKey, local_public_e // Due to the very high similarity of acts 1 and 2, this method is used to process both // https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#act-one (receiver) // https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#act-two (receiver) -fn process_act_message(act_bytes: [u8; 50], local_private_key: &SecretKey, chaining_key: SymmetricKey, hash: HandshakeHash) -> Result<(PublicKey, HandshakeHash, SymmetricKey, SymmetricKey), String> { +fn process_act_message(read_buffer: &mut Vec, local_private_key: &SecretKey, chaining_key: SymmetricKey, hash: HandshakeHash) -> Result<(PublicKey, HandshakeHash, SymmetricKey, SymmetricKey), String> { + // 1. Read exactly 50 bytes from the network buffer + if read_buffer.len() < ACT_ONE_TWO_LENGTH { + return Err("need at least 50 bytes".to_string()); + } + + let mut act_bytes = [0u8; ACT_ONE_TWO_LENGTH]; + act_bytes.copy_from_slice(&read_buffer[..ACT_ONE_TWO_LENGTH]); + read_buffer.drain(..ACT_ONE_TWO_LENGTH); + // 2.Parse the read message (m) into v, re, and c let version = act_bytes[0]; @@ -557,7 +548,7 @@ mod test { } // Responder::AwaitingActOne -> AwaitingActThree - // TODO: Should this fail since we don't expect data > ACT_ONE_LENGTH and likely indicates + // TODO: Should this fail since we don't expect data > ACT_ONE_TWO_LENGTH and likely indicates // a bad peer? // TODO: Should the behavior be changed to handle act1 data that is striped across multiple // next() calls? From 1e816425e5ac51da893beec49a2ccd7853bb983a Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Wed, 19 Aug 2020 19:18:22 -0700 Subject: [PATCH 17/72] refactor: Introduce ChainingKey type alias --- lightning/src/ln/peers/handshake/states.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lightning/src/ln/peers/handshake/states.rs b/lightning/src/ln/peers/handshake/states.rs index d6c190250f7..b9d2047fc10 100644 --- a/lightning/src/ln/peers/handshake/states.rs +++ b/lightning/src/ln/peers/handshake/states.rs @@ -11,6 +11,7 @@ use ln::peers::conduit::{Conduit, SymmetricKey}; // Alias type to allow passing handshake hash easier type HandshakeHash = [u8; 32]; +type ChainingKey = [u8; 32]; // Generate a SHA-256 hash from one or more elements macro_rules! sha256 { @@ -62,7 +63,7 @@ pub struct UninitiatedHandshakeState { initiator_ephemeral_private_key: SecretKey, initiator_ephemeral_public_key: PublicKey, responder_static_public_key: PublicKey, - chaining_key: [u8; 32], + chaining_key: ChainingKey, hash: HandshakeHash, } @@ -70,7 +71,7 @@ pub struct AwaitingActOneHandshakeState { responder_static_private_key: SecretKey, responder_ephemeral_private_key: SecretKey, responder_ephemeral_public_key: PublicKey, - chaining_key: [u8; 32], + chaining_key: ChainingKey, hash: HandshakeHash, read_buffer: Vec } @@ -80,7 +81,7 @@ pub struct AwaitingActTwoHandshakeState { initiator_static_public_key: PublicKey, initiator_ephemeral_private_key: SecretKey, responder_static_public_key: PublicKey, - chaining_key: [u8; 32], + chaining_key: ChainingKey, hash: HandshakeHash, read_buffer: Vec } @@ -88,7 +89,7 @@ pub struct AwaitingActTwoHandshakeState { pub struct AwaitingActThreeHandshakeState { hash: HandshakeHash, responder_ephemeral_private_key: SecretKey, - chaining_key: [u8; 32], + chaining_key: ChainingKey, temporary_key: [u8; 32], read_buffer: Vec } @@ -346,7 +347,7 @@ impl IHandshakeState for AwaitingActThreeHandshakeState { } // https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#handshake-state-initialization -fn handshake_state_initialization(responder_static_public_key: &PublicKey) -> (HandshakeHash, [u8; 32]) { +fn handshake_state_initialization(responder_static_public_key: &PublicKey) -> (HandshakeHash, ChainingKey) { let protocol_name = b"Noise_XK_secp256k1_ChaChaPoly_SHA256"; let prologue = b"lightning"; @@ -365,7 +366,7 @@ fn handshake_state_initialization(responder_static_public_key: &PublicKey) -> (H // https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#act-one (sender) // https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#act-two (sender) -fn calculate_act_message(local_private_ephemeral_key: &SecretKey, local_public_ephemeral_key: &PublicKey, remote_public_key: &PublicKey, chaining_key: [u8; 32], hash: HandshakeHash) -> ([u8; 50], HandshakeHash, SymmetricKey, SymmetricKey) { +fn calculate_act_message(local_private_ephemeral_key: &SecretKey, local_public_ephemeral_key: &PublicKey, remote_public_key: &PublicKey, chaining_key: ChainingKey, hash: HandshakeHash) -> ([u8; 50], HandshakeHash, SymmetricKey, SymmetricKey) { // 1. e = generateKey() (passed in) // 2. h = SHA-256(h || e.pub.serializeCompressed()) let serialized_local_public_key = local_public_ephemeral_key.serialize(); From de5a2e86acd6f5eb1af4e3c7046e3cc85742f73c Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Wed, 19 Aug 2020 20:16:22 -0700 Subject: [PATCH 18/72] refactor: Remove Act object All consumers immediately serialize the struct so just do it for them and hide the implementation details of the handshake. --- lightning/src/ln/peers/handler.rs | 4 +- lightning/src/ln/peers/handshake/acts.rs | 41 ----------------- lightning/src/ln/peers/handshake/mod.rs | 17 +++---- lightning/src/ln/peers/handshake/states.rs | 53 ++++++++++++---------- 4 files changed, 38 insertions(+), 77 deletions(-) delete mode 100644 lightning/src/ln/peers/handshake/acts.rs diff --git a/lightning/src/ln/peers/handler.rs b/lightning/src/ln/peers/handler.rs index 903f4ccfaa3..05431529644 100644 --- a/lightning/src/ln/peers/handler.rs +++ b/lightning/src/ln/peers/handler.rs @@ -155,7 +155,7 @@ impl PeerState { let requires_response = next_act.is_some(); if let Some(act) = next_act { - mutable_response_buffer.push_back(act.serialize()); + mutable_response_buffer.push_back(act); } let remote_pubkey_option = handshake.get_remote_pubkey(); @@ -366,7 +366,7 @@ impl PeerManager Result, PeerHandleError> { let mut handshake = PeerHandshake::new_outbound(&self.our_node_secret, &their_node_id, &self.get_ephemeral_key()); let (act, ..) = handshake.process_act(&[]).unwrap(); - let res = act.unwrap().serialize(); + let res = act.unwrap(); let mut peers = self.peers.lock().unwrap(); if peers.peers.insert(descriptor, Peer { diff --git a/lightning/src/ln/peers/handshake/acts.rs b/lightning/src/ln/peers/handshake/acts.rs deleted file mode 100644 index cddc2a1ac4b..00000000000 --- a/lightning/src/ln/peers/handshake/acts.rs +++ /dev/null @@ -1,41 +0,0 @@ -pub const ACT_ONE_TWO_LENGTH: usize = 50; -pub const ACT_THREE_LENGTH: usize = 66; - -/// Wrapper for the first act message -pub struct ActOne( - pub(super) [u8; ACT_ONE_TWO_LENGTH] -); - -/// Wrapper for the second act message -pub struct ActTwo( - pub(super) [u8; ACT_ONE_TWO_LENGTH] -); - -/// Wrapper for the third act message -pub struct ActThree( - pub(super) [u8; ACT_THREE_LENGTH] -); - -/// Wrapper for any act message -pub enum Act { - One(ActOne), - Two(ActTwo), - Three(ActThree), -} - -impl Act { - /// Convert any act into a byte vector - pub fn serialize(&self) -> Vec { - match self { - &Act::One(ref act) => { - act.0.to_vec() - } - &Act::Two(ref act) => { - act.0.to_vec() - } - &Act::Three(ref act) => { - act.0.to_vec() - } - } - } -} \ No newline at end of file diff --git a/lightning/src/ln/peers/handshake/mod.rs b/lightning/src/ln/peers/handshake/mod.rs index 13eae16a380..07728d81fa9 100644 --- a/lightning/src/ln/peers/handshake/mod.rs +++ b/lightning/src/ln/peers/handshake/mod.rs @@ -5,10 +5,8 @@ use bitcoin::secp256k1::{PublicKey, SecretKey}; use ln::peers::conduit::Conduit; -use ln::peers::handshake::acts::Act; use ln::peers::handshake::states::{HandshakeState2, UninitiatedHandshakeState, AwaitingActOneHandshakeState}; -mod acts; mod states; /// Object for managing handshakes. @@ -49,7 +47,7 @@ impl PeerHandshake { /// Returns a tuple with the following components: /// `.0`: Byte vector containing the next act to send back to the peer per the handshake protocol /// `.1`: Conduit option if the handshake was just processed to completion and messages can now be encrypted and decrypted - pub fn process_act(&mut self, input: &[u8]) -> Result<(Option, Option), String> { + pub fn process_act(&mut self, input: &[u8]) -> Result<(Option>, Option), String> { let cur_state = self.state.take().unwrap(); let (act_opt, mut next_state) = cur_state.next(input)?; @@ -78,7 +76,6 @@ mod test { use bitcoin::secp256k1::key::{PublicKey, SecretKey}; use ln::peers::handshake::PeerHandshake; - use ln::peers::handshake::acts::Act; use ln::peers::handshake::states::HandshakeState2; struct TestCtx { @@ -123,7 +120,7 @@ mod test { macro_rules! do_process_act_or_panic { ($handshake:expr, $input:expr) => { - $handshake.process_act($input).unwrap().0.unwrap().serialize() + $handshake.process_act($input).unwrap().0.unwrap() } } @@ -154,7 +151,7 @@ mod test { fn peer_handshake_outbound_uninitiated_to_awaiting_act_two() { let mut test_ctx = TestCtx::new(); - assert_matches!(test_ctx.outbound_handshake.process_act(&[]).unwrap(), (Some(Act::One(_)), None)); + assert_matches!(test_ctx.outbound_handshake.process_act(&[]).unwrap(), (Some(_), None)); assert_matches!(test_ctx.outbound_handshake.state, Some(HandshakeState2::AwaitingActTwo2(_))); assert_eq!(test_ctx.outbound_handshake.get_remote_pubkey(), Some(test_ctx.inbound_public_key)); } @@ -165,7 +162,7 @@ mod test { let mut test_ctx = TestCtx::new(); // TODO: process_act() should error if state does not use vec, but it is non-empty - assert_matches!(test_ctx.outbound_handshake.process_act(&[1]).unwrap(), (Some(Act::One(_)), None)); + assert_matches!(test_ctx.outbound_handshake.process_act(&[1]).unwrap(), (Some(_), None)); assert_matches!(test_ctx.outbound_handshake.state, Some(HandshakeState2::AwaitingActTwo2(_))); assert_eq!(test_ctx.outbound_handshake.get_remote_pubkey(), Some(test_ctx.inbound_public_key)); } @@ -186,7 +183,7 @@ mod test { let mut act1 = do_process_act_or_panic!(test_ctx.outbound_handshake, &[]); act1.extend_from_slice(&[1]); - assert_matches!(test_ctx.inbound_handshake.process_act(&act1).unwrap(), (Some(Act::Two(_)), None)); + assert_matches!(test_ctx.inbound_handshake.process_act(&act1).unwrap(), (Some(_), None)); assert_matches!(test_ctx.inbound_handshake.state, Some(HandshakeState2::AwaitingActThree2(_))); assert!(test_ctx.inbound_handshake.get_remote_pubkey().is_none()); } @@ -230,7 +227,7 @@ mod test { let mut test_ctx = TestCtx::new(); let act1 = do_process_act_or_panic!(test_ctx.outbound_handshake, &[]); - assert_matches!(test_ctx.inbound_handshake.process_act(&act1).unwrap(), (Some(Act::Two(_)), None)); + assert_matches!(test_ctx.inbound_handshake.process_act(&act1).unwrap(), (Some(_), None)); assert_matches!(test_ctx.inbound_handshake.state, Some(HandshakeState2::AwaitingActThree2(_))); assert!(test_ctx.inbound_handshake.get_remote_pubkey().is_none()); } @@ -242,7 +239,7 @@ mod test { let act1 = do_process_act_or_panic!(test_ctx.outbound_handshake, &[]); let act2 = do_process_act_or_panic!(test_ctx.inbound_handshake, &act1); - assert_matches!(test_ctx.outbound_handshake.process_act(&act2).unwrap(), (Some(Act::Three(_)), Some(_))); + assert_matches!(test_ctx.outbound_handshake.process_act(&act2).unwrap(), (Some(_), Some(_))); assert_matches!(test_ctx.outbound_handshake.state, Some(HandshakeState2::Complete2(_))); assert_eq!(test_ctx.outbound_handshake.get_remote_pubkey(), Some(test_ctx.inbound_public_key)); } diff --git a/lightning/src/ln/peers/handshake/states.rs b/lightning/src/ln/peers/handshake/states.rs index b9d2047fc10..949ec3f5817 100644 --- a/lightning/src/ln/peers/handshake/states.rs +++ b/lightning/src/ln/peers/handshake/states.rs @@ -4,11 +4,13 @@ use bitcoin::hashes::{Hash, HashEngine}; use bitcoin::hashes::sha256::Hash as Sha256; use bitcoin::secp256k1::{SecretKey, PublicKey}; -use ln::peers::handshake::acts::{Act, ActOne, ACT_ONE_TWO_LENGTH, ActTwo, ACT_THREE_LENGTH, ActThree}; use ln::peers::handshake::states::HandshakeState2::{AwaitingActTwo2, AwaitingActThree2, Complete2}; use ln::peers::{chacha, hkdf}; use ln::peers::conduit::{Conduit, SymmetricKey}; +const ACT_ONE_TWO_LENGTH: usize = 50; +const ACT_THREE_LENGTH: usize = 66; + // Alias type to allow passing handshake hash easier type HandshakeHash = [u8; 32]; type ChainingKey = [u8; 32]; @@ -42,7 +44,7 @@ pub enum HandshakeState2 { } impl HandshakeState2 { - pub(crate) fn next(self, input: &[u8]) -> Result<(Option, HandshakeState2), String> { + pub(crate) fn next(self, input: &[u8]) -> Result<(Option>, HandshakeState2), String> { match self { HandshakeState2::Uninitiated2(state) => { state.next(input) }, HandshakeState2::AwaitingActOne2(state) => { state.next(input) }, @@ -54,7 +56,7 @@ impl HandshakeState2 { } trait IHandshakeState { - fn next(self, input: &[u8]) -> Result<(Option, HandshakeState2), String>; + fn next(self, input: &[u8]) -> Result<(Option>, HandshakeState2), String>; } pub struct UninitiatedHandshakeState { @@ -113,7 +115,7 @@ impl UninitiatedHandshakeState { impl IHandshakeState for UninitiatedHandshakeState { // https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#act-one (sender) - fn next(self, _input: &[u8]) -> Result<(Option, HandshakeState2), String> { + fn next(self, _input: &[u8]) -> Result<(Option>, HandshakeState2), String> { let initiator_static_private_key = self.initiator_static_private_key; let initiator_static_public_key = self.initiator_static_public_key; let initiator_ephemeral_private_key = self.initiator_ephemeral_private_key; @@ -132,7 +134,7 @@ impl IHandshakeState for UninitiatedHandshakeState { ); Ok(( - Some(Act::One(ActOne(act_one))), + Some(act_one.to_vec()), AwaitingActTwo2(AwaitingActTwoHandshakeState { initiator_static_private_key, initiator_static_public_key, @@ -166,7 +168,7 @@ impl AwaitingActOneHandshakeState { impl IHandshakeState for AwaitingActOneHandshakeState { // https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#act-one (receiver) // https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#act-two (sender) - fn next(self, input: &[u8]) -> Result<(Option, HandshakeState2), String> { + fn next(self, input: &[u8]) -> Result<(Option>, HandshakeState2), String> { let mut read_buffer = self.read_buffer; read_buffer.extend_from_slice(input); @@ -192,7 +194,7 @@ impl IHandshakeState for AwaitingActOneHandshakeState { ); Ok(( - Some(Act::Two(ActTwo(act_two))), + Some(act_two), AwaitingActThree2(AwaitingActThreeHandshakeState { hash, responder_ephemeral_private_key, @@ -207,7 +209,7 @@ impl IHandshakeState for AwaitingActOneHandshakeState { impl IHandshakeState for AwaitingActTwoHandshakeState { // https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#act-two (receiver) // https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#act-three (sender) - fn next(self, input: &[u8]) -> Result<(Option, HandshakeState2), String> { + fn next(self, input: &[u8]) -> Result<(Option>, HandshakeState2), String> { let mut read_buffer = self.read_buffer; read_buffer.extend_from_slice(input); @@ -249,9 +251,11 @@ impl IHandshakeState for AwaitingActTwoHandshakeState { let mut conduit = Conduit::new(sending_key, receiving_key, chaining_key); // Send m = 0 || c || t over the network buffer - let mut act_three = [0u8; ACT_THREE_LENGTH]; - act_three[1..50].copy_from_slice(&tagged_encrypted_pubkey); - act_three[50..].copy_from_slice(authentication_tag.as_slice()); + let mut act_three = Vec::with_capacity(ACT_THREE_LENGTH); + act_three.extend(&[0]); + act_three.extend(&tagged_encrypted_pubkey); + act_three.extend(&authentication_tag); + assert_eq!(act_three.len(), ACT_THREE_LENGTH); // Any remaining data in the read buffer would be encrypted, so transfer ownership // to the Conduit for future use. In this case, it is unlikely that any valid data @@ -262,7 +266,7 @@ impl IHandshakeState for AwaitingActTwoHandshakeState { } Ok(( - Some(Act::Three(ActThree(act_three))), + Some(act_three), Complete2(Some((conduit, responder_static_public_key))) )) } @@ -270,7 +274,7 @@ impl IHandshakeState for AwaitingActTwoHandshakeState { impl IHandshakeState for AwaitingActThreeHandshakeState { // https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#act-three (receiver) - fn next(self, input: &[u8]) -> Result<(Option, HandshakeState2), String> { + fn next(self, input: &[u8]) -> Result<(Option>, HandshakeState2), String> { let mut read_buffer = self.read_buffer; read_buffer.extend_from_slice(input); @@ -366,7 +370,7 @@ fn handshake_state_initialization(responder_static_public_key: &PublicKey) -> (H // https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#act-one (sender) // https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#act-two (sender) -fn calculate_act_message(local_private_ephemeral_key: &SecretKey, local_public_ephemeral_key: &PublicKey, remote_public_key: &PublicKey, chaining_key: ChainingKey, hash: HandshakeHash) -> ([u8; 50], HandshakeHash, SymmetricKey, SymmetricKey) { +fn calculate_act_message(local_private_ephemeral_key: &SecretKey, local_public_ephemeral_key: &PublicKey, remote_public_key: &PublicKey, chaining_key: ChainingKey, hash: HandshakeHash) -> (Vec, HandshakeHash, SymmetricKey, SymmetricKey) { // 1. e = generateKey() (passed in) // 2. h = SHA-256(h || e.pub.serializeCompressed()) let serialized_local_public_key = local_public_ephemeral_key.serialize(); @@ -388,9 +392,11 @@ fn calculate_act_message(local_private_ephemeral_key: &SecretKey, local_public_e let hash = sha256!(hash, tagged_ciphertext); // Send m = 0 || e.pub.serializeCompressed() || c - let mut act = [0u8; 50]; - act[1..34].copy_from_slice(&serialized_local_public_key); - act[34..].copy_from_slice(tagged_ciphertext.as_slice()); + let mut act = Vec::with_capacity(ACT_ONE_TWO_LENGTH); + act.extend(&[0]); + act.extend_from_slice(&serialized_local_public_key); + act.extend(&tagged_ciphertext); + assert_eq!(act.len(), ACT_ONE_TWO_LENGTH); (act, hash, chaining_key, temporary_key) } @@ -472,7 +478,6 @@ mod test { use bitcoin::secp256k1; use bitcoin::secp256k1::{PublicKey, SecretKey}; - use ln::peers::handshake::acts::Act; use ln::peers::handshake::states::{UninitiatedHandshakeState, AwaitingActOneHandshakeState, HandshakeState2}; use ln::peers::handshake::states::HandshakeState2::{AwaitingActThree2, AwaitingActTwo2, Complete2}; @@ -509,7 +514,7 @@ mod test { macro_rules! do_next_or_panic { ($state:expr, $input:expr) => { if let (Some(output_act), next_state) = $state.next($input).unwrap() { - (output_act.serialize(), next_state) + (output_act, next_state) } else { panic!(); } @@ -530,7 +535,7 @@ mod test { fn uninitiated_to_awaiting_act_two() { let test_ctx = TestCtx::new(); - assert_matches!(test_ctx.initiator.next(&[]).unwrap(), (Some(Act::One(_)), AwaitingActTwo2(_))); + assert_matches!(test_ctx.initiator.next(&[]).unwrap(), (Some(_), AwaitingActTwo2(_))); } // Initiator::Uninitiated -> AwaitingActTwo (extra bytes in argument) @@ -538,7 +543,7 @@ mod test { fn uninitiated_to_awaiting_act_two_extra_bytes() { let test_ctx = TestCtx::new(); - assert_matches!(test_ctx.initiator.next(&[1]).unwrap(), (Some(Act::One(_)), AwaitingActTwo2(_))); + assert_matches!(test_ctx.initiator.next(&[1]).unwrap(), (Some(_), AwaitingActTwo2(_))); } // Responder::AwaitingActOne -> Error (input too small) @@ -559,7 +564,7 @@ mod test { let (mut act1, _) = do_next_or_panic!(test_ctx.initiator, &[]); act1.extend_from_slice(&[1]); - assert_matches!(test_ctx.responder.next(&act1).unwrap(), (Some(Act::Two(_)), AwaitingActThree2(_))); + assert_matches!(test_ctx.responder.next(&act1).unwrap(), (Some(_), AwaitingActThree2(_))); } // Responder::AwaitingActOne -> Error (bad version byte) @@ -603,7 +608,7 @@ mod test { let test_ctx = TestCtx::new(); let (act1, _) = do_next_or_panic!(test_ctx.initiator, &[]); - assert_matches!(test_ctx.responder.next(&act1).unwrap(), (Some(Act::Two(_)), AwaitingActThree2(_))); + assert_matches!(test_ctx.responder.next(&act1).unwrap(), (Some(_), AwaitingActThree2(_))); } // Initiator::AwaitingActTwo -> Complete @@ -613,7 +618,7 @@ mod test { let (act1, awaiting_act_two_state) = do_next_or_panic!(test_ctx.initiator, &[]); let (act2, _awaiting_act_three_state) = do_next_or_panic!(test_ctx.responder, &act1); - let remote_pubkey = if let (Some(Act::Three(_)), Complete2(Some((_, remote_pubkey)))) = awaiting_act_two_state.next(&act2).unwrap() { + let remote_pubkey = if let (Some(_), Complete2(Some((_, remote_pubkey)))) = awaiting_act_two_state.next(&act2).unwrap() { remote_pubkey } else { panic!(); From e0a1e71e8981a72975c219027caa31bbdd1813d6 Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Wed, 19 Aug 2020 21:21:18 -0700 Subject: [PATCH 19/72] refactor: Remove get_remote_pubkey interface Now that final process_act() returns (conduit, remote_pubkey), the callers can just receive it when the handshake is complete. --- lightning/src/ln/peers/handler.rs | 18 +++----------- lightning/src/ln/peers/handshake/mod.rs | 33 +++---------------------- 2 files changed, 8 insertions(+), 43 deletions(-) diff --git a/lightning/src/ln/peers/handler.rs b/lightning/src/ln/peers/handler.rs index 05431529644..eb92785187e 100644 --- a/lightning/src/ln/peers/handler.rs +++ b/lightning/src/ln/peers/handler.rs @@ -141,12 +141,9 @@ impl PeerState { } fn process_peer_data(&mut self, data: &[u8], mutable_response_buffer: &mut LinkedList>) -> PeerDataProcessingDecision { - let mut conduit_option = None; - let mut decision_option = None; - match self { &mut PeerState::Authenticating(ref mut handshake) => { - let (next_act, conduit) = match handshake.process_act(data) { + let (next_act, conduit_and_remote_pubkey) = match handshake.process_act(data) { Ok(act_result) => act_result, Err(e) => { return PeerDataProcessingDecision::Disconnect(PeerHandleError { no_connection_possible: false }); @@ -158,23 +155,16 @@ impl PeerState { mutable_response_buffer.push_back(act); } - let remote_pubkey_option = handshake.get_remote_pubkey(); - if let Some(conduit) = conduit { - conduit_option = Some(conduit); - decision_option = Some(PeerDataProcessingDecision::CompleteHandshake(requires_response, remote_pubkey_option)); + if let Some((conduit, remote_pubkey)) = conduit_and_remote_pubkey { + *self = PeerState::Connected(conduit); + return PeerDataProcessingDecision::CompleteHandshake(requires_response, Some(remote_pubkey)); } } &mut PeerState::Connected(ref mut conduit) => { conduit.read(data); } - }; - - if let (Some(conduit), Some(decision)) = (conduit_option, decision_option) { - *self = PeerState::Connected(conduit); - return decision; } - PeerDataProcessingDecision::Continue } } diff --git a/lightning/src/ln/peers/handshake/mod.rs b/lightning/src/ln/peers/handshake/mod.rs index 07728d81fa9..472aabcd439 100644 --- a/lightning/src/ln/peers/handshake/mod.rs +++ b/lightning/src/ln/peers/handshake/mod.rs @@ -13,7 +13,6 @@ mod states; /// Currently requires explicit ephemeral private key specification. pub struct PeerHandshake { state: Option, - remote_public_key: Option, } impl PeerHandshake { @@ -21,7 +20,6 @@ impl PeerHandshake { pub fn new_outbound(private_key: &SecretKey, remote_public_key: &PublicKey, ephemeral_private_key: &SecretKey) -> Self { Self { state: Some(HandshakeState2::Uninitiated2(UninitiatedHandshakeState::new(private_key.clone(), ephemeral_private_key.clone(), remote_public_key.clone()))), - remote_public_key: Some(remote_public_key.clone()), } } @@ -29,16 +27,9 @@ impl PeerHandshake { pub fn new_inbound(private_key: &SecretKey, ephemeral_private_key: &SecretKey) -> Self { Self { state: Some(HandshakeState2::AwaitingActOne2(AwaitingActOneHandshakeState::new(private_key.clone(), ephemeral_private_key.clone()))), - remote_public_key: None, } } - /// Return the remote public key once it has been extracted from the third act. - /// Potentially useful for inbound connections - pub fn get_remote_pubkey(&self) -> Option { - self.remote_public_key - } - /// Process act dynamically /// # Arguments /// `input`: Byte slice received from peer as part of the handshake protocol @@ -47,17 +38,14 @@ impl PeerHandshake { /// Returns a tuple with the following components: /// `.0`: Byte vector containing the next act to send back to the peer per the handshake protocol /// `.1`: Conduit option if the handshake was just processed to completion and messages can now be encrypted and decrypted - pub fn process_act(&mut self, input: &[u8]) -> Result<(Option>, Option), String> { + pub fn process_act(&mut self, input: &[u8]) -> Result<(Option>, Option<(Conduit, PublicKey)>), String> { let cur_state = self.state.take().unwrap(); let (act_opt, mut next_state) = cur_state.next(input)?; let result = match next_state { HandshakeState2::Complete2(ref mut conduit_and_pubkey) => { - let (conduit, remote_pubkey) = conduit_and_pubkey.take().unwrap(); - self.remote_public_key = Some(remote_pubkey); - - Ok((act_opt, Some(conduit))) + Ok((act_opt, conduit_and_pubkey.take())) }, _ => { Ok((act_opt, None)) } }; @@ -80,9 +68,7 @@ mod test { struct TestCtx { outbound_handshake: PeerHandshake, - outbound_public_key: PublicKey, inbound_handshake: PeerHandshake, - inbound_public_key: PublicKey } impl TestCtx { @@ -102,9 +88,7 @@ mod test { TestCtx { outbound_handshake, - outbound_public_key, inbound_handshake, - inbound_public_key } } } @@ -130,7 +114,6 @@ mod test { let test_ctx = TestCtx::new(); assert_matches!(test_ctx.outbound_handshake.state, Some(HandshakeState2::Uninitiated2(_))); - assert_eq!(test_ctx.outbound_handshake.get_remote_pubkey(), Some(test_ctx.inbound_public_key)); } // Default Inbound::AwaitingActOne @@ -139,7 +122,6 @@ mod test { let test_ctx = TestCtx::new(); assert_matches!(test_ctx.inbound_handshake.state, Some(HandshakeState2::AwaitingActOne2(_))); - assert!(test_ctx.inbound_handshake.get_remote_pubkey().is_none()); } /* @@ -153,7 +135,6 @@ mod test { assert_matches!(test_ctx.outbound_handshake.process_act(&[]).unwrap(), (Some(_), None)); assert_matches!(test_ctx.outbound_handshake.state, Some(HandshakeState2::AwaitingActTwo2(_))); - assert_eq!(test_ctx.outbound_handshake.get_remote_pubkey(), Some(test_ctx.inbound_public_key)); } // Outbound::Uninitiated -> AwaitingActTwo (extra bytes in argument) @@ -164,7 +145,6 @@ mod test { // TODO: process_act() should error if state does not use vec, but it is non-empty assert_matches!(test_ctx.outbound_handshake.process_act(&[1]).unwrap(), (Some(_), None)); assert_matches!(test_ctx.outbound_handshake.state, Some(HandshakeState2::AwaitingActTwo2(_))); - assert_eq!(test_ctx.outbound_handshake.get_remote_pubkey(), Some(test_ctx.inbound_public_key)); } // Inbound::AwaitingActOne -> Error (input too small) @@ -185,7 +165,6 @@ mod test { assert_matches!(test_ctx.inbound_handshake.process_act(&act1).unwrap(), (Some(_), None)); assert_matches!(test_ctx.inbound_handshake.state, Some(HandshakeState2::AwaitingActThree2(_))); - assert!(test_ctx.inbound_handshake.get_remote_pubkey().is_none()); } // Inbound::AwaitingActOne -> Error (bad version byte) @@ -229,7 +208,6 @@ mod test { assert_matches!(test_ctx.inbound_handshake.process_act(&act1).unwrap(), (Some(_), None)); assert_matches!(test_ctx.inbound_handshake.state, Some(HandshakeState2::AwaitingActThree2(_))); - assert!(test_ctx.inbound_handshake.get_remote_pubkey().is_none()); } // Outbound::AwaitingActTwo -> Complete (valid conduit) @@ -241,7 +219,6 @@ mod test { assert_matches!(test_ctx.outbound_handshake.process_act(&act2).unwrap(), (Some(_), Some(_))); assert_matches!(test_ctx.outbound_handshake.state, Some(HandshakeState2::Complete2(_))); - assert_eq!(test_ctx.outbound_handshake.get_remote_pubkey(), Some(test_ctx.inbound_public_key)); } @@ -256,14 +233,13 @@ mod test { let mut act2 = do_process_act_or_panic!(test_ctx.inbound_handshake, &act1); act2.extend_from_slice(&[1; 100]); - let conduit = if let (_, Some(conduit)) = test_ctx.outbound_handshake.process_act(&act2).unwrap() { + let (conduit, _) = if let (_, Some(conduit)) = test_ctx.outbound_handshake.process_act(&act2).unwrap() { conduit } else { panic!(); }; assert_eq!(100, conduit.decryptor.read_buffer_length()); - assert_eq!(test_ctx.outbound_handshake.get_remote_pubkey(), Some(test_ctx.inbound_public_key)); } // Outbound::AwaitingActTwo -> Error (input too small) @@ -321,7 +297,6 @@ mod test { assert_matches!(test_ctx.inbound_handshake.process_act(&act3).unwrap(), (None, Some(_))); assert_matches!(test_ctx.inbound_handshake.state, Some(HandshakeState2::Complete2(_))); - assert_eq!(test_ctx.inbound_handshake.get_remote_pubkey(), Some(test_ctx.outbound_public_key)); } // Inbound::AwaitingActThree -> Complete (with extra bytes) @@ -335,7 +310,7 @@ mod test { let mut act3 = do_process_act_or_panic!(test_ctx.outbound_handshake, &act2); act3.extend_from_slice(&[2; 100]); - let conduit = if let (None, Some(conduit)) = test_ctx.inbound_handshake.process_act(&act3).unwrap() { + let (conduit, _) = if let (None, Some(conduit)) = test_ctx.inbound_handshake.process_act(&act3).unwrap() { conduit } else { panic!(); From 2f67eed1c6c8639a92a49652bd40cdeaeffeaec4 Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Thu, 20 Aug 2020 10:15:01 -0700 Subject: [PATCH 20/72] refactor: Clean up process_peer_data Now that the remote_pubkey is returned from process_act(), the logic can be more straightforward. Each branch is responsible for returning the next PeerState and PeerDataProcessingDecision. Remove Option from CompleteHandshake now that the PublicKey is guaranteed to exist --- lightning/src/ln/peers/handler.rs | 56 ++++++++++++++++++------------- 1 file changed, 33 insertions(+), 23 deletions(-) diff --git a/lightning/src/ln/peers/handler.rs b/lightning/src/ln/peers/handler.rs index eb92785187e..c8ace0e4439 100644 --- a/lightning/src/ln/peers/handler.rs +++ b/lightning/src/ln/peers/handler.rs @@ -127,7 +127,7 @@ enum PeerState { } enum PeerDataProcessingDecision { - CompleteHandshake(bool, Option), + CompleteHandshake(bool, PublicKey), Continue, Disconnect(PeerHandleError) } @@ -141,31 +141,44 @@ impl PeerState { } fn process_peer_data(&mut self, data: &[u8], mutable_response_buffer: &mut LinkedList>) -> PeerDataProcessingDecision { - match self { + let (new_state_opt, decision) = match self { &mut PeerState::Authenticating(ref mut handshake) => { - let (next_act, conduit_and_remote_pubkey) = match handshake.process_act(data) { - Ok(act_result) => act_result, + match handshake.process_act(data) { + // Any errors originating from the handshake sequence result in Disconnect Err(e) => { - return PeerDataProcessingDecision::Disconnect(PeerHandleError { no_connection_possible: false }); - } - }; - - let requires_response = next_act.is_some(); - if let Some(act) = next_act { - mutable_response_buffer.push_back(act); - } + (None, PeerDataProcessingDecision::Disconnect(PeerHandleError { no_connection_possible: false })) + }, + // Otherwise, handshake may or may not be complete depending on whether or not + // we receive a (conduit, pubkey) + Ok((response_vec_opt, conduit_and_remote_pubkey_opt)) => { + + // Any response generated by the handshake sequence is put into the response buffer + let requires_response = response_vec_opt.is_some(); + if let Some(response_vec) = response_vec_opt { + mutable_response_buffer.push_back(response_vec); + } - if let Some((conduit, remote_pubkey)) = conduit_and_remote_pubkey { - *self = PeerState::Connected(conduit); - return PeerDataProcessingDecision::CompleteHandshake(requires_response, Some(remote_pubkey)); + // if process_act() returns the conduit and remote_pubkey the handshake + // is complete + if let Some((conduit, remote_pubkey)) = conduit_and_remote_pubkey_opt { + (Some(PeerState::Connected(conduit)), PeerDataProcessingDecision::CompleteHandshake(requires_response, remote_pubkey)) + } else { + (None, PeerDataProcessingDecision::Continue) + } + } } - } - + }, &mut PeerState::Connected(ref mut conduit) => { conduit.read(data); + (None, PeerDataProcessingDecision::Continue) } + }; + + if let Some(new_state) = new_state_opt { + *self = new_state; } - PeerDataProcessingDecision::Continue + + decision } } @@ -565,12 +578,9 @@ impl PeerManager { + PeerDataProcessingDecision::CompleteHandshake(needs_to_send_init_message, remote_pubkey) => { send_init_message = needs_to_send_init_message; - - if let Some(key) = remote_pubkey_option { - peer.their_node_id = Some(key); - } + peer.their_node_id = Some(remote_pubkey); // insert node id match peers.node_id_to_descriptor.entry(peer.their_node_id.unwrap()) { From ab4ff644ec6a560108c8b7279cd229f7730417d0 Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Thu, 20 Aug 2020 10:28:22 -0700 Subject: [PATCH 21/72] refactor: Doc and variable name cleanup in handshake/mod.rs Clean up loose threads (wrong docs, variable names) now that this code is in a more stable state. --- lightning/src/ln/peers/handshake/mod.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lightning/src/ln/peers/handshake/mod.rs b/lightning/src/ln/peers/handshake/mod.rs index 472aabcd439..ec5a91f6ad5 100644 --- a/lightning/src/ln/peers/handshake/mod.rs +++ b/lightning/src/ln/peers/handshake/mod.rs @@ -17,16 +17,16 @@ pub struct PeerHandshake { impl PeerHandshake { /// Instantiate a new handshake with a node identity secret key and an ephemeral private key - pub fn new_outbound(private_key: &SecretKey, remote_public_key: &PublicKey, ephemeral_private_key: &SecretKey) -> Self { + pub fn new_outbound(initiator_static_private_key: &SecretKey, responder_static_public_key: &PublicKey, initiator_ephemeral_private_key: &SecretKey) -> Self { Self { - state: Some(HandshakeState2::Uninitiated2(UninitiatedHandshakeState::new(private_key.clone(), ephemeral_private_key.clone(), remote_public_key.clone()))), + state: Some(HandshakeState2::Uninitiated2(UninitiatedHandshakeState::new(initiator_static_private_key.clone(), initiator_ephemeral_private_key.clone(), responder_static_public_key.clone()))), } } /// Instantiate a new handshake in anticipation of a peer's first handshake act - pub fn new_inbound(private_key: &SecretKey, ephemeral_private_key: &SecretKey) -> Self { + pub fn new_inbound(responder_static_private_key: &SecretKey, responder_ephemeral_private_key: &SecretKey) -> Self { Self { - state: Some(HandshakeState2::AwaitingActOne2(AwaitingActOneHandshakeState::new(private_key.clone(), ephemeral_private_key.clone()))), + state: Some(HandshakeState2::AwaitingActOne2(AwaitingActOneHandshakeState::new(responder_static_private_key.clone(), responder_ephemeral_private_key.clone()))), } } @@ -37,7 +37,7 @@ impl PeerHandshake { /// # Return values /// Returns a tuple with the following components: /// `.0`: Byte vector containing the next act to send back to the peer per the handshake protocol - /// `.1`: Conduit option if the handshake was just processed to completion and messages can now be encrypted and decrypted + /// `.1`: Some(Conduit, PublicKey) if the handshake was just processed to completion and messages can now be encrypted and decrypted pub fn process_act(&mut self, input: &[u8]) -> Result<(Option>, Option<(Conduit, PublicKey)>), String> { let cur_state = self.state.take().unwrap(); From f29936b9ac80aa72fcd85fc8de4205dfb43f6305 Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Thu, 20 Aug 2020 13:02:14 -0700 Subject: [PATCH 22/72] tests: Finish testing handshake/mod.rs Now that the implementation is complete and swapped over, remove the duplicated tests for process_act(). At this layer, all that needs to be tested is: * The new state in PeerHandshake is set to the state returned from state_machine.next() * Any error generated from state_machine.next() is returned * Any calls to process_next() after an error was returned panic These tests are a good use case for a mocking library, but for now just leverage implementation details to trigger the proper errors and states. --- lightning/src/ln/peers/handshake/mod.rs | 305 +++--------------------- 1 file changed, 38 insertions(+), 267 deletions(-) diff --git a/lightning/src/ln/peers/handshake/mod.rs b/lightning/src/ln/peers/handshake/mod.rs index ec5a91f6ad5..87f568e0d78 100644 --- a/lightning/src/ln/peers/handshake/mod.rs +++ b/lightning/src/ln/peers/handshake/mod.rs @@ -58,8 +58,6 @@ impl PeerHandshake { #[cfg(test)] mod test { - use hex; - use bitcoin::secp256k1; use bitcoin::secp256k1::key::{PublicKey, SecretKey}; @@ -68,27 +66,31 @@ mod test { struct TestCtx { outbound_handshake: PeerHandshake, + outbound_static_public_key: PublicKey, inbound_handshake: PeerHandshake, + inbound_static_public_key: PublicKey } impl TestCtx { fn new() -> TestCtx { let curve = secp256k1::Secp256k1::new(); - let outbound_private_key = SecretKey::from_slice(&[0x_11_u8; 32]).unwrap(); - let outbound_public_key = PublicKey::from_secret_key(&curve, &outbound_private_key); - let outbound_ephemeral_key = SecretKey::from_slice(&[0x_12_u8; 32]).unwrap(); + let outbound_static_private_key = SecretKey::from_slice(&[0x_11_u8; 32]).unwrap(); + let outbound_static_public_key = PublicKey::from_secret_key(&curve, &outbound_static_private_key); + let outbound_ephemeral_private_key = SecretKey::from_slice(&[0x_12_u8; 32]).unwrap(); - let inbound_private_key = SecretKey::from_slice(&[0x_21_u8; 32]).unwrap(); - let inbound_public_key = PublicKey::from_secret_key(&curve, &inbound_private_key); - let inbound_ephemeral_key = SecretKey::from_slice(&[0x_22_u8; 32]).unwrap(); + let inbound_static_private_key = SecretKey::from_slice(&[0x_21_u8; 32]).unwrap(); + let inbound_static_public_key = PublicKey::from_secret_key(&curve, &inbound_static_private_key); + let inbound_ephemeral_private_key = SecretKey::from_slice(&[0x_22_u8; 32]).unwrap(); - let outbound_handshake = PeerHandshake::new_outbound(&outbound_private_key, &inbound_public_key, &outbound_ephemeral_key); - let inbound_handshake = PeerHandshake::new_inbound(&inbound_private_key, &inbound_ephemeral_key); + let outbound_handshake = PeerHandshake::new_outbound(&outbound_static_private_key, &inbound_static_public_key, &outbound_ephemeral_private_key); + let inbound_handshake = PeerHandshake::new_inbound(&inbound_static_private_key, &inbound_ephemeral_private_key); TestCtx { outbound_handshake, + outbound_static_public_key, inbound_handshake, + inbound_static_public_key, } } } @@ -128,287 +130,56 @@ mod test { * PeerHandshake::process_act() tests */ - // Outbound::Uninitiated -> AwaitingActTwo - #[test] - fn peer_handshake_outbound_uninitiated_to_awaiting_act_two() { - let mut test_ctx = TestCtx::new(); - - assert_matches!(test_ctx.outbound_handshake.process_act(&[]).unwrap(), (Some(_), None)); - assert_matches!(test_ctx.outbound_handshake.state, Some(HandshakeState2::AwaitingActTwo2(_))); - } - - // Outbound::Uninitiated -> AwaitingActTwo (extra bytes in argument) - #[test] - fn peer_handshake_outbound_uninitiated_to_awaiting_act_two_nonempty_input() { - let mut test_ctx = TestCtx::new(); - - // TODO: process_act() should error if state does not use vec, but it is non-empty - assert_matches!(test_ctx.outbound_handshake.process_act(&[1]).unwrap(), (Some(_), None)); - assert_matches!(test_ctx.outbound_handshake.state, Some(HandshakeState2::AwaitingActTwo2(_))); - } - - // Inbound::AwaitingActOne -> Error (input too small) - #[test] - fn peer_handshake_new_inbound_awaiting_act_one_input_too_small() { - let mut test_ctx = TestCtx::new(); - - assert_eq!(test_ctx.inbound_handshake.process_act(&[]).err(), Some(String::from("need at least 50 bytes"))); - } - - // Inbound::AwaitingActOne -> AwaitingActThree (excess bytes) - // TODO: This should error early if we receive act3 data prior to sending act2 - #[test] - fn peer_handshake_new_inbound_awaiting_act_one_input_too_large() { - let mut test_ctx = TestCtx::new(); - let mut act1 = do_process_act_or_panic!(test_ctx.outbound_handshake, &[]); - act1.extend_from_slice(&[1]); - - assert_matches!(test_ctx.inbound_handshake.process_act(&act1).unwrap(), (Some(_), None)); - assert_matches!(test_ctx.inbound_handshake.state, Some(HandshakeState2::AwaitingActThree2(_))); - } - - // Inbound::AwaitingActOne -> Error (bad version byte) - #[test] - fn peer_handshake_new_inbound_awaiting_act_one_bad_version() { - let mut test_ctx = TestCtx::new(); - let mut act1 = do_process_act_or_panic!(test_ctx.outbound_handshake, &[]); - // Set bad version byte - act1[0] = 1; - - assert_eq!(test_ctx.inbound_handshake.process_act(&act1).err(), Some(String::from("unexpected version"))); - } - - // Inbound::AwaitingActOne -> Error (invalid hmac) - #[test] - fn peer_handshake_new_inbound_awaiting_act_invalid_hmac() { - let mut test_ctx = TestCtx::new(); - let mut act1 = do_process_act_or_panic!(test_ctx.outbound_handshake, &[]); - // corrupt the ciphertext - act1[34] = 0; - - assert_eq!(test_ctx.inbound_handshake.process_act(&act1).err(), Some(String::from("invalid hmac"))); - } - - // Inbound::AwaitingActOne -> Error (invalid remote ephemeral key) - #[test] - fn peer_handshake_new_inbound_awaiting_act_invalid_remote_ephemeral_key() { - let mut test_ctx = TestCtx::new(); - let mut act1 = do_process_act_or_panic!(test_ctx.outbound_handshake, &[]); - // corrupt the ephemeral public key - act1[1] = 0; - - assert_eq!(test_ctx.inbound_handshake.process_act(&act1).err(), Some(String::from("invalid remote ephemeral public key"))); - } - - // Inbound::AwaitingActOne -> AwaitingActThree - #[test] - fn peer_handshake_new_inbound_awaiting_act_one_to_awaiting_act_three() { - let mut test_ctx = TestCtx::new(); - let act1 = do_process_act_or_panic!(test_ctx.outbound_handshake, &[]); - - assert_matches!(test_ctx.inbound_handshake.process_act(&act1).unwrap(), (Some(_), None)); - assert_matches!(test_ctx.inbound_handshake.state, Some(HandshakeState2::AwaitingActThree2(_))); - } - - // Outbound::AwaitingActTwo -> Complete (valid conduit) + // Full sequence from initiator and responder as a sanity test. State machine is tested in states.rs #[test] - fn peer_handshake_outbound_awaiting_act_two_process() { + fn full_sequence_sanity_test() { let mut test_ctx = TestCtx::new(); let act1 = do_process_act_or_panic!(test_ctx.outbound_handshake, &[]); let act2 = do_process_act_or_panic!(test_ctx.inbound_handshake, &act1); - assert_matches!(test_ctx.outbound_handshake.process_act(&act2).unwrap(), (Some(_), Some(_))); - assert_matches!(test_ctx.outbound_handshake.state, Some(HandshakeState2::Complete2(_))); - } - - - // Outbound::AwaitingActTwo -> Complete (with extra data) - // Ensures that any remaining data in the read buffer is transferred to the conduit once - // the handshake is complete - // TODO: Is this valid? Don't we expect peers to need ActThree before sending additional data? - #[test] - fn peer_handshake_new_outbound_excess_bytes_after_complete_are_in_conduit() { - let mut test_ctx = TestCtx::new(); - let act1 = do_process_act_or_panic!(test_ctx.outbound_handshake, &[]); - let mut act2 = do_process_act_or_panic!(test_ctx.inbound_handshake, &act1); - act2.extend_from_slice(&[1; 100]); - - let (conduit, _) = if let (_, Some(conduit)) = test_ctx.outbound_handshake.process_act(&act2).unwrap() { - conduit + let (act3, inbound_remote_pubkey) = if let (Some(act3), Some((_, remote_pubkey))) = test_ctx.outbound_handshake.process_act(&act2).unwrap() { + (act3, remote_pubkey) } else { panic!(); }; - assert_eq!(100, conduit.decryptor.read_buffer_length()); - } - - // Outbound::AwaitingActTwo -> Error (input too small) - #[test] - fn peer_handshake_outbound_awaiting_act_two_input_too_small() { - let mut test_ctx = TestCtx::new(); - let _act1 = do_process_act_or_panic!(test_ctx.outbound_handshake, &[]); - - assert_eq!(test_ctx.outbound_handshake.process_act(&[1]).err(), Some(String::from("need at least 50 bytes"))); - } - - // Outbound::AwaitingActTwo -> Error (bad version byte) - #[test] - fn peer_handshake_outbound_awaiting_act_two_bad_version() { - let mut test_ctx = TestCtx::new(); - let act1 = do_process_act_or_panic!(test_ctx.outbound_handshake, &[]); - let mut act2 = do_process_act_or_panic!(test_ctx.inbound_handshake, &act1); - // Set version byte to 1 - act2[0] = 1; - - assert_eq!(test_ctx.outbound_handshake.process_act(&act2).err(), Some(String::from("unexpected version"))); - } - - // Outbound::AwaitingActTwo -> Error (invalid hmac) - #[test] - fn peer_handshake_outbound_awaiting_act_two_invalid_hmac() { - let mut test_ctx = TestCtx::new(); - let act1 = do_process_act_or_panic!(test_ctx.outbound_handshake, &[]); - let mut act2 = do_process_act_or_panic!(test_ctx.inbound_handshake, &act1); - // corrupt the ciphertext - act2[34] = 1; - - assert_eq!(test_ctx.outbound_handshake.process_act(&act2).err(), Some(String::from("invalid hmac"))); - } - - // Outbound::AwaitingActTwo -> Error (invalid remote ephemeral key) - #[test] - fn peer_handshake_outbound_awaiting_act_two_invalid_remote_ephemeral_key() { - let mut test_ctx = TestCtx::new(); - let act1 = do_process_act_or_panic!(test_ctx.outbound_handshake, &[]); - let mut act2 = do_process_act_or_panic!(test_ctx.inbound_handshake, &act1); - // corrupt the ephemeral public key - act2[1] = 1; - - assert_eq!(test_ctx.outbound_handshake.process_act(&act2).err(), Some(String::from("invalid remote ephemeral public key"))); - } - - // Inbound::AwaitingActThree -> Complete - #[test] - fn peer_handshake_new_inbound_awaiting_act_three_to_awaiting_act_three() { - let mut test_ctx = TestCtx::new(); - let act1 = do_process_act_or_panic!(test_ctx.outbound_handshake, &[]); - let act2 = do_process_act_or_panic!(test_ctx.inbound_handshake, &act1); - let act3 = do_process_act_or_panic!(test_ctx.outbound_handshake, &act2); - - assert_matches!(test_ctx.inbound_handshake.process_act(&act3).unwrap(), (None, Some(_))); - assert_matches!(test_ctx.inbound_handshake.state, Some(HandshakeState2::Complete2(_))); - } - - // Inbound::AwaitingActThree -> Complete (with extra bytes) - // Ensures that any remaining data in the read buffer is transferred to the conduit once - // the handshake is complete - #[test] - fn peer_handshake_new_inbound_excess_bytes_after_complete_are_in_conduit() { - let mut test_ctx = TestCtx::new(); - let act1 = do_process_act_or_panic!(test_ctx.outbound_handshake, &[]); - let act2 = do_process_act_or_panic!(test_ctx.inbound_handshake, &act1); - let mut act3 = do_process_act_or_panic!(test_ctx.outbound_handshake, &act2); - act3.extend_from_slice(&[2; 100]); - - let (conduit, _) = if let (None, Some(conduit)) = test_ctx.inbound_handshake.process_act(&act3).unwrap() { - conduit + let outbound_remote_pubkey = if let (None, Some((_, remote_pubkey))) = test_ctx.inbound_handshake.process_act(&act3).unwrap() { + remote_pubkey } else { panic!(); }; - assert_eq!(100, conduit.decryptor.read_buffer_length()); + assert_eq!(inbound_remote_pubkey, test_ctx.inbound_static_public_key); + assert_eq!(outbound_remote_pubkey, test_ctx.outbound_static_public_key); } - // Inbound::AwaitingActThree -> Error (input too small) + // Test that the internal state object matches the return from state_machine.next() + // This could make use of a mocking library to remove the dependency on the state machine. All + // that needs to be tested is that the expected state (returned) from state_machine.next() matchse + // the internal set state. #[test] - fn peer_handshake_new_inbound_awaiting_act_three_input_too_small() { + fn process_act_properly_updates_state() { let mut test_ctx = TestCtx::new(); - let act1 = do_process_act_or_panic!(test_ctx.outbound_handshake, &[]); - let act2 = do_process_act_or_panic!(test_ctx.inbound_handshake, &act1); - let act3 = do_process_act_or_panic!(test_ctx.outbound_handshake, &act2); - - assert_eq!(test_ctx.inbound_handshake.process_act(&act3[..65]).err(), Some(String::from("need at least 66 bytes"))); - } - - // Inbound::AwaitingActThree -> Error (bad version byte) - #[test] - fn peer_handshake_new_inbound_awaiting_act_three_bad_version() { - let mut test_ctx = TestCtx::new(); - let act1 = do_process_act_or_panic!(test_ctx.outbound_handshake, &[]); - let act2 = do_process_act_or_panic!(test_ctx.inbound_handshake, &act1); - let mut act3 = do_process_act_or_panic!(test_ctx.outbound_handshake, &act2); - // set version byte to 1 - act3[0] = 1; - - assert_eq!(test_ctx.inbound_handshake.process_act(&act3).err(), Some(String::from("unexpected version"))); - } - - // Inbound::AwaitingActThree -> Error (invalid hmac) - #[test] - fn peer_handshake_new_inbound_awaiting_act_three_invalid_hmac() { - let mut test_ctx = TestCtx::new(); - let act1 = do_process_act_or_panic!(test_ctx.outbound_handshake, &[]); - let act2 = do_process_act_or_panic!(test_ctx.inbound_handshake, &act1); - let mut act3 = do_process_act_or_panic!(test_ctx.outbound_handshake, &act2); - // trigger decryption error by corrupting byte 1 - act3[1] = 0; - - assert_eq!(test_ctx.inbound_handshake.process_act(&act3).err(), Some(String::from("invalid hmac"))); - } - - // Inbound::AwaitingActThree -> Error (invalid tag hmac) - #[test] - fn peer_handshake_new_inbound_awaiting_act_three_invalid_tag_hmac() { - let mut test_ctx = TestCtx::new(); - let act1 = do_process_act_or_panic!(test_ctx.outbound_handshake, &[]); - let act2 = do_process_act_or_panic!(test_ctx.inbound_handshake, &act1); - let mut act3 = do_process_act_or_panic!(test_ctx.outbound_handshake, &act2); - // trigger tag decryption error by corrupting byte 50 - act3[50] = 0; - - assert_eq!(test_ctx.inbound_handshake.process_act(&act3).err(), Some(String::from("invalid hmac"))); - } - - // Inbound::Complete -> Panic - #[test] - #[should_panic(expected = "no acts left to process")] - fn peer_handshake_new_inbound_complete_then_process_act() { - let mut test_ctx = TestCtx::new(); - let act1 = do_process_act_or_panic!(test_ctx.outbound_handshake, &[]); - let act2 = do_process_act_or_panic!(test_ctx.inbound_handshake, &act1); - let act3 = do_process_act_or_panic!(test_ctx.outbound_handshake, &act2); - test_ctx.inbound_handshake.process_act(&act3).unwrap(); - - do_process_act_or_panic!(test_ctx.inbound_handshake, &[]); + do_process_act_or_panic!(test_ctx.outbound_handshake, &[]); + assert_matches!(test_ctx.outbound_handshake.state, Some(HandshakeState2::AwaitingActTwo2(_))); } - // Outbound::None -> Panic + // Test that any errors from the state machine are passed back to the caller + // This could make use of a mocking library to remove the dependency on the state machine + // logic. All that needs to be tested is that an error from state_machine.next() + // results in an error in process_act() #[test] - #[should_panic(expected = "no acts left to process")] - fn peer_handshake_new_outbound_complete_then_process_act() { + fn errors_properly_returned() { let mut test_ctx = TestCtx::new(); - let act1 = do_process_act_or_panic!(test_ctx.outbound_handshake, &[]); - let act2 = do_process_act_or_panic!(test_ctx.inbound_handshake, &act1); - let act3 = do_process_act_or_panic!(test_ctx.outbound_handshake, &act2); - test_ctx.inbound_handshake.process_act(&act3).unwrap(); - - do_process_act_or_panic!(test_ctx.outbound_handshake, &[]); + assert_matches!(test_ctx.inbound_handshake.process_act(&[]).err(), Some(_)); } - // Test the Act byte generation against known good hard-coded values in case the implementation - // changes in a symmetric way that makes the other tests useless + // Test that any use of the PeerHandshake after returning an error panics #[test] - fn peer_handshake_external_spec() { + #[should_panic(expected = "called `Option::unwrap()` on a `None` value")] + fn use_after_error_panics() { let mut test_ctx = TestCtx::new(); - let act1 = do_process_act_or_panic!(test_ctx.outbound_handshake, &[]); - let act2 = do_process_act_or_panic!(test_ctx.inbound_handshake, &act1); - let act3 = do_process_act_or_panic!(test_ctx.outbound_handshake, &act2); - - assert_eq!(hex::encode(&act1), - "00036360e856310ce5d294e8be33fc807077dc56ac80d95d9cd4ddbd21325eff73f70df6086551151f58b8afe6c195782c6a"); - assert_eq!(hex::encode(&act2), - "0002466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f276e2470b93aac583c9ef6eafca3f730ae"); - assert_eq!(hex::encode(&act3), - "00b9e3a702e93e3a9948c2ed6e5fd7590a6e1c3a0344cfc9d5b57357049aa22355361aa02e55a8fc28fef5bd6d71ad0c38228dc68b1c466263b47fdf31e560e139ba"); + assert_matches!(test_ctx.inbound_handshake.process_act(&[]).err(), Some(_)); + test_ctx.inbound_handshake.process_act(&[]).unwrap(); } } From ab277b4a6be481c4ee9dfecbd1c5767a84aea16b Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Thu, 20 Aug 2020 13:46:26 -0700 Subject: [PATCH 23/72] refactor: Clean up use of Sha256 in states.rs The previous concat!() macro and the variadic sha256!() had the same functionality due to the way sha.input() works. Reduce to a common code path that now uses concat_then_sha256() in all places and reduces the number of potential copies. Where appropriate, use the actual Sha256 type. The full type usage is hard due to the usages of hkdf::derive() in other parts of the code, but this is a good start on the path to cleaner type usage. --- lightning/src/ln/peers/handshake/states.rs | 65 +++++++++------------- 1 file changed, 26 insertions(+), 39 deletions(-) diff --git a/lightning/src/ln/peers/handshake/states.rs b/lightning/src/ln/peers/handshake/states.rs index 949ec3f5817..a67ee07b37e 100644 --- a/lightning/src/ln/peers/handshake/states.rs +++ b/lightning/src/ln/peers/handshake/states.rs @@ -11,27 +11,16 @@ use ln::peers::conduit::{Conduit, SymmetricKey}; const ACT_ONE_TWO_LENGTH: usize = 50; const ACT_THREE_LENGTH: usize = 66; -// Alias type to allow passing handshake hash easier -type HandshakeHash = [u8; 32]; type ChainingKey = [u8; 32]; -// Generate a SHA-256 hash from one or more elements -macro_rules! sha256 { +// Generate a SHA-256 hash from one or more elements concatenated together +macro_rules! concat_then_sha256 { ( $( $x:expr ),+ ) => {{ let mut sha = Sha256::engine(); $( sha.input($x.as_ref()); )+ - Sha256::from_engine(sha).into_inner() - }} -} - -// Concatenate two slices in a Vec -macro_rules! concat { - ($arg1:expr, $arg2:expr) => {{ - let mut result = $arg1.to_vec(); - result.extend_from_slice($arg2.as_ref()); - result + Sha256::from_engine(sha) }} } @@ -65,16 +54,16 @@ pub struct UninitiatedHandshakeState { initiator_ephemeral_private_key: SecretKey, initiator_ephemeral_public_key: PublicKey, responder_static_public_key: PublicKey, - chaining_key: ChainingKey, - hash: HandshakeHash, + chaining_key: Sha256, + hash: Sha256 } pub struct AwaitingActOneHandshakeState { responder_static_private_key: SecretKey, responder_ephemeral_private_key: SecretKey, responder_ephemeral_public_key: PublicKey, - chaining_key: ChainingKey, - hash: HandshakeHash, + chaining_key: Sha256, + hash: Sha256, read_buffer: Vec } @@ -84,12 +73,12 @@ pub struct AwaitingActTwoHandshakeState { initiator_ephemeral_private_key: SecretKey, responder_static_public_key: PublicKey, chaining_key: ChainingKey, - hash: HandshakeHash, + hash: Sha256, read_buffer: Vec } pub struct AwaitingActThreeHandshakeState { - hash: HandshakeHash, + hash: Sha256, responder_ephemeral_private_key: SecretKey, chaining_key: ChainingKey, temporary_key: [u8; 32], @@ -122,14 +111,14 @@ impl IHandshakeState for UninitiatedHandshakeState { let initiator_ephemeral_public_key = self.initiator_ephemeral_public_key; let responder_static_public_key = self.responder_static_public_key; let chaining_key = self.chaining_key; - let mut hash = self.hash; + let hash = self.hash; // serialize act one let (act_one, hash, chaining_key, _) = calculate_act_message( &initiator_ephemeral_private_key, &initiator_ephemeral_public_key, &responder_static_public_key, - chaining_key, + chaining_key.into_inner(), hash, ); @@ -181,7 +170,7 @@ impl IHandshakeState for AwaitingActOneHandshakeState { let (initiator_ephemeral_public_key, hash, chaining_key, _) = process_act_message( &mut read_buffer, &responder_static_private_key, - chaining_key, + chaining_key.into_inner(), hash, )?; @@ -232,7 +221,7 @@ impl IHandshakeState for AwaitingActTwoHandshakeState { let tagged_encrypted_pubkey = chacha::encrypt(&temporary_key, 1, &hash, &initiator_static_public_key.serialize()); // 2. h = SHA-256(h || c) - let hash = sha256!(concat!(hash, tagged_encrypted_pubkey)); + let hash = concat_then_sha256!(hash, tagged_encrypted_pubkey); // 3. se = ECDH(s.priv, re) let ecdh = ecdh(&initiator_static_private_key, &responder_ephemeral_public_key); @@ -282,7 +271,7 @@ impl IHandshakeState for AwaitingActThreeHandshakeState { return Err("need at least 66 bytes".to_string()); } - let mut hash = self.hash; + let hash = self.hash; let temporary_key = self.temporary_key; let responder_ephemeral_private_key = self.responder_ephemeral_private_key; let chaining_key = self.chaining_key; @@ -318,7 +307,7 @@ impl IHandshakeState for AwaitingActThreeHandshakeState { }; // 5. h = SHA-256(h || c) - let hash = sha256!(concat!(hash, tagged_encrypted_pubkey)); + let hash = concat_then_sha256!(hash, tagged_encrypted_pubkey); // 6. se = ECDH(e.priv, rs) let ecdh = ecdh(&responder_ephemeral_private_key, &initiator_pubkey); @@ -351,30 +340,30 @@ impl IHandshakeState for AwaitingActThreeHandshakeState { } // https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#handshake-state-initialization -fn handshake_state_initialization(responder_static_public_key: &PublicKey) -> (HandshakeHash, ChainingKey) { +fn handshake_state_initialization(responder_static_public_key: &PublicKey) -> (Sha256, Sha256) { let protocol_name = b"Noise_XK_secp256k1_ChaChaPoly_SHA256"; let prologue = b"lightning"; // 1. h = SHA-256(protocolName) // 2. ck = h - let chaining_key = sha256!(protocol_name); + let chaining_key = concat_then_sha256!(protocol_name); // 3. h = SHA-256(h || prologue) - let hash = sha256!(concat!(chaining_key, prologue)); + let hash = concat_then_sha256!(chaining_key, prologue); // h = SHA-256(h || responderPublicKey) - let hash = sha256!(concat!(hash, responder_static_public_key.serialize())); + let hash = concat_then_sha256!(hash, responder_static_public_key.serialize()); (hash, chaining_key) } // https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#act-one (sender) // https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#act-two (sender) -fn calculate_act_message(local_private_ephemeral_key: &SecretKey, local_public_ephemeral_key: &PublicKey, remote_public_key: &PublicKey, chaining_key: ChainingKey, hash: HandshakeHash) -> (Vec, HandshakeHash, SymmetricKey, SymmetricKey) { +fn calculate_act_message(local_private_ephemeral_key: &SecretKey, local_public_ephemeral_key: &PublicKey, remote_public_key: &PublicKey, chaining_key: ChainingKey, hash: Sha256) -> (Vec, Sha256, SymmetricKey, SymmetricKey) { // 1. e = generateKey() (passed in) // 2. h = SHA-256(h || e.pub.serializeCompressed()) let serialized_local_public_key = local_public_ephemeral_key.serialize(); - let hash = sha256!(concat!(hash, serialized_local_public_key)); + let hash = concat_then_sha256!(hash, serialized_local_public_key); // 3. ACT1: es = ECDH(e.priv, rs) // 3. ACT2: es = ECDH(e.priv, re) @@ -389,7 +378,7 @@ fn calculate_act_message(local_private_ephemeral_key: &SecretKey, local_public_e let tagged_ciphertext = chacha::encrypt(&temporary_key, 0, &hash, &[0; 0]); // 6. h = SHA-256(h || c) - let hash = sha256!(hash, tagged_ciphertext); + let hash = concat_then_sha256!(hash, tagged_ciphertext); // Send m = 0 || e.pub.serializeCompressed() || c let mut act = Vec::with_capacity(ACT_ONE_TWO_LENGTH); @@ -404,7 +393,7 @@ fn calculate_act_message(local_private_ephemeral_key: &SecretKey, local_public_e // Due to the very high similarity of acts 1 and 2, this method is used to process both // https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#act-one (receiver) // https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#act-two (receiver) -fn process_act_message(read_buffer: &mut Vec, local_private_key: &SecretKey, chaining_key: SymmetricKey, hash: HandshakeHash) -> Result<(PublicKey, HandshakeHash, SymmetricKey, SymmetricKey), String> { +fn process_act_message(read_buffer: &mut Vec, local_private_key: &SecretKey, chaining_key: ChainingKey, hash: Sha256) -> Result<(PublicKey, Sha256, SymmetricKey, SymmetricKey), String> { // 1. Read exactly 50 bytes from the network buffer if read_buffer.len() < ACT_ONE_TWO_LENGTH { return Err("need at least 50 bytes".to_string()); @@ -435,7 +424,7 @@ fn process_act_message(read_buffer: &mut Vec, local_private_key: &SecretKey, } // 4. h = SHA-256(h || re.serializeCompressed()) - let hash = sha256!(concat!(hash, ephemeral_public_key_bytes)); + let hash = concat_then_sha256!(hash, ephemeral_public_key_bytes); // 5. Act1: es = ECDH(s.priv, re) // 5. Act2: ee = ECDH(e.priv, ee) @@ -449,7 +438,7 @@ fn process_act_message(read_buffer: &mut Vec, local_private_key: &SecretKey, let _tag_check = chacha::decrypt(&temporary_key, 0, &hash, &chacha_tag)?; // 8. h = SHA-256(h || c) - let hash = sha256!(concat!(hash, chacha_tag)); + let hash = concat_then_sha256!(hash, chacha_tag); Ok((ephemeral_public_key, hash, chaining_key, temporary_key)) } @@ -466,9 +455,7 @@ fn ecdh(private_key: &SecretKey, public_key: &PublicKey) -> SymmetricKey { pk_object.mul_assign(&curve, &private_key[..]).expect("invalid multiplication"); let preimage = pk_object.serialize(); - let mut sha = Sha256::engine(); - sha.input(preimage.as_ref()); - Sha256::from_engine(sha).into_inner() + concat_then_sha256!(preimage).into_inner() } #[cfg(test)] From 57f95fe3fecaf2263cad83912eece67e21e56fff Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Sat, 22 Aug 2020 14:14:47 -0700 Subject: [PATCH 24/72] refactor: Clean up hkdf::derive and add test Add documentation and a unit test against the relevant test vector from the RFC. --- lightning/src/ln/peers/hkdf.rs | 81 ++++++++++++++++++++++++++++------ 1 file changed, 67 insertions(+), 14 deletions(-) diff --git a/lightning/src/ln/peers/hkdf.rs b/lightning/src/ln/peers/hkdf.rs index 48415594114..6ac1391666d 100644 --- a/lightning/src/ln/peers/hkdf.rs +++ b/lightning/src/ln/peers/hkdf.rs @@ -1,18 +1,71 @@ use bitcoin::hashes::{Hash, HashEngine, Hmac, HmacEngine}; use bitcoin::hashes::sha256::Hash as Sha256; -pub fn derive(salt: &[u8], master: &[u8]) -> ([u8; 32], [u8; 32]) { - let mut hmac = HmacEngine::::new(salt); - hmac.input(master); - let prk = Hmac::from_engine(hmac).into_inner(); // prk = sha256(master) - - let mut hmac = HmacEngine::::new(&prk[..]); - hmac.input(&[1; 1]); - let t1 = Hmac::from_engine(hmac).into_inner(); // t1 = sha256(prk | 1) - - let mut hmac = HmacEngine::::new(&prk[..]); - hmac.input(&t1); - hmac.input(&[2; 1]); - // sha256(prk | t1 | 2) = sha256(sha256(master) | sha256(sha256(sha256(master) | 1) | 2) - (t1, Hmac::from_engine(hmac).into_inner()) +// Allows 1 or more inputs and "concatenates" them together using the input() function +// of HmacEngine:: +macro_rules! hmac_hash { + ( $salt:expr, ($( $input:expr ),+ )) => {{ + let mut engine = HmacEngine::::new($salt); + $( + engine.input($input); + )+ + Hmac::from_engine(engine).into_inner() + }} } + +/// Implements HKDF defined in [BOLT #8](https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#handshake-state) +/// [RFC 5869](https://tools.ietf.org/html/rfc5869pub) +/// Returns the first 64 octets as two 32 byte arrays +pub(super) fn derive(salt: &[u8], ikm: &[u8]) -> ([u8; 32], [u8; 32]) { + // 2.1. Notation + // + // HMAC-Hash denotes the HMAC function [HMAC] instantiated with hash + // function 'Hash'. HMAC always has two arguments: the first is a key + // and the second an input (or message). (Note that in the extract + // step, 'IKM' is used as the HMAC input, not as the HMAC key.) + // + // When the message is composed of several elements we use concatenation + // (denoted |) in the second argument; for example, HMAC(K, elem1 | + // elem2 | elem3). + + // 2.2. Step 1: Extract + // HKDF-Extract(salt, IKM) -> PRK + // PRK = HMAC-Hash(salt, IKM) + let prk = hmac_hash!(salt, (ikm)); + + // 2.3. Step 2: Expand + // HKDF-Expand(PRK, info, L) -> OKM + // N = ceil(L/HashLen) + // T = T(1) | T(2) | T(3) | ... | T(N) + // OKM = first L octets of T + // + // where: + // T(0) = empty string (zero length) + // T(1) = HMAC-Hash(PRK, T(0) | info | 0x01) + let t1 = hmac_hash!(&prk, (&[1])); + // T(2) = HMAC-Hash(PRK, T(1) | info | 0x02) + let t2 = hmac_hash!(&prk, (&t1, &[2])); + + return (t1, t2) +} + +// Appendix A. Test Vectors +#[cfg(test)] +mod test { + use hex; + use ln::peers::hkdf::derive; + + // Test with SHA-256 and zero-length salt/info + // Our implementation uses a zero-length info field and returns the first 64 octets. As a result, + // this test will be a prefix match on the vector provided by the RFC which is 42 bytes. + #[test] + fn rfc_5869_test_vector_3() { + let ikm = hex::decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b").unwrap(); + let (t1, t2) = derive(&[], &ikm); + + let mut calculated_okm = t1.to_vec(); + calculated_okm.extend_from_slice(&t2); + calculated_okm.truncate(42); + assert_eq!(calculated_okm, hex::decode("8da4e775a563c18f715f802a063c5a31b8a11f5c5ee1879ec3454e5f3c738d2d9d201395faa4b61a96c8").unwrap()); + } +} \ No newline at end of file From ea0ff518c7adf02d0f478d985917b0408a405725 Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Thu, 20 Aug 2020 14:09:57 -0700 Subject: [PATCH 25/72] refactor: Rename Handshake2 and related structs/enums --- lightning/src/ln/peers/handshake/mod.rs | 18 +-- lightning/src/ln/peers/handshake/states.rs | 123 +++++++++++---------- 2 files changed, 74 insertions(+), 67 deletions(-) diff --git a/lightning/src/ln/peers/handshake/mod.rs b/lightning/src/ln/peers/handshake/mod.rs index 87f568e0d78..6970e2a6c9e 100644 --- a/lightning/src/ln/peers/handshake/mod.rs +++ b/lightning/src/ln/peers/handshake/mod.rs @@ -5,28 +5,28 @@ use bitcoin::secp256k1::{PublicKey, SecretKey}; use ln::peers::conduit::Conduit; -use ln::peers::handshake::states::{HandshakeState2, UninitiatedHandshakeState, AwaitingActOneHandshakeState}; +use ln::peers::handshake::states::{HandshakeState, InitiatorStartingState, ResponderAwaitingActOneState}; mod states; /// Object for managing handshakes. /// Currently requires explicit ephemeral private key specification. pub struct PeerHandshake { - state: Option, + state: Option, } impl PeerHandshake { /// Instantiate a new handshake with a node identity secret key and an ephemeral private key pub fn new_outbound(initiator_static_private_key: &SecretKey, responder_static_public_key: &PublicKey, initiator_ephemeral_private_key: &SecretKey) -> Self { Self { - state: Some(HandshakeState2::Uninitiated2(UninitiatedHandshakeState::new(initiator_static_private_key.clone(), initiator_ephemeral_private_key.clone(), responder_static_public_key.clone()))), + state: Some(HandshakeState::InitiatorStarting(InitiatorStartingState::new(initiator_static_private_key.clone(), initiator_ephemeral_private_key.clone(), responder_static_public_key.clone()))), } } /// Instantiate a new handshake in anticipation of a peer's first handshake act pub fn new_inbound(responder_static_private_key: &SecretKey, responder_ephemeral_private_key: &SecretKey) -> Self { Self { - state: Some(HandshakeState2::AwaitingActOne2(AwaitingActOneHandshakeState::new(responder_static_private_key.clone(), responder_ephemeral_private_key.clone()))), + state: Some(HandshakeState::ResponderAwaitingActOne(ResponderAwaitingActOneState::new(responder_static_private_key.clone(), responder_ephemeral_private_key.clone()))), } } @@ -44,7 +44,7 @@ impl PeerHandshake { let (act_opt, mut next_state) = cur_state.next(input)?; let result = match next_state { - HandshakeState2::Complete2(ref mut conduit_and_pubkey) => { + HandshakeState::Complete(ref mut conduit_and_pubkey) => { Ok((act_opt, conduit_and_pubkey.take())) }, _ => { Ok((act_opt, None)) } @@ -62,7 +62,7 @@ mod test { use bitcoin::secp256k1::key::{PublicKey, SecretKey}; use ln::peers::handshake::PeerHandshake; - use ln::peers::handshake::states::HandshakeState2; + use ln::peers::handshake::states::HandshakeState; struct TestCtx { outbound_handshake: PeerHandshake, @@ -115,7 +115,7 @@ mod test { fn peer_handshake_new_outbound() { let test_ctx = TestCtx::new(); - assert_matches!(test_ctx.outbound_handshake.state, Some(HandshakeState2::Uninitiated2(_))); + assert_matches!(test_ctx.outbound_handshake.state, Some(HandshakeState::InitiatorStarting(_))); } // Default Inbound::AwaitingActOne @@ -123,7 +123,7 @@ mod test { fn peer_handshake_new_inbound() { let test_ctx = TestCtx::new(); - assert_matches!(test_ctx.inbound_handshake.state, Some(HandshakeState2::AwaitingActOne2(_))); + assert_matches!(test_ctx.inbound_handshake.state, Some(HandshakeState::ResponderAwaitingActOne(_))); } /* @@ -161,7 +161,7 @@ mod test { fn process_act_properly_updates_state() { let mut test_ctx = TestCtx::new(); do_process_act_or_panic!(test_ctx.outbound_handshake, &[]); - assert_matches!(test_ctx.outbound_handshake.state, Some(HandshakeState2::AwaitingActTwo2(_))); + assert_matches!(test_ctx.outbound_handshake.state, Some(HandshakeState::InitiatorAwaitingActTwo(_))); } // Test that any errors from the state machine are passed back to the caller diff --git a/lightning/src/ln/peers/handshake/states.rs b/lightning/src/ln/peers/handshake/states.rs index a67ee07b37e..1434cf28d82 100644 --- a/lightning/src/ln/peers/handshake/states.rs +++ b/lightning/src/ln/peers/handshake/states.rs @@ -4,7 +4,7 @@ use bitcoin::hashes::{Hash, HashEngine}; use bitcoin::hashes::sha256::Hash as Sha256; use bitcoin::secp256k1::{SecretKey, PublicKey}; -use ln::peers::handshake::states::HandshakeState2::{AwaitingActTwo2, AwaitingActThree2, Complete2}; +use ln::peers::handshake::states::HandshakeState::{InitiatorAwaitingActTwo, ResponderAwaitingActThree, Complete}; use ln::peers::{chacha, hkdf}; use ln::peers::conduit::{Conduit, SymmetricKey}; @@ -24,31 +24,35 @@ macro_rules! concat_then_sha256 { }} } -pub enum HandshakeState2 { - Uninitiated2(UninitiatedHandshakeState), - AwaitingActOne2(AwaitingActOneHandshakeState), - AwaitingActTwo2(AwaitingActTwoHandshakeState), - AwaitingActThree2(AwaitingActThreeHandshakeState), - Complete2(Option<(Conduit, PublicKey)>), +pub enum HandshakeState { + InitiatorStarting(InitiatorStartingState), + ResponderAwaitingActOne(ResponderAwaitingActOneState), + InitiatorAwaitingActTwo(InitiatorAwaitingActTwoState), + ResponderAwaitingActThree(ResponderAwaitingActThreeState), + Complete(Option<(Conduit, PublicKey)>), } -impl HandshakeState2 { - pub(crate) fn next(self, input: &[u8]) -> Result<(Option>, HandshakeState2), String> { +// Trait for all individual states to implement that ensure HandshakeState::next() can +// delegate to a common function signature. +trait IHandshakeState { + fn next(self, input: &[u8]) -> Result<(Option>, HandshakeState), String>; +} + +// Enum dispatch for state machine. Single public interface can statically dispatch to all states +impl HandshakeState { + pub fn next(self, input: &[u8]) -> Result<(Option>, HandshakeState), String> { match self { - HandshakeState2::Uninitiated2(state) => { state.next(input) }, - HandshakeState2::AwaitingActOne2(state) => { state.next(input) }, - HandshakeState2::AwaitingActTwo2(state) => { state.next(input) }, - HandshakeState2::AwaitingActThree2(state) => { state.next(input) }, - HandshakeState2::Complete2(_conduit) => { panic!("no acts left to process") } + HandshakeState::InitiatorStarting(state) => { state.next(input) }, + HandshakeState::ResponderAwaitingActOne(state) => { state.next(input) }, + HandshakeState::InitiatorAwaitingActTwo(state) => { state.next(input) }, + HandshakeState::ResponderAwaitingActThree(state) => { state.next(input) }, + HandshakeState::Complete(_conduit) => { panic!("no acts left to process") } } } } -trait IHandshakeState { - fn next(self, input: &[u8]) -> Result<(Option>, HandshakeState2), String>; -} - -pub struct UninitiatedHandshakeState { +// Handshake state of the Initiator prior to generating Act 1 +pub struct InitiatorStartingState { initiator_static_private_key: SecretKey, initiator_static_public_key: PublicKey, initiator_ephemeral_private_key: SecretKey, @@ -58,7 +62,8 @@ pub struct UninitiatedHandshakeState { hash: Sha256 } -pub struct AwaitingActOneHandshakeState { +// Handshake state of the Responder prior to receiving Act 1 +pub struct ResponderAwaitingActOneState { responder_static_private_key: SecretKey, responder_ephemeral_private_key: SecretKey, responder_ephemeral_public_key: PublicKey, @@ -67,7 +72,8 @@ pub struct AwaitingActOneHandshakeState { read_buffer: Vec } -pub struct AwaitingActTwoHandshakeState { +// Handshake state of the Initiator prior to receiving Act 2 +pub struct InitiatorAwaitingActTwoState { initiator_static_private_key: SecretKey, initiator_static_public_key: PublicKey, initiator_ephemeral_private_key: SecretKey, @@ -77,7 +83,8 @@ pub struct AwaitingActTwoHandshakeState { read_buffer: Vec } -pub struct AwaitingActThreeHandshakeState { +// Handshake state of the Responder prior to receiving Act 3 +pub struct ResponderAwaitingActThreeState { hash: Sha256, responder_ephemeral_private_key: SecretKey, chaining_key: ChainingKey, @@ -85,12 +92,12 @@ pub struct AwaitingActThreeHandshakeState { read_buffer: Vec } -impl UninitiatedHandshakeState { +impl InitiatorStartingState { pub(crate) fn new(initiator_static_private_key: SecretKey, initiator_ephemeral_private_key: SecretKey, responder_static_public_key: PublicKey) -> Self { let initiator_static_public_key = private_key_to_public_key(&initiator_static_private_key); let (hash, chaining_key) = handshake_state_initialization(&responder_static_public_key); let initiator_ephemeral_public_key = private_key_to_public_key(&initiator_ephemeral_private_key); - UninitiatedHandshakeState { + InitiatorStartingState { initiator_static_private_key, initiator_static_public_key, initiator_ephemeral_private_key, @@ -102,9 +109,9 @@ impl UninitiatedHandshakeState { } } -impl IHandshakeState for UninitiatedHandshakeState { +impl IHandshakeState for InitiatorStartingState { // https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#act-one (sender) - fn next(self, _input: &[u8]) -> Result<(Option>, HandshakeState2), String> { + fn next(self, _input: &[u8]) -> Result<(Option>, HandshakeState), String> { let initiator_static_private_key = self.initiator_static_private_key; let initiator_static_public_key = self.initiator_static_public_key; let initiator_ephemeral_private_key = self.initiator_ephemeral_private_key; @@ -124,7 +131,7 @@ impl IHandshakeState for UninitiatedHandshakeState { Ok(( Some(act_one.to_vec()), - AwaitingActTwo2(AwaitingActTwoHandshakeState { + InitiatorAwaitingActTwo(InitiatorAwaitingActTwoState { initiator_static_private_key, initiator_static_public_key, initiator_ephemeral_private_key, @@ -137,13 +144,13 @@ impl IHandshakeState for UninitiatedHandshakeState { } } -impl AwaitingActOneHandshakeState { +impl ResponderAwaitingActOneState { pub(crate) fn new(responder_static_private_key: SecretKey, responder_ephemeral_private_key: SecretKey) -> Self { let responder_static_public_key = private_key_to_public_key(&responder_static_private_key); let (hash, chaining_key) = handshake_state_initialization(&responder_static_public_key); let responder_ephemeral_public_key = private_key_to_public_key(&responder_ephemeral_private_key); - AwaitingActOneHandshakeState { + ResponderAwaitingActOneState { responder_static_private_key, responder_ephemeral_private_key, responder_ephemeral_public_key, @@ -154,10 +161,10 @@ impl AwaitingActOneHandshakeState { } } -impl IHandshakeState for AwaitingActOneHandshakeState { +impl IHandshakeState for ResponderAwaitingActOneState { // https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#act-one (receiver) // https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#act-two (sender) - fn next(self, input: &[u8]) -> Result<(Option>, HandshakeState2), String> { + fn next(self, input: &[u8]) -> Result<(Option>, HandshakeState), String> { let mut read_buffer = self.read_buffer; read_buffer.extend_from_slice(input); @@ -184,7 +191,7 @@ impl IHandshakeState for AwaitingActOneHandshakeState { Ok(( Some(act_two), - AwaitingActThree2(AwaitingActThreeHandshakeState { + ResponderAwaitingActThree(ResponderAwaitingActThreeState { hash, responder_ephemeral_private_key, chaining_key, @@ -195,10 +202,10 @@ impl IHandshakeState for AwaitingActOneHandshakeState { } } -impl IHandshakeState for AwaitingActTwoHandshakeState { +impl IHandshakeState for InitiatorAwaitingActTwoState { // https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#act-two (receiver) // https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#act-three (sender) - fn next(self, input: &[u8]) -> Result<(Option>, HandshakeState2), String> { + fn next(self, input: &[u8]) -> Result<(Option>, HandshakeState), String> { let mut read_buffer = self.read_buffer; read_buffer.extend_from_slice(input); @@ -256,14 +263,14 @@ impl IHandshakeState for AwaitingActTwoHandshakeState { Ok(( Some(act_three), - Complete2(Some((conduit, responder_static_public_key))) + Complete(Some((conduit, responder_static_public_key))) )) } } -impl IHandshakeState for AwaitingActThreeHandshakeState { +impl IHandshakeState for ResponderAwaitingActThreeState { // https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#act-three (receiver) - fn next(self, input: &[u8]) -> Result<(Option>, HandshakeState2), String> { + fn next(self, input: &[u8]) -> Result<(Option>, HandshakeState), String> { let mut read_buffer = self.read_buffer; read_buffer.extend_from_slice(input); @@ -334,7 +341,7 @@ impl IHandshakeState for AwaitingActThreeHandshakeState { Ok(( None, - Complete2(Some((conduit, initiator_pubkey))) + Complete(Some((conduit, initiator_pubkey))) )) } } @@ -465,13 +472,13 @@ mod test { use bitcoin::secp256k1; use bitcoin::secp256k1::{PublicKey, SecretKey}; - use ln::peers::handshake::states::{UninitiatedHandshakeState, AwaitingActOneHandshakeState, HandshakeState2}; - use ln::peers::handshake::states::HandshakeState2::{AwaitingActThree2, AwaitingActTwo2, Complete2}; + use ln::peers::handshake::states::{InitiatorStartingState, ResponderAwaitingActOneState, HandshakeState}; + use ln::peers::handshake::states::HandshakeState::{ResponderAwaitingActThree, InitiatorAwaitingActTwo, Complete}; struct TestCtx { - initiator: HandshakeState2, + initiator: HandshakeState, initiator_public_key: PublicKey, - responder: HandshakeState2, + responder: HandshakeState, responder_static_public_key: PublicKey } @@ -486,13 +493,13 @@ mod test { let responder_static_public_key = PublicKey::from_secret_key(&curve, &responder_static_private_key); let responder_ephemeral_private_key = SecretKey::from_slice(&[0x_22_u8; 32]).unwrap(); - let initiator = UninitiatedHandshakeState::new(initiator_static_private_key, initiator_ephemeral_private_key, responder_static_public_key); - let responder = AwaitingActOneHandshakeState::new(responder_static_private_key, responder_ephemeral_private_key); + let initiator = InitiatorStartingState::new(initiator_static_private_key, initiator_ephemeral_private_key, responder_static_public_key); + let responder = ResponderAwaitingActOneState::new(responder_static_private_key, responder_ephemeral_private_key); TestCtx { - initiator: HandshakeState2::Uninitiated2(initiator), + initiator: HandshakeState::InitiatorStarting(initiator), initiator_public_key, - responder: HandshakeState2::AwaitingActOne2(responder), + responder: HandshakeState::ResponderAwaitingActOne(responder), responder_static_public_key, } } @@ -517,20 +524,20 @@ mod test { } } - // Initiator::Uninitiated -> AwaitingActTwo + // Initiator::Starting -> AwaitingActTwo #[test] - fn uninitiated_to_awaiting_act_two() { + fn starting_to_awaiting_act_two() { let test_ctx = TestCtx::new(); - assert_matches!(test_ctx.initiator.next(&[]).unwrap(), (Some(_), AwaitingActTwo2(_))); + assert_matches!(test_ctx.initiator.next(&[]).unwrap(), (Some(_), InitiatorAwaitingActTwo(_))); } - // Initiator::Uninitiated -> AwaitingActTwo (extra bytes in argument) + // Initiator::Starting -> AwaitingActTwo (extra bytes in argument) #[test] - fn uninitiated_to_awaiting_act_two_extra_bytes() { + fn starting_to_awaiting_act_two_extra_bytes() { let test_ctx = TestCtx::new(); - assert_matches!(test_ctx.initiator.next(&[1]).unwrap(), (Some(_), AwaitingActTwo2(_))); + assert_matches!(test_ctx.initiator.next(&[1]).unwrap(), (Some(_), InitiatorAwaitingActTwo(_))); } // Responder::AwaitingActOne -> Error (input too small) @@ -551,7 +558,7 @@ mod test { let (mut act1, _) = do_next_or_panic!(test_ctx.initiator, &[]); act1.extend_from_slice(&[1]); - assert_matches!(test_ctx.responder.next(&act1).unwrap(), (Some(_), AwaitingActThree2(_))); + assert_matches!(test_ctx.responder.next(&act1).unwrap(), (Some(_), ResponderAwaitingActThree(_))); } // Responder::AwaitingActOne -> Error (bad version byte) @@ -595,7 +602,7 @@ mod test { let test_ctx = TestCtx::new(); let (act1, _) = do_next_or_panic!(test_ctx.initiator, &[]); - assert_matches!(test_ctx.responder.next(&act1).unwrap(), (Some(_), AwaitingActThree2(_))); + assert_matches!(test_ctx.responder.next(&act1).unwrap(), (Some(_), ResponderAwaitingActThree(_))); } // Initiator::AwaitingActTwo -> Complete @@ -605,7 +612,7 @@ mod test { let (act1, awaiting_act_two_state) = do_next_or_panic!(test_ctx.initiator, &[]); let (act2, _awaiting_act_three_state) = do_next_or_panic!(test_ctx.responder, &act1); - let remote_pubkey = if let (Some(_), Complete2(Some((_, remote_pubkey)))) = awaiting_act_two_state.next(&act2).unwrap() { + let remote_pubkey = if let (Some(_), Complete(Some((_, remote_pubkey)))) = awaiting_act_two_state.next(&act2).unwrap() { remote_pubkey } else { panic!(); @@ -627,7 +634,7 @@ mod test { let (_act3, complete_state) = do_next_or_panic!(awaiting_act_two_state, &act2); - let conduit = if let Complete2(Some((conduit, _))) = complete_state { + let conduit = if let Complete(Some((conduit, _))) = complete_state { conduit } else { panic!(); @@ -689,7 +696,7 @@ mod test { let (act2, awaiting_act_three_state) = do_next_or_panic!(test_ctx.responder, &act1); let (act3, _complete_state) = do_next_or_panic!(awaiting_act_two_state, &act2); - let remote_pubkey = if let (None, Complete2(Some((_, remote_pubkey)))) = awaiting_act_three_state.next(&act3).unwrap() { + let remote_pubkey = if let (None, Complete(Some((_, remote_pubkey)))) = awaiting_act_three_state.next(&act3).unwrap() { remote_pubkey } else { panic!(); @@ -709,7 +716,7 @@ mod test { let (mut act3, _complete_state) = do_next_or_panic!(awaiting_act_two_state, &act2); act3.extend_from_slice(&[2; 100]); - let conduit = if let (_, Complete2(Some((conduit, _)))) = awaiting_act_three_state.next(&act3).unwrap() { + let conduit = if let (_, Complete(Some((conduit, _)))) = awaiting_act_three_state.next(&act3).unwrap() { conduit } else { panic!(); From 816a83c38086d286c3cb08a0690cc4d9a0dae14a Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Thu, 20 Aug 2020 16:11:10 -0700 Subject: [PATCH 26/72] refactor: Clean up visibility/imports in handshake/ Not using IHandshakeState slipped through the cracks until this point, but using it makes the enum dispatch follow a more standardized pattern. --- lightning/src/ln/peers/handshake/mod.rs | 11 +++---- lightning/src/ln/peers/handshake/states.rs | 38 +++++++++++++--------- 2 files changed, 28 insertions(+), 21 deletions(-) diff --git a/lightning/src/ln/peers/handshake/mod.rs b/lightning/src/ln/peers/handshake/mod.rs index 6970e2a6c9e..52efde4ad97 100644 --- a/lightning/src/ln/peers/handshake/mod.rs +++ b/lightning/src/ln/peers/handshake/mod.rs @@ -5,7 +5,7 @@ use bitcoin::secp256k1::{PublicKey, SecretKey}; use ln::peers::conduit::Conduit; -use ln::peers::handshake::states::{HandshakeState, InitiatorStartingState, ResponderAwaitingActOneState}; +use ln::peers::handshake::states::{HandshakeState, IHandshakeState}; mod states; @@ -19,14 +19,14 @@ impl PeerHandshake { /// Instantiate a new handshake with a node identity secret key and an ephemeral private key pub fn new_outbound(initiator_static_private_key: &SecretKey, responder_static_public_key: &PublicKey, initiator_ephemeral_private_key: &SecretKey) -> Self { Self { - state: Some(HandshakeState::InitiatorStarting(InitiatorStartingState::new(initiator_static_private_key.clone(), initiator_ephemeral_private_key.clone(), responder_static_public_key.clone()))), + state: Some(HandshakeState::new_initiator(initiator_static_private_key, responder_static_public_key, initiator_ephemeral_private_key)) } } /// Instantiate a new handshake in anticipation of a peer's first handshake act pub fn new_inbound(responder_static_private_key: &SecretKey, responder_ephemeral_private_key: &SecretKey) -> Self { Self { - state: Some(HandshakeState::ResponderAwaitingActOne(ResponderAwaitingActOneState::new(responder_static_private_key.clone(), responder_ephemeral_private_key.clone()))), + state: Some(HandshakeState::new_responder(responder_static_private_key, responder_ephemeral_private_key)) } } @@ -58,12 +58,11 @@ impl PeerHandshake { #[cfg(test)] mod test { + use super::*; + use bitcoin::secp256k1; use bitcoin::secp256k1::key::{PublicKey, SecretKey}; - use ln::peers::handshake::PeerHandshake; - use ln::peers::handshake::states::HandshakeState; - struct TestCtx { outbound_handshake: PeerHandshake, outbound_static_public_key: PublicKey, diff --git a/lightning/src/ln/peers/handshake/states.rs b/lightning/src/ln/peers/handshake/states.rs index 1434cf28d82..a95ee0158fc 100644 --- a/lightning/src/ln/peers/handshake/states.rs +++ b/lightning/src/ln/peers/handshake/states.rs @@ -4,7 +4,6 @@ use bitcoin::hashes::{Hash, HashEngine}; use bitcoin::hashes::sha256::Hash as Sha256; use bitcoin::secp256k1::{SecretKey, PublicKey}; -use ln::peers::handshake::states::HandshakeState::{InitiatorAwaitingActTwo, ResponderAwaitingActThree, Complete}; use ln::peers::{chacha, hkdf}; use ln::peers::conduit::{Conduit, SymmetricKey}; @@ -24,7 +23,7 @@ macro_rules! concat_then_sha256 { }} } -pub enum HandshakeState { +pub(super) enum HandshakeState { InitiatorStarting(InitiatorStartingState), ResponderAwaitingActOne(ResponderAwaitingActOneState), InitiatorAwaitingActTwo(InitiatorAwaitingActTwoState), @@ -34,13 +33,22 @@ pub enum HandshakeState { // Trait for all individual states to implement that ensure HandshakeState::next() can // delegate to a common function signature. -trait IHandshakeState { +pub(super) trait IHandshakeState { fn next(self, input: &[u8]) -> Result<(Option>, HandshakeState), String>; } // Enum dispatch for state machine. Single public interface can statically dispatch to all states impl HandshakeState { - pub fn next(self, input: &[u8]) -> Result<(Option>, HandshakeState), String> { + pub(super) fn new_initiator(initiator_static_private_key: &SecretKey, responder_static_public_key: &PublicKey, initiator_ephemeral_private_key: &SecretKey) -> Self { + HandshakeState::InitiatorStarting(InitiatorStartingState::new(initiator_static_private_key.clone(), initiator_ephemeral_private_key.clone(), responder_static_public_key.clone())) + } + pub(super) fn new_responder(responder_static_private_key: &SecretKey, responder_ephemeral_private_key: &SecretKey) -> Self { + HandshakeState::ResponderAwaitingActOne(ResponderAwaitingActOneState::new(responder_static_private_key.clone(), responder_ephemeral_private_key.clone())) + } +} + +impl IHandshakeState for HandshakeState { + fn next(self, input: &[u8]) -> Result<(Option>, HandshakeState), String> { match self { HandshakeState::InitiatorStarting(state) => { state.next(input) }, HandshakeState::ResponderAwaitingActOne(state) => { state.next(input) }, @@ -52,7 +60,7 @@ impl HandshakeState { } // Handshake state of the Initiator prior to generating Act 1 -pub struct InitiatorStartingState { +pub(super) struct InitiatorStartingState { initiator_static_private_key: SecretKey, initiator_static_public_key: PublicKey, initiator_ephemeral_private_key: SecretKey, @@ -63,7 +71,7 @@ pub struct InitiatorStartingState { } // Handshake state of the Responder prior to receiving Act 1 -pub struct ResponderAwaitingActOneState { +pub(super) struct ResponderAwaitingActOneState { responder_static_private_key: SecretKey, responder_ephemeral_private_key: SecretKey, responder_ephemeral_public_key: PublicKey, @@ -73,7 +81,7 @@ pub struct ResponderAwaitingActOneState { } // Handshake state of the Initiator prior to receiving Act 2 -pub struct InitiatorAwaitingActTwoState { +pub(super) struct InitiatorAwaitingActTwoState { initiator_static_private_key: SecretKey, initiator_static_public_key: PublicKey, initiator_ephemeral_private_key: SecretKey, @@ -84,7 +92,7 @@ pub struct InitiatorAwaitingActTwoState { } // Handshake state of the Responder prior to receiving Act 3 -pub struct ResponderAwaitingActThreeState { +pub(super) struct ResponderAwaitingActThreeState { hash: Sha256, responder_ephemeral_private_key: SecretKey, chaining_key: ChainingKey, @@ -131,7 +139,7 @@ impl IHandshakeState for InitiatorStartingState { Ok(( Some(act_one.to_vec()), - InitiatorAwaitingActTwo(InitiatorAwaitingActTwoState { + HandshakeState::InitiatorAwaitingActTwo(InitiatorAwaitingActTwoState { initiator_static_private_key, initiator_static_public_key, initiator_ephemeral_private_key, @@ -191,7 +199,7 @@ impl IHandshakeState for ResponderAwaitingActOneState { Ok(( Some(act_two), - ResponderAwaitingActThree(ResponderAwaitingActThreeState { + HandshakeState::ResponderAwaitingActThree(ResponderAwaitingActThreeState { hash, responder_ephemeral_private_key, chaining_key, @@ -263,7 +271,7 @@ impl IHandshakeState for InitiatorAwaitingActTwoState { Ok(( Some(act_three), - Complete(Some((conduit, responder_static_public_key))) + HandshakeState::Complete(Some((conduit, responder_static_public_key))) )) } } @@ -341,7 +349,7 @@ impl IHandshakeState for ResponderAwaitingActThreeState { Ok(( None, - Complete(Some((conduit, initiator_pubkey))) + HandshakeState::Complete(Some((conduit, initiator_pubkey))) )) } } @@ -467,14 +475,14 @@ fn ecdh(private_key: &SecretKey, public_key: &PublicKey) -> SymmetricKey { #[cfg(test)] mod test { + use super::*; + use super::HandshakeState::*; + use hex; use bitcoin::secp256k1; use bitcoin::secp256k1::{PublicKey, SecretKey}; - use ln::peers::handshake::states::{InitiatorStartingState, ResponderAwaitingActOneState, HandshakeState}; - use ln::peers::handshake::states::HandshakeState::{ResponderAwaitingActThree, InitiatorAwaitingActTwo, Complete}; - struct TestCtx { initiator: HandshakeState, initiator_public_key: PublicKey, From 9c5a4b7f28345f528e0eea66297f3308529e6eb8 Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Fri, 21 Aug 2020 10:41:55 -0700 Subject: [PATCH 27/72] tests: Leverage RFC test vectors in unit tests This uses hard-coded test vectors to minimize the code under test as well as ensures full coverage. One additional test was added to account for an Act3 bad rs value. --- lightning/src/ln/peers/handshake/states.rs | 216 ++++++++++++--------- 1 file changed, 122 insertions(+), 94 deletions(-) diff --git a/lightning/src/ln/peers/handshake/states.rs b/lightning/src/ln/peers/handshake/states.rs index a95ee0158fc..d7de6845b71 100644 --- a/lightning/src/ln/peers/handshake/states.rs +++ b/lightning/src/ln/peers/handshake/states.rs @@ -119,7 +119,12 @@ impl InitiatorStartingState { impl IHandshakeState for InitiatorStartingState { // https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#act-one (sender) - fn next(self, _input: &[u8]) -> Result<(Option>, HandshakeState), String> { + fn next(self, input: &[u8]) -> Result<(Option>, HandshakeState), String> { + + if input.len() > 0 { + return Err("first call for initiator must be empty".to_string()); + } + let initiator_static_private_key = self.initiator_static_private_key; let initiator_static_public_key = self.initiator_static_public_key; let initiator_ephemeral_private_key = self.initiator_ephemeral_private_key; @@ -474,6 +479,8 @@ fn ecdh(private_key: &SecretKey, public_key: &PublicKey) -> SymmetricKey { } #[cfg(test)] +// Reference RFC test vectors for hard-coded values +// https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#appendix-a-transport-test-vectors mod test { use super::*; use super::HandshakeState::*; @@ -487,7 +494,11 @@ mod test { initiator: HandshakeState, initiator_public_key: PublicKey, responder: HandshakeState, - responder_static_public_key: PublicKey + responder_static_public_key: PublicKey, + valid_act1: Vec, + valid_act2: Vec, + valid_act3: Vec, + } impl TestCtx { @@ -509,6 +520,9 @@ mod test { initiator_public_key, responder: HandshakeState::ResponderAwaitingActOne(responder), responder_static_public_key, + valid_act1: hex::decode("00036360e856310ce5d294e8be33fc807077dc56ac80d95d9cd4ddbd21325eff73f70df6086551151f58b8afe6c195782c6a").unwrap(), + valid_act2: hex::decode("0002466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f276e2470b93aac583c9ef6eafca3f730ae").unwrap(), + valid_act3: hex::decode("00b9e3a702e93e3a9948c2ed6e5fd7590a6e1c3a0344cfc9d5b57357049aa22355361aa02e55a8fc28fef5bd6d71ad0c38228dc68b1c466263b47fdf31e560e139ba").unwrap() } } } @@ -536,23 +550,31 @@ mod test { #[test] fn starting_to_awaiting_act_two() { let test_ctx = TestCtx::new(); + let (act1, awaiting_act_two_state) = do_next_or_panic!(test_ctx.initiator, &[]); - assert_matches!(test_ctx.initiator.next(&[]).unwrap(), (Some(_), InitiatorAwaitingActTwo(_))); + assert_eq!(act1, test_ctx.valid_act1); + assert_matches!(awaiting_act_two_state, InitiatorAwaitingActTwo(_)); } // Initiator::Starting -> AwaitingActTwo (extra bytes in argument) #[test] fn starting_to_awaiting_act_two_extra_bytes() { let test_ctx = TestCtx::new(); + let (act1, awaiting_act_two_state) = do_next_or_panic!(test_ctx.initiator, &[]); - assert_matches!(test_ctx.initiator.next(&[1]).unwrap(), (Some(_), InitiatorAwaitingActTwo(_))); + assert_eq!(act1, test_ctx.valid_act1); + assert_matches!(awaiting_act_two_state, InitiatorAwaitingActTwo(_)); } - // Responder::AwaitingActOne -> Error (input too small) + // Responder::AwaitingActOne -> AwaitingActThree + // RFC test vector: transport-responder successful handshake #[test] - fn awaiting_act_one_to_awaiting_act_three_input_too_small() { + fn awaiting_act_one_to_awaiting_act_three() { let test_ctx = TestCtx::new(); - assert_eq!(test_ctx.responder.next(&[]).err(), Some(String::from("need at least 50 bytes"))) + let (act2, awaiting_act_three_state) = test_ctx.responder.next(&test_ctx.valid_act1).unwrap(); + + assert_eq!(act2.unwrap(), test_ctx.valid_act2); + assert_matches!(awaiting_act_three_state, ResponderAwaitingActThree(_)); } // Responder::AwaitingActOne -> AwaitingActThree @@ -563,70 +585,70 @@ mod test { #[test] fn awaiting_act_one_to_awaiting_act_three_input_extra_bytes() { let test_ctx = TestCtx::new(); - let (mut act1, _) = do_next_or_panic!(test_ctx.initiator, &[]); + let mut act1 = test_ctx.valid_act1; act1.extend_from_slice(&[1]); + let (act2, awaiting_act_three_state) = test_ctx.responder.next(&act1).unwrap(); - assert_matches!(test_ctx.responder.next(&act1).unwrap(), (Some(_), ResponderAwaitingActThree(_))); + assert_eq!(act2.unwrap(), test_ctx.valid_act2); + assert_matches!(awaiting_act_three_state, ResponderAwaitingActThree(_)); } - // Responder::AwaitingActOne -> Error (bad version byte) + // Responder::AwaitingActOne -> Error (input too small) + // RFC test vector: transport-responder act1 short read test #[test] - fn awaiting_act_one_to_awaiting_act_three_input_bad_version() { + fn awaiting_act_one_to_awaiting_act_three_input_too_small() { let test_ctx = TestCtx::new(); - let (mut act1, _) = do_next_or_panic!(test_ctx.initiator, &[]); - // set version byte to 1 - act1[0] = 1; - - assert_eq!(test_ctx.responder.next(&act1).err(), Some(String::from("unexpected version"))); + let act1 = hex::decode("00036360e856310ce5d294e8be33fc807077dc56ac80d95d9cd4ddbd21325eff73f70df6086551151f58b8afe6c195782c").unwrap(); + assert_eq!(test_ctx.responder.next(&act1).err(), Some(String::from("need at least 50 bytes"))) } - // Responder::AwaitingActOne -> Error (invalid hmac) + // Responder::AwaitingActOne -> Error (bad version byte) + // RFC test vector: transport-responder act1 bad version test #[test] - fn awaiting_act_one_to_awaiting_act_three_invalid_hmac() { + fn awaiting_act_one_to_awaiting_act_three_input_bad_version() { let test_ctx = TestCtx::new(); - // Modify the initiator to point to a different responder - let (mut act1, _) = do_next_or_panic!(test_ctx.initiator, &[]); - // corrupt the ciphertext - act1[34] = 0; + let act1 = hex::decode("01036360e856310ce5d294e8be33fc807077dc56ac80d95d9cd4ddbd21325eff73f70df6086551151f58b8afe6c195782c6a").unwrap(); - assert_eq!(test_ctx.responder.next(&act1).err(), Some(String::from("invalid hmac"))); + assert_eq!(test_ctx.responder.next(&act1).err(), Some(String::from("unexpected version"))); } // Responder::AwaitingActOne -> Error (invalid remote ephemeral key) + // RFC test vector: transport-responder act1 bad key serialization test #[test] fn awaiting_act_one_to_awaiting_act_three_invalid_remote_ephemeral_key() { let test_ctx = TestCtx::new(); - // Modify the initiator to point to a different responder - let (mut act1, _) = do_next_or_panic!(test_ctx.initiator, &[]); - // corrupt the ephemeral public key - act1[1] = 0; + let act1 = hex::decode("00046360e856310ce5d294e8be33fc807077dc56ac80d95d9cd4ddbd21325eff73f70df6086551151f58b8afe6c195782c6a").unwrap(); assert_eq!(test_ctx.responder.next(&act1).err(), Some(String::from("invalid remote ephemeral public key"))); } - // Responder::AwaitingActOne -> AwaitingActThree + // Responder::AwaitingActOne -> Error (invalid hmac) + // RFC test vector: transport-responder act1 bad MAC test #[test] - fn awaiting_act_one_to_awaiting_act_three() { + fn awaiting_act_one_to_awaiting_act_three_invalid_hmac() { let test_ctx = TestCtx::new(); - let (act1, _) = do_next_or_panic!(test_ctx.initiator, &[]); + let act1 = hex::decode("00036360e856310ce5d294e8be33fc807077dc56ac80d95d9cd4ddbd21325eff73f70df6086551151f58b8afe6c195782c6b").unwrap(); - assert_matches!(test_ctx.responder.next(&act1).unwrap(), (Some(_), ResponderAwaitingActThree(_))); + assert_eq!(test_ctx.responder.next(&act1).err(), Some(String::from("invalid hmac"))); } // Initiator::AwaitingActTwo -> Complete + // RFC test vector: transport-initiator successful handshake #[test] fn awaiting_act_two_to_complete() { let test_ctx = TestCtx::new(); - let (act1, awaiting_act_two_state) = do_next_or_panic!(test_ctx.initiator, &[]); - let (act2, _awaiting_act_three_state) = do_next_or_panic!(test_ctx.responder, &act1); + let (_act1, awaiting_act_two_state) = do_next_or_panic!(test_ctx.initiator, &[]); + let (act3, complete_state) = do_next_or_panic!(awaiting_act_two_state, &test_ctx.valid_act2); - let remote_pubkey = if let (Some(_), Complete(Some((_, remote_pubkey)))) = awaiting_act_two_state.next(&act2).unwrap() { - remote_pubkey + let (conduit, remote_pubkey) = if let Complete(Some((conduit, remote_pubkey))) = complete_state { + (conduit, remote_pubkey) } else { panic!(); }; + assert_eq!(act3, test_ctx.valid_act3); assert_eq!(remote_pubkey, test_ctx.responder_static_public_key); + assert_eq!(0, conduit.decryptor.read_buffer_length()); } // Initiator::AwaitingActTwo -> Complete (with extra data) @@ -636,81 +658,81 @@ mod test { #[test] fn awaiting_act_two_to_complete_excess_bytes_are_in_conduit() { let test_ctx = TestCtx::new(); - let (act1, awaiting_act_two_state) = do_next_or_panic!(test_ctx.initiator, &[]); - let (mut act2, _awaiting_act_three_state) = do_next_or_panic!(test_ctx.responder, &act1); + let (_act1, awaiting_act_two_state) = do_next_or_panic!(test_ctx.initiator, &[]); + let mut act2 = test_ctx.valid_act2; act2.extend_from_slice(&[1; 100]); + let (act3, complete_state) = do_next_or_panic!(awaiting_act_two_state, &act2); - let (_act3, complete_state) = do_next_or_panic!(awaiting_act_two_state, &act2); - - let conduit = if let Complete(Some((conduit, _))) = complete_state { - conduit + let (conduit, remote_pubkey) = if let Complete(Some((conduit, remote_pubkey))) = complete_state { + (conduit, remote_pubkey) } else { panic!(); }; + assert_eq!(act3, test_ctx.valid_act3); + assert_eq!(remote_pubkey, test_ctx.responder_static_public_key); assert_eq!(100, conduit.decryptor.read_buffer_length()); } // Initiator::AwaitingActTwo -> Error (input too small) + // RFC test vector: transport-initiator act2 short read test #[test] fn awaiting_act_two_input_too_small() { let test_ctx = TestCtx::new(); let (_act1, awaiting_act_two_state) = do_next_or_panic!(test_ctx.initiator, &[]); + let act2 = hex::decode("0002466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f276e2470b93aac583c9ef6eafca3f730").unwrap(); - assert_eq!(awaiting_act_two_state.next(&[]).err(), Some(String::from("need at least 50 bytes"))); + assert_eq!(awaiting_act_two_state.next(&act2).err(), Some(String::from("need at least 50 bytes"))); } // Initiator::AwaitingActTwo -> Error (bad version byte) + // RFC test vector: transport-initiator act2 bad version test #[test] fn awaiting_act_two_bad_version_byte() { let test_ctx = TestCtx::new(); - let (act1, awaiting_act_two_state) = do_next_or_panic!(test_ctx.initiator, &[]); - let (mut act2, _awaiting_act_three_state) = do_next_or_panic!(test_ctx.responder, &act1); - // set invalid version byte - act2[0] = 1; + let (_act1, awaiting_act_two_state) = do_next_or_panic!(test_ctx.initiator, &[]); + let act2 = hex::decode("0102466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f276e2470b93aac583c9ef6eafca3f730ae").unwrap(); assert_eq!(awaiting_act_two_state.next(&act2).err(), Some(String::from("unexpected version"))); } - // Initiator::AwaitingActTwo -> Error (invalid hmac) + // Initiator::AwaitingActTwo -> Error (invalid ephemeral public key) + // RFC test vector: transport-initiator act2 bad key serialization test #[test] - fn awaiting_act_two_invalid_hmac() { + fn awaiting_act_two_invalid_ephemeral_public_key() { let test_ctx = TestCtx::new(); - let (act1, awaiting_act_two_state) = do_next_or_panic!(test_ctx.initiator, &[]); - let (mut act2, _awaiting_act_three_state) = do_next_or_panic!(test_ctx.responder, &act1); - // corrupt the ciphertext - act2[34] = 0; + let (_act1, awaiting_act_two_state) = do_next_or_panic!(test_ctx.initiator, &[]); + let act2 = hex::decode("0004466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f276e2470b93aac583c9ef6eafca3f730ae").unwrap(); - assert_eq!(awaiting_act_two_state.next(&act2).err(), Some(String::from("invalid hmac"))); + assert_eq!(awaiting_act_two_state.next(&act2).err(), Some(String::from("invalid remote ephemeral public key"))); } - // Initiator::AwaitingActTwo -> Error (invalid ephemeral public key) + // Initiator::AwaitingActTwo -> Error (invalid hmac) + // RFC test vector: transport-initiator act2 bad MAC test #[test] - fn awaiting_act_two_invalid_ephemeral_public_key() { + fn awaiting_act_two_invalid_hmac() { let test_ctx = TestCtx::new(); - let (act1, awaiting_act_two_state) = do_next_or_panic!(test_ctx.initiator, &[]); - let (mut act2, _awaiting_act_three_state) = do_next_or_panic!(test_ctx.responder, &act1); - // corrupt the ephemeral public key - act2[1] = 0; + let (_act1, awaiting_act_two_state) = do_next_or_panic!(test_ctx.initiator, &[]); + let act2 = hex::decode("0002466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f276e2470b93aac583c9ef6eafca3f730af").unwrap(); - assert_eq!(awaiting_act_two_state.next(&act2).err(), Some(String::from("invalid remote ephemeral public key"))); + assert_eq!(awaiting_act_two_state.next(&act2).err(), Some(String::from("invalid hmac"))); } // Responder::AwaitingActThree -> Complete + // RFC test vector: transport-responder successful handshake #[test] fn awaiting_act_three_to_complete() { let test_ctx = TestCtx::new(); - let (act1, awaiting_act_two_state) = do_next_or_panic!(test_ctx.initiator, &[]); - let (act2, awaiting_act_three_state) = do_next_or_panic!(test_ctx.responder, &act1); - let (act3, _complete_state) = do_next_or_panic!(awaiting_act_two_state, &act2); + let (_act2, awaiting_act_three_state) = do_next_or_panic!(test_ctx.responder, &test_ctx.valid_act1); - let remote_pubkey = if let (None, Complete(Some((_, remote_pubkey)))) = awaiting_act_three_state.next(&act3).unwrap() { - remote_pubkey + let (conduit, remote_pubkey) = if let (None, Complete(Some((conduit, remote_pubkey)))) = awaiting_act_three_state.next(&test_ctx.valid_act3).unwrap() { + (conduit, remote_pubkey) } else { panic!(); }; assert_eq!(remote_pubkey, test_ctx.initiator_public_key); + assert_eq!(0, conduit.decryptor.read_buffer_length()); } // Responder::AwaitingActThree -> None (with extra bytes) @@ -719,66 +741,71 @@ mod test { #[test] fn awaiting_act_three_excess_bytes_after_complete_are_in_conduit() { let test_ctx = TestCtx::new(); - let (act1, awaiting_act_two_state) = do_next_or_panic!(test_ctx.initiator, &[]); - let (act2, awaiting_act_three_state) = do_next_or_panic!(test_ctx.responder, &act1); - let (mut act3, _complete_state) = do_next_or_panic!(awaiting_act_two_state, &act2); + let (_act2, awaiting_act_three_state) = do_next_or_panic!(test_ctx.responder, &test_ctx.valid_act1); + let mut act3 = test_ctx.valid_act3; act3.extend_from_slice(&[2; 100]); - let conduit = if let (_, Complete(Some((conduit, _)))) = awaiting_act_three_state.next(&act3).unwrap() { - conduit + let (conduit, remote_pubkey) = if let (None, Complete(Some((conduit, remote_pubkey)))) = awaiting_act_three_state.next(&act3).unwrap() { + (conduit, remote_pubkey) } else { panic!(); }; + assert_eq!(remote_pubkey, test_ctx.initiator_public_key); assert_eq!(100, conduit.decryptor.read_buffer_length()); } - // Responder::AwaitingActThree -> Error (input too small) + // Responder::AwaitingActThree -> Error (bad version bytes) + // RFC test vector: transport-responder act3 bad version test #[test] - fn awaiting_act_three_input_too_small() { + fn awaiting_act_three_bad_version_bytes() { let test_ctx = TestCtx::new(); - let (act1, awaiting_act_two_state) = do_next_or_panic!(test_ctx.initiator, &[]); - let (act2, awaiting_act_three_state) = do_next_or_panic!(test_ctx.responder, &act1); - let (act3, _complete) = do_next_or_panic!(awaiting_act_two_state, &act2); + let (_act2, awaiting_act_three_state) = do_next_or_panic!(test_ctx.responder, &test_ctx.valid_act1); + let act3 = hex::decode("01b9e3a702e93e3a9948c2ed6e5fd7590a6e1c3a0344cfc9d5b57357049aa22355361aa02e55a8fc28fef5bd6d71ad0c38228dc68b1c466263b47fdf31e560e139ba").unwrap(); - assert_eq!(awaiting_act_three_state.next(&act3[..65]).err(), Some(String::from("need at least 66 bytes"))); + assert_eq!(awaiting_act_three_state.next(&act3).err(), Some(String::from("unexpected version"))); } - // Responder::AwaitingActThree -> Error (bad version bytes) + // Responder::AwaitingActThree -> Error (input too small) + // RFC test vector: transport-responder act3 short read test #[test] - fn awaiting_act_three_bad_version_bytes() { + fn awaiting_act_three_input_too_small() { let test_ctx = TestCtx::new(); - let (act1, awaiting_act_two_state) = do_next_or_panic!(test_ctx.initiator, &[]); - let (act2, awaiting_act_three_state) = do_next_or_panic!(test_ctx.responder, &act1); - let (mut act3, _complete_state) = do_next_or_panic!(awaiting_act_two_state, &act2); - // set version byte to 1 - act3[0] = 1; + let (_act2, awaiting_act_three_state) = do_next_or_panic!(test_ctx.responder, &test_ctx.valid_act1); + let act3 = hex::decode("00b9e3a702e93e3a9948c2ed6e5fd7590a6e1c3a0344cfc9d5b57357049aa22355361aa02e55a8fc28fef5bd6d71ad0c38228dc68b1c466263b47fdf31e560e139").unwrap(); - assert_eq!(awaiting_act_three_state.next(&act3).err(), Some(String::from("unexpected version"))); + assert_eq!(awaiting_act_three_state.next(&act3).err(), Some(String::from("need at least 66 bytes"))); } // Responder::AwaitingActThree -> Error (invalid hmac) + // RFC test vector: transport-responder act3 bad MAC for ciphertext test #[test] fn awaiting_act_three_invalid_hmac() { let test_ctx = TestCtx::new(); - let (act1, awaiting_act_two_state) = do_next_or_panic!(test_ctx.initiator, &[]); - let (act2, awaiting_act_three_state) = do_next_or_panic!(test_ctx.responder, &act1); - let (mut act3, _complete_state) = do_next_or_panic!(awaiting_act_two_state, &act2); - // corrupt encrypted pubkey - act3[1] = 1; + let (_act2, awaiting_act_three_state) = do_next_or_panic!(test_ctx.responder, &test_ctx.valid_act1); + let act3 = hex::decode("00c9e3a702e93e3a9948c2ed6e5fd7590a6e1c3a0344cfc9d5b57357049aa22355361aa02e55a8fc28fef5bd6d71ad0c38228dc68b1c466263b47fdf31e560e139ba").unwrap(); assert_eq!(awaiting_act_three_state.next(&act3).err(), Some(String::from("invalid hmac"))); } + // Responder::AwaitingActThree -> Error (invalid remote_static_key) + // RFC test vector: transport-responder act3 bad rs test + #[test] + fn awaiting_act_three_invalid_rs() { + let test_ctx = TestCtx::new(); + let (_act2, awaiting_act_three_state) = do_next_or_panic!(test_ctx.responder, &test_ctx.valid_act1); + let act3 = hex::decode("00bfe3a702e93e3a9948c2ed6e5fd7590a6e1c3a0344cfc9d5b57357049aa2235536ad09a8ee351870c2bb7f78b754a26c6cef79a98d25139c856d7efd252c2ae73c").unwrap(); + + assert_eq!(awaiting_act_three_state.next(&act3).err(), Some(String::from("invalid remote public key"))); + } + // Responder::AwaitingActThree -> Error (invalid tag hmac) + // RFC test vector: transport-responder act3 bad MAC test #[test] fn awaiting_act_three_invalid_tag_hmac() { let test_ctx = TestCtx::new(); - let (act1, awaiting_act_two_state) = do_next_or_panic!(test_ctx.initiator, &[]); - let (act2, awaiting_act_three_state) = do_next_or_panic!(test_ctx.responder, &act1); - let (mut act3, _complete_state) = do_next_or_panic!(awaiting_act_two_state, &act2); - // corrupt tag - act3[50] = 1; + let (_act2, awaiting_act_three_state) = do_next_or_panic!(test_ctx.responder, &test_ctx.valid_act1); + let act3 = hex::decode("00b9e3a702e93e3a9948c2ed6e5fd7590a6e1c3a0344cfc9d5b57357049aa22355361aa02e55a8fc28fef5bd6d71ad0c38228dc68b1c466263b47fdf31e560e139bb").unwrap(); assert_eq!(awaiting_act_three_state.next(&act3).err(), Some(String::from("invalid hmac"))); } @@ -830,3 +857,4 @@ mod test { "00b9e3a702e93e3a9948c2ed6e5fd7590a6e1c3a0344cfc9d5b57357049aa22355361aa02e55a8fc28fef5bd6d71ad0c38228dc68b1c466263b47fdf31e560e139ba"); } } + From 3ae760452596dbb6eceb17b4e20b6035004187f3 Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Fri, 21 Aug 2020 11:36:53 -0700 Subject: [PATCH 28/72] refactor: Simplify logic for init message sending The previous implementation used implementation details of the act byte returns from process_next_act() to determine whether or not to send an init message, but the Peer already has that information in it's outbound field. Use it instead to clean up the layering. --- lightning/src/ln/peers/handler.rs | 33 +++++++++++++------------------ 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/lightning/src/ln/peers/handler.rs b/lightning/src/ln/peers/handler.rs index c8ace0e4439..373b821e0bf 100644 --- a/lightning/src/ln/peers/handler.rs +++ b/lightning/src/ln/peers/handler.rs @@ -127,7 +127,7 @@ enum PeerState { } enum PeerDataProcessingDecision { - CompleteHandshake(bool, PublicKey), + CompleteHandshake(PublicKey), Continue, Disconnect(PeerHandleError) } @@ -153,7 +153,6 @@ impl PeerState { Ok((response_vec_opt, conduit_and_remote_pubkey_opt)) => { // Any response generated by the handshake sequence is put into the response buffer - let requires_response = response_vec_opt.is_some(); if let Some(response_vec) = response_vec_opt { mutable_response_buffer.push_back(response_vec); } @@ -161,7 +160,7 @@ impl PeerState { // if process_act() returns the conduit and remote_pubkey the handshake // is complete if let Some((conduit, remote_pubkey)) = conduit_and_remote_pubkey_opt { - (Some(PeerState::Connected(conduit)), PeerDataProcessingDecision::CompleteHandshake(requires_response, remote_pubkey)) + (Some(PeerState::Connected(conduit)), PeerDataProcessingDecision::CompleteHandshake(remote_pubkey)) } else { (None, PeerDataProcessingDecision::Continue) } @@ -569,8 +568,6 @@ impl PeerManager panic!("Descriptor for read_event is not already known to PeerManager"), Some(peer) => { - let mut send_init_message = false; - let data_processing_decision = peer.encryptor.process_peer_data(data, &mut peer.pending_outbound_buffer); match data_processing_decision { PeerDataProcessingDecision::Disconnect(e) => { @@ -578,10 +575,19 @@ impl PeerManager { - send_init_message = needs_to_send_init_message; + PeerDataProcessingDecision::CompleteHandshake(remote_pubkey) => { peer.their_node_id = Some(remote_pubkey); + if peer.outbound { + let mut features = InitFeatures::known(); + if !self.message_handler.route_handler.should_request_full_sync(&peer.their_node_id.unwrap()) { + features.clear_initial_routing_sync(); + } + + let resp = msgs::Init { features }; + self.enqueue_message(&mut peers.peers_needing_send, peer, peer_descriptor.clone(), &resp); + } + // insert node id match peers.node_id_to_descriptor.entry(peer.their_node_id.unwrap()) { hash_map::Entry::Occupied(_) => { @@ -595,20 +601,9 @@ impl PeerManager {} + _ => { } }; - if send_init_message { - let mut features = InitFeatures::known(); - if !self.message_handler.route_handler.should_request_full_sync(&peer.their_node_id.unwrap()) { - features.clear_initial_routing_sync(); - } - - let resp = msgs::Init { features }; - self.enqueue_message(&mut peers.peers_needing_send, peer, peer_descriptor.clone(), &resp); - send_init_message = false - } - let mut received_messages = vec![]; if let &mut PeerState::Connected(ref mut conduit) = &mut peer.encryptor { let encryptor = &mut conduit.encryptor; From afd0334c17523641745688441549a5ae5663afb8 Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Fri, 21 Aug 2020 11:42:49 -0700 Subject: [PATCH 29/72] refactor: Clean up unused variable warnings --- lightning/src/ln/peers/conduit.rs | 19 +++---------------- lightning/src/ln/peers/handler.rs | 8 ++------ 2 files changed, 5 insertions(+), 22 deletions(-) diff --git a/lightning/src/ln/peers/conduit.rs b/lightning/src/ln/peers/conduit.rs index 57f191a8c0c..c753f0b8ff2 100644 --- a/lightning/src/ln/peers/conduit.rs +++ b/lightning/src/ln/peers/conduit.rs @@ -79,19 +79,6 @@ impl Conduit { self.decryptor.decrypt_single_message(new_data) } - /// Decrypt a message from the beginning of the provided buffer. Returns the consumed number of bytes. - fn decrypt(&mut self, buffer: &[u8]) -> (Option>, usize) { - self.decryptor.decrypt(buffer) - } - - fn increment_sending_nonce(&mut self) { - self.encryptor.increment_nonce() - } - - fn increment_receiving_nonce(&mut self) { - self.decryptor.increment_nonce() - } - fn increment_nonce(nonce: &mut u32, chaining_key: &mut SymmetricKey, key: &mut SymmetricKey) { *nonce += 1; if *nonce == KEY_ROTATION_INDEX { @@ -247,7 +234,7 @@ mod tests { #[test] fn test_nonce_chaining() { - let (mut connected_peer, mut remote_peer) = setup_peers(); + let (mut connected_peer, _remote_peer) = setup_peers(); let message = hex::decode("68656c6c6f").unwrap(); let encrypted_message = connected_peer.encrypt(&message); @@ -261,7 +248,7 @@ mod tests { #[test] /// Based on RFC test vectors: https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#message-encryption-tests fn test_key_rotation() { - let (mut connected_peer, mut remote_peer) = setup_peers(); + let (mut connected_peer, _remote_peer) = setup_peers(); let message = hex::decode("68656c6c6f").unwrap(); let mut encrypted_messages: Vec> = Vec::new(); @@ -292,7 +279,7 @@ mod tests { for _ in 0..501 { // read two messages at once, filling buffer let mut current_encrypted_message = encrypted_messages.remove(0); - let mut next_encrypted_message = encrypted_messages.remove(0); + let next_encrypted_message = encrypted_messages.remove(0); current_encrypted_message.extend_from_slice(&next_encrypted_message); let decrypted_message = remote_peer.decrypt_single_message(Some(¤t_encrypted_message)).unwrap(); assert_eq!(decrypted_message, message); diff --git a/lightning/src/ln/peers/handler.rs b/lightning/src/ln/peers/handler.rs index 373b821e0bf..9f50bd06340 100644 --- a/lightning/src/ln/peers/handler.rs +++ b/lightning/src/ln/peers/handler.rs @@ -22,7 +22,6 @@ use ln::msgs; use ln::msgs::{ChannelMessageHandler, LightningError, RoutingMessageHandler}; use ln::channelmanager::{SimpleArcChannelManager, SimpleRefChannelManager}; use util::ser::{VecWriter, Writeable}; -use ln::peer_channel_encryptor::{PeerChannelEncryptor,NextNoiseStep}; use ln::wire; use ln::wire::Encode; use util::byte_utils; @@ -145,7 +144,7 @@ impl PeerState { &mut PeerState::Authenticating(ref mut handshake) => { match handshake.process_act(data) { // Any errors originating from the handshake sequence result in Disconnect - Err(e) => { + Err(_e) => { (None, PeerDataProcessingDecision::Disconnect(PeerHandleError { no_connection_possible: false })) }, // Otherwise, handshake may or may not be complete depending on whether or not @@ -606,10 +605,7 @@ impl PeerManager Date: Thu, 20 Aug 2020 19:53:12 -0700 Subject: [PATCH 30/72] refactor: Hide empty next() requirement inside PeerHandshake The fact that we have to call next() to generate act1 is an implementation detail of the handshake. Rename the old constructor to indicate that it creates and initializes the state and fix up the uses. --- lightning/src/ln/peers/handler.rs | 6 ++---- lightning/src/ln/peers/handshake/mod.rs | 23 ++++++++++++---------- lightning/src/ln/peers/handshake/states.rs | 4 +--- 3 files changed, 16 insertions(+), 17 deletions(-) diff --git a/lightning/src/ln/peers/handler.rs b/lightning/src/ln/peers/handler.rs index 9f50bd06340..05c72c84a16 100644 --- a/lightning/src/ln/peers/handler.rs +++ b/lightning/src/ln/peers/handler.rs @@ -365,9 +365,7 @@ impl PeerManager Result, PeerHandleError> { - let mut handshake = PeerHandshake::new_outbound(&self.our_node_secret, &their_node_id, &self.get_ephemeral_key()); - let (act, ..) = handshake.process_act(&[]).unwrap(); - let res = act.unwrap(); + let (initial_bytes, handshake) = PeerHandshake::create_and_initialize_outbound(&self.our_node_secret, &their_node_id, &self.get_ephemeral_key()); let mut peers = self.peers.lock().unwrap(); if peers.peers.insert(descriptor, Peer { @@ -386,7 +384,7 @@ impl PeerManager Self { - Self { - state: Some(HandshakeState::new_initiator(initiator_static_private_key, responder_static_public_key, initiator_ephemeral_private_key)) - } + pub fn create_and_initialize_outbound(initiator_static_private_key: &SecretKey, responder_static_public_key: &PublicKey, initiator_ephemeral_private_key: &SecretKey) -> (Vec, Self) { + let state = HandshakeState::new_initiator(initiator_static_private_key, responder_static_public_key, initiator_ephemeral_private_key); + + // This transition does not have a failure path + let (response_vec_opt, next_state) = state.next(&[]).unwrap(); + (response_vec_opt.unwrap(), Self { state: Some(next_state) }) } /// Instantiate a new handshake in anticipation of a peer's first handshake act @@ -64,6 +66,7 @@ mod test { use bitcoin::secp256k1::key::{PublicKey, SecretKey}; struct TestCtx { + act1: Vec, outbound_handshake: PeerHandshake, outbound_static_public_key: PublicKey, inbound_handshake: PeerHandshake, @@ -82,10 +85,11 @@ mod test { let inbound_static_public_key = PublicKey::from_secret_key(&curve, &inbound_static_private_key); let inbound_ephemeral_private_key = SecretKey::from_slice(&[0x_22_u8; 32]).unwrap(); - let outbound_handshake = PeerHandshake::new_outbound(&outbound_static_private_key, &inbound_static_public_key, &outbound_ephemeral_private_key); + let (act1, outbound_handshake) = PeerHandshake::create_and_initialize_outbound(&outbound_static_private_key, &inbound_static_public_key, &outbound_ephemeral_private_key); let inbound_handshake = PeerHandshake::new_inbound(&inbound_static_private_key, &inbound_ephemeral_private_key); TestCtx { + act1, outbound_handshake, outbound_static_public_key, inbound_handshake, @@ -114,7 +118,7 @@ mod test { fn peer_handshake_new_outbound() { let test_ctx = TestCtx::new(); - assert_matches!(test_ctx.outbound_handshake.state, Some(HandshakeState::InitiatorStarting(_))); + assert_matches!(test_ctx.outbound_handshake.state, Some(HandshakeState::InitiatorAwaitingActTwo(_))); } // Default Inbound::AwaitingActOne @@ -133,8 +137,7 @@ mod test { #[test] fn full_sequence_sanity_test() { let mut test_ctx = TestCtx::new(); - let act1 = do_process_act_or_panic!(test_ctx.outbound_handshake, &[]); - let act2 = do_process_act_or_panic!(test_ctx.inbound_handshake, &act1); + let act2 = do_process_act_or_panic!(test_ctx.inbound_handshake, &test_ctx.act1); let (act3, inbound_remote_pubkey) = if let (Some(act3), Some((_, remote_pubkey))) = test_ctx.outbound_handshake.process_act(&act2).unwrap() { (act3, remote_pubkey) @@ -159,8 +162,8 @@ mod test { #[test] fn process_act_properly_updates_state() { let mut test_ctx = TestCtx::new(); - do_process_act_or_panic!(test_ctx.outbound_handshake, &[]); - assert_matches!(test_ctx.outbound_handshake.state, Some(HandshakeState::InitiatorAwaitingActTwo(_))); + do_process_act_or_panic!(test_ctx.inbound_handshake, &test_ctx.act1); + assert_matches!(test_ctx.inbound_handshake.state, Some(HandshakeState::ResponderAwaitingActThree(_))); } // Test that any errors from the state machine are passed back to the caller diff --git a/lightning/src/ln/peers/handshake/states.rs b/lightning/src/ln/peers/handshake/states.rs index d7de6845b71..8392dda3fff 100644 --- a/lightning/src/ln/peers/handshake/states.rs +++ b/lightning/src/ln/peers/handshake/states.rs @@ -560,10 +560,8 @@ mod test { #[test] fn starting_to_awaiting_act_two_extra_bytes() { let test_ctx = TestCtx::new(); - let (act1, awaiting_act_two_state) = do_next_or_panic!(test_ctx.initiator, &[]); - assert_eq!(act1, test_ctx.valid_act1); - assert_matches!(awaiting_act_two_state, InitiatorAwaitingActTwo(_)); + assert_eq!(test_ctx.initiator.next(&[1]).err(), Some(String::from("first call for initiator must be empty"))); } // Responder::AwaitingActOne -> AwaitingActThree From cc5cc9abd267c943f571fe19dcec5fad3e228b25 Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Mon, 24 Aug 2020 09:41:05 -0700 Subject: [PATCH 31/72] refactor: Remove unnecessary copy_from_slice calls in states.rs Use the slice refs from the act array instead. --- lightning/src/ln/peers/handshake/states.rs | 27 ++++++---------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/lightning/src/ln/peers/handshake/states.rs b/lightning/src/ln/peers/handshake/states.rs index 8392dda3fff..e94554b0636 100644 --- a/lightning/src/ln/peers/handshake/states.rs +++ b/lightning/src/ln/peers/handshake/states.rs @@ -297,18 +297,12 @@ impl IHandshakeState for ResponderAwaitingActThreeState { let chaining_key = self.chaining_key; // 1. Read exactly 66 bytes from the network buffer - let mut act_three_bytes = [0u8; ACT_THREE_LENGTH]; - act_three_bytes.copy_from_slice(&read_buffer[..ACT_THREE_LENGTH]); - read_buffer.drain(..ACT_THREE_LENGTH); + let act_three_bytes: Vec = read_buffer.drain(..ACT_THREE_LENGTH).collect(); // 2. Parse the read message (m) into v, c, and t let version = act_three_bytes[0]; - - let mut tagged_encrypted_pubkey = [0u8; 49]; - tagged_encrypted_pubkey.copy_from_slice(&act_three_bytes[1..50]); - - let mut chacha_tag = [0u8; 16]; - chacha_tag.copy_from_slice(&act_three_bytes[50..66]); + let tagged_encrypted_pubkey = &act_three_bytes[1..50]; + let chacha_tag = &act_three_bytes[50..66]; // 3. If v is an unrecognized handshake version, then the responder MUST abort the connection attempt. if version != 0 { @@ -318,9 +312,7 @@ impl IHandshakeState for ResponderAwaitingActThreeState { // 4. rs = decryptWithAD(temp_k2, 1, h, c) let remote_pubkey_vec = chacha::decrypt(&temporary_key, 1, &hash, &tagged_encrypted_pubkey)?; - let mut initiator_pubkey_bytes = [0u8; 33]; - initiator_pubkey_bytes.copy_from_slice(remote_pubkey_vec.as_slice()); - let initiator_pubkey = if let Ok(public_key) = PublicKey::from_slice(&initiator_pubkey_bytes) { + let initiator_pubkey = if let Ok(public_key) = PublicKey::from_slice(remote_pubkey_vec.as_slice()) { public_key } else { return Err("invalid remote public key".to_string()); @@ -419,24 +411,19 @@ fn process_act_message(read_buffer: &mut Vec, local_private_key: &SecretKey, return Err("need at least 50 bytes".to_string()); } - let mut act_bytes = [0u8; ACT_ONE_TWO_LENGTH]; - act_bytes.copy_from_slice(&read_buffer[..ACT_ONE_TWO_LENGTH]); - read_buffer.drain(..ACT_ONE_TWO_LENGTH); + let act_bytes: Vec = read_buffer.drain(..ACT_ONE_TWO_LENGTH).collect(); // 2.Parse the read message (m) into v, re, and c let version = act_bytes[0]; + let ephemeral_public_key_bytes = &act_bytes[1..34]; + let chacha_tag = &act_bytes[34..50]; - let mut ephemeral_public_key_bytes = [0u8; 33]; - ephemeral_public_key_bytes.copy_from_slice(&act_bytes[1..34]); let ephemeral_public_key = if let Ok(public_key) = PublicKey::from_slice(&ephemeral_public_key_bytes) { public_key } else { return Err("invalid remote ephemeral public key".to_string()); }; - let mut chacha_tag = [0u8; 16]; - chacha_tag.copy_from_slice(&act_bytes[34..50]); - // 3. If v is an unrecognized handshake version, then the responder MUST abort the connection attempt if version != 0 { // this should not crash the process, hence no panic From b617dfa38b2d3532c72980033eb58728a8a86135 Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Sat, 22 Aug 2020 18:49:10 -0700 Subject: [PATCH 32/72] tests: Add new fuzz testing for PeerHandshake Write a new fuzz test for the PeerHandshake module that is able to generate failure paths that occur after a partial handshake sequence has been completed. To enable this, a new testing object FuzzGen has been introduced that can deterministically generate bytes and bools based on the random fuzz input. These building blocks are enough for the test code to generates different execution paths and complete partial phases of the handshake protocol before generating an error. When going through a test cycle where the handshake completes, it will also verify that the initiator and responder can communicate successfully through the returned conduits sending variable length data and validating the contents. --- fuzz/src/peer_crypt.rs | 331 +++++++++++++++++++++++++++++++++-------- 1 file changed, 273 insertions(+), 58 deletions(-) diff --git a/fuzz/src/peer_crypt.rs b/fuzz/src/peer_crypt.rs index f41137fc828..4679601a8c1 100644 --- a/fuzz/src/peer_crypt.rs +++ b/fuzz/src/peer_crypt.rs @@ -7,81 +7,241 @@ // You may not use this file except in accordance with one or both of these // licenses. -use lightning::ln::peer_channel_encryptor::PeerChannelEncryptor; +use bitcoin::secp256k1; use bitcoin::secp256k1::key::{PublicKey,SecretKey}; - +use lightning::ln::peers::conduit::Conduit; +use lightning::ln::peers::handshake::PeerHandshake; use utils::test_logger; -#[inline] -fn slice_to_be16(v: &[u8]) -> u16 { - ((v[0] as u16) << 8*1) | - ((v[1] as u16) << 8*0) +// Test structure used to generate "random" values based on input data. It is used throughout +// the various test cases to send random messages between nodes and to ensure the code does not fail +// unexpectedly. +pub struct FuzzGen<'a> { + read_pos: usize, + data: &'a [u8], } -#[inline] -pub fn do_test(data: &[u8]) { - let mut read_pos = 0; - macro_rules! get_slice { - ($len: expr) => { - { - let slice_len = $len as usize; - if data.len() < read_pos + slice_len { - return; - } - read_pos += slice_len; - &data[read_pos - slice_len..read_pos] +impl<'a> FuzzGen<'a> { + pub fn new(data: &'a [u8]) -> Self { + Self { + read_pos: 0, + data + } + } + + pub fn generate_bytes(&mut self, num: usize) -> Result<&'a [u8], String> { + if self.data.len() < self.read_pos + num { + return Err("out of bytes".to_string()); + } + + self.read_pos += num; + Ok(&self.data[self.read_pos - num..self.read_pos]) + } + + pub fn generate_secret_key(&mut self) -> Result { + // Loop through the input 32 bytes at a time until a valid + // secret key can be created or we run out of bytes + loop { + match SecretKey::from_slice(self.generate_bytes(32)?) { + Ok(key) => { return Ok(key) }, + _ => {} } } } - let our_network_key = match SecretKey::from_slice(get_slice!(32)) { - Ok(key) => key, - Err(_) => return, + pub fn generate_bool(&mut self) -> Result { + let next_byte = self.generate_bytes(1)?; + Ok(next_byte[0] & 1 == 1) + } +} + +struct TestCtx { + initiator_static_public_key: PublicKey, + initiator_handshake: PeerHandshake, + responder_static_public_key: PublicKey, + responder_handshake: PeerHandshake, + act1: Vec +} + +impl TestCtx { + fn make(generator: &mut FuzzGen) -> Result { + let curve = secp256k1::Secp256k1::new(); + + // Generate needed keys for successful handshake + let initiator_static_private_key = generator.generate_secret_key()?; + let initiator_static_public_key = PublicKey::from_secret_key(&curve, &initiator_static_private_key); + let initiator_ephemeral_private_key = generator.generate_secret_key()?; + let responder_static_private_key = generator.generate_secret_key()?; + let responder_static_public_key = PublicKey::from_secret_key(&curve, &responder_static_private_key); + let responder_ephemeral_private_key = generator.generate_secret_key()?; + + let (act1, initiator_handshake) = PeerHandshake::create_and_initialize_outbound(&initiator_static_private_key, &responder_static_public_key, &initiator_ephemeral_private_key); + let responder_handshake = PeerHandshake::new_inbound(&responder_static_private_key, &responder_ephemeral_private_key); + + Ok(TestCtx { + initiator_static_public_key, + initiator_handshake, + responder_static_public_key, + responder_handshake, + act1 + }) + } +} + +// Common test function that sends encrypted messages between two conduits until the source data +// runs out. +#[inline] +fn do_conduit_tests(generator: &mut FuzzGen, initiator_conduit: &mut Conduit, responder_conduit: &mut Conduit) -> Result<(), String> { + // Keep sending messages back and forth until the input data is consumed + loop { + // Randomly generate message length + let msg_len_raw = generator.generate_bytes(1)?; + let msg_len = msg_len_raw[0] as usize; + + // Randomly generate message + let sender_unencrypted_msg = generator.generate_bytes(msg_len)?; + + // randomly choose sender of message + let receiver_unencrypted_msg = if generator.generate_bool()? { + let encrypted_msg = initiator_conduit.encrypt(sender_unencrypted_msg); + responder_conduit.decrypt_single_message(Some(&encrypted_msg)) + } else { + let encrypted_msg = responder_conduit.encrypt(sender_unencrypted_msg); + initiator_conduit.decrypt_single_message(Some(&encrypted_msg)) + }; + + assert_eq!(sender_unencrypted_msg, receiver_unencrypted_msg.unwrap().as_slice()); + } +} + +// This test completes a valid handshake based on "random" private keys and then sends variable +// length encrypted messages between two conduits to validate that they can communicate. +#[inline] +fn do_completed_handshake_test(generator: &mut FuzzGen) -> Result<(), String> { + let mut test_ctx = TestCtx::make(generator)?; + + // The handshake should complete with any valid private keys + let act2 = test_ctx.responder_handshake.process_act(&test_ctx.act1).unwrap().0.unwrap(); + let (act3, (mut initiator_conduit, responder_pubkey)) = match test_ctx.initiator_handshake.process_act(&act2) { + Ok((Some(act3), Some((conduit, remote_pubkey)))) => { + (act3, (conduit, remote_pubkey)) + } + _ => panic!("handshake failed") }; - let ephemeral_key = match SecretKey::from_slice(get_slice!(32)) { - Ok(key) => key, - Err(_) => return, + + let (mut responder_conduit, initiator_pubkey) = match test_ctx.responder_handshake.process_act(&act3) { + Ok((None, Some((conduit, remote_pubkey)))) => { + (conduit, remote_pubkey) + } + _ => panic!("handshake failed") }; - let mut crypter = if get_slice!(1)[0] != 0 { - let their_pubkey = match PublicKey::from_slice(get_slice!(33)) { - Ok(key) => key, - Err(_) => return, - }; - let mut crypter = PeerChannelEncryptor::new_outbound(their_pubkey, ephemeral_key); - crypter.get_act_one(); - match crypter.process_act_two(get_slice!(50), &our_network_key) { - Ok(_) => {}, - Err(_) => return, + // The handshake should complete with each peer knowing the static_public_key of the remote peer + assert_eq!(initiator_pubkey, test_ctx.initiator_static_public_key); + assert_eq!(responder_pubkey, test_ctx.responder_static_public_key); + + // The nodes should be able to communicate over the conduit + do_conduit_tests(generator, &mut initiator_conduit, &mut responder_conduit)?; + + unreachable!(); +} + +// This test variant goes through the peer handshake between the initiator and responder with +// "random" failures to verify that nothing panics. +// In the event of an error from process_act() we validate that the input data was generated +// randomly to ensure real act generation still produces valid transitions. +#[inline] +fn do_handshake_test(generator: &mut FuzzGen) -> Result<(), String> { + let mut test_ctx = TestCtx::make(generator)?; + let mut used_generated_data = false; + + // Possibly generate bad data for act1 and ensure that the responder does not panic + let mut act1 = test_ctx.act1; + if generator.generate_bool()? { + act1 = (generator.generate_bytes(50)?).to_vec(); + used_generated_data = true; + } + + let mut act2 = match test_ctx.responder_handshake.process_act(&act1) { + Ok((Some(act2), None)) => { + act2 } - assert!(crypter.is_ready_for_encryption()); - crypter - } else { - let mut crypter = PeerChannelEncryptor::new_inbound(&our_network_key); - match crypter.process_act_one_with_keys(get_slice!(50), &our_network_key, ephemeral_key) { - Ok(_) => {}, - Err(_) => return, + Err(_) => { + assert!(used_generated_data); + return Err("generated expected failure with bad data".to_string()); } - match crypter.process_act_three(get_slice!(66)) { - Ok(_) => {}, - Err(_) => return, + _ => panic!("responder required to output act bytes and no conduit/pubkey") + }; + + // Possibly generate bad data for act2 and ensure that the initiator does not panic + if generator.generate_bool()? { + act2 = (generator.generate_bytes(50)?).to_vec(); + used_generated_data = true; + } + + let (mut act3, (mut initiator_conduit, responder_pubkey)) = match test_ctx.initiator_handshake.process_act(&act2) { + Ok((Some(act3), Some((conduit, remote_pubkey)))) => { + (act3, (conduit, remote_pubkey)) } - assert!(crypter.is_ready_for_encryption()); - crypter + Err(_) => { + assert!(used_generated_data); + return Err("generated expected failure with bad data".to_string()); + } + _ => panic!("initiator required to output act bytes and no conduit/pubkey") }; - loop { - if get_slice!(1)[0] == 0 { - crypter.encrypt_message(get_slice!(slice_to_be16(get_slice!(2)))); - } else { - let len = match crypter.decrypt_length_header(get_slice!(16+2)) { - Ok(len) => len, - Err(_) => return, - }; - match crypter.decrypt_message(get_slice!(len as usize + 16)) { - Ok(_) => {}, - Err(_) => return, - } + + // Possibly generate bad data for act3 and ensure that the responder does not panic + if generator.generate_bool()? { + act3 = (generator.generate_bytes(66)?).to_vec(); + used_generated_data = true; + } + + let (mut responder_conduit, initiator_pubkey) = match test_ctx.responder_handshake.process_act(&act3) { + Ok((None, Some((conduit, remote_pubkey)))) => { + (conduit, remote_pubkey) + } + Err(_) => { + // extremely unlikely we randomly generate a correct act3, but if so.. reset this + assert!(used_generated_data); + return Err("generated expected failure with bad data".to_string()); + }, + _ => panic!("responder required to output conduit/remote pubkey and no act bytes") + }; + + // The handshake should complete with each peer knowing the static_public_key of the remote peer + if initiator_pubkey != test_ctx.initiator_static_public_key { + assert!(used_generated_data); + return Ok(()); + } + if responder_pubkey != test_ctx.responder_static_public_key { + assert!(used_generated_data); + return Ok(()); + } + + // The nodes should be able to communicate over the conduit + do_conduit_tests(generator, &mut initiator_conduit, &mut responder_conduit)?; + + unreachable!(); +} + +#[inline] +fn do_test(data: &[u8]) { + let mut generator = FuzzGen::new(data); + + // Based on a "random" bool, decide which test variant to run + let do_valid_handshake = match generator.generate_bool() { + Ok(value) => { value }, + _ => { return } + }; + + if do_valid_handshake { + match do_completed_handshake_test(&mut generator) { + _ => {} + } + } else { + match do_handshake_test(&mut generator) { + _ => {} } } } @@ -94,3 +254,58 @@ pub fn peer_crypt_test(data: &[u8], _out: Out) { pub extern "C" fn peer_crypt_run(data: *const u8, datalen: usize) { do_test(unsafe { std::slice::from_raw_parts(data, datalen) }); } + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn data_generator_empty() { + let mut generator = FuzzGen::new(&[]); + assert_eq!(generator.generate_bool().err(), Some("out of bytes".to_string())); + } + + #[test] + fn data_generator_bool_true() { + let mut generator = FuzzGen::new(&[1]); + assert!(generator.generate_bool().unwrap()); + } + + #[test] + fn data_generator_bool_false() { + let mut generator = FuzzGen::new(&[0]); + assert!(!generator.generate_bool().unwrap()); + } + + #[test] + fn data_generator_bool_then_error() { + let mut generator = FuzzGen::new(&[1]); + assert!(generator.generate_bool().unwrap()); + assert_eq!(generator.generate_bool().err(), Some("out of bytes".to_string())); + } + + #[test] + fn data_generator_bytes_too_many() { + let mut generator = FuzzGen::new(&[1, 2, 3, 4]); + assert_eq!(generator.generate_bytes(5).err(), Some("out of bytes".to_string())); + } + + #[test] + fn data_generator_bytes() { + let input = [1, 2, 3, 4]; + let mut generator = FuzzGen::new(&input); + let result = generator.generate_bytes(4).unwrap(); + assert_eq!(result, input); + } + + #[test] + fn data_generator_bytes_sequential() { + let input = [1, 2, 3, 4]; + let mut generator = FuzzGen::new(&input); + let result = generator.generate_bytes(2).unwrap(); + assert_eq!(result, &input[..2]); + let result = generator.generate_bytes(2).unwrap(); + assert_eq!(result, &input[2..]); + assert_eq!(generator.generate_bytes(1).err(), Some("out of bytes".to_string())); + } +} \ No newline at end of file From 4435b126ac58fcb765fd48ed0f35df684ef778b3 Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Sat, 22 Aug 2020 19:46:45 -0700 Subject: [PATCH 33/72] delete: Remove peer_channel_encryptor.rs Functionality moved to: - peers/handshake/mod.rs - peers/handshake/states.rs - peers/conduit.rs Fuzz testing rewritten here: - fuzz/src/peer_crypt.rs --- lightning/src/ln/mod.rs | 6 - lightning/src/ln/peer_channel_encryptor.rs | 738 --------------------- lightning/src/ln/peers/conduit.rs | 39 ++ lightning/src/ln/peers/mod.rs | 12 +- 4 files changed, 49 insertions(+), 746 deletions(-) delete mode 100644 lightning/src/ln/peer_channel_encryptor.rs diff --git a/lightning/src/ln/mod.rs b/lightning/src/ln/mod.rs index 14e9cb86f48..5f0f1f59f6a 100644 --- a/lightning/src/ln/mod.rs +++ b/lightning/src/ln/mod.rs @@ -25,11 +25,6 @@ pub mod chan_utils; pub mod features; pub(crate) mod onchaintx; -#[cfg(feature = "fuzztarget")] -pub mod peer_channel_encryptor; -#[cfg(not(feature = "fuzztarget"))] -pub(crate) mod peer_channel_encryptor; - mod channel; mod onion_utils; mod wire; @@ -53,4 +48,3 @@ mod reorg_tests; #[allow(unused_mut)] mod onion_route_tests; -pub use self::peer_channel_encryptor::LN_MAX_MSG_LEN; diff --git a/lightning/src/ln/peer_channel_encryptor.rs b/lightning/src/ln/peer_channel_encryptor.rs deleted file mode 100644 index 8310edb64e2..00000000000 --- a/lightning/src/ln/peer_channel_encryptor.rs +++ /dev/null @@ -1,738 +0,0 @@ -// This file is Copyright its original authors, visible in version control -// history. -// -// This file is licensed under the Apache License, Version 2.0 or the MIT license -// , at your option. -// You may not use this file except in accordance with one or both of these -// licenses. - -use ln::msgs::LightningError; -use ln::msgs; - -use bitcoin::hashes::{Hash, HashEngine, Hmac, HmacEngine}; -use bitcoin::hashes::sha256::Hash as Sha256; - -use bitcoin::secp256k1::Secp256k1; -use bitcoin::secp256k1::key::{PublicKey,SecretKey}; -use bitcoin::secp256k1::ecdh::SharedSecret; -use bitcoin::secp256k1; - -use util::chacha20poly1305rfc::ChaCha20Poly1305RFC; -use util::byte_utils; -use bitcoin::hashes::hex::ToHex; - -/// Maximum Lightning message data length according to -/// [BOLT-8](https://github.com/lightningnetwork/lightning-rfc/blob/v1.0/08-transport.md#lightning-message-specification) -/// and [BOLT-1](https://github.com/lightningnetwork/lightning-rfc/blob/master/01-messaging.md#lightning-message-format): -pub const LN_MAX_MSG_LEN: usize = ::std::u16::MAX as usize; // Must be equal to 65535 - -// Sha256("Noise_XK_secp256k1_ChaChaPoly_SHA256") -const NOISE_CK: [u8; 32] = [0x26, 0x40, 0xf5, 0x2e, 0xeb, 0xcd, 0x9e, 0x88, 0x29, 0x58, 0x95, 0x1c, 0x79, 0x42, 0x50, 0xee, 0xdb, 0x28, 0x00, 0x2c, 0x05, 0xd7, 0xdc, 0x2e, 0xa0, 0xf1, 0x95, 0x40, 0x60, 0x42, 0xca, 0xf1]; -// Sha256(NOISE_CK || "lightning") -const NOISE_H: [u8; 32] = [0xd1, 0xfb, 0xf6, 0xde, 0xe4, 0xf6, 0x86, 0xf1, 0x32, 0xfd, 0x70, 0x2c, 0x4a, 0xbf, 0x8f, 0xba, 0x4b, 0xb4, 0x20, 0xd8, 0x9d, 0x2a, 0x04, 0x8a, 0x3c, 0x4f, 0x4c, 0x09, 0x2e, 0x37, 0xb6, 0x76]; - -pub enum NextNoiseStep { - ActOne, - ActTwo, - ActThree, - NoiseComplete, -} - -#[derive(PartialEq)] -enum NoiseStep { - PreActOne, - PostActOne, - PostActTwo, - // When done swap noise_state for NoiseState::Finished -} - -struct BidirectionalNoiseState { - h: [u8; 32], - ck: [u8; 32], -} -enum DirectionalNoiseState { - Outbound { - ie: SecretKey, - }, - Inbound { - ie: Option, // filled in if state >= PostActOne - re: Option, // filled in if state >= PostActTwo - temp_k2: Option<[u8; 32]>, // filled in if state >= PostActTwo - } -} -enum NoiseState { - InProgress { - state: NoiseStep, - directional_state: DirectionalNoiseState, - bidirectional_state: BidirectionalNoiseState, - }, - Finished { - sk: [u8; 32], - sn: u64, - sck: [u8; 32], - rk: [u8; 32], - rn: u64, - rck: [u8; 32], - } -} - -pub struct PeerChannelEncryptor { - secp_ctx: Secp256k1, - their_node_id: Option, // filled in for outbound, or inbound after noise_state is Finished - - noise_state: NoiseState, -} - -impl PeerChannelEncryptor { - pub fn new_outbound(their_node_id: PublicKey, ephemeral_key: SecretKey) -> PeerChannelEncryptor { - let secp_ctx = Secp256k1::signing_only(); - - let mut sha = Sha256::engine(); - sha.input(&NOISE_H); - sha.input(&their_node_id.serialize()[..]); - let h = Sha256::from_engine(sha).into_inner(); - - PeerChannelEncryptor { - their_node_id: Some(their_node_id), - secp_ctx: secp_ctx, - noise_state: NoiseState::InProgress { - state: NoiseStep::PreActOne, - directional_state: DirectionalNoiseState::Outbound { - ie: ephemeral_key, - }, - bidirectional_state: BidirectionalNoiseState { - h: h, - ck: NOISE_CK, - }, - } - } - } - - pub fn new_inbound(our_node_secret: &SecretKey) -> PeerChannelEncryptor { - let secp_ctx = Secp256k1::signing_only(); - - let mut sha = Sha256::engine(); - sha.input(&NOISE_H); - let our_node_id = PublicKey::from_secret_key(&secp_ctx, our_node_secret); - sha.input(&our_node_id.serialize()[..]); - let h = Sha256::from_engine(sha).into_inner(); - - PeerChannelEncryptor { - their_node_id: None, - secp_ctx: secp_ctx, - noise_state: NoiseState::InProgress { - state: NoiseStep::PreActOne, - directional_state: DirectionalNoiseState::Inbound { - ie: None, - re: None, - temp_k2: None, - }, - bidirectional_state: BidirectionalNoiseState { - h: h, - ck: NOISE_CK, - }, - } - } - } - - #[inline] - fn encrypt_with_ad(res: &mut[u8], n: u64, key: &[u8; 32], h: &[u8], plaintext: &[u8]) { - let mut nonce = [0; 12]; - nonce[4..].copy_from_slice(&byte_utils::le64_to_array(n)); - - let mut chacha = ChaCha20Poly1305RFC::new(key, &nonce, h); - let mut tag = [0; 16]; - chacha.encrypt(plaintext, &mut res[0..plaintext.len()], &mut tag); - res[plaintext.len()..].copy_from_slice(&tag); - } - - #[inline] - fn decrypt_with_ad(res: &mut[u8], n: u64, key: &[u8; 32], h: &[u8], cyphertext: &[u8]) -> Result<(), LightningError> { - let mut nonce = [0; 12]; - nonce[4..].copy_from_slice(&byte_utils::le64_to_array(n)); - - let mut chacha = ChaCha20Poly1305RFC::new(key, &nonce, h); - if !chacha.decrypt(&cyphertext[0..cyphertext.len() - 16], res, &cyphertext[cyphertext.len() - 16..]) { - return Err(LightningError{err: "Bad MAC".to_owned(), action: msgs::ErrorAction::DisconnectPeer{ msg: None }}); - } - Ok(()) - } - - fn hkdf_extract_expand(salt: &[u8], ikm: &[u8]) -> ([u8; 32], [u8; 32]) { - let mut hmac = HmacEngine::::new(salt); - hmac.input(ikm); - let prk = Hmac::from_engine(hmac).into_inner(); - let mut hmac = HmacEngine::::new(&prk[..]); - hmac.input(&[1; 1]); - let t1 = Hmac::from_engine(hmac).into_inner(); - let mut hmac = HmacEngine::::new(&prk[..]); - hmac.input(&t1); - hmac.input(&[2; 1]); - (t1, Hmac::from_engine(hmac).into_inner()) - } - - #[inline] - fn hkdf(state: &mut BidirectionalNoiseState, ss: SharedSecret) -> [u8; 32] { - let (t1, t2) = Self::hkdf_extract_expand(&state.ck, &ss[..]); - state.ck = t1; - t2 - } - - #[inline] - fn outbound_noise_act(secp_ctx: &Secp256k1, state: &mut BidirectionalNoiseState, our_key: &SecretKey, their_key: &PublicKey) -> ([u8; 50], [u8; 32]) { - let our_pub = PublicKey::from_secret_key(secp_ctx, &our_key); - - let mut sha = Sha256::engine(); - sha.input(&state.h); - sha.input(&our_pub.serialize()[..]); - state.h = Sha256::from_engine(sha).into_inner(); - - let ss = SharedSecret::new(&their_key, &our_key); - let temp_k = PeerChannelEncryptor::hkdf(state, ss); - - let mut res = [0; 50]; - res[1..34].copy_from_slice(&our_pub.serialize()[..]); - PeerChannelEncryptor::encrypt_with_ad(&mut res[34..], 0, &temp_k, &state.h, &[0; 0]); - - let mut sha = Sha256::engine(); - sha.input(&state.h); - sha.input(&res[34..]); - state.h = Sha256::from_engine(sha).into_inner(); - - (res, temp_k) - } - - #[inline] - fn inbound_noise_act(state: &mut BidirectionalNoiseState, act: &[u8], our_key: &SecretKey) -> Result<(PublicKey, [u8; 32]), LightningError> { - assert_eq!(act.len(), 50); - - if act[0] != 0 { - return Err(LightningError{err: format!("Unknown handshake version number {}", act[0]), action: msgs::ErrorAction::DisconnectPeer{ msg: None }}); - } - - let their_pub = match PublicKey::from_slice(&act[1..34]) { - Err(_) => return Err(LightningError{err: format!("Invalid public key {}", &act[1..34].to_hex()), action: msgs::ErrorAction::DisconnectPeer{ msg: None }}), - Ok(key) => key, - }; - - let mut sha = Sha256::engine(); - sha.input(&state.h); - sha.input(&their_pub.serialize()[..]); - state.h = Sha256::from_engine(sha).into_inner(); - - let ss = SharedSecret::new(&their_pub, &our_key); - let temp_k = PeerChannelEncryptor::hkdf(state, ss); - - let mut dec = [0; 0]; - PeerChannelEncryptor::decrypt_with_ad(&mut dec, 0, &temp_k, &state.h, &act[34..])?; - - let mut sha = Sha256::engine(); - sha.input(&state.h); - sha.input(&act[34..]); - state.h = Sha256::from_engine(sha).into_inner(); - - Ok((their_pub, temp_k)) - } - - pub fn get_act_one(&mut self) -> [u8; 50] { - match self.noise_state { - NoiseState::InProgress { ref mut state, ref directional_state, ref mut bidirectional_state } => - match directional_state { - &DirectionalNoiseState::Outbound { ref ie } => { - if *state != NoiseStep::PreActOne { - panic!("Requested act at wrong step"); - } - - let (res, _) = PeerChannelEncryptor::outbound_noise_act(&self.secp_ctx, bidirectional_state, &ie, &self.their_node_id.unwrap()); - *state = NoiseStep::PostActOne; - res - }, - _ => panic!("Wrong direction for act"), - }, - _ => panic!("Cannot get act one after noise handshake completes"), - } - } - - pub fn process_act_one_with_keys(&mut self, act_one: &[u8], our_node_secret: &SecretKey, our_ephemeral: SecretKey) -> Result<[u8; 50], LightningError> { - assert_eq!(act_one.len(), 50); - - match self.noise_state { - NoiseState::InProgress { ref mut state, ref mut directional_state, ref mut bidirectional_state } => - match directional_state { - &mut DirectionalNoiseState::Inbound { ref mut ie, ref mut re, ref mut temp_k2 } => { - if *state != NoiseStep::PreActOne { - panic!("Requested act at wrong step"); - } - - let (their_pub, _) = PeerChannelEncryptor::inbound_noise_act(bidirectional_state, act_one, &our_node_secret)?; - ie.get_or_insert(their_pub); - - re.get_or_insert(our_ephemeral); - - let (res, temp_k) = PeerChannelEncryptor::outbound_noise_act(&self.secp_ctx, bidirectional_state, &re.unwrap(), &ie.unwrap()); - *temp_k2 = Some(temp_k); - *state = NoiseStep::PostActTwo; - Ok(res) - }, - _ => panic!("Wrong direction for act"), - }, - _ => panic!("Cannot get act one after noise handshake completes"), - } - } - - pub fn process_act_two(&mut self, act_two: &[u8], our_node_secret: &SecretKey) -> Result<([u8; 66], PublicKey), LightningError> { - assert_eq!(act_two.len(), 50); - - let final_hkdf; - let ck; - let res: [u8; 66] = match self.noise_state { - NoiseState::InProgress { ref state, ref directional_state, ref mut bidirectional_state } => - match directional_state { - &DirectionalNoiseState::Outbound { ref ie } => { - if *state != NoiseStep::PostActOne { - panic!("Requested act at wrong step"); - } - - let (re, temp_k2) = PeerChannelEncryptor::inbound_noise_act(bidirectional_state, act_two, &ie)?; - - let mut res = [0; 66]; - let our_node_id = PublicKey::from_secret_key(&self.secp_ctx, &our_node_secret); - - PeerChannelEncryptor::encrypt_with_ad(&mut res[1..50], 1, &temp_k2, &bidirectional_state.h, &our_node_id.serialize()[..]); - - let mut sha = Sha256::engine(); - sha.input(&bidirectional_state.h); - sha.input(&res[1..50]); - bidirectional_state.h = Sha256::from_engine(sha).into_inner(); - - let ss = SharedSecret::new(&re, our_node_secret); - let temp_k = PeerChannelEncryptor::hkdf(bidirectional_state, ss); - - PeerChannelEncryptor::encrypt_with_ad(&mut res[50..], 0, &temp_k, &bidirectional_state.h, &[0; 0]); - final_hkdf = Self::hkdf_extract_expand(&bidirectional_state.ck, &[0; 0]); - ck = bidirectional_state.ck.clone(); - res - }, - _ => panic!("Wrong direction for act"), - }, - _ => panic!("Cannot get act one after noise handshake completes"), - }; - - let (sk, rk) = final_hkdf; - self.noise_state = NoiseState::Finished { - sk: sk, - sn: 0, - sck: ck.clone(), - rk: rk, - rn: 0, - rck: ck, - }; - - Ok((res, self.their_node_id.unwrap().clone())) - } - - pub fn process_act_three(&mut self, act_three: &[u8]) -> Result { - assert_eq!(act_three.len(), 66); - - let final_hkdf; - let ck; - match self.noise_state { - NoiseState::InProgress { ref state, ref directional_state, ref mut bidirectional_state } => - match directional_state { - &DirectionalNoiseState::Inbound { ie: _, ref re, ref temp_k2 } => { - if *state != NoiseStep::PostActTwo { - panic!("Requested act at wrong step"); - } - if act_three[0] != 0 { - return Err(LightningError{err: format!("Unknown handshake version number {}", act_three[0]), action: msgs::ErrorAction::DisconnectPeer{ msg: None }}); - } - - let mut their_node_id = [0; 33]; - PeerChannelEncryptor::decrypt_with_ad(&mut their_node_id, 1, &temp_k2.unwrap(), &bidirectional_state.h, &act_three[1..50])?; - self.their_node_id = Some(match PublicKey::from_slice(&their_node_id) { - Ok(key) => key, - Err(_) => return Err(LightningError{err: format!("Bad node_id from peer, {}", &their_node_id.to_hex()), action: msgs::ErrorAction::DisconnectPeer{ msg: None }}), - }); - - let mut sha = Sha256::engine(); - sha.input(&bidirectional_state.h); - sha.input(&act_three[1..50]); - bidirectional_state.h = Sha256::from_engine(sha).into_inner(); - - let ss = SharedSecret::new(&self.their_node_id.unwrap(), &re.unwrap()); - let temp_k = PeerChannelEncryptor::hkdf(bidirectional_state, ss); - - PeerChannelEncryptor::decrypt_with_ad(&mut [0; 0], 0, &temp_k, &bidirectional_state.h, &act_three[50..])?; - final_hkdf = Self::hkdf_extract_expand(&bidirectional_state.ck, &[0; 0]); - ck = bidirectional_state.ck.clone(); - }, - _ => panic!("Wrong direction for act"), - }, - _ => panic!("Cannot get act one after noise handshake completes"), - } - - let (rk, sk) = final_hkdf; - self.noise_state = NoiseState::Finished { - sk: sk, - sn: 0, - sck: ck.clone(), - rk: rk, - rn: 0, - rck: ck, - }; - - Ok(self.their_node_id.unwrap().clone()) - } - - /// Encrypts the given message, returning the encrypted version - /// panics if msg.len() > 65535 or Noise handshake has not finished. - pub fn encrypt_message(&mut self, msg: &[u8]) -> Vec { - if msg.len() > LN_MAX_MSG_LEN { - panic!("Attempted to encrypt message longer than 65535 bytes!"); - } - - let mut res = Vec::with_capacity(msg.len() + 16*2 + 2); - res.resize(msg.len() + 16*2 + 2, 0); - - match self.noise_state { - NoiseState::Finished { ref mut sk, ref mut sn, ref mut sck, rk: _, rn: _, rck: _ } => { - if *sn >= 1000 { - let (new_sck, new_sk) = Self::hkdf_extract_expand(sck, sk); - *sck = new_sck; - *sk = new_sk; - *sn = 0; - } - - Self::encrypt_with_ad(&mut res[0..16+2], *sn, sk, &[0; 0], &byte_utils::be16_to_array(msg.len() as u16)); - *sn += 1; - - Self::encrypt_with_ad(&mut res[16+2..], *sn, sk, &[0; 0], msg); - *sn += 1; - }, - _ => panic!("Tried to encrypt a message prior to noise handshake completion"), - } - - res - } - - /// Decrypts a message length header from the remote peer. - /// panics if noise handshake has not yet finished or msg.len() != 18 - pub fn decrypt_length_header(&mut self, msg: &[u8]) -> Result { - assert_eq!(msg.len(), 16+2); - - match self.noise_state { - NoiseState::Finished { sk: _, sn: _, sck: _, ref mut rk, ref mut rn, ref mut rck } => { - if *rn >= 1000 { - let (new_rck, new_rk) = Self::hkdf_extract_expand(rck, rk); - *rck = new_rck; - *rk = new_rk; - *rn = 0; - } - - let mut res = [0; 2]; - Self::decrypt_with_ad(&mut res, *rn, rk, &[0; 0], msg)?; - *rn += 1; - Ok(byte_utils::slice_to_be16(&res)) - }, - _ => panic!("Tried to decrypt a message prior to noise handshake completion"), - } - } - - /// Decrypts the given message. - /// panics if msg.len() > 65535 + 16 - pub fn decrypt_message(&mut self, msg: &[u8]) -> Result, LightningError> { - if msg.len() > LN_MAX_MSG_LEN + 16 { - panic!("Attempted to decrypt message longer than 65535 + 16 bytes!"); - } - - match self.noise_state { - NoiseState::Finished { sk: _, sn: _, sck: _, ref rk, ref mut rn, rck: _ } => { - let mut res = Vec::with_capacity(msg.len() - 16); - res.resize(msg.len() - 16, 0); - Self::decrypt_with_ad(&mut res[..], *rn, rk, &[0; 0], msg)?; - *rn += 1; - - Ok(res) - }, - _ => panic!("Tried to decrypt a message prior to noise handshake completion"), - } - } - - pub fn get_noise_step(&self) -> NextNoiseStep { - match self.noise_state { - NoiseState::InProgress {ref state, ..} => { - match state { - &NoiseStep::PreActOne => NextNoiseStep::ActOne, - &NoiseStep::PostActOne => NextNoiseStep::ActTwo, - &NoiseStep::PostActTwo => NextNoiseStep::ActThree, - } - }, - NoiseState::Finished {..} => NextNoiseStep::NoiseComplete, - } - } - - pub fn is_ready_for_encryption(&self) -> bool { - match self.noise_state { - NoiseState::InProgress {..} => { false }, - NoiseState::Finished {..} => { true } - } - } -} - -#[cfg(test)] -mod tests { - use super::LN_MAX_MSG_LEN; - - use bitcoin::secp256k1::key::{PublicKey,SecretKey}; - - use hex; - - use ln::peer_channel_encryptor::{PeerChannelEncryptor,NoiseState}; - - fn get_outbound_peer_for_initiator_test_vectors() -> PeerChannelEncryptor { - let their_node_id = PublicKey::from_slice(&hex::decode("028d7500dd4c12685d1f568b4c2b5048e8534b873319f3a8daa612b469132ec7f7").unwrap()[..]).unwrap(); - - let mut outbound_peer = PeerChannelEncryptor::new_outbound(their_node_id, SecretKey::from_slice(&hex::decode("1212121212121212121212121212121212121212121212121212121212121212").unwrap()[..]).unwrap()); - assert_eq!(outbound_peer.get_act_one()[..], hex::decode("00036360e856310ce5d294e8be33fc807077dc56ac80d95d9cd4ddbd21325eff73f70df6086551151f58b8afe6c195782c6a").unwrap()[..]); - outbound_peer - } - - fn get_inbound_peer_for_test_vectors() -> PeerChannelEncryptor { - // transport-responder successful handshake - let our_node_id = SecretKey::from_slice(&hex::decode("2121212121212121212121212121212121212121212121212121212121212121").unwrap()[..]).unwrap(); - let our_ephemeral = SecretKey::from_slice(&hex::decode("2222222222222222222222222222222222222222222222222222222222222222").unwrap()[..]).unwrap(); - - let mut inbound_peer = PeerChannelEncryptor::new_inbound(&our_node_id); - - let act_one = hex::decode("00036360e856310ce5d294e8be33fc807077dc56ac80d95d9cd4ddbd21325eff73f70df6086551151f58b8afe6c195782c6a").unwrap().to_vec(); - assert_eq!(inbound_peer.process_act_one_with_keys(&act_one[..], &our_node_id, our_ephemeral.clone()).unwrap()[..], hex::decode("0002466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f276e2470b93aac583c9ef6eafca3f730ae").unwrap()[..]); - - let act_three = hex::decode("00b9e3a702e93e3a9948c2ed6e5fd7590a6e1c3a0344cfc9d5b57357049aa22355361aa02e55a8fc28fef5bd6d71ad0c38228dc68b1c466263b47fdf31e560e139ba").unwrap().to_vec(); - // test vector doesn't specify the initiator static key, but it's the same as the one - // from transport-initiator successful handshake - assert_eq!(inbound_peer.process_act_three(&act_three[..]).unwrap().serialize()[..], hex::decode("034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa").unwrap()[..]); - - match inbound_peer.noise_state { - NoiseState::Finished { sk, sn, sck, rk, rn, rck } => { - assert_eq!(sk, hex::decode("bb9020b8965f4df047e07f955f3c4b88418984aadc5cdb35096b9ea8fa5c3442").unwrap()[..]); - assert_eq!(sn, 0); - assert_eq!(sck, hex::decode("919219dbb2920afa8db80f9a51787a840bcf111ed8d588caf9ab4be716e42b01").unwrap()[..]); - assert_eq!(rk, hex::decode("969ab31b4d288cedf6218839b27a3e2140827047f2c0f01bf5c04435d43511a9").unwrap()[..]); - assert_eq!(rn, 0); - assert_eq!(rck, hex::decode("919219dbb2920afa8db80f9a51787a840bcf111ed8d588caf9ab4be716e42b01").unwrap()[..]); - }, - _ => panic!() - } - - inbound_peer - } - - #[test] - fn noise_initiator_test_vectors() { - let our_node_id = SecretKey::from_slice(&hex::decode("1111111111111111111111111111111111111111111111111111111111111111").unwrap()[..]).unwrap(); - - { - // transport-initiator successful handshake - let mut outbound_peer = get_outbound_peer_for_initiator_test_vectors(); - - let act_two = hex::decode("0002466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f276e2470b93aac583c9ef6eafca3f730ae").unwrap().to_vec(); - assert_eq!(outbound_peer.process_act_two(&act_two[..], &our_node_id).unwrap().0[..], hex::decode("00b9e3a702e93e3a9948c2ed6e5fd7590a6e1c3a0344cfc9d5b57357049aa22355361aa02e55a8fc28fef5bd6d71ad0c38228dc68b1c466263b47fdf31e560e139ba").unwrap()[..]); - - match outbound_peer.noise_state { - NoiseState::Finished { sk, sn, sck, rk, rn, rck } => { - assert_eq!(sk, hex::decode("969ab31b4d288cedf6218839b27a3e2140827047f2c0f01bf5c04435d43511a9").unwrap()[..]); - assert_eq!(sn, 0); - assert_eq!(sck, hex::decode("919219dbb2920afa8db80f9a51787a840bcf111ed8d588caf9ab4be716e42b01").unwrap()[..]); - assert_eq!(rk, hex::decode("bb9020b8965f4df047e07f955f3c4b88418984aadc5cdb35096b9ea8fa5c3442").unwrap()[..]); - assert_eq!(rn, 0); - assert_eq!(rck, hex::decode("919219dbb2920afa8db80f9a51787a840bcf111ed8d588caf9ab4be716e42b01").unwrap()[..]); - }, - _ => panic!() - } - } - { - // transport-initiator act2 short read test - // Can't actually test this cause process_act_two requires you pass the right length! - } - { - // transport-initiator act2 bad version test - let mut outbound_peer = get_outbound_peer_for_initiator_test_vectors(); - - let act_two = hex::decode("0102466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f276e2470b93aac583c9ef6eafca3f730ae").unwrap().to_vec(); - assert!(outbound_peer.process_act_two(&act_two[..], &our_node_id).is_err()); - } - - { - // transport-initiator act2 bad key serialization test - let mut outbound_peer = get_outbound_peer_for_initiator_test_vectors(); - - let act_two = hex::decode("0004466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f276e2470b93aac583c9ef6eafca3f730ae").unwrap().to_vec(); - assert!(outbound_peer.process_act_two(&act_two[..], &our_node_id).is_err()); - } - - { - // transport-initiator act2 bad MAC test - let mut outbound_peer = get_outbound_peer_for_initiator_test_vectors(); - - let act_two = hex::decode("0002466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f276e2470b93aac583c9ef6eafca3f730af").unwrap().to_vec(); - assert!(outbound_peer.process_act_two(&act_two[..], &our_node_id).is_err()); - } - } - - #[test] - fn noise_responder_test_vectors() { - let our_node_id = SecretKey::from_slice(&hex::decode("2121212121212121212121212121212121212121212121212121212121212121").unwrap()[..]).unwrap(); - let our_ephemeral = SecretKey::from_slice(&hex::decode("2222222222222222222222222222222222222222222222222222222222222222").unwrap()[..]).unwrap(); - - { - let _ = get_inbound_peer_for_test_vectors(); - } - { - // transport-responder act1 short read test - // Can't actually test this cause process_act_one requires you pass the right length! - } - { - // transport-responder act1 bad version test - let mut inbound_peer = PeerChannelEncryptor::new_inbound(&our_node_id); - - let act_one = hex::decode("01036360e856310ce5d294e8be33fc807077dc56ac80d95d9cd4ddbd21325eff73f70df6086551151f58b8afe6c195782c6a").unwrap().to_vec(); - assert!(inbound_peer.process_act_one_with_keys(&act_one[..], &our_node_id, our_ephemeral.clone()).is_err()); - } - { - // transport-responder act1 bad key serialization test - let mut inbound_peer = PeerChannelEncryptor::new_inbound(&our_node_id); - - let act_one =hex::decode("00046360e856310ce5d294e8be33fc807077dc56ac80d95d9cd4ddbd21325eff73f70df6086551151f58b8afe6c195782c6a").unwrap().to_vec(); - assert!(inbound_peer.process_act_one_with_keys(&act_one[..], &our_node_id, our_ephemeral.clone()).is_err()); - } - { - // transport-responder act1 bad MAC test - let mut inbound_peer = PeerChannelEncryptor::new_inbound(&our_node_id); - - let act_one = hex::decode("00036360e856310ce5d294e8be33fc807077dc56ac80d95d9cd4ddbd21325eff73f70df6086551151f58b8afe6c195782c6b").unwrap().to_vec(); - assert!(inbound_peer.process_act_one_with_keys(&act_one[..], &our_node_id, our_ephemeral.clone()).is_err()); - } - { - // transport-responder act3 bad version test - let mut inbound_peer = PeerChannelEncryptor::new_inbound(&our_node_id); - - let act_one = hex::decode("00036360e856310ce5d294e8be33fc807077dc56ac80d95d9cd4ddbd21325eff73f70df6086551151f58b8afe6c195782c6a").unwrap().to_vec(); - assert_eq!(inbound_peer.process_act_one_with_keys(&act_one[..], &our_node_id, our_ephemeral.clone()).unwrap()[..], hex::decode("0002466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f276e2470b93aac583c9ef6eafca3f730ae").unwrap()[..]); - - let act_three = hex::decode("01b9e3a702e93e3a9948c2ed6e5fd7590a6e1c3a0344cfc9d5b57357049aa22355361aa02e55a8fc28fef5bd6d71ad0c38228dc68b1c466263b47fdf31e560e139ba").unwrap().to_vec(); - assert!(inbound_peer.process_act_three(&act_three[..]).is_err()); - } - { - // transport-responder act3 short read test - // Can't actually test this cause process_act_three requires you pass the right length! - } - { - // transport-responder act3 bad MAC for ciphertext test - let mut inbound_peer = PeerChannelEncryptor::new_inbound(&our_node_id); - - let act_one = hex::decode("00036360e856310ce5d294e8be33fc807077dc56ac80d95d9cd4ddbd21325eff73f70df6086551151f58b8afe6c195782c6a").unwrap().to_vec(); - assert_eq!(inbound_peer.process_act_one_with_keys(&act_one[..], &our_node_id, our_ephemeral.clone()).unwrap()[..], hex::decode("0002466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f276e2470b93aac583c9ef6eafca3f730ae").unwrap()[..]); - - let act_three = hex::decode("00c9e3a702e93e3a9948c2ed6e5fd7590a6e1c3a0344cfc9d5b57357049aa22355361aa02e55a8fc28fef5bd6d71ad0c38228dc68b1c466263b47fdf31e560e139ba").unwrap().to_vec(); - assert!(inbound_peer.process_act_three(&act_three[..]).is_err()); - } - { - // transport-responder act3 bad rs test - let mut inbound_peer = PeerChannelEncryptor::new_inbound(&our_node_id); - - let act_one = hex::decode("00036360e856310ce5d294e8be33fc807077dc56ac80d95d9cd4ddbd21325eff73f70df6086551151f58b8afe6c195782c6a").unwrap().to_vec(); - assert_eq!(inbound_peer.process_act_one_with_keys(&act_one[..], &our_node_id, our_ephemeral.clone()).unwrap()[..], hex::decode("0002466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f276e2470b93aac583c9ef6eafca3f730ae").unwrap()[..]); - - let act_three = hex::decode("00bfe3a702e93e3a9948c2ed6e5fd7590a6e1c3a0344cfc9d5b57357049aa2235536ad09a8ee351870c2bb7f78b754a26c6cef79a98d25139c856d7efd252c2ae73c").unwrap().to_vec(); - assert!(inbound_peer.process_act_three(&act_three[..]).is_err()); - } - { - // transport-responder act3 bad MAC test - let mut inbound_peer = PeerChannelEncryptor::new_inbound(&our_node_id); - - let act_one = hex::decode("00036360e856310ce5d294e8be33fc807077dc56ac80d95d9cd4ddbd21325eff73f70df6086551151f58b8afe6c195782c6a").unwrap().to_vec(); - assert_eq!(inbound_peer.process_act_one_with_keys(&act_one[..], &our_node_id, our_ephemeral.clone()).unwrap()[..], hex::decode("0002466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f276e2470b93aac583c9ef6eafca3f730ae").unwrap()[..]); - - let act_three = hex::decode("00b9e3a702e93e3a9948c2ed6e5fd7590a6e1c3a0344cfc9d5b57357049aa22355361aa02e55a8fc28fef5bd6d71ad0c38228dc68b1c466263b47fdf31e560e139bb").unwrap().to_vec(); - assert!(inbound_peer.process_act_three(&act_three[..]).is_err()); - } - } - - - #[test] - fn message_encryption_decryption_test_vectors() { - // We use the same keys as the initiator and responder test vectors, so we copy those tests - // here and use them to encrypt. - let mut outbound_peer = get_outbound_peer_for_initiator_test_vectors(); - - { - let our_node_id = SecretKey::from_slice(&hex::decode("1111111111111111111111111111111111111111111111111111111111111111").unwrap()[..]).unwrap(); - - let act_two = hex::decode("0002466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f276e2470b93aac583c9ef6eafca3f730ae").unwrap().to_vec(); - assert_eq!(outbound_peer.process_act_two(&act_two[..], &our_node_id).unwrap().0[..], hex::decode("00b9e3a702e93e3a9948c2ed6e5fd7590a6e1c3a0344cfc9d5b57357049aa22355361aa02e55a8fc28fef5bd6d71ad0c38228dc68b1c466263b47fdf31e560e139ba").unwrap()[..]); - - match outbound_peer.noise_state { - NoiseState::Finished { sk, sn, sck, rk, rn, rck } => { - assert_eq!(sk, hex::decode("969ab31b4d288cedf6218839b27a3e2140827047f2c0f01bf5c04435d43511a9").unwrap()[..]); - assert_eq!(sn, 0); - assert_eq!(sck, hex::decode("919219dbb2920afa8db80f9a51787a840bcf111ed8d588caf9ab4be716e42b01").unwrap()[..]); - assert_eq!(rk, hex::decode("bb9020b8965f4df047e07f955f3c4b88418984aadc5cdb35096b9ea8fa5c3442").unwrap()[..]); - assert_eq!(rn, 0); - assert_eq!(rck, hex::decode("919219dbb2920afa8db80f9a51787a840bcf111ed8d588caf9ab4be716e42b01").unwrap()[..]); - }, - _ => panic!() - } - } - - let mut inbound_peer = get_inbound_peer_for_test_vectors(); - - for i in 0..1005 { - let msg = [0x68, 0x65, 0x6c, 0x6c, 0x6f]; - let res = outbound_peer.encrypt_message(&msg); - assert_eq!(res.len(), 5 + 2*16 + 2); - - let len_header = res[0..2+16].to_vec(); - assert_eq!(inbound_peer.decrypt_length_header(&len_header[..]).unwrap() as usize, msg.len()); - assert_eq!(inbound_peer.decrypt_message(&res[2+16..]).unwrap()[..], msg[..]); - - if i == 0 { - assert_eq!(res, hex::decode("cf2b30ddf0cf3f80e7c35a6e6730b59fe802473180f396d88a8fb0db8cbcf25d2f214cf9ea1d95").unwrap()); - } else if i == 1 { - assert_eq!(res, hex::decode("72887022101f0b6753e0c7de21657d35a4cb2a1f5cde2650528bbc8f837d0f0d7ad833b1a256a1").unwrap()); - } else if i == 500 { - assert_eq!(res, hex::decode("178cb9d7387190fa34db9c2d50027d21793c9bc2d40b1e14dcf30ebeeeb220f48364f7a4c68bf8").unwrap()); - } else if i == 501 { - assert_eq!(res, hex::decode("1b186c57d44eb6de4c057c49940d79bb838a145cb528d6e8fd26dbe50a60ca2c104b56b60e45bd").unwrap()); - } else if i == 1000 { - assert_eq!(res, hex::decode("4a2f3cc3b5e78ddb83dcb426d9863d9d9a723b0337c89dd0b005d89f8d3c05c52b76b29b740f09").unwrap()); - } else if i == 1001 { - assert_eq!(res, hex::decode("2ecd8c8a5629d0d02ab457a0fdd0f7b90a192cd46be5ecb6ca570bfc5e268338b1a16cf4ef2d36").unwrap()); - } - } - } - - #[test] - fn max_msg_len_limit_value() { - assert_eq!(LN_MAX_MSG_LEN, 65535); - assert_eq!(LN_MAX_MSG_LEN, ::std::u16::MAX as usize); - } - - #[test] - #[should_panic(expected = "Attempted to encrypt message longer than 65535 bytes!")] - fn max_message_len_encryption() { - let mut outbound_peer = get_outbound_peer_for_initiator_test_vectors(); - let msg = [4u8; LN_MAX_MSG_LEN + 1]; - outbound_peer.encrypt_message(&msg); - } - - #[test] - #[should_panic(expected = "Attempted to decrypt message longer than 65535 + 16 bytes!")] - fn max_message_len_decryption() { - let mut inbound_peer = get_inbound_peer_for_test_vectors(); - - // MSG should not exceed LN_MAX_MSG_LEN + 16 - let msg = [4u8; LN_MAX_MSG_LEN + 17]; - inbound_peer.decrypt_message(&msg).unwrap(); - } -} diff --git a/lightning/src/ln/peers/conduit.rs b/lightning/src/ln/peers/conduit.rs index c753f0b8ff2..11ebfbca2f6 100644 --- a/lightning/src/ln/peers/conduit.rs +++ b/lightning/src/ln/peers/conduit.rs @@ -5,6 +5,11 @@ use util::byte_utils; pub(super) type SymmetricKey = [u8; 32]; +/// Maximum Lightning message data length according to +/// [BOLT-8](https://github.com/lightningnetwork/lightning-rfc/blob/v1.0/08-transport.md#lightning-message-specification) +/// and [BOLT-1](https://github.com/lightningnetwork/lightning-rfc/blob/master/01-messaging.md#lightning-message-format): +const LN_MAX_MSG_LEN: usize = ::std::u16::MAX as usize; // Must be equal to 65535 + const MESSAGE_LENGTH_HEADER_SIZE: usize = 2; const TAGGED_MESSAGE_LENGTH_HEADER_SIZE: usize = MESSAGE_LENGTH_HEADER_SIZE + chacha::TAG_SIZE; @@ -75,6 +80,7 @@ impl Conduit { /// only the first message will be returned, and the rest stored in the internal buffer. /// If a message pending in the buffer still hasn't been decrypted, that message will be /// returned in lieu of anything new, even if new data is provided. + #[cfg(any(test, feature = "fuzztarget"))] pub fn decrypt_single_message(&mut self, new_data: Option<&[u8]>) -> Option> { self.decryptor.decrypt_single_message(new_data) } @@ -96,6 +102,10 @@ impl Conduit { impl Encryptor { pub(super) fn encrypt(&mut self, buffer: &[u8]) -> Vec { + if buffer.len() > LN_MAX_MSG_LEN { + panic!("Attempted to encrypt message longer than 65535 bytes!"); + } + let length = buffer.len() as u16; let length_bytes = byte_utils::be16_to_array(length); @@ -136,6 +146,10 @@ impl Decryptor { read_buffer.extend_from_slice(data); } + if read_buffer.len() > LN_MAX_MSG_LEN + 16 { + panic!("Attempted to decrypt message longer than 65535 + 16 bytes!"); + } + let (current_message, offset) = self.decrypt(&read_buffer[..]); read_buffer.drain(..offset); // drain the read buffer self.read_buffer = Some(read_buffer); // assign the new value to the built-in buffer @@ -197,6 +211,7 @@ impl Decryptor { #[cfg(test)] mod tests { + use super::*; use hex; use ln::peers::conduit::Conduit; @@ -291,4 +306,28 @@ mod tests { assert_eq!(decrypted_message, message); } } + + #[test] + fn max_msg_len_limit_value() { + assert_eq!(LN_MAX_MSG_LEN, 65535); + assert_eq!(LN_MAX_MSG_LEN, ::std::u16::MAX as usize); + } + + #[test] + #[should_panic(expected = "Attempted to encrypt message longer than 65535 bytes!")] + fn max_message_len_encryption() { + let (mut connected_peer, _) = setup_peers(); + let msg = [4u8; LN_MAX_MSG_LEN + 1]; + connected_peer.encrypt(&msg); + } + + #[test] + #[should_panic(expected = "Attempted to decrypt message longer than 65535 + 16 bytes!")] + fn max_message_len_decryption() { + let (mut connected_peer, _) = setup_peers(); + + // MSG should not exceed LN_MAX_MSG_LEN + 16 + let msg = [4u8; LN_MAX_MSG_LEN + 17]; + connected_peer.decrypt_single_message(Some(&msg)); + } } \ No newline at end of file diff --git a/lightning/src/ln/peers/mod.rs b/lightning/src/ln/peers/mod.rs index f8082c50f10..072950c0dba 100644 --- a/lightning/src/ln/peers/mod.rs +++ b/lightning/src/ln/peers/mod.rs @@ -4,7 +4,15 @@ //! Conduit enables message encryption and decryption, and automatically handles key rotation. mod chacha; -pub mod conduit; -pub mod handshake; pub mod handler; mod hkdf; + +#[cfg(feature = "fuzztarget")] +pub mod conduit; +#[cfg(not(feature = "fuzztarget"))] +mod conduit; + +#[cfg(feature = "fuzztarget")] +pub mod handshake; +#[cfg(not(feature = "fuzztarget"))] +mod handshake; From a123f5ee761829c175117f256c0adba1268e39fc Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Sun, 23 Aug 2020 21:09:41 -0700 Subject: [PATCH 34/72] fix: Allow partial act messages to be received Fix regression introduced in eb6a371 that would error and subsequently cause a disconnect if a partial act was sent to the state machine, even if future calls could be added to the read buffer to create a valid act. Add appropriate unit tests and update fuzz testing to send partial acts some of the time. --- lightning/src/ln/peers/handshake/mod.rs | 6 +- lightning/src/ln/peers/handshake/states.rs | 93 +++++++++++++++++----- 2 files changed, 76 insertions(+), 23 deletions(-) diff --git a/lightning/src/ln/peers/handshake/mod.rs b/lightning/src/ln/peers/handshake/mod.rs index 35d5b9627d8..3bf47ebb60a 100644 --- a/lightning/src/ln/peers/handshake/mod.rs +++ b/lightning/src/ln/peers/handshake/mod.rs @@ -173,7 +173,8 @@ mod test { #[test] fn errors_properly_returned() { let mut test_ctx = TestCtx::new(); - assert_matches!(test_ctx.inbound_handshake.process_act(&[]).err(), Some(_)); + let invalid_act1 = [0; 50]; + assert_matches!(test_ctx.inbound_handshake.process_act(&invalid_act1).err(), Some(_)); } // Test that any use of the PeerHandshake after returning an error panics @@ -181,7 +182,8 @@ mod test { #[should_panic(expected = "called `Option::unwrap()` on a `None` value")] fn use_after_error_panics() { let mut test_ctx = TestCtx::new(); - assert_matches!(test_ctx.inbound_handshake.process_act(&[]).err(), Some(_)); + let invalid_act1 = [0; 50]; + assert_matches!(test_ctx.inbound_handshake.process_act(&invalid_act1).err(), Some(_)); test_ctx.inbound_handshake.process_act(&[]).unwrap(); } } diff --git a/lightning/src/ln/peers/handshake/states.rs b/lightning/src/ln/peers/handshake/states.rs index e94554b0636..8aba9fe20c1 100644 --- a/lightning/src/ln/peers/handshake/states.rs +++ b/lightning/src/ln/peers/handshake/states.rs @@ -181,6 +181,22 @@ impl IHandshakeState for ResponderAwaitingActOneState { let mut read_buffer = self.read_buffer; read_buffer.extend_from_slice(input); + // In the event of a partial fill, stay in the same state and wait for more data + if read_buffer.len() < ACT_ONE_TWO_LENGTH { + return Ok(( + None, + HandshakeState::ResponderAwaitingActOne(Self { + responder_static_private_key: self.responder_static_private_key, + responder_ephemeral_private_key: self.responder_ephemeral_private_key, + responder_ephemeral_public_key: self.responder_ephemeral_public_key, + chaining_key: self.chaining_key, + hash: self.hash, + read_buffer + }) + )); + } + + let hash = self.hash; let responder_static_private_key = self.responder_static_private_key; let chaining_key = self.chaining_key; @@ -222,6 +238,22 @@ impl IHandshakeState for InitiatorAwaitingActTwoState { let mut read_buffer = self.read_buffer; read_buffer.extend_from_slice(input); + // In the event of a partial fill, stay in the same state and wait for more data + if read_buffer.len() < ACT_ONE_TWO_LENGTH { + return Ok(( + None, + HandshakeState::InitiatorAwaitingActTwo(Self { + initiator_static_private_key: self.initiator_static_private_key, + initiator_static_public_key: self.initiator_static_public_key, + initiator_ephemeral_private_key: self.initiator_ephemeral_private_key, + responder_static_public_key: self.responder_static_public_key, + chaining_key: self.chaining_key, + hash: self.hash, + read_buffer + }) + )); + } + let initiator_static_private_key = self.initiator_static_private_key; let initiator_static_public_key = self.initiator_static_public_key; let initiator_ephemeral_private_key = self.initiator_ephemeral_private_key; @@ -287,8 +319,18 @@ impl IHandshakeState for ResponderAwaitingActThreeState { let mut read_buffer = self.read_buffer; read_buffer.extend_from_slice(input); + // In the event of a partial fill, stay in the same state and wait for more data if read_buffer.len() < ACT_THREE_LENGTH { - return Err("need at least 66 bytes".to_string()); + return Ok(( + None, + HandshakeState::ResponderAwaitingActThree(Self { + hash: self.hash, + responder_ephemeral_private_key: self.responder_ephemeral_private_key, + chaining_key: self.chaining_key, + temporary_key: self.temporary_key, + read_buffer + }) + )); } let hash = self.hash; @@ -407,10 +449,6 @@ fn calculate_act_message(local_private_ephemeral_key: &SecretKey, local_public_e // https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#act-two (receiver) fn process_act_message(read_buffer: &mut Vec, local_private_key: &SecretKey, chaining_key: ChainingKey, hash: Sha256) -> Result<(PublicKey, Sha256, SymmetricKey, SymmetricKey), String> { // 1. Read exactly 50 bytes from the network buffer - if read_buffer.len() < ACT_ONE_TWO_LENGTH { - return Err("need at least 50 bytes".to_string()); - } - let act_bytes: Vec = read_buffer.drain(..ACT_ONE_TWO_LENGTH).collect(); // 2.Parse the read message (m) into v, re, and c @@ -503,9 +541,9 @@ mod test { let responder = ResponderAwaitingActOneState::new(responder_static_private_key, responder_ephemeral_private_key); TestCtx { - initiator: HandshakeState::InitiatorStarting(initiator), + initiator: InitiatorStarting(initiator), initiator_public_key, - responder: HandshakeState::ResponderAwaitingActOne(responder), + responder: ResponderAwaitingActOne(responder), responder_static_public_key, valid_act1: hex::decode("00036360e856310ce5d294e8be33fc807077dc56ac80d95d9cd4ddbd21325eff73f70df6086551151f58b8afe6c195782c6a").unwrap(), valid_act2: hex::decode("0002466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f276e2470b93aac583c9ef6eafca3f730ae").unwrap(), @@ -565,8 +603,6 @@ mod test { // Responder::AwaitingActOne -> AwaitingActThree // TODO: Should this fail since we don't expect data > ACT_ONE_TWO_LENGTH and likely indicates // a bad peer? - // TODO: Should the behavior be changed to handle act1 data that is striped across multiple - // next() calls? #[test] fn awaiting_act_one_to_awaiting_act_three_input_extra_bytes() { let test_ctx = TestCtx::new(); @@ -578,13 +614,18 @@ mod test { assert_matches!(awaiting_act_three_state, ResponderAwaitingActThree(_)); } - // Responder::AwaitingActOne -> Error (input too small) + // Responder::AwaitingActOne -> AwaitingActThree (segmented calls) // RFC test vector: transport-responder act1 short read test + // Divergence from RFC tests due to not reading directly from the socket (partial message OK) #[test] - fn awaiting_act_one_to_awaiting_act_three_input_too_small() { + fn awaiting_act_one_to_awaiting_act_three_segmented() { let test_ctx = TestCtx::new(); - let act1 = hex::decode("00036360e856310ce5d294e8be33fc807077dc56ac80d95d9cd4ddbd21325eff73f70df6086551151f58b8afe6c195782c").unwrap(); - assert_eq!(test_ctx.responder.next(&act1).err(), Some(String::from("need at least 50 bytes"))) + let act1_partial1 = &test_ctx.valid_act1[..25]; + let act1_partial2 = &test_ctx.valid_act1[25..]; + + let next_state = test_ctx.responder.next(&act1_partial1).unwrap(); + assert_matches!(next_state, (None, ResponderAwaitingActOne(_))); + assert_matches!(next_state.1.next(&act1_partial2).unwrap(), (Some(_), ResponderAwaitingActThree(_))); } // Responder::AwaitingActOne -> Error (bad version byte) @@ -659,15 +700,20 @@ mod test { assert_eq!(100, conduit.decryptor.read_buffer_length()); } - // Initiator::AwaitingActTwo -> Error (input too small) + // Initiator::AwaitingActTwo -> Complete (segmented calls) // RFC test vector: transport-initiator act2 short read test + // Divergence from RFC tests due to not reading directly from the socket (partial message OK) #[test] - fn awaiting_act_two_input_too_small() { + fn awaiting_act_two_to_complete_segmented() { let test_ctx = TestCtx::new(); let (_act1, awaiting_act_two_state) = do_next_or_panic!(test_ctx.initiator, &[]); - let act2 = hex::decode("0002466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f276e2470b93aac583c9ef6eafca3f730").unwrap(); - assert_eq!(awaiting_act_two_state.next(&act2).err(), Some(String::from("need at least 50 bytes"))); + let act2_partial1 = &test_ctx.valid_act2[..25]; + let act2_partial2 = &test_ctx.valid_act2[25..]; + + let next_state = awaiting_act_two_state.next(&act2_partial1).unwrap(); + assert_matches!(next_state, (None, InitiatorAwaitingActTwo(_))); + assert_matches!(next_state.1.next(&act2_partial2).unwrap(), (Some(_), Complete(_))); } // Initiator::AwaitingActTwo -> Error (bad version byte) @@ -751,15 +797,20 @@ mod test { assert_eq!(awaiting_act_three_state.next(&act3).err(), Some(String::from("unexpected version"))); } - // Responder::AwaitingActThree -> Error (input too small) + // Responder::AwaitingActThree -> Complete (segmented calls) // RFC test vector: transport-responder act3 short read test + // Divergence from RFC tests due to not reading directly from the socket (partial message OK) #[test] - fn awaiting_act_three_input_too_small() { + fn awaiting_act_three_to_complete_segmented() { let test_ctx = TestCtx::new(); let (_act2, awaiting_act_three_state) = do_next_or_panic!(test_ctx.responder, &test_ctx.valid_act1); - let act3 = hex::decode("00b9e3a702e93e3a9948c2ed6e5fd7590a6e1c3a0344cfc9d5b57357049aa22355361aa02e55a8fc28fef5bd6d71ad0c38228dc68b1c466263b47fdf31e560e139").unwrap(); - assert_eq!(awaiting_act_three_state.next(&act3).err(), Some(String::from("need at least 66 bytes"))); + let act3_partial1 = &test_ctx.valid_act3[..35]; + let act3_partial2 = &test_ctx.valid_act3[35..]; + + let next_state = awaiting_act_three_state.next(&act3_partial1).unwrap(); + assert_matches!(next_state, (None, ResponderAwaitingActThree(_))); + assert_matches!(next_state.1.next(&act3_partial2), Ok((None, Complete(_)))); } // Responder::AwaitingActThree -> Error (invalid hmac) From 070d19bcb7b4068ce0c15fc35eb7600669f79707 Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Mon, 24 Aug 2020 16:52:15 -0700 Subject: [PATCH 35/72] fix: Conduit::Decryptor should not panic on decryption failure introduced in eb6a371 On decryption failure, the Decryptor should return an error to the caller so it can disconnect instead of panicking. Implement this behavior by plumbing Result through the iterator code path and adding the appropriate tests and comments. --- fuzz/src/peer_crypt.rs | 2 +- lightning/src/ln/peers/conduit.rs | 159 ++++++++++++++++++++++++++---- lightning/src/ln/peers/handler.rs | 70 ++++++++----- 3 files changed, 187 insertions(+), 44 deletions(-) diff --git a/fuzz/src/peer_crypt.rs b/fuzz/src/peer_crypt.rs index 4679601a8c1..6cbfc274919 100644 --- a/fuzz/src/peer_crypt.rs +++ b/fuzz/src/peer_crypt.rs @@ -111,7 +111,7 @@ fn do_conduit_tests(generator: &mut FuzzGen, initiator_conduit: &mut Conduit, re initiator_conduit.decrypt_single_message(Some(&encrypted_msg)) }; - assert_eq!(sender_unencrypted_msg, receiver_unencrypted_msg.unwrap().as_slice()); + assert_eq!(sender_unencrypted_msg, receiver_unencrypted_msg.unwrap().unwrap().as_slice()); } } diff --git a/lightning/src/ln/peers/conduit.rs b/lightning/src/ln/peers/conduit.rs index 11ebfbca2f6..807a06ddd12 100644 --- a/lightning/src/ln/peers/conduit.rs +++ b/lightning/src/ln/peers/conduit.rs @@ -38,13 +38,29 @@ pub(super) struct Decryptor { pending_message_length: Option, read_buffer: Option>, + poisoned: bool, // signal an error has occurred so None is returned on iteration after failure } impl Iterator for Decryptor { - type Item = Vec; + type Item = Result>, String>; fn next(&mut self) -> Option { - self.decrypt_single_message(None) + if self.poisoned { + return None; + } + + match self.decrypt_single_message(None) { + Ok(Some(result)) => { + Some(Ok(Some(result))) + }, + Ok(None) => { + None + } + Err(e) => { + self.poisoned = true; + Some(Err(e)) + } + } } } @@ -62,7 +78,8 @@ impl Conduit { receiving_chaining_key: chaining_key, receiving_nonce: 0, read_buffer: None, - pending_message_length: None + pending_message_length: None, + poisoned: false } } } @@ -81,8 +98,8 @@ impl Conduit { /// If a message pending in the buffer still hasn't been decrypted, that message will be /// returned in lieu of anything new, even if new data is provided. #[cfg(any(test, feature = "fuzztarget"))] - pub fn decrypt_single_message(&mut self, new_data: Option<&[u8]>) -> Option> { - self.decryptor.decrypt_single_message(new_data) + pub fn decrypt_single_message(&mut self, new_data: Option<&[u8]>) -> Result>, String> { + Ok(self.decryptor.decrypt_single_message(new_data)?) } fn increment_nonce(nonce: &mut u32, chaining_key: &mut SymmetricKey, key: &mut SymmetricKey) { @@ -135,7 +152,7 @@ impl Decryptor { /// only the first message will be returned, and the rest stored in the internal buffer. /// If a message pending in the buffer still hasn't been decrypted, that message will be /// returned in lieu of anything new, even if new data is provided. - pub fn decrypt_single_message(&mut self, new_data: Option<&[u8]>) -> Option> { + pub fn decrypt_single_message(&mut self, new_data: Option<&[u8]>) -> Result>, String> { let mut read_buffer = if let Some(buffer) = self.read_buffer.take() { buffer } else { @@ -150,25 +167,25 @@ impl Decryptor { panic!("Attempted to decrypt message longer than 65535 + 16 bytes!"); } - let (current_message, offset) = self.decrypt(&read_buffer[..]); + let (current_message, offset) = self.decrypt(&read_buffer[..])?; read_buffer.drain(..offset); // drain the read buffer self.read_buffer = Some(read_buffer); // assign the new value to the built-in buffer - current_message + Ok(current_message) } - fn decrypt(&mut self, buffer: &[u8]) -> (Option>, usize) { + fn decrypt(&mut self, buffer: &[u8]) -> Result<(Option>, usize), String> { let message_length = if let Some(length) = self.pending_message_length { // we have already decrypted the header length } else { if buffer.len() < TAGGED_MESSAGE_LENGTH_HEADER_SIZE { // A message must be at least 18 bytes (2 for encrypted length, 16 for the tag) - return (None, 0); + return Ok((None, 0)); } let encrypted_length = &buffer[0..TAGGED_MESSAGE_LENGTH_HEADER_SIZE]; let mut length_bytes = [0u8; MESSAGE_LENGTH_HEADER_SIZE]; - length_bytes.copy_from_slice(&chacha::decrypt(&self.receiving_key, self.receiving_nonce as u64, &[0; 0], encrypted_length).unwrap()); + length_bytes.copy_from_slice(&chacha::decrypt(&self.receiving_key, self.receiving_nonce as u64, &[0; 0], encrypted_length)?); self.increment_nonce(); @@ -180,18 +197,18 @@ impl Decryptor { if buffer.len() < message_end_index { self.pending_message_length = Some(message_length); - return (None, 0); + return Ok((None, 0)); } self.pending_message_length = None; let encrypted_message = &buffer[TAGGED_MESSAGE_LENGTH_HEADER_SIZE..message_end_index]; - let message = chacha::decrypt(&self.receiving_key, self.receiving_nonce as u64, &[0; 0], encrypted_message).unwrap(); + let message = chacha::decrypt(&self.receiving_key, self.receiving_nonce as u64, &[0; 0], encrypted_message)?; self.increment_nonce(); - (Some(message), message_end_index) + Ok((Some(message), message_end_index)) } fn increment_nonce(&mut self) { @@ -243,7 +260,7 @@ mod tests { let encrypted_message = connected_peer.encrypt(&message); assert_eq!(encrypted_message.len(), 2 + 16 + 16); - let decrypted_message = remote_peer.decrypt_single_message(Some(&encrypted_message)).unwrap(); + let decrypted_message = remote_peer.decrypt_single_message(Some(&encrypted_message)).unwrap().unwrap(); assert_eq!(decrypted_message, Vec::::new()); } @@ -296,17 +313,123 @@ mod tests { let mut current_encrypted_message = encrypted_messages.remove(0); let next_encrypted_message = encrypted_messages.remove(0); current_encrypted_message.extend_from_slice(&next_encrypted_message); - let decrypted_message = remote_peer.decrypt_single_message(Some(¤t_encrypted_message)).unwrap(); + let decrypted_message = remote_peer.decrypt_single_message(Some(¤t_encrypted_message)).unwrap().unwrap(); assert_eq!(decrypted_message, message); } for _ in 0..501 { // decrypt messages directly from buffer without adding to it - let decrypted_message = remote_peer.decrypt_single_message(None).unwrap(); + let decrypted_message = remote_peer.decrypt_single_message(None).unwrap().unwrap(); assert_eq!(decrypted_message, message); } } + // Decryption errors should result in Err + #[test] + fn decryption_failure_errors() { + let (mut connected_peer, mut remote_peer) = setup_peers(); + let encrypted = remote_peer.encrypt(&[1]); + + connected_peer.decryptor.receiving_key = [0; 32]; + assert_eq!(connected_peer.decrypt_single_message(Some(&encrypted)), Err("invalid hmac".to_string())); + } + + // Test next()::None + #[test] + fn decryptor_iterator_empty() { + let (mut connected_peer, _) = setup_peers(); + + assert_eq!(connected_peer.decryptor.next(), None); + } + + // Test next() -> next()::None + #[test] + fn decryptor_iterator_one_item_valid() { + let (mut connected_peer, mut remote_peer) = setup_peers(); + let encrypted = remote_peer.encrypt(&[1]); + connected_peer.read(&encrypted); + + assert_eq!(connected_peer.decryptor.next(), Some(Ok(Some(vec![1])))); + assert_eq!(connected_peer.decryptor.next(), None); + } + + // Test next()::err -> next()::None + #[test] + fn decryptor_iterator_error() { + let (mut connected_peer, mut remote_peer) = setup_peers(); + let encrypted = remote_peer.encrypt(&[1]); + connected_peer.read(&encrypted); + + connected_peer.decryptor.receiving_key = [0; 32]; + assert_eq!(connected_peer.decryptor.next(), Some(Err("invalid hmac".to_string()))); + assert_eq!(connected_peer.decryptor.next(), None); + } + + // Test next()::Some -> next()::err -> next()::None + #[test] + fn decryptor_iterator_error_after_success() { + let (mut connected_peer, mut remote_peer) = setup_peers(); + let encrypted = remote_peer.encrypt(&[1]); + connected_peer.read(&encrypted); + let encrypted = remote_peer.encrypt(&[2]); + connected_peer.read(&encrypted); + + assert_eq!(connected_peer.decryptor.next(), Some(Ok(Some(vec![1])))); + connected_peer.decryptor.receiving_key = [0; 32]; + assert_eq!(connected_peer.decryptor.next(), Some(Err("invalid hmac".to_string()))); + assert_eq!(connected_peer.decryptor.next(), None); + } + + // Test that next()::Some -> next()::err -> next()::None + // Error should poison decryptor + #[test] + fn decryptor_iterator_next_after_error_returns_none() { + let (mut connected_peer, mut remote_peer) = setup_peers(); + let encrypted = remote_peer.encrypt(&[1]); + connected_peer.read(&encrypted); + let encrypted = remote_peer.encrypt(&[2]); + connected_peer.read(&encrypted); + let encrypted = remote_peer.encrypt(&[3]); + connected_peer.read(&encrypted); + + // Get one valid value + assert_eq!(connected_peer.decryptor.next(), Some(Ok(Some(vec![1])))); + let valid_receiving_key = connected_peer.decryptor.receiving_key; + + // Corrupt the receiving key and ensure we get a failure + connected_peer.decryptor.receiving_key = [0; 32]; + assert_eq!(connected_peer.decryptor.next(), Some(Err("invalid hmac".to_string()))); + + // Restore the receiving key, do a read and ensure None is returned (poisoned) + connected_peer.decryptor.receiving_key = valid_receiving_key; + assert_eq!(connected_peer.decryptor.next(), None); + } + + // Test next()::Some -> next()::err -> read() -> next()::None + // Error should poison decryptor even after future reads + #[test] + fn decryptor_iterator_read_next_after_error_returns_none() { + let (mut connected_peer, mut remote_peer) = setup_peers(); + let encrypted = remote_peer.encrypt(&[1]); + connected_peer.read(&encrypted); + let encrypted = remote_peer.encrypt(&[2]); + connected_peer.read(&encrypted); + + // Get one valid value + assert_eq!(connected_peer.decryptor.next(), Some(Ok(Some(vec![1])))); + let valid_receiving_key = connected_peer.decryptor.receiving_key; + + // Corrupt the receiving key and ensure we get a failure + connected_peer.decryptor.receiving_key = [0; 32]; + assert_eq!(connected_peer.decryptor.next(), Some(Err("invalid hmac".to_string()))); + + // Restore the receiving key, do a read and ensure None is returned (poisoned) + let encrypted = remote_peer.encrypt(&[3]); + connected_peer.read(&encrypted); + connected_peer.decryptor.receiving_key = valid_receiving_key; + assert_eq!(connected_peer.decryptor.next(), None); + } + #[test] fn max_msg_len_limit_value() { assert_eq!(LN_MAX_MSG_LEN, 65535); @@ -328,6 +451,6 @@ mod tests { // MSG should not exceed LN_MAX_MSG_LEN + 16 let msg = [4u8; LN_MAX_MSG_LEN + 17]; - connected_peer.decrypt_single_message(Some(&msg)); + connected_peer.decrypt_single_message(Some(&msg)).unwrap(); } } \ No newline at end of file diff --git a/lightning/src/ln/peers/handler.rs b/lightning/src/ln/peers/handler.rs index 05c72c84a16..91de3d6920d 100644 --- a/lightning/src/ln/peers/handler.rs +++ b/lightning/src/ln/peers/handler.rs @@ -603,33 +603,53 @@ impl PeerManager x, - Err(e) => { - match e { - msgs::DecodeError::UnknownVersion => return Err(PeerHandleError { no_connection_possible: false }), - msgs::DecodeError::UnknownRequiredFeature => { - log_debug!(self.logger, "Got a channel/node announcement with an known required feature flag, you may want to update!"); - continue; - } - msgs::DecodeError::InvalidValue => { - log_debug!(self.logger, "Got an invalid value while deserializing message"); - return Err(PeerHandleError { no_connection_possible: false }); - } - msgs::DecodeError::ShortRead => { - log_debug!(self.logger, "Deserialization failed due to shortness of message"); - return Err(PeerHandleError { no_connection_possible: false }); + // Using Iterators that can error requires special handling + // The item returned from next() has type Option, String>> + // The Some wrapper is stripped for each item inside the loop + // There are 3 valid match cases: + // 1) Some(Ok(Some(msg_data))) => Indicates a valid decrypted msg accessed via msg_data + // 2) Some(Err(_)) => Indicates an error during decryption that should be handled + // 3) None -> Indicates there were no messages available to decrypt + // Invalid Cases + // 1) Some(Ok(None)) => Translated to None case above so users of iterators can stop correctly + for msg_data_result in &mut conduit.decryptor { + match msg_data_result { + Ok(Some(msg_data)) => { + let mut reader = ::std::io::Cursor::new(&msg_data[..]); + let message_result = wire::read(&mut reader); + let message = match message_result { + Ok(x) => x, + Err(e) => { + match e { + msgs::DecodeError::UnknownVersion => return Err(PeerHandleError { no_connection_possible: false }), + msgs::DecodeError::UnknownRequiredFeature => { + log_debug!(self.logger, "Got a channel/node announcement with an known required feature flag, you may want to update!"); + continue; + } + msgs::DecodeError::InvalidValue => { + log_debug!(self.logger, "Got an invalid value while deserializing message"); + return Err(PeerHandleError { no_connection_possible: false }); + } + msgs::DecodeError::ShortRead => { + log_debug!(self.logger, "Deserialization failed due to shortness of message"); + return Err(PeerHandleError { no_connection_possible: false }); + } + msgs::DecodeError::BadLengthDescriptor => return Err(PeerHandleError { no_connection_possible: false }), + msgs::DecodeError::Io(_) => return Err(PeerHandleError { no_connection_possible: false }), + } } - msgs::DecodeError::BadLengthDescriptor => return Err(PeerHandleError { no_connection_possible: false }), - msgs::DecodeError::Io(_) => return Err(PeerHandleError { no_connection_possible: false }), - } - } - }; + }; - received_messages.push(message); + received_messages.push(message); + }, + Err(e) => { + log_trace!(self.logger, "Message decryption failed due to: {}", e); + return Err(PeerHandleError { no_connection_possible: false }); + } + Ok(None) => { + panic!("Invalid behavior. Conduit iterator should never return this match.") + } + } } } From 3a43c218597fb80ceb78bfb7477bc6ae8bb4bfc6 Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Mon, 24 Aug 2020 14:11:52 -0700 Subject: [PATCH 36/72] test: Update handshake fuzz test to cover more cases Enhanced the fuzz testing to simulate more test cases: * Valid or random act data * Optionally sending garbage data along with act * Optionally sending act data in multiple segments Found regression introduced in eb6a371 relating to the Conduit panicking during an hmac failure. --- fuzz/src/peer_crypt.rs | 336 ++++++++++++++++++++++++++++++++--------- 1 file changed, 265 insertions(+), 71 deletions(-) diff --git a/fuzz/src/peer_crypt.rs b/fuzz/src/peer_crypt.rs index 6cbfc274919..572856a1d83 100644 --- a/fuzz/src/peer_crypt.rs +++ b/fuzz/src/peer_crypt.rs @@ -7,6 +7,9 @@ // You may not use this file except in accordance with one or both of these // licenses. +use std::cmp; +use std::{error, fmt}; + use bitcoin::secp256k1; use bitcoin::secp256k1::key::{PublicKey,SecretKey}; @@ -23,39 +26,56 @@ pub struct FuzzGen<'a> { } impl<'a> FuzzGen<'a> { - pub fn new(data: &'a [u8]) -> Self { + fn new(data: &'a [u8]) -> Self { Self { read_pos: 0, data } } - pub fn generate_bytes(&mut self, num: usize) -> Result<&'a [u8], String> { + fn generate_bytes(&mut self, num: usize) -> Result<&'a [u8], GeneratorFinishedError> { if self.data.len() < self.read_pos + num { - return Err("out of bytes".to_string()); + return Err(GeneratorFinishedError { }); } self.read_pos += num; Ok(&self.data[self.read_pos - num..self.read_pos]) } - pub fn generate_secret_key(&mut self) -> Result { + fn generate_secret_key(&mut self) -> Result { // Loop through the input 32 bytes at a time until a valid // secret key can be created or we run out of bytes loop { match SecretKey::from_slice(self.generate_bytes(32)?) { Ok(key) => { return Ok(key) }, - _ => {} + _ => { } } } } - pub fn generate_bool(&mut self) -> Result { + fn generate_bool(&mut self) -> Result { let next_byte = self.generate_bytes(1)?; Ok(next_byte[0] & 1 == 1) } } +#[derive(PartialEq)] +struct GeneratorFinishedError { } + +impl fmt::Debug for GeneratorFinishedError { + fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { + formatter.write_str("Generator out of bytes") + } +} +impl fmt::Display for GeneratorFinishedError { + fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { + formatter.write_str("Generator out of bytes") + } +} +impl error::Error for GeneratorFinishedError { + fn description(&self) -> &str { "Generator out of bytes" } +} + struct TestCtx { initiator_static_public_key: PublicKey, initiator_handshake: PeerHandshake, @@ -65,7 +85,7 @@ struct TestCtx { } impl TestCtx { - fn make(generator: &mut FuzzGen) -> Result { + fn make(generator: &mut FuzzGen) -> Result { let curve = secp256k1::Secp256k1::new(); // Generate needed keys for successful handshake @@ -92,7 +112,7 @@ impl TestCtx { // Common test function that sends encrypted messages between two conduits until the source data // runs out. #[inline] -fn do_conduit_tests(generator: &mut FuzzGen, initiator_conduit: &mut Conduit, responder_conduit: &mut Conduit) -> Result<(), String> { +fn do_conduit_tests(generator: &mut FuzzGen, initiator_conduit: &mut Conduit, responder_conduit: &mut Conduit, failures_expected: bool) -> Result<(), GeneratorFinishedError> { // Keep sending messages back and forth until the input data is consumed loop { // Randomly generate message length @@ -105,20 +125,30 @@ fn do_conduit_tests(generator: &mut FuzzGen, initiator_conduit: &mut Conduit, re // randomly choose sender of message let receiver_unencrypted_msg = if generator.generate_bool()? { let encrypted_msg = initiator_conduit.encrypt(sender_unencrypted_msg); - responder_conduit.decrypt_single_message(Some(&encrypted_msg)) + if let Ok(msg) = responder_conduit.decrypt_single_message(Some(&encrypted_msg)) { + msg + } else { + assert!(failures_expected); + return Ok(()); + } } else { let encrypted_msg = responder_conduit.encrypt(sender_unencrypted_msg); - initiator_conduit.decrypt_single_message(Some(&encrypted_msg)) + if let Ok(msg) = initiator_conduit.decrypt_single_message(Some(&encrypted_msg)) { + msg + } else { + assert!(failures_expected); + return Ok(()); + } }; - assert_eq!(sender_unencrypted_msg, receiver_unencrypted_msg.unwrap().unwrap().as_slice()); + assert_eq!(sender_unencrypted_msg, receiver_unencrypted_msg.unwrap().as_slice()); } } // This test completes a valid handshake based on "random" private keys and then sends variable // length encrypted messages between two conduits to validate that they can communicate. #[inline] -fn do_completed_handshake_test(generator: &mut FuzzGen) -> Result<(), String> { +fn do_completed_handshake_test(generator: &mut FuzzGen) -> Result<(), GeneratorFinishedError> { let mut test_ctx = TestCtx::make(generator)?; // The handshake should complete with any valid private keys @@ -141,10 +171,37 @@ fn do_completed_handshake_test(generator: &mut FuzzGen) -> Result<(), String> { assert_eq!(initiator_pubkey, test_ctx.initiator_static_public_key); assert_eq!(responder_pubkey, test_ctx.responder_static_public_key); - // The nodes should be able to communicate over the conduit - do_conduit_tests(generator, &mut initiator_conduit, &mut responder_conduit)?; + // The nodes should be able to communicate over the conduit. + do_conduit_tests(generator, &mut initiator_conduit, &mut responder_conduit, false) +} - unreachable!(); +// Either returns (act, false) or (random_bytes, true) where random_bytes is the same len as act +fn maybe_generate_bad_act(generator: &mut FuzzGen, act: Vec) -> Result<(Vec, bool), GeneratorFinishedError> { + if generator.generate_bool()? { + Ok(((generator.generate_bytes(act.len())?).to_vec(), true)) + } else { + Ok((act, false)) + } +} + +// Add between 0..15 bytes of garbage to a vec and returns whether or not it added bytes +fn maybe_add_garbage(generator: &mut FuzzGen, input: &mut Vec) -> Result { + let garbage_amount = (generator.generate_bytes(1)?)[0] >> 4; + + if garbage_amount != 0 { + input.extend(generator.generate_bytes(garbage_amount as usize)?); + Ok(true) + } else { + Ok(false) + } +} + +// Splits a Vec into between 1..7 chunks returning a Vec of slices into the original data +fn split_vec<'a>(generator: &mut FuzzGen, input: &'a Vec) -> Result, GeneratorFinishedError> { + let num_chunks = cmp::max(1, ((generator.generate_bytes(1)?)[0] as u8) >> 5) as usize; + let chunk_size = input.len() / num_chunks; + + Ok(input.chunks(chunk_size).collect()) } // This test variant goes through the peer handshake between the initiator and responder with @@ -152,62 +209,108 @@ fn do_completed_handshake_test(generator: &mut FuzzGen) -> Result<(), String> { // In the event of an error from process_act() we validate that the input data was generated // randomly to ensure real act generation still produces valid transitions. #[inline] -fn do_handshake_test(generator: &mut FuzzGen) -> Result<(), String> { +fn do_handshake_test(generator: &mut FuzzGen) -> Result<(), GeneratorFinishedError> { let mut test_ctx = TestCtx::make(generator)?; - let mut used_generated_data = false; - // Possibly generate bad data for act1 and ensure that the responder does not panic - let mut act1 = test_ctx.act1; - if generator.generate_bool()? { - act1 = (generator.generate_bytes(50)?).to_vec(); - used_generated_data = true; + // Possibly generate bad data for act1 + let (mut act1, mut used_generated_data) = maybe_generate_bad_act(generator, test_ctx.act1)?; + + // Optionally, add garbage data to the end + used_generated_data |= maybe_add_garbage(generator, &mut act1)?; + + // Split act1 into between 1..7 chunks + let act1_chunks = split_vec(generator, &act1)?; + + let mut act2_option = None; + for partial_act1 in act1_chunks { + match test_ctx.responder_handshake.process_act(&partial_act1) { + // Save valid act2 for initiator + Ok((Some(act2_option_inner), None)) => { + act2_option = Some(act2_option_inner); + }, + // Partial act + Ok((None, None)) => { } + // errors are fine as long as they happened due to using bad data + Err(_) => { + assert!(used_generated_data); + return Ok(()); + } + _ => panic!("responder required to output act bytes and no conduit/pubkey") + }; } + let act2 = act2_option.unwrap(); - let mut act2 = match test_ctx.responder_handshake.process_act(&act1) { - Ok((Some(act2), None)) => { - act2 - } - Err(_) => { - assert!(used_generated_data); - return Err("generated expected failure with bad data".to_string()); - } - _ => panic!("responder required to output act bytes and no conduit/pubkey") - }; + // Possibly generate bad data for act2 + let (mut act2, is_bad_data) = maybe_generate_bad_act(generator, act2)?; - // Possibly generate bad data for act2 and ensure that the initiator does not panic - if generator.generate_bool()? { - act2 = (generator.generate_bytes(50)?).to_vec(); - used_generated_data = true; - } + // Optionally, add garbage data to the end + let did_add_garbage = maybe_add_garbage(generator, &mut act2)?; - let (mut act3, (mut initiator_conduit, responder_pubkey)) = match test_ctx.initiator_handshake.process_act(&act2) { - Ok((Some(act3), Some((conduit, remote_pubkey)))) => { - (act3, (conduit, remote_pubkey)) - } - Err(_) => { - assert!(used_generated_data); - return Err("generated expected failure with bad data".to_string()); - } - _ => panic!("initiator required to output act bytes and no conduit/pubkey") - }; + // Keep track of whether or not we have generated bad data up to this point + used_generated_data |= is_bad_data | did_add_garbage; - // Possibly generate bad data for act3 and ensure that the responder does not panic - if generator.generate_bool()? { - act3 = (generator.generate_bytes(66)?).to_vec(); - used_generated_data = true; + // Split act2 into between 1..7 chunks + let act2_chunks = split_vec(generator, &act2)?; + + let mut act3_option = None; + let mut conduit_and_remote_pubkey_option = None; + for partial_act2 in act2_chunks { + match test_ctx.initiator_handshake.process_act(&partial_act2) { + Ok((Some(act3), Some(conduit_and_remote_pubkey_option_inner))) => { + act3_option = Some(act3); + conduit_and_remote_pubkey_option = Some(conduit_and_remote_pubkey_option_inner); + + // Valid conduit and pubkey indicates handshake is over + break; + } + // Partial act + Ok((None, None)) => { }, + // errors are fine as long as they happened due to using bad data + Err(_) => { + assert!(used_generated_data); + return Ok(()); + } + _ => panic!("initiator required to output act bytes, conduit, and pubkey") + }; } - let (mut responder_conduit, initiator_pubkey) = match test_ctx.responder_handshake.process_act(&act3) { - Ok((None, Some((conduit, remote_pubkey)))) => { - (conduit, remote_pubkey) - } - Err(_) => { - // extremely unlikely we randomly generate a correct act3, but if so.. reset this - assert!(used_generated_data); - return Err("generated expected failure with bad data".to_string()); - }, - _ => panic!("responder required to output conduit/remote pubkey and no act bytes") - }; + // Ensure we actually received act3 bytes, conduit, and remote pubkey from process_act() + let act3 = act3_option.unwrap(); + let (mut initiator_conduit, responder_pubkey) = conduit_and_remote_pubkey_option.unwrap(); + + // Possibly generate bad data for act3 + let (mut act3, is_bad_data) = maybe_generate_bad_act(generator, act3)?; + + // Optionally, add garbage data to the end + let did_add_garbage = maybe_add_garbage(generator, &mut act3)?; + + // Keep track of whether or not we have generated bad data up to this point + used_generated_data |= is_bad_data | did_add_garbage; + + // Split act3 into between 1..7 chunks + let act3_chunks = split_vec(generator, &act3)?; + + let mut conduit_and_remote_pubkey_option = None; + for partial_act3 in act3_chunks { + match test_ctx.responder_handshake.process_act(&partial_act3) { + Ok((None, Some(conduit_and_remote_pubkey_option_inner))) => { + conduit_and_remote_pubkey_option = Some(conduit_and_remote_pubkey_option_inner); + + // Valid conduit and pubkey indicates handshake is over + break; + }, + // partial act + Ok((None, None)) => { }, + // errors are fine as long as they happened due to using bad data + Err(_) => { + assert!(used_generated_data); + return Ok(()); + }, + _ => panic!("responder required to output conduit, and pubkey") + }; + } + // Ensure we actually received conduit and remote pubkey from process_act() + let (mut responder_conduit, initiator_pubkey) = conduit_and_remote_pubkey_option.unwrap(); // The handshake should complete with each peer knowing the static_public_key of the remote peer if initiator_pubkey != test_ctx.initiator_static_public_key { @@ -220,9 +323,7 @@ fn do_handshake_test(generator: &mut FuzzGen) -> Result<(), String> { } // The nodes should be able to communicate over the conduit - do_conduit_tests(generator, &mut initiator_conduit, &mut responder_conduit)?; - - unreachable!(); + do_conduit_tests(generator, &mut initiator_conduit, &mut responder_conduit, used_generated_data) } #[inline] @@ -235,13 +336,19 @@ fn do_test(data: &[u8]) { _ => { return } }; + // The only valid error that can leak here is the FuzzGen error to indicate + // the input bytes have been exhausted and the test can't proceed. Everything + // else should be caught and handled by the individual tests to validate any + // errors. if do_valid_handshake { match do_completed_handshake_test(&mut generator) { - _ => {} + Err(_) => { } + _ => { } } } else { match do_handshake_test(&mut generator) { - _ => {} + Err(_) => { } + _ => { } } } } @@ -262,7 +369,7 @@ mod test { #[test] fn data_generator_empty() { let mut generator = FuzzGen::new(&[]); - assert_eq!(generator.generate_bool().err(), Some("out of bytes".to_string())); + assert_eq!(generator.generate_bool(), Err(GeneratorFinishedError { })); } #[test] @@ -281,13 +388,13 @@ mod test { fn data_generator_bool_then_error() { let mut generator = FuzzGen::new(&[1]); assert!(generator.generate_bool().unwrap()); - assert_eq!(generator.generate_bool().err(), Some("out of bytes".to_string())); + assert_eq!(generator.generate_bool(), Err(GeneratorFinishedError { })); } #[test] fn data_generator_bytes_too_many() { let mut generator = FuzzGen::new(&[1, 2, 3, 4]); - assert_eq!(generator.generate_bytes(5).err(), Some("out of bytes".to_string())); + assert_eq!(generator.generate_bytes(5), Err(GeneratorFinishedError { })); } #[test] @@ -306,6 +413,93 @@ mod test { assert_eq!(result, &input[..2]); let result = generator.generate_bytes(2).unwrap(); assert_eq!(result, &input[2..]); - assert_eq!(generator.generate_bytes(1).err(), Some("out of bytes".to_string())); + assert_eq!(generator.generate_bytes(1), Err(GeneratorFinishedError { })); + } + + #[test] + fn maybe_generate_bad_act_gen_bad() { + // 1 is used to take bad branch and 2 is used to generate bad act + let input = [1, 2]; + let mut generator = FuzzGen::new(&input); + + let original_act = &[5]; + + let (act, is_bad) = maybe_generate_bad_act(&mut generator, original_act.to_vec()).unwrap(); + assert!(is_bad); + assert_eq!(act, &[2]); + } + + #[test] + fn maybe_generate_bad_act_gen_good() { + // 0 is used to take good branch + let input = [0]; + let mut generator = FuzzGen::new(&input); + let original_act = &[5]; + + let (act, is_bad) = maybe_generate_bad_act(&mut generator, original_act.to_vec()).unwrap(); + assert!(!is_bad); + assert_eq!(act, &[5]); + } + + #[test] + fn maybe_add_garbage_did_add() { + // 0x10 consumed to specify amount of garbage (1 byte) and 2 is consumed to add garbage + let input = [0x10, 2]; + let mut generator = FuzzGen::new(&input); + let mut act = vec![5]; + + let did_add_garbage = maybe_add_garbage(&mut generator, &mut act).unwrap(); + assert!(did_add_garbage); + assert_eq!(act, &[5, 2]); + } + + #[test] + fn maybe_add_garbage_no_add() { + // 0x10 consumed to specify amount of garbage (1 byte) and 2 is consumed to add garbage + let input = [0]; + let mut generator = FuzzGen::new(&input); + let mut act = vec![5]; + + let did_add_garbage = maybe_add_garbage(&mut generator, &mut act).unwrap(); + assert!(!did_add_garbage); + assert_eq!(act, &[5]); + } + + #[test] + fn split_vec_1_chunk() { + // 0 consumed for number of chunks (1 is min) + let input = [0]; + let mut generator = FuzzGen::new(&input); + let act = vec![5, 6]; + + let act_parts = split_vec(&mut generator, &act).unwrap(); + assert_eq!(act_parts.len(), 1); + assert_eq!(act_parts[0], &[5, 6]); + } + + #[test] + fn split_vec_2_chunks() { + // 40 consumed for number of chunks. Chunk size is equal to the high three bits (2) + let input = [0x40]; + let mut generator = FuzzGen::new(&input); + let act = vec![5, 6]; + + let act_parts = split_vec(&mut generator, &act).unwrap(); + assert_eq!(act_parts.len(), 2); + assert_eq!(act_parts[0], &[5]); + assert_eq!(act_parts[1], &[6]); + } + #[test] + fn split_vec_2_chunks_odd() { + // 40 consumed for number of chunks. Chunk size is equal to the high three bits (2) + let input = [0x40]; + let mut generator = FuzzGen::new(&input); + let act = vec![5, 6, 7, 8, 9]; + + let act_parts = split_vec(&mut generator, &act).unwrap(); + assert_eq!(act_parts.len(), 3); + assert_eq!(act_parts[0], &[5, 6]); + assert_eq!(act_parts[1], &[7, 8]); + assert_eq!(act_parts[2], &[9]); } } \ No newline at end of file From ec5803c9d27816e7a7b05b2d1425fcba0d590f54 Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Mon, 24 Aug 2020 17:35:00 -0700 Subject: [PATCH 37/72] review: Split outbound initialization into new()/set_up() To avoid tuple returns from a single initialization function, create a setup_outbound() function that is only callable by the outbound PeerHandshake, but doesn't leak implementation details. --- fuzz/src/peer_crypt.rs | 3 +- lightning/src/ln/peers/handler.rs | 3 +- lightning/src/ln/peers/handshake/mod.rs | 61 +++++++++++++++++++++---- 3 files changed, 57 insertions(+), 10 deletions(-) diff --git a/fuzz/src/peer_crypt.rs b/fuzz/src/peer_crypt.rs index 572856a1d83..d21c8009898 100644 --- a/fuzz/src/peer_crypt.rs +++ b/fuzz/src/peer_crypt.rs @@ -96,7 +96,8 @@ impl TestCtx { let responder_static_public_key = PublicKey::from_secret_key(&curve, &responder_static_private_key); let responder_ephemeral_private_key = generator.generate_secret_key()?; - let (act1, initiator_handshake) = PeerHandshake::create_and_initialize_outbound(&initiator_static_private_key, &responder_static_public_key, &initiator_ephemeral_private_key); + let mut initiator_handshake = PeerHandshake::new_outbound(&initiator_static_private_key, &responder_static_public_key, &initiator_ephemeral_private_key); + let act1 = initiator_handshake.set_up_outbound(); let responder_handshake = PeerHandshake::new_inbound(&responder_static_private_key, &responder_ephemeral_private_key); Ok(TestCtx { diff --git a/lightning/src/ln/peers/handler.rs b/lightning/src/ln/peers/handler.rs index 91de3d6920d..64c433e0624 100644 --- a/lightning/src/ln/peers/handler.rs +++ b/lightning/src/ln/peers/handler.rs @@ -365,7 +365,8 @@ impl PeerManager Result, PeerHandleError> { - let (initial_bytes, handshake) = PeerHandshake::create_and_initialize_outbound(&self.our_node_secret, &their_node_id, &self.get_ephemeral_key()); + let mut handshake = PeerHandshake::new_outbound(&self.our_node_secret, &their_node_id, &self.get_ephemeral_key()); + let initial_bytes = handshake.set_up_outbound(); let mut peers = self.peers.lock().unwrap(); if peers.peers.insert(descriptor, Peer { diff --git a/lightning/src/ln/peers/handshake/mod.rs b/lightning/src/ln/peers/handshake/mod.rs index 3bf47ebb60a..571a912674c 100644 --- a/lightning/src/ln/peers/handshake/mod.rs +++ b/lightning/src/ln/peers/handshake/mod.rs @@ -13,25 +13,42 @@ mod states; /// Currently requires explicit ephemeral private key specification. pub struct PeerHandshake { state: Option, + ready_for_process: bool } impl PeerHandshake { /// Instantiate a new handshake with a node identity secret key and an ephemeral private key - pub fn create_and_initialize_outbound(initiator_static_private_key: &SecretKey, responder_static_public_key: &PublicKey, initiator_ephemeral_private_key: &SecretKey) -> (Vec, Self) { + pub fn new_outbound(initiator_static_private_key: &SecretKey, responder_static_public_key: &PublicKey, initiator_ephemeral_private_key: &SecretKey) -> Self { let state = HandshakeState::new_initiator(initiator_static_private_key, responder_static_public_key, initiator_ephemeral_private_key); - // This transition does not have a failure path - let (response_vec_opt, next_state) = state.next(&[]).unwrap(); - (response_vec_opt.unwrap(), Self { state: Some(next_state) }) + Self { + state: Some(state), + ready_for_process: false + } } /// Instantiate a new handshake in anticipation of a peer's first handshake act pub fn new_inbound(responder_static_private_key: &SecretKey, responder_ephemeral_private_key: &SecretKey) -> Self { Self { - state: Some(HandshakeState::new_responder(responder_static_private_key, responder_ephemeral_private_key)) + state: Some(HandshakeState::new_responder(responder_static_private_key, responder_ephemeral_private_key)), + ready_for_process: true } } + /// Initializes the outbound handshake and provides the initial bytes to send to the responder + pub fn set_up_outbound(&mut self) -> Vec { + assert!(!self.ready_for_process); + let cur_state = self.state.take().unwrap(); + + // This transition does not have a failure path + let (response_vec_opt, next_state) = cur_state.next(&[]).unwrap(); + + self.state = Some(next_state); + + self.ready_for_process = true; + response_vec_opt.unwrap() + } + /// Process act dynamically /// # Arguments /// `input`: Byte slice received from peer as part of the handshake protocol @@ -41,6 +58,7 @@ impl PeerHandshake { /// `.0`: Byte vector containing the next act to send back to the peer per the handshake protocol /// `.1`: Some(Conduit, PublicKey) if the handshake was just processed to completion and messages can now be encrypted and decrypted pub fn process_act(&mut self, input: &[u8]) -> Result<(Option>, Option<(Conduit, PublicKey)>), String> { + assert!(self.ready_for_process); let cur_state = self.state.take().unwrap(); let (act_opt, mut next_state) = cur_state.next(input)?; @@ -85,7 +103,8 @@ mod test { let inbound_static_public_key = PublicKey::from_secret_key(&curve, &inbound_static_private_key); let inbound_ephemeral_private_key = SecretKey::from_slice(&[0x_22_u8; 32]).unwrap(); - let (act1, outbound_handshake) = PeerHandshake::create_and_initialize_outbound(&outbound_static_private_key, &inbound_static_public_key, &outbound_ephemeral_private_key); + let mut outbound_handshake= PeerHandshake::new_outbound(&outbound_static_private_key, &inbound_static_public_key, &outbound_ephemeral_private_key); + let act1 = outbound_handshake.set_up_outbound(); let inbound_handshake = PeerHandshake::new_inbound(&inbound_static_private_key, &inbound_ephemeral_private_key); TestCtx { @@ -113,9 +132,35 @@ mod test { } } + // Test that the outbound needs to call set_up_outbound() before process_act() + #[test] + #[should_panic(expected = "assertion failed: self.ready_for_process")] + fn new_outbound_no_set_up_panics() { + let curve = secp256k1::Secp256k1::new(); + + let outbound_static_private_key = SecretKey::from_slice(&[0x_11_u8; 32]).unwrap(); + let outbound_ephemeral_private_key = SecretKey::from_slice(&[0x_12_u8; 32]).unwrap(); + let inbound_static_private_key = SecretKey::from_slice(&[0x_21_u8; 32]).unwrap(); + let inbound_static_public_key = PublicKey::from_secret_key(&curve, &inbound_static_private_key); + + let mut outbound_handshake= PeerHandshake::new_outbound(&outbound_static_private_key, &inbound_static_public_key, &outbound_ephemeral_private_key); + outbound_handshake.process_act(&[]).unwrap(); + } + + // Test that calling set_up_outbound() on the inbound panics + #[test] + #[should_panic(expected = "assertion failed: !self.ready_for_process")] + fn new_inbound_calling_set_up_panics() { + let inbound_static_private_key = SecretKey::from_slice(&[0x_21_u8; 32]).unwrap(); + let inbound_ephemeral_private_key = SecretKey::from_slice(&[0x_22_u8; 32]).unwrap(); + + let mut inbound_handshake = PeerHandshake::new_inbound(&inbound_static_private_key, &inbound_ephemeral_private_key); + inbound_handshake.set_up_outbound(); + } + // Default Outbound::Uninitiated #[test] - fn peer_handshake_new_outbound() { + fn new_outbound() { let test_ctx = TestCtx::new(); assert_matches!(test_ctx.outbound_handshake.state, Some(HandshakeState::InitiatorAwaitingActTwo(_))); @@ -123,7 +168,7 @@ mod test { // Default Inbound::AwaitingActOne #[test] - fn peer_handshake_new_inbound() { + fn new_inbound() { let test_ctx = TestCtx::new(); assert_matches!(test_ctx.inbound_handshake.state, Some(HandshakeState::ResponderAwaitingActOne(_))); From 1189e845d8be5ba765e4086bd4a2539525aacda0 Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Wed, 26 Aug 2020 11:32:42 -0700 Subject: [PATCH 38/72] feature: Tighten the constraints for act message sizes Proper handshake peers won't send more bytes than required for act1 and act2 and it is impossible to generate future acts without first receiving an interleaved response. In those cases, the peer is misbehaving and we can fail early. This unlocks a Vec-less design in the handshake code now that there is an upper limit on read buffer sizes. --- lightning/src/ln/peers/handshake/states.rs | 66 ++++++++++------------ 1 file changed, 29 insertions(+), 37 deletions(-) diff --git a/lightning/src/ln/peers/handshake/states.rs b/lightning/src/ln/peers/handshake/states.rs index 8aba9fe20c1..7eca40f68c8 100644 --- a/lightning/src/ln/peers/handshake/states.rs +++ b/lightning/src/ln/peers/handshake/states.rs @@ -181,6 +181,12 @@ impl IHandshakeState for ResponderAwaitingActOneState { let mut read_buffer = self.read_buffer; read_buffer.extend_from_slice(input); + // Any payload larger than ACT_ONE_TWO_LENGTH indicates a bad peer since initiator data + // is required to generate act3 (so it can't come before we transition) + if read_buffer.len() > ACT_ONE_TWO_LENGTH { + return Err("Act One too large".to_string()); + } + // In the event of a partial fill, stay in the same state and wait for more data if read_buffer.len() < ACT_ONE_TWO_LENGTH { return Ok(( @@ -238,6 +244,12 @@ impl IHandshakeState for InitiatorAwaitingActTwoState { let mut read_buffer = self.read_buffer; read_buffer.extend_from_slice(input); + // Any payload larger than ACT_ONE_TWO_LENGTH indicates a bad peer since responder data + // is required to generate post-authentication messages (so it can't come before we transition) + if read_buffer.len() > ACT_ONE_TWO_LENGTH { + return Err("Act Two too large".to_string()); + } + // In the event of a partial fill, stay in the same state and wait for more data if read_buffer.len() < ACT_ONE_TWO_LENGTH { return Ok(( @@ -289,7 +301,7 @@ impl IHandshakeState for InitiatorAwaitingActTwoState { // 7. rn = 0, sn = 0 // - done by Conduit - let mut conduit = Conduit::new(sending_key, receiving_key, chaining_key); + let conduit = Conduit::new(sending_key, receiving_key, chaining_key); // Send m = 0 || c || t over the network buffer let mut act_three = Vec::with_capacity(ACT_THREE_LENGTH); @@ -298,14 +310,6 @@ impl IHandshakeState for InitiatorAwaitingActTwoState { act_three.extend(&authentication_tag); assert_eq!(act_three.len(), ACT_THREE_LENGTH); - // Any remaining data in the read buffer would be encrypted, so transfer ownership - // to the Conduit for future use. In this case, it is unlikely that any valid data - // exists, since the responder doesn't have Act3 - if read_buffer.len() > 0 { - conduit.read(&read_buffer[..]); - read_buffer.drain(..); - } - Ok(( Some(act_three), HandshakeState::Complete(Some((conduit, responder_static_public_key))) @@ -600,18 +604,16 @@ mod test { assert_matches!(awaiting_act_three_state, ResponderAwaitingActThree(_)); } - // Responder::AwaitingActOne -> AwaitingActThree - // TODO: Should this fail since we don't expect data > ACT_ONE_TWO_LENGTH and likely indicates - // a bad peer? + // Responder::AwaitingActOne -> AwaitingActThree (bad peer) + // Act2 requires data from the initiator. If we receive a payload for act1 that is larger than + // expected it indicates a bad peer #[test] fn awaiting_act_one_to_awaiting_act_three_input_extra_bytes() { let test_ctx = TestCtx::new(); let mut act1 = test_ctx.valid_act1; act1.extend_from_slice(&[1]); - let (act2, awaiting_act_three_state) = test_ctx.responder.next(&act1).unwrap(); - assert_eq!(act2.unwrap(), test_ctx.valid_act2); - assert_matches!(awaiting_act_three_state, ResponderAwaitingActThree(_)); + assert_eq!(test_ctx.responder.next(&act1).err(), Some(String::from("Act One too large"))); } // Responder::AwaitingActOne -> AwaitingActThree (segmented calls) @@ -658,36 +660,26 @@ mod test { assert_eq!(test_ctx.responder.next(&act1).err(), Some(String::from("invalid hmac"))); } - // Initiator::AwaitingActTwo -> Complete - // RFC test vector: transport-initiator successful handshake + // Initiator::AwaitingActTwo -> Complete (bad peer) + // Initiator data is required to generate post-authentication messages. This means any extra + // data indicates a bad peer. #[test] - fn awaiting_act_two_to_complete() { + fn awaiting_act_two_to_complete_extra_bytes() { let test_ctx = TestCtx::new(); let (_act1, awaiting_act_two_state) = do_next_or_panic!(test_ctx.initiator, &[]); - let (act3, complete_state) = do_next_or_panic!(awaiting_act_two_state, &test_ctx.valid_act2); - - let (conduit, remote_pubkey) = if let Complete(Some((conduit, remote_pubkey))) = complete_state { - (conduit, remote_pubkey) - } else { - panic!(); - }; + let mut act2 = test_ctx.valid_act2; + act2.extend_from_slice(&[1]); - assert_eq!(act3, test_ctx.valid_act3); - assert_eq!(remote_pubkey, test_ctx.responder_static_public_key); - assert_eq!(0, conduit.decryptor.read_buffer_length()); + assert_eq!(awaiting_act_two_state.next(&act2).err(), Some(String::from("Act Two too large"))); } - // Initiator::AwaitingActTwo -> Complete (with extra data) - // Ensures that any remaining data in the read buffer is transferred to the conduit once - // the handshake is complete - // TODO: Is this valid? Don't we expect peers to need ActThree before sending additional data? + // Initiator::AwaitingActTwo -> Complete + // RFC test vector: transport-initiator successful handshake #[test] - fn awaiting_act_two_to_complete_excess_bytes_are_in_conduit() { + fn awaiting_act_two_to_complete() { let test_ctx = TestCtx::new(); let (_act1, awaiting_act_two_state) = do_next_or_panic!(test_ctx.initiator, &[]); - let mut act2 = test_ctx.valid_act2; - act2.extend_from_slice(&[1; 100]); - let (act3, complete_state) = do_next_or_panic!(awaiting_act_two_state, &act2); + let (act3, complete_state) = do_next_or_panic!(awaiting_act_two_state, &test_ctx.valid_act2); let (conduit, remote_pubkey) = if let Complete(Some((conduit, remote_pubkey))) = complete_state { (conduit, remote_pubkey) @@ -697,7 +689,7 @@ mod test { assert_eq!(act3, test_ctx.valid_act3); assert_eq!(remote_pubkey, test_ctx.responder_static_public_key); - assert_eq!(100, conduit.decryptor.read_buffer_length()); + assert_eq!(0, conduit.decryptor.read_buffer_length()); } // Initiator::AwaitingActTwo -> Complete (segmented calls) From 229c35fb117e2fb971fbd4960558758ce0408054 Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Tue, 25 Aug 2020 17:02:53 -0700 Subject: [PATCH 39/72] perf: Replace Vec w/ static arrays for act buffers After reviewing #494 there was design feedback on the use of Vec vs. arrays in this layer of the code for fragmentation reasons. To get ahead of the same issues in this new state machine and to limit the behavior changes from master, remove Vec from the state machine in favor of thin wrappers around fixed-size arrays. This patch reintroduces the Act object (a thin wrapper around fixed size arrays) with some convenience features to make them a bit easier to pass around and build. The Handshake code is still note Vec-clean as the encryption code uses Vecs during encryption, but it is one step closer to passing back slices to the peer_handler. --- lightning/src/ln/peers/handshake/acts.rs | 171 +++++++++++++++++++++ lightning/src/ln/peers/handshake/mod.rs | 10 +- lightning/src/ln/peers/handshake/states.rs | 117 +++++++------- 3 files changed, 236 insertions(+), 62 deletions(-) create mode 100644 lightning/src/ln/peers/handshake/acts.rs diff --git a/lightning/src/ln/peers/handshake/acts.rs b/lightning/src/ln/peers/handshake/acts.rs new file mode 100644 index 00000000000..81591be6c50 --- /dev/null +++ b/lightning/src/ln/peers/handshake/acts.rs @@ -0,0 +1,171 @@ +//! Helper library for working with NOISE handshake Act data. Contains utilities for passing Act +//! objects around and building them from received data. +//! Act objects are thin wrappers about raw arrays for stack-based processing as well as convenient +//! coercion to slices for flexible use by the higher-level modules. + +use std::{cmp, ops}; + +pub const ACT_ONE_TWO_LENGTH: usize = 50; +pub const ACT_THREE_LENGTH: usize = 66; +pub const EMPTY_ACT_ONE: ActOne = [0; ACT_ONE_TWO_LENGTH]; +pub const EMPTY_ACT_TWO: ActTwo = EMPTY_ACT_ONE; +pub const EMPTY_ACT_THREE: ActThree = [0; ACT_THREE_LENGTH]; +type ActOne = [u8; ACT_ONE_TWO_LENGTH]; +type ActTwo = ActOne; +type ActThree = [u8; ACT_THREE_LENGTH]; + +/// Wrapper for any act message +pub(super) enum Act { + One(ActOne), + Two(ActTwo), + Three(ActThree) +} + +impl Act { + /// Returns the size of the underlying array + fn len(&self) -> usize { + self.as_ref().len() + } +} + +impl From for Act { + /// Convert a finished ActBuilder into an Act + fn from(act_builder: ActBuilder) -> Self { + assert!(act_builder.is_finished()); + act_builder.partial_act + } +} + +impl ops::Deref for Act { + type Target = [u8]; + + /// Allows automatic coercion to slices in function calls + /// &Act -> &[u8] + fn deref(&self) -> &Self::Target { + match self { + &Act::One(ref act) => { + act + } + &Act::Two(ref act) => { + act + } + &Act::Three(ref act) => { + act + } + } + } +} + +impl AsRef<[u8]> for Act { + /// Allow convenient exposure of the underlying array through as_ref() + /// Act.as_ref() -> &[u8] + fn as_ref(&self) -> &[u8] { + &self + } +} + +// Simple fill implementation for both almost-identical structs to deduplicate code +// $act: Act[One|Two|Three], $input: &[u8]; returns &[u8] of remaining input that was not processed +macro_rules! fill_impl { + ($act:expr, $write_pos:expr, $input:expr) => {{ + let fill_amount = cmp::min($act.len() - $write_pos, $input.len()); + + $act[$write_pos..$write_pos + fill_amount].copy_from_slice(&$input[..fill_amount]); + + $write_pos += fill_amount; + &$input[fill_amount..] + }} +} + +/// Light wrapper around an Act that allows multiple fill() calls before finally +/// converting to an Act via Act::from(act_builder). Handles all of the bookkeeping +/// and edge cases of the array fill +pub(super) struct ActBuilder { + partial_act: Act, + write_pos: usize +} + +impl ActBuilder { + /// Returns a new ActBuilder for Act::One + pub(super) fn new(empty_act: Act) -> Self { + Self { + partial_act: empty_act, + write_pos: 0 + } + } + + /// Fills the Act with bytes from input and returns the unprocessed bytes + pub(super) fn fill<'a>(&mut self, input: &'a [u8]) -> &'a [u8] { + match &mut self.partial_act { + &mut Act::One(ref mut act) => { + fill_impl!(act, self.write_pos, input) + } + &mut Act::Two(ref mut act) => { + fill_impl!(act, self.write_pos, input) + } + &mut Act::Three(ref mut act) => { + fill_impl!(act, self.write_pos, input) + } + } + } + + /// Returns true if the Act is finished building (enough bytes via fill()) + pub(super) fn is_finished(&self) -> bool { + self.write_pos == self.partial_act.len() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + // Test bookkeeping of partial fill + #[test] + fn partial_fill() { + let mut builder = ActBuilder::new(Act::One(EMPTY_ACT_ONE)); + + let remaining = builder.fill(&[1, 2, 3]); + assert_eq!(builder.partial_act.len(), ACT_ONE_TWO_LENGTH); + assert_eq!(builder.write_pos, 3); + assert!(!builder.is_finished()); + assert_eq!(remaining, &[]); + } + + // Test bookkeeping of exact fill + #[test] + fn exact_fill() { + let mut builder = ActBuilder::new(Act::One(EMPTY_ACT_ONE)); + + let input = [0; 50]; + let remaining = builder.fill(&input); + assert_eq!(builder.partial_act.len(), ACT_ONE_TWO_LENGTH); + assert_eq!(builder.write_pos, ACT_ONE_TWO_LENGTH); + assert!(builder.is_finished()); + assert_eq!(Act::from(builder).as_ref(), &input[..]); + assert_eq!(remaining, &[]); + } + + // Test bookkeeping of overfill + #[test] + fn over_fill() { + let mut builder = ActBuilder::new(Act::One(EMPTY_ACT_ONE)); + + let input = [0; 51]; + let remaining = builder.fill(&input); + + assert_eq!(builder.partial_act.len(), ACT_ONE_TWO_LENGTH); + assert_eq!(builder.write_pos, ACT_ONE_TWO_LENGTH); + assert!(builder.is_finished()); + assert_eq!(Act::from(builder).as_ref(), &input[..50]); + assert_eq!(remaining, &[0]); + } + + // Converting an unfinished ActBuilder panics + #[test] + #[should_panic(expected="as")] + fn convert_not_finished_panics() { + let builder = ActBuilder::new(Act::One(EMPTY_ACT_ONE)); + let _should_panic = Act::from(builder); + } +} + diff --git a/lightning/src/ln/peers/handshake/mod.rs b/lightning/src/ln/peers/handshake/mod.rs index 571a912674c..7443207c137 100644 --- a/lightning/src/ln/peers/handshake/mod.rs +++ b/lightning/src/ln/peers/handshake/mod.rs @@ -7,6 +7,7 @@ use bitcoin::secp256k1::{PublicKey, SecretKey}; use ln::peers::conduit::Conduit; use ln::peers::handshake::states::{HandshakeState, IHandshakeState}; +mod acts; mod states; /// Object for managing handshakes. @@ -46,7 +47,7 @@ impl PeerHandshake { self.state = Some(next_state); self.ready_for_process = true; - response_vec_opt.unwrap() + response_vec_opt.unwrap().to_vec() } /// Process act dynamically @@ -61,7 +62,12 @@ impl PeerHandshake { assert!(self.ready_for_process); let cur_state = self.state.take().unwrap(); - let (act_opt, mut next_state) = cur_state.next(input)?; + // Convert the Act to a Vec before passing it back. Next patch removes this in favor + // of passing back a slice. + let (act_opt, mut next_state) = match cur_state.next(input)? { + (Some(act), next_state) => (Some(act.to_vec()), next_state), + (None, next_state) => (None, next_state) + }; let result = match next_state { HandshakeState::Complete(ref mut conduit_and_pubkey) => { diff --git a/lightning/src/ln/peers/handshake/states.rs b/lightning/src/ln/peers/handshake/states.rs index 7eca40f68c8..a2b64928bfc 100644 --- a/lightning/src/ln/peers/handshake/states.rs +++ b/lightning/src/ln/peers/handshake/states.rs @@ -6,9 +6,7 @@ use bitcoin::secp256k1::{SecretKey, PublicKey}; use ln::peers::{chacha, hkdf}; use ln::peers::conduit::{Conduit, SymmetricKey}; - -const ACT_ONE_TWO_LENGTH: usize = 50; -const ACT_THREE_LENGTH: usize = 66; +use ln::peers::handshake::acts::{Act, ActBuilder, EMPTY_ACT_ONE, EMPTY_ACT_TWO, EMPTY_ACT_THREE}; type ChainingKey = [u8; 32]; @@ -34,7 +32,7 @@ pub(super) enum HandshakeState { // Trait for all individual states to implement that ensure HandshakeState::next() can // delegate to a common function signature. pub(super) trait IHandshakeState { - fn next(self, input: &[u8]) -> Result<(Option>, HandshakeState), String>; + fn next(self, input: &[u8]) -> Result<(Option, HandshakeState), String>; } // Enum dispatch for state machine. Single public interface can statically dispatch to all states @@ -48,7 +46,7 @@ impl HandshakeState { } impl IHandshakeState for HandshakeState { - fn next(self, input: &[u8]) -> Result<(Option>, HandshakeState), String> { + fn next(self, input: &[u8]) -> Result<(Option, HandshakeState), String> { match self { HandshakeState::InitiatorStarting(state) => { state.next(input) }, HandshakeState::ResponderAwaitingActOne(state) => { state.next(input) }, @@ -77,7 +75,7 @@ pub(super) struct ResponderAwaitingActOneState { responder_ephemeral_public_key: PublicKey, chaining_key: Sha256, hash: Sha256, - read_buffer: Vec + act_one_builder: ActBuilder } // Handshake state of the Initiator prior to receiving Act 2 @@ -88,7 +86,7 @@ pub(super) struct InitiatorAwaitingActTwoState { responder_static_public_key: PublicKey, chaining_key: ChainingKey, hash: Sha256, - read_buffer: Vec + act_two_builder: ActBuilder } // Handshake state of the Responder prior to receiving Act 3 @@ -97,7 +95,7 @@ pub(super) struct ResponderAwaitingActThreeState { responder_ephemeral_private_key: SecretKey, chaining_key: ChainingKey, temporary_key: [u8; 32], - read_buffer: Vec + act_three_builder: ActBuilder } impl InitiatorStartingState { @@ -119,7 +117,7 @@ impl InitiatorStartingState { impl IHandshakeState for InitiatorStartingState { // https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#act-one (sender) - fn next(self, input: &[u8]) -> Result<(Option>, HandshakeState), String> { + fn next(self, input: &[u8]) -> Result<(Option, HandshakeState), String> { if input.len() > 0 { return Err("first call for initiator must be empty".to_string()); @@ -134,16 +132,18 @@ impl IHandshakeState for InitiatorStartingState { let hash = self.hash; // serialize act one - let (act_one, hash, chaining_key, _) = calculate_act_message( + let mut act_one = EMPTY_ACT_ONE; + let (hash, chaining_key, _) = calculate_act_message( &initiator_ephemeral_private_key, &initiator_ephemeral_public_key, &responder_static_public_key, chaining_key.into_inner(), hash, + &mut act_one ); Ok(( - Some(act_one.to_vec()), + Some(Act::One(act_one)), HandshakeState::InitiatorAwaitingActTwo(InitiatorAwaitingActTwoState { initiator_static_private_key, initiator_static_public_key, @@ -151,7 +151,7 @@ impl IHandshakeState for InitiatorStartingState { responder_static_public_key, chaining_key, hash, - read_buffer: Vec::new() + act_two_builder: ActBuilder::new(Act::Two(EMPTY_ACT_TWO)) }) )) } @@ -169,7 +169,7 @@ impl ResponderAwaitingActOneState { responder_ephemeral_public_key, chaining_key, hash, - read_buffer: Vec::new() + act_one_builder: ActBuilder::new(Act::One(EMPTY_ACT_ONE)) } } } @@ -177,18 +177,19 @@ impl ResponderAwaitingActOneState { impl IHandshakeState for ResponderAwaitingActOneState { // https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#act-one (receiver) // https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#act-two (sender) - fn next(self, input: &[u8]) -> Result<(Option>, HandshakeState), String> { - let mut read_buffer = self.read_buffer; - read_buffer.extend_from_slice(input); + fn next(self, input: &[u8]) -> Result<(Option, HandshakeState), String> { + let mut act_one_builder = self.act_one_builder; + let remaining = act_one_builder.fill(input); // Any payload larger than ACT_ONE_TWO_LENGTH indicates a bad peer since initiator data // is required to generate act3 (so it can't come before we transition) - if read_buffer.len() > ACT_ONE_TWO_LENGTH { + if remaining.len() != 0 { return Err("Act One too large".to_string()); } // In the event of a partial fill, stay in the same state and wait for more data - if read_buffer.len() < ACT_ONE_TWO_LENGTH { + if !act_one_builder.is_finished() { + assert_eq!(remaining.len(), 0); return Ok(( None, HandshakeState::ResponderAwaitingActOne(Self { @@ -197,41 +198,43 @@ impl IHandshakeState for ResponderAwaitingActOneState { responder_ephemeral_public_key: self.responder_ephemeral_public_key, chaining_key: self.chaining_key, hash: self.hash, - read_buffer + act_one_builder }) )); } - let hash = self.hash; let responder_static_private_key = self.responder_static_private_key; let chaining_key = self.chaining_key; let responder_ephemeral_private_key = self.responder_ephemeral_private_key; let responder_ephemeral_public_key = self.responder_ephemeral_public_key; + let act_one = Act::from(act_one_builder); let (initiator_ephemeral_public_key, hash, chaining_key, _) = process_act_message( - &mut read_buffer, + &act_one, &responder_static_private_key, chaining_key.into_inner(), hash, )?; - let (act_two, hash, chaining_key, temporary_key) = calculate_act_message( + let mut act_two = EMPTY_ACT_TWO; + let (hash, chaining_key, temporary_key) = calculate_act_message( &responder_ephemeral_private_key, &responder_ephemeral_public_key, &initiator_ephemeral_public_key, chaining_key, hash, + &mut act_two ); Ok(( - Some(act_two), + Some(Act::Two(act_two)), HandshakeState::ResponderAwaitingActThree(ResponderAwaitingActThreeState { hash, responder_ephemeral_private_key, chaining_key, temporary_key, - read_buffer + act_three_builder: ActBuilder::new(Act::Three(EMPTY_ACT_THREE)) }) )) } @@ -240,18 +243,19 @@ impl IHandshakeState for ResponderAwaitingActOneState { impl IHandshakeState for InitiatorAwaitingActTwoState { // https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#act-two (receiver) // https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#act-three (sender) - fn next(self, input: &[u8]) -> Result<(Option>, HandshakeState), String> { - let mut read_buffer = self.read_buffer; - read_buffer.extend_from_slice(input); + fn next(self, input: &[u8]) -> Result<(Option, HandshakeState), String> { + let mut act_two_builder = self.act_two_builder; + let remaining = act_two_builder.fill(input); // Any payload larger than ACT_ONE_TWO_LENGTH indicates a bad peer since responder data // is required to generate post-authentication messages (so it can't come before we transition) - if read_buffer.len() > ACT_ONE_TWO_LENGTH { + if remaining.len() != 0 { return Err("Act Two too large".to_string()); } // In the event of a partial fill, stay in the same state and wait for more data - if read_buffer.len() < ACT_ONE_TWO_LENGTH { + if !act_two_builder.is_finished() { + assert_eq!(remaining.len(), 0); return Ok(( None, HandshakeState::InitiatorAwaitingActTwo(Self { @@ -261,7 +265,7 @@ impl IHandshakeState for InitiatorAwaitingActTwoState { responder_static_public_key: self.responder_static_public_key, chaining_key: self.chaining_key, hash: self.hash, - read_buffer + act_two_builder }) )); } @@ -272,9 +276,10 @@ impl IHandshakeState for InitiatorAwaitingActTwoState { let responder_static_public_key = self.responder_static_public_key; let hash = self.hash; let chaining_key = self.chaining_key; + let act_two = Act::from(act_two_builder); let (responder_ephemeral_public_key, hash, chaining_key, temporary_key) = process_act_message( - &mut read_buffer, + &act_two, &initiator_ephemeral_private_key, chaining_key, hash, @@ -304,14 +309,12 @@ impl IHandshakeState for InitiatorAwaitingActTwoState { let conduit = Conduit::new(sending_key, receiving_key, chaining_key); // Send m = 0 || c || t over the network buffer - let mut act_three = Vec::with_capacity(ACT_THREE_LENGTH); - act_three.extend(&[0]); - act_three.extend(&tagged_encrypted_pubkey); - act_three.extend(&authentication_tag); - assert_eq!(act_three.len(), ACT_THREE_LENGTH); + let mut act_three = EMPTY_ACT_THREE; + act_three[1..50].copy_from_slice(&tagged_encrypted_pubkey); + act_three[50..].copy_from_slice(&authentication_tag); Ok(( - Some(act_three), + Some(Act::Three(act_three)), HandshakeState::Complete(Some((conduit, responder_static_public_key))) )) } @@ -319,12 +322,13 @@ impl IHandshakeState for InitiatorAwaitingActTwoState { impl IHandshakeState for ResponderAwaitingActThreeState { // https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#act-three (receiver) - fn next(self, input: &[u8]) -> Result<(Option>, HandshakeState), String> { - let mut read_buffer = self.read_buffer; - read_buffer.extend_from_slice(input); + fn next(self, input: &[u8]) -> Result<(Option, HandshakeState), String> { + let mut act_three_builder = self.act_three_builder; + let remaining = act_three_builder.fill(input); // In the event of a partial fill, stay in the same state and wait for more data - if read_buffer.len() < ACT_THREE_LENGTH { + if !act_three_builder.is_finished() { + assert_eq!(remaining.len(), 0); return Ok(( None, HandshakeState::ResponderAwaitingActThree(Self { @@ -332,7 +336,7 @@ impl IHandshakeState for ResponderAwaitingActThreeState { responder_ephemeral_private_key: self.responder_ephemeral_private_key, chaining_key: self.chaining_key, temporary_key: self.temporary_key, - read_buffer + act_three_builder }) )); } @@ -343,7 +347,7 @@ impl IHandshakeState for ResponderAwaitingActThreeState { let chaining_key = self.chaining_key; // 1. Read exactly 66 bytes from the network buffer - let act_three_bytes: Vec = read_buffer.drain(..ACT_THREE_LENGTH).collect(); + let act_three_bytes = Act::from(act_three_builder); // 2. Parse the read message (m) into v, c, and t let version = act_three_bytes[0]; @@ -385,10 +389,7 @@ impl IHandshakeState for ResponderAwaitingActThreeState { // Any remaining data in the read buffer would be encrypted, so transfer ownership // to the Conduit for future use. - if read_buffer.len() > 0 { // have we received more data still? - conduit.read(&read_buffer[..]); - read_buffer.drain(..); - } + conduit.read(remaining); Ok(( None, @@ -417,7 +418,7 @@ fn handshake_state_initialization(responder_static_public_key: &PublicKey) -> (S // https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#act-one (sender) // https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#act-two (sender) -fn calculate_act_message(local_private_ephemeral_key: &SecretKey, local_public_ephemeral_key: &PublicKey, remote_public_key: &PublicKey, chaining_key: ChainingKey, hash: Sha256) -> (Vec, Sha256, SymmetricKey, SymmetricKey) { +fn calculate_act_message(local_private_ephemeral_key: &SecretKey, local_public_ephemeral_key: &PublicKey, remote_public_key: &PublicKey, chaining_key: ChainingKey, hash: Sha256, act_out: &mut [u8]) -> (Sha256, SymmetricKey, SymmetricKey) { // 1. e = generateKey() (passed in) // 2. h = SHA-256(h || e.pub.serializeCompressed()) let serialized_local_public_key = local_public_ephemeral_key.serialize(); @@ -439,22 +440,18 @@ fn calculate_act_message(local_private_ephemeral_key: &SecretKey, local_public_e let hash = concat_then_sha256!(hash, tagged_ciphertext); // Send m = 0 || e.pub.serializeCompressed() || c - let mut act = Vec::with_capacity(ACT_ONE_TWO_LENGTH); - act.extend(&[0]); - act.extend_from_slice(&serialized_local_public_key); - act.extend(&tagged_ciphertext); - assert_eq!(act.len(), ACT_ONE_TWO_LENGTH); - (act, hash, chaining_key, temporary_key) + act_out[1..34].copy_from_slice(&serialized_local_public_key); + act_out[34..50].copy_from_slice(&tagged_ciphertext); + + (hash, chaining_key, temporary_key) } // Due to the very high similarity of acts 1 and 2, this method is used to process both // https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#act-one (receiver) // https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#act-two (receiver) -fn process_act_message(read_buffer: &mut Vec, local_private_key: &SecretKey, chaining_key: ChainingKey, hash: Sha256) -> Result<(PublicKey, Sha256, SymmetricKey, SymmetricKey), String> { +fn process_act_message(act_bytes: &[u8], local_private_key: &SecretKey, chaining_key: ChainingKey, hash: Sha256) -> Result<(PublicKey, Sha256, SymmetricKey, SymmetricKey), String> { // 1. Read exactly 50 bytes from the network buffer - let act_bytes: Vec = read_buffer.drain(..ACT_ONE_TWO_LENGTH).collect(); - // 2.Parse the read message (m) into v, re, and c let version = act_bytes[0]; let ephemeral_public_key_bytes = &act_bytes[1..34]; @@ -581,7 +578,7 @@ mod test { let test_ctx = TestCtx::new(); let (act1, awaiting_act_two_state) = do_next_or_panic!(test_ctx.initiator, &[]); - assert_eq!(act1, test_ctx.valid_act1); + assert_eq!(act1.as_ref(), test_ctx.valid_act1.as_slice()); assert_matches!(awaiting_act_two_state, InitiatorAwaitingActTwo(_)); } @@ -600,7 +597,7 @@ mod test { let test_ctx = TestCtx::new(); let (act2, awaiting_act_three_state) = test_ctx.responder.next(&test_ctx.valid_act1).unwrap(); - assert_eq!(act2.unwrap(), test_ctx.valid_act2); + assert_eq!(act2.unwrap().as_ref(), test_ctx.valid_act2.as_slice()); assert_matches!(awaiting_act_three_state, ResponderAwaitingActThree(_)); } @@ -687,7 +684,7 @@ mod test { panic!(); }; - assert_eq!(act3, test_ctx.valid_act3); + assert_eq!(act3.as_ref(), test_ctx.valid_act3.as_slice()); assert_eq!(remote_pubkey, test_ctx.responder_static_public_key); assert_eq!(0, conduit.decryptor.read_buffer_length()); } From 753b51784d5c397db11d7102eb0a32585fe91c12 Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Wed, 26 Aug 2020 11:12:16 -0700 Subject: [PATCH 40/72] perf: Remove Vec from chacha::encrypt/decrypt In order to limit perf/allocation changes from master, remove the last use of Vec from the handshake code. The decrypt/encrypt code now takes an out parameter to place the decrypted bytes. All handshake users know the fixed-size of the output decryption so they can use arrays. The Conduit code will still allocate a Vec based on the input message and pass it through. After this patch, the handshake code is now Vec-less and copies are minimized. --- lightning/src/ln/peers/chacha.rs | 23 +++++++--------- lightning/src/ln/peers/conduit.rs | 9 +++--- lightning/src/ln/peers/handshake/states.rs | 32 ++++++++++------------ 3 files changed, 29 insertions(+), 35 deletions(-) diff --git a/lightning/src/ln/peers/chacha.rs b/lightning/src/ln/peers/chacha.rs index 4e8d948706f..76742bad452 100644 --- a/lightning/src/ln/peers/chacha.rs +++ b/lightning/src/ln/peers/chacha.rs @@ -3,21 +3,18 @@ use util::chacha20poly1305rfc::ChaCha20Poly1305RFC; pub const TAG_SIZE: usize = 16; -pub fn encrypt(key: &[u8], nonce: u64, associated_data: &[u8], plaintext: &[u8]) -> Vec { +pub fn encrypt(key: &[u8], nonce: u64, associated_data: &[u8], plaintext: &[u8], ciphertext_out: &mut [u8]) { let mut nonce_bytes = [0; 12]; nonce_bytes[4..].copy_from_slice(&byte_utils::le64_to_array(nonce)); let mut chacha = ChaCha20Poly1305RFC::new(key, &nonce_bytes, associated_data); - let mut ciphertext = vec![0u8; plaintext.len()]; let mut authentication_tag = [0u8; 16]; - chacha.encrypt(plaintext, &mut ciphertext, &mut authentication_tag); + chacha.encrypt(plaintext, &mut ciphertext_out[..plaintext.len()], &mut authentication_tag); - let mut tagged_ciphertext = ciphertext; - tagged_ciphertext.extend_from_slice(&authentication_tag); - tagged_ciphertext + ciphertext_out[plaintext.len()..].copy_from_slice(&authentication_tag); } -pub fn decrypt(key: &[u8], nonce: u64, associated_data: &[u8], tagged_ciphertext: &[u8]) -> Result, String> { +pub fn decrypt(key: &[u8], nonce: u64, associated_data: &[u8], tagged_ciphertext: &[u8], plaintext_out: &mut [u8]) -> Result<(), String> { let mut nonce_bytes = [0; 12]; nonce_bytes[4..].copy_from_slice(&byte_utils::le64_to_array(nonce)); @@ -27,14 +24,14 @@ pub fn decrypt(key: &[u8], nonce: u64, associated_data: &[u8], tagged_ciphertext } let end_index = length - 16; let ciphertext = &tagged_ciphertext[0..end_index]; - let authentication_tag = &tagged_ciphertext[end_index..length]; + let authentication_tag = &tagged_ciphertext[end_index..]; let mut chacha = ChaCha20Poly1305RFC::new(key, &nonce_bytes, associated_data); - let mut plaintext = vec![0u8; length - 16]; - let success = chacha.decrypt(ciphertext, &mut plaintext, authentication_tag); - if success { - Ok(plaintext.to_vec()) - } else { + let success = chacha.decrypt(ciphertext, plaintext_out, authentication_tag); + + if !success { Err("invalid hmac".to_string()) + } else { + Ok(()) } } diff --git a/lightning/src/ln/peers/conduit.rs b/lightning/src/ln/peers/conduit.rs index 807a06ddd12..d820d0497b8 100644 --- a/lightning/src/ln/peers/conduit.rs +++ b/lightning/src/ln/peers/conduit.rs @@ -128,10 +128,10 @@ impl Encryptor { let mut ciphertext = vec![0u8; TAGGED_MESSAGE_LENGTH_HEADER_SIZE + length as usize + chacha::TAG_SIZE]; - ciphertext[0..TAGGED_MESSAGE_LENGTH_HEADER_SIZE].copy_from_slice(&chacha::encrypt(&self.sending_key, self.sending_nonce as u64, &[0; 0], &length_bytes)); + chacha::encrypt(&self.sending_key, self.sending_nonce as u64, &[0; 0], &length_bytes, &mut ciphertext[..TAGGED_MESSAGE_LENGTH_HEADER_SIZE]); self.increment_nonce(); - ciphertext[TAGGED_MESSAGE_LENGTH_HEADER_SIZE..].copy_from_slice(&chacha::encrypt(&self.sending_key, self.sending_nonce as u64, &[0; 0], buffer)); + &chacha::encrypt(&self.sending_key, self.sending_nonce as u64, &[0; 0], buffer, &mut ciphertext[TAGGED_MESSAGE_LENGTH_HEADER_SIZE..]); self.increment_nonce(); ciphertext @@ -185,7 +185,7 @@ impl Decryptor { let encrypted_length = &buffer[0..TAGGED_MESSAGE_LENGTH_HEADER_SIZE]; let mut length_bytes = [0u8; MESSAGE_LENGTH_HEADER_SIZE]; - length_bytes.copy_from_slice(&chacha::decrypt(&self.receiving_key, self.receiving_nonce as u64, &[0; 0], encrypted_length)?); + chacha::decrypt(&self.receiving_key, self.receiving_nonce as u64, &[0; 0], encrypted_length, &mut length_bytes)?; self.increment_nonce(); @@ -203,8 +203,9 @@ impl Decryptor { self.pending_message_length = None; let encrypted_message = &buffer[TAGGED_MESSAGE_LENGTH_HEADER_SIZE..message_end_index]; + let mut message = vec![0u8; message_length]; - let message = chacha::decrypt(&self.receiving_key, self.receiving_nonce as u64, &[0; 0], encrypted_message)?; + chacha::decrypt(&self.receiving_key, self.receiving_nonce as u64, &[0; 0], encrypted_message, &mut message)?; self.increment_nonce(); diff --git a/lightning/src/ln/peers/handshake/states.rs b/lightning/src/ln/peers/handshake/states.rs index a2b64928bfc..6b0a7be63b9 100644 --- a/lightning/src/ln/peers/handshake/states.rs +++ b/lightning/src/ln/peers/handshake/states.rs @@ -285,12 +285,14 @@ impl IHandshakeState for InitiatorAwaitingActTwoState { hash, )?; + let mut act_three = EMPTY_ACT_THREE; + // start serializing act three // 1. c = encryptWithAD(temp_k2, 1, h, s.pub.serializeCompressed()) - let tagged_encrypted_pubkey = chacha::encrypt(&temporary_key, 1, &hash, &initiator_static_public_key.serialize()); + chacha::encrypt(&temporary_key, 1, &hash, &initiator_static_public_key.serialize(), &mut act_three[1..50]); // 2. h = SHA-256(h || c) - let hash = concat_then_sha256!(hash, tagged_encrypted_pubkey); + let hash = concat_then_sha256!(hash, act_three[1..50]); // 3. se = ECDH(s.priv, re) let ecdh = ecdh(&initiator_static_private_key, &responder_ephemeral_public_key); @@ -299,7 +301,7 @@ impl IHandshakeState for InitiatorAwaitingActTwoState { let (chaining_key, temporary_key) = hkdf::derive(&chaining_key, &ecdh); // 5. t = encryptWithAD(temp_k3, 0, h, zero) - let authentication_tag = chacha::encrypt(&temporary_key, 0, &hash, &[0; 0]); + chacha::encrypt(&temporary_key, 0, &hash, &[0; 0], &mut act_three[50..]); // 6. sk, rk = HKDF(ck, zero) let (sending_key, receiving_key) = hkdf::derive(&chaining_key, &[0; 0]); @@ -308,11 +310,6 @@ impl IHandshakeState for InitiatorAwaitingActTwoState { // - done by Conduit let conduit = Conduit::new(sending_key, receiving_key, chaining_key); - // Send m = 0 || c || t over the network buffer - let mut act_three = EMPTY_ACT_THREE; - act_three[1..50].copy_from_slice(&tagged_encrypted_pubkey); - act_three[50..].copy_from_slice(&authentication_tag); - Ok(( Some(Act::Three(act_three)), HandshakeState::Complete(Some((conduit, responder_static_public_key))) @@ -352,7 +349,7 @@ impl IHandshakeState for ResponderAwaitingActThreeState { // 2. Parse the read message (m) into v, c, and t let version = act_three_bytes[0]; let tagged_encrypted_pubkey = &act_three_bytes[1..50]; - let chacha_tag = &act_three_bytes[50..66]; + let chacha_tag = &act_three_bytes[50..]; // 3. If v is an unrecognized handshake version, then the responder MUST abort the connection attempt. if version != 0 { @@ -361,8 +358,9 @@ impl IHandshakeState for ResponderAwaitingActThreeState { } // 4. rs = decryptWithAD(temp_k2, 1, h, c) - let remote_pubkey_vec = chacha::decrypt(&temporary_key, 1, &hash, &tagged_encrypted_pubkey)?; - let initiator_pubkey = if let Ok(public_key) = PublicKey::from_slice(remote_pubkey_vec.as_slice()) { + let mut remote_pubkey = [0; 33]; + chacha::decrypt(&temporary_key, 1, &hash, &tagged_encrypted_pubkey, &mut remote_pubkey)?; + let initiator_pubkey = if let Ok(public_key) = PublicKey::from_slice(&remote_pubkey) { public_key } else { return Err("invalid remote public key".to_string()); @@ -378,7 +376,7 @@ impl IHandshakeState for ResponderAwaitingActThreeState { let (chaining_key, temporary_key) = hkdf::derive(&chaining_key, &ecdh); // 8. p = decryptWithAD(temp_k3, 0, h, t) - let _tag_check = chacha::decrypt(&temporary_key, 0, &hash, &chacha_tag)?; + chacha::decrypt(&temporary_key, 0, &hash, &chacha_tag, &mut [0; 0])?; // 9. rk, sk = HKDF(ck, zero) let (receiving_key, sending_key) = hkdf::derive(&chaining_key, &[0; 0]); @@ -434,15 +432,13 @@ fn calculate_act_message(local_private_ephemeral_key: &SecretKey, local_public_e // 5. ACT1: c = encryptWithAD(temp_k1, 0, h, zero) // 5. ACT2: c = encryptWithAD(temp_k2, 0, h, zero) - let tagged_ciphertext = chacha::encrypt(&temporary_key, 0, &hash, &[0; 0]); + chacha::encrypt(&temporary_key, 0, &hash, &[0; 0], &mut act_out[34..]); // 6. h = SHA-256(h || c) - let hash = concat_then_sha256!(hash, tagged_ciphertext); + let hash = concat_then_sha256!(hash, &act_out[34..]); // Send m = 0 || e.pub.serializeCompressed() || c - act_out[1..34].copy_from_slice(&serialized_local_public_key); - act_out[34..50].copy_from_slice(&tagged_ciphertext); (hash, chaining_key, temporary_key) } @@ -455,7 +451,7 @@ fn process_act_message(act_bytes: &[u8], local_private_key: &SecretKey, chaining // 2.Parse the read message (m) into v, re, and c let version = act_bytes[0]; let ephemeral_public_key_bytes = &act_bytes[1..34]; - let chacha_tag = &act_bytes[34..50]; + let chacha_tag = &act_bytes[34..]; let ephemeral_public_key = if let Ok(public_key) = PublicKey::from_slice(&ephemeral_public_key_bytes) { public_key @@ -481,7 +477,7 @@ fn process_act_message(act_bytes: &[u8], local_private_key: &SecretKey, chaining let (chaining_key, temporary_key) = hkdf::derive(&chaining_key, &ecdh); // 7. p = decryptWithAD(temp_k1, 0, h, c) - let _tag_check = chacha::decrypt(&temporary_key, 0, &hash, &chacha_tag)?; + chacha::decrypt(&temporary_key, 0, &hash, &chacha_tag, &mut [0; 0])?; // 8. h = SHA-256(h || c) let hash = concat_then_sha256!(hash, chacha_tag); From a7a25ca431f7ac05796d16c378a675a64c92d2c3 Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Wed, 2 Sep 2020 09:41:15 -0700 Subject: [PATCH 41/72] review: Non-controversial renames, nits, small fixups Clean up small items based on review comments that are expected to be non-controversial so they can all go in one commit. --- fuzz/src/peer_crypt.rs | 15 +++++- lightning/src/ln/peers/handler.rs | 12 ++--- lightning/src/ln/peers/handshake/acts.rs | 55 +++++++++++----------- lightning/src/ln/peers/handshake/mod.rs | 37 +++++++-------- lightning/src/ln/peers/handshake/states.rs | 30 ++++++++---- lightning/src/ln/peers/hkdf.rs | 8 ++-- 6 files changed, 90 insertions(+), 67 deletions(-) diff --git a/fuzz/src/peer_crypt.rs b/fuzz/src/peer_crypt.rs index d21c8009898..7d832122584 100644 --- a/fuzz/src/peer_crypt.rs +++ b/fuzz/src/peer_crypt.rs @@ -85,6 +85,9 @@ struct TestCtx { } impl TestCtx { + // At the completion of this call each handshake has the following state: + // initiator_handshake: HandshakeState::InitiatorStarting + // responder_handshake: HandshakeState::ResponderAwaitingActOne fn make(generator: &mut FuzzGen) -> Result { let curve = secp256k1::Secp256k1::new(); @@ -98,6 +101,14 @@ impl TestCtx { let mut initiator_handshake = PeerHandshake::new_outbound(&initiator_static_private_key, &responder_static_public_key, &initiator_ephemeral_private_key); let act1 = initiator_handshake.set_up_outbound(); + + // Sanity checks around initialization to catch errors before we get to the fuzzing + + // act 1 is the correct size + assert_eq!(act1.len(), 50); + + // act 1 has the correct version + assert_eq!(act1[0], 0); let responder_handshake = PeerHandshake::new_inbound(&responder_static_private_key, &responder_ephemeral_private_key); Ok(TestCtx { @@ -146,8 +157,8 @@ fn do_conduit_tests(generator: &mut FuzzGen, initiator_conduit: &mut Conduit, re } } -// This test completes a valid handshake based on "random" private keys and then sends variable -// length encrypted messages between two conduits to validate that they can communicate. +// This test completes a valid handshake based on fuzzer-provided private keys and then sends +// variable length encrypted messages between two conduits to validate that they can communicate. #[inline] fn do_completed_handshake_test(generator: &mut FuzzGen) -> Result<(), GeneratorFinishedError> { let mut test_ctx = TestCtx::make(generator)?; diff --git a/lightning/src/ln/peers/handler.rs b/lightning/src/ln/peers/handler.rs index 64c433e0624..25ecfcaaadd 100644 --- a/lightning/src/ln/peers/handler.rs +++ b/lightning/src/ln/peers/handler.rs @@ -149,17 +149,17 @@ impl PeerState { }, // Otherwise, handshake may or may not be complete depending on whether or not // we receive a (conduit, pubkey) - Ok((response_vec_opt, conduit_and_remote_pubkey_opt)) => { + Ok((response_vec_option, conduit_and_remote_static_public_key_option)) => { // Any response generated by the handshake sequence is put into the response buffer - if let Some(response_vec) = response_vec_opt { + if let Some(response_vec) = response_vec_option { mutable_response_buffer.push_back(response_vec); } - // if process_act() returns the conduit and remote_pubkey the handshake - // is complete - if let Some((conduit, remote_pubkey)) = conduit_and_remote_pubkey_opt { - (Some(PeerState::Connected(conduit)), PeerDataProcessingDecision::CompleteHandshake(remote_pubkey)) + // if process_act() returns the conduit and remote static public key (node id) + // the handshake is complete + if let Some((conduit, remote_static_public_key)) = conduit_and_remote_static_public_key_option { + (Some(PeerState::Connected(conduit)), PeerDataProcessingDecision::CompleteHandshake(remote_static_public_key)) } else { (None, PeerDataProcessingDecision::Continue) } diff --git a/lightning/src/ln/peers/handshake/acts.rs b/lightning/src/ln/peers/handshake/acts.rs index 81591be6c50..fa7a2ece5a6 100644 --- a/lightning/src/ln/peers/handshake/acts.rs +++ b/lightning/src/ln/peers/handshake/acts.rs @@ -5,13 +5,14 @@ use std::{cmp, ops}; -pub const ACT_ONE_TWO_LENGTH: usize = 50; +pub const ACT_ONE_LENGTH: usize = 50; +pub const ACT_TWO_LENGTH: usize = 50; pub const ACT_THREE_LENGTH: usize = 66; -pub const EMPTY_ACT_ONE: ActOne = [0; ACT_ONE_TWO_LENGTH]; -pub const EMPTY_ACT_TWO: ActTwo = EMPTY_ACT_ONE; +pub const EMPTY_ACT_ONE: ActOne = [0; ACT_ONE_LENGTH]; +pub const EMPTY_ACT_TWO: ActTwo = [0; ACT_TWO_LENGTH]; pub const EMPTY_ACT_THREE: ActThree = [0; ACT_THREE_LENGTH]; -type ActOne = [u8; ACT_ONE_TWO_LENGTH]; -type ActTwo = ActOne; +type ActOne = [u8; ACT_ONE_LENGTH]; +type ActTwo = [u8; ACT_TWO_LENGTH]; type ActThree = [u8; ACT_THREE_LENGTH]; /// Wrapper for any act message @@ -64,19 +65,6 @@ impl AsRef<[u8]> for Act { } } -// Simple fill implementation for both almost-identical structs to deduplicate code -// $act: Act[One|Two|Three], $input: &[u8]; returns &[u8] of remaining input that was not processed -macro_rules! fill_impl { - ($act:expr, $write_pos:expr, $input:expr) => {{ - let fill_amount = cmp::min($act.len() - $write_pos, $input.len()); - - $act[$write_pos..$write_pos + fill_amount].copy_from_slice(&$input[..fill_amount]); - - $write_pos += fill_amount; - &$input[fill_amount..] - }} -} - /// Light wrapper around an Act that allows multiple fill() calls before finally /// converting to an Act via Act::from(act_builder). Handles all of the bookkeeping /// and edge cases of the array fill @@ -96,15 +84,28 @@ impl ActBuilder { /// Fills the Act with bytes from input and returns the unprocessed bytes pub(super) fn fill<'a>(&mut self, input: &'a [u8]) -> &'a [u8] { + // Simple fill implementation for both almost-identical structs to deduplicate code + // $act: Act[One|Two|Three], $input: &[u8]; returns &[u8] of remaining input that was not processed + macro_rules! fill_act_content { + ($act:expr, $write_pos:expr, $input:expr) => {{ + let fill_amount = cmp::min($act.len() - $write_pos, $input.len()); + + $act[$write_pos..$write_pos + fill_amount].copy_from_slice(&$input[..fill_amount]); + + $write_pos += fill_amount; + &$input[fill_amount..] + }} + } + match &mut self.partial_act { &mut Act::One(ref mut act) => { - fill_impl!(act, self.write_pos, input) + fill_act_content!(act, self.write_pos, input) } &mut Act::Two(ref mut act) => { - fill_impl!(act, self.write_pos, input) + fill_act_content!(act, self.write_pos, input) } &mut Act::Three(ref mut act) => { - fill_impl!(act, self.write_pos, input) + fill_act_content!(act, self.write_pos, input) } } } @@ -125,7 +126,7 @@ mod tests { let mut builder = ActBuilder::new(Act::One(EMPTY_ACT_ONE)); let remaining = builder.fill(&[1, 2, 3]); - assert_eq!(builder.partial_act.len(), ACT_ONE_TWO_LENGTH); + assert_eq!(builder.partial_act.len(), ACT_ONE_LENGTH); assert_eq!(builder.write_pos, 3); assert!(!builder.is_finished()); assert_eq!(remaining, &[]); @@ -138,8 +139,8 @@ mod tests { let input = [0; 50]; let remaining = builder.fill(&input); - assert_eq!(builder.partial_act.len(), ACT_ONE_TWO_LENGTH); - assert_eq!(builder.write_pos, ACT_ONE_TWO_LENGTH); + assert_eq!(builder.partial_act.len(), ACT_ONE_LENGTH); + assert_eq!(builder.write_pos, ACT_ONE_LENGTH); assert!(builder.is_finished()); assert_eq!(Act::from(builder).as_ref(), &input[..]); assert_eq!(remaining, &[]); @@ -153,8 +154,8 @@ mod tests { let input = [0; 51]; let remaining = builder.fill(&input); - assert_eq!(builder.partial_act.len(), ACT_ONE_TWO_LENGTH); - assert_eq!(builder.write_pos, ACT_ONE_TWO_LENGTH); + assert_eq!(builder.partial_act.len(), ACT_ONE_LENGTH); + assert_eq!(builder.write_pos, ACT_ONE_LENGTH); assert!(builder.is_finished()); assert_eq!(Act::from(builder).as_ref(), &input[..50]); assert_eq!(remaining, &[0]); @@ -162,7 +163,7 @@ mod tests { // Converting an unfinished ActBuilder panics #[test] - #[should_panic(expected="as")] + #[should_panic(expected="assertion failed: act_builder.is_finished()")] fn convert_not_finished_panics() { let builder = ActBuilder::new(Act::One(EMPTY_ACT_ONE)); let _should_panic = Act::from(builder); diff --git a/lightning/src/ln/peers/handshake/mod.rs b/lightning/src/ln/peers/handshake/mod.rs index 7443207c137..d914b299518 100644 --- a/lightning/src/ln/peers/handshake/mod.rs +++ b/lightning/src/ln/peers/handshake/mod.rs @@ -14,40 +14,39 @@ mod states; /// Currently requires explicit ephemeral private key specification. pub struct PeerHandshake { state: Option, - ready_for_process: bool + ready_to_process: bool, } impl PeerHandshake { - /// Instantiate a new handshake with a node identity secret key and an ephemeral private key + /// Instantiate a handshake given the peer's static public key. The ephemeral private key MUST + /// generate a new session with strong cryptographic randomness. pub fn new_outbound(initiator_static_private_key: &SecretKey, responder_static_public_key: &PublicKey, initiator_ephemeral_private_key: &SecretKey) -> Self { let state = HandshakeState::new_initiator(initiator_static_private_key, responder_static_public_key, initiator_ephemeral_private_key); Self { state: Some(state), - ready_for_process: false + ready_to_process: false, } } - /// Instantiate a new handshake in anticipation of a peer's first handshake act + /// Instantiate a handshake in anticipation of a peer's first handshake act pub fn new_inbound(responder_static_private_key: &SecretKey, responder_ephemeral_private_key: &SecretKey) -> Self { Self { state: Some(HandshakeState::new_responder(responder_static_private_key, responder_ephemeral_private_key)), - ready_for_process: true + ready_to_process: true, } } /// Initializes the outbound handshake and provides the initial bytes to send to the responder pub fn set_up_outbound(&mut self) -> Vec { - assert!(!self.ready_for_process); - let cur_state = self.state.take().unwrap(); + assert!(!self.ready_to_process); + self.ready_to_process = true; // This transition does not have a failure path - let (response_vec_opt, next_state) = cur_state.next(&[]).unwrap(); - - self.state = Some(next_state); + let (response_vec_option, conduit_and_remote_static_public_key_option) = self.process_act(&[]).unwrap(); + assert!(conduit_and_remote_static_public_key_option.is_none()); - self.ready_for_process = true; - response_vec_opt.unwrap().to_vec() + response_vec_option.unwrap() } /// Process act dynamically @@ -59,21 +58,19 @@ impl PeerHandshake { /// `.0`: Byte vector containing the next act to send back to the peer per the handshake protocol /// `.1`: Some(Conduit, PublicKey) if the handshake was just processed to completion and messages can now be encrypted and decrypted pub fn process_act(&mut self, input: &[u8]) -> Result<(Option>, Option<(Conduit, PublicKey)>), String> { - assert!(self.ready_for_process); + assert!(self.ready_to_process); let cur_state = self.state.take().unwrap(); - // Convert the Act to a Vec before passing it back. Next patch removes this in favor - // of passing back a slice. - let (act_opt, mut next_state) = match cur_state.next(input)? { + let (act_option, mut next_state) = match cur_state.next(input)? { (Some(act), next_state) => (Some(act.to_vec()), next_state), (None, next_state) => (None, next_state) }; let result = match next_state { HandshakeState::Complete(ref mut conduit_and_pubkey) => { - Ok((act_opt, conduit_and_pubkey.take())) + Ok((act_option, conduit_and_pubkey.take())) }, - _ => { Ok((act_opt, None)) } + _ => { Ok((act_option, None)) } }; self.state = Some(next_state); @@ -140,7 +137,7 @@ mod test { // Test that the outbound needs to call set_up_outbound() before process_act() #[test] - #[should_panic(expected = "assertion failed: self.ready_for_process")] + #[should_panic(expected = "assertion failed: self.ready_to_process")] fn new_outbound_no_set_up_panics() { let curve = secp256k1::Secp256k1::new(); @@ -155,7 +152,7 @@ mod test { // Test that calling set_up_outbound() on the inbound panics #[test] - #[should_panic(expected = "assertion failed: !self.ready_for_process")] + #[should_panic(expected = "assertion failed: !self.ready_to_process")] fn new_inbound_calling_set_up_panics() { let inbound_static_private_key = SecretKey::from_slice(&[0x_21_u8; 32]).unwrap(); let inbound_ephemeral_private_key = SecretKey::from_slice(&[0x_22_u8; 32]).unwrap(); diff --git a/lightning/src/ln/peers/handshake/states.rs b/lightning/src/ln/peers/handshake/states.rs index 6b0a7be63b9..c3195976fc3 100644 --- a/lightning/src/ln/peers/handshake/states.rs +++ b/lightning/src/ln/peers/handshake/states.rs @@ -6,8 +6,9 @@ use bitcoin::secp256k1::{SecretKey, PublicKey}; use ln::peers::{chacha, hkdf}; use ln::peers::conduit::{Conduit, SymmetricKey}; -use ln::peers::handshake::acts::{Act, ActBuilder, EMPTY_ACT_ONE, EMPTY_ACT_TWO, EMPTY_ACT_THREE}; +use ln::peers::handshake::acts::{Act, ActBuilder, ACT_ONE_LENGTH, ACT_TWO_LENGTH, ACT_THREE_LENGTH, EMPTY_ACT_ONE, EMPTY_ACT_TWO, EMPTY_ACT_THREE}; +// Alias type to help differentiate between temporary key and chaining key when passing bytes around type ChainingKey = [u8; 32]; // Generate a SHA-256 hash from one or more elements concatenated together @@ -30,7 +31,8 @@ pub(super) enum HandshakeState { } // Trait for all individual states to implement that ensure HandshakeState::next() can -// delegate to a common function signature. +// delegate to a common function signature. May transition to the same state in the event there are +// not yet enough bytes to move forward with the handshake. pub(super) trait IHandshakeState { fn next(self, input: &[u8]) -> Result<(Option, HandshakeState), String>; } @@ -101,7 +103,7 @@ pub(super) struct ResponderAwaitingActThreeState { impl InitiatorStartingState { pub(crate) fn new(initiator_static_private_key: SecretKey, initiator_ephemeral_private_key: SecretKey, responder_static_public_key: PublicKey) -> Self { let initiator_static_public_key = private_key_to_public_key(&initiator_static_private_key); - let (hash, chaining_key) = handshake_state_initialization(&responder_static_public_key); + let (hash, chaining_key) = initialize_handshake_state(&responder_static_public_key); let initiator_ephemeral_public_key = private_key_to_public_key(&initiator_ephemeral_private_key); InitiatorStartingState { initiator_static_private_key, @@ -160,7 +162,7 @@ impl IHandshakeState for InitiatorStartingState { impl ResponderAwaitingActOneState { pub(crate) fn new(responder_static_private_key: SecretKey, responder_ephemeral_private_key: SecretKey) -> Self { let responder_static_public_key = private_key_to_public_key(&responder_static_private_key); - let (hash, chaining_key) = handshake_state_initialization(&responder_static_public_key); + let (hash, chaining_key) = initialize_handshake_state(&responder_static_public_key); let responder_ephemeral_public_key = private_key_to_public_key(&responder_ephemeral_private_key); ResponderAwaitingActOneState { @@ -181,7 +183,7 @@ impl IHandshakeState for ResponderAwaitingActOneState { let mut act_one_builder = self.act_one_builder; let remaining = act_one_builder.fill(input); - // Any payload larger than ACT_ONE_TWO_LENGTH indicates a bad peer since initiator data + // Any payload larger than ACT_ONE_LENGTH indicates a bad peer since initiator data // is required to generate act3 (so it can't come before we transition) if remaining.len() != 0 { return Err("Act One too large".to_string()); @@ -247,7 +249,7 @@ impl IHandshakeState for InitiatorAwaitingActTwoState { let mut act_two_builder = self.act_two_builder; let remaining = act_two_builder.fill(input); - // Any payload larger than ACT_ONE_TWO_LENGTH indicates a bad peer since responder data + // Any payload larger than ACT_TWO_LENGTH indicates a bad peer since responder data // is required to generate post-authentication messages (so it can't come before we transition) if remaining.len() != 0 { return Err("Act Two too large".to_string()); @@ -310,6 +312,7 @@ impl IHandshakeState for InitiatorAwaitingActTwoState { // - done by Conduit let conduit = Conduit::new(sending_key, receiving_key, chaining_key); + // 8. Send m = 0 || c || t Ok(( Some(Act::Three(act_three)), HandshakeState::Complete(Some((conduit, responder_static_public_key))) @@ -345,6 +348,7 @@ impl IHandshakeState for ResponderAwaitingActThreeState { // 1. Read exactly 66 bytes from the network buffer let act_three_bytes = Act::from(act_three_builder); + assert_eq!(act_three_bytes.len(), ACT_THREE_LENGTH); // 2. Parse the read message (m) into v, c, and t let version = act_three_bytes[0]; @@ -396,8 +400,11 @@ impl IHandshakeState for ResponderAwaitingActThreeState { } } +// The handshake state always uses the responder's static public key. When running on the initiator, +// the initiator provides the remote's static public key and running on the responder they provide +// their own. // https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#handshake-state-initialization -fn handshake_state_initialization(responder_static_public_key: &PublicKey) -> (Sha256, Sha256) { +fn initialize_handshake_state(responder_static_public_key: &PublicKey) -> (Sha256, Sha256) { let protocol_name = b"Noise_XK_secp256k1_ChaChaPoly_SHA256"; let prologue = b"lightning"; @@ -414,6 +421,7 @@ fn handshake_state_initialization(responder_static_public_key: &PublicKey) -> (S (hash, chaining_key) } +// Due to the very high similarity of acts 1 and 2, this method is used to process both // https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#act-one (sender) // https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#act-two (sender) fn calculate_act_message(local_private_ephemeral_key: &SecretKey, local_public_ephemeral_key: &PublicKey, remote_public_key: &PublicKey, chaining_key: ChainingKey, hash: Sha256, act_out: &mut [u8]) -> (Sha256, SymmetricKey, SymmetricKey) { @@ -448,6 +456,11 @@ fn calculate_act_message(local_private_ephemeral_key: &SecretKey, local_public_e // https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#act-two (receiver) fn process_act_message(act_bytes: &[u8], local_private_key: &SecretKey, chaining_key: ChainingKey, hash: Sha256) -> Result<(PublicKey, Sha256, SymmetricKey, SymmetricKey), String> { // 1. Read exactly 50 bytes from the network buffer + // Partial act messages are handled by the callers. By the time it gets here, it + // must be the correct size. + assert_eq!(act_bytes.len(), ACT_ONE_LENGTH); + assert_eq!(act_bytes.len(), ACT_TWO_LENGTH); + // 2.Parse the read message (m) into v, re, and c let version = act_bytes[0]; let ephemeral_public_key_bytes = &act_bytes[1..34]; @@ -476,7 +489,8 @@ fn process_act_message(act_bytes: &[u8], local_private_key: &SecretKey, chaining // 6. Act2: ck, temp_k2 = HKDF(ck, ee) let (chaining_key, temporary_key) = hkdf::derive(&chaining_key, &ecdh); - // 7. p = decryptWithAD(temp_k1, 0, h, c) + // 7. Act1: p = decryptWithAD(temp_k1, 0, h, c) + // 7. Act2: p = decryptWithAD(temp_k2, 0, h, c) chacha::decrypt(&temporary_key, 0, &hash, &chacha_tag, &mut [0; 0])?; // 8. h = SHA-256(h || c) diff --git a/lightning/src/ln/peers/hkdf.rs b/lightning/src/ln/peers/hkdf.rs index 6ac1391666d..61f6c49f92a 100644 --- a/lightning/src/ln/peers/hkdf.rs +++ b/lightning/src/ln/peers/hkdf.rs @@ -3,7 +3,7 @@ use bitcoin::hashes::sha256::Hash as Sha256; // Allows 1 or more inputs and "concatenates" them together using the input() function // of HmacEngine:: -macro_rules! hmac_hash { +macro_rules! hmac_sha256 { ( $salt:expr, ($( $input:expr ),+ )) => {{ let mut engine = HmacEngine::::new($salt); $( @@ -31,7 +31,7 @@ pub(super) fn derive(salt: &[u8], ikm: &[u8]) -> ([u8; 32], [u8; 32]) { // 2.2. Step 1: Extract // HKDF-Extract(salt, IKM) -> PRK // PRK = HMAC-Hash(salt, IKM) - let prk = hmac_hash!(salt, (ikm)); + let prk = hmac_sha256!(salt, (ikm)); // 2.3. Step 2: Expand // HKDF-Expand(PRK, info, L) -> OKM @@ -42,9 +42,9 @@ pub(super) fn derive(salt: &[u8], ikm: &[u8]) -> ([u8; 32], [u8; 32]) { // where: // T(0) = empty string (zero length) // T(1) = HMAC-Hash(PRK, T(0) | info | 0x01) - let t1 = hmac_hash!(&prk, (&[1])); + let t1 = hmac_sha256!(&prk, (&[1])); // T(2) = HMAC-Hash(PRK, T(1) | info | 0x02) - let t2 = hmac_hash!(&prk, (&t1, &[2])); + let t2 = hmac_sha256!(&prk, (&t1, &[2])); return (t1, t2) } From b8136beaae913d6583f4fe55f1a1ca315050c546 Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Wed, 2 Sep 2020 09:51:54 -0700 Subject: [PATCH 42/72] review: rename hkdf -> hkdf5869rfc.rs Follow existing pattern used in util/ for consistency. --- lightning/src/ln/peers/conduit.rs | 4 ++-- lightning/src/ln/peers/handshake/states.rs | 14 +++++++------- lightning/src/ln/peers/{hkdf.rs => hkdf5869rfc.rs} | 2 +- lightning/src/ln/peers/mod.rs | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) rename lightning/src/ln/peers/{hkdf.rs => hkdf5869rfc.rs} (98%) diff --git a/lightning/src/ln/peers/conduit.rs b/lightning/src/ln/peers/conduit.rs index d820d0497b8..5ffc1aa1e89 100644 --- a/lightning/src/ln/peers/conduit.rs +++ b/lightning/src/ln/peers/conduit.rs @@ -1,6 +1,6 @@ //! Handles all over the wire message encryption and decryption upon handshake completion. -use ln::peers::{chacha, hkdf}; +use ln::peers::{chacha, hkdf5869rfc}; use util::byte_utils; pub(super) type SymmetricKey = [u8; 32]; @@ -111,7 +111,7 @@ impl Conduit { } fn rotate_key(chaining_key: &mut SymmetricKey, key: &mut SymmetricKey) { - let (new_chaining_key, new_key) = hkdf::derive(chaining_key, key); + let (new_chaining_key, new_key) = hkdf5869rfc::derive(chaining_key, key); chaining_key.copy_from_slice(&new_chaining_key); key.copy_from_slice(&new_key); } diff --git a/lightning/src/ln/peers/handshake/states.rs b/lightning/src/ln/peers/handshake/states.rs index c3195976fc3..13b392ba980 100644 --- a/lightning/src/ln/peers/handshake/states.rs +++ b/lightning/src/ln/peers/handshake/states.rs @@ -4,7 +4,7 @@ use bitcoin::hashes::{Hash, HashEngine}; use bitcoin::hashes::sha256::Hash as Sha256; use bitcoin::secp256k1::{SecretKey, PublicKey}; -use ln::peers::{chacha, hkdf}; +use ln::peers::{chacha, hkdf5869rfc}; use ln::peers::conduit::{Conduit, SymmetricKey}; use ln::peers::handshake::acts::{Act, ActBuilder, ACT_ONE_LENGTH, ACT_TWO_LENGTH, ACT_THREE_LENGTH, EMPTY_ACT_ONE, EMPTY_ACT_TWO, EMPTY_ACT_THREE}; @@ -300,13 +300,13 @@ impl IHandshakeState for InitiatorAwaitingActTwoState { let ecdh = ecdh(&initiator_static_private_key, &responder_ephemeral_public_key); // 4. ck, temp_k3 = HKDF(ck, se) - let (chaining_key, temporary_key) = hkdf::derive(&chaining_key, &ecdh); + let (chaining_key, temporary_key) = hkdf5869rfc::derive(&chaining_key, &ecdh); // 5. t = encryptWithAD(temp_k3, 0, h, zero) chacha::encrypt(&temporary_key, 0, &hash, &[0; 0], &mut act_three[50..]); // 6. sk, rk = HKDF(ck, zero) - let (sending_key, receiving_key) = hkdf::derive(&chaining_key, &[0; 0]); + let (sending_key, receiving_key) = hkdf5869rfc::derive(&chaining_key, &[0; 0]); // 7. rn = 0, sn = 0 // - done by Conduit @@ -377,13 +377,13 @@ impl IHandshakeState for ResponderAwaitingActThreeState { let ecdh = ecdh(&responder_ephemeral_private_key, &initiator_pubkey); // 7. ck, temp_k3 = HKDF(ck, se) - let (chaining_key, temporary_key) = hkdf::derive(&chaining_key, &ecdh); + let (chaining_key, temporary_key) = hkdf5869rfc::derive(&chaining_key, &ecdh); // 8. p = decryptWithAD(temp_k3, 0, h, t) chacha::decrypt(&temporary_key, 0, &hash, &chacha_tag, &mut [0; 0])?; // 9. rk, sk = HKDF(ck, zero) - let (receiving_key, sending_key) = hkdf::derive(&chaining_key, &[0; 0]); + let (receiving_key, sending_key) = hkdf5869rfc::derive(&chaining_key, &[0; 0]); // 10. rn = 0, sn = 0 // - done by Conduit @@ -436,7 +436,7 @@ fn calculate_act_message(local_private_ephemeral_key: &SecretKey, local_public_e // 4. ACT1: ck, temp_k1 = HKDF(ck, es) // 4. ACT2: ck, temp_k2 = HKDF(ck, ee) - let (chaining_key, temporary_key) = hkdf::derive(&chaining_key, &ecdh); + let (chaining_key, temporary_key) = hkdf5869rfc::derive(&chaining_key, &ecdh); // 5. ACT1: c = encryptWithAD(temp_k1, 0, h, zero) // 5. ACT2: c = encryptWithAD(temp_k2, 0, h, zero) @@ -487,7 +487,7 @@ fn process_act_message(act_bytes: &[u8], local_private_key: &SecretKey, chaining // 6. Act1: ck, temp_k1 = HKDF(ck, es) // 6. Act2: ck, temp_k2 = HKDF(ck, ee) - let (chaining_key, temporary_key) = hkdf::derive(&chaining_key, &ecdh); + let (chaining_key, temporary_key) = hkdf5869rfc::derive(&chaining_key, &ecdh); // 7. Act1: p = decryptWithAD(temp_k1, 0, h, c) // 7. Act2: p = decryptWithAD(temp_k2, 0, h, c) diff --git a/lightning/src/ln/peers/hkdf.rs b/lightning/src/ln/peers/hkdf5869rfc.rs similarity index 98% rename from lightning/src/ln/peers/hkdf.rs rename to lightning/src/ln/peers/hkdf5869rfc.rs index 61f6c49f92a..546e64e6fc7 100644 --- a/lightning/src/ln/peers/hkdf.rs +++ b/lightning/src/ln/peers/hkdf5869rfc.rs @@ -53,7 +53,7 @@ pub(super) fn derive(salt: &[u8], ikm: &[u8]) -> ([u8; 32], [u8; 32]) { #[cfg(test)] mod test { use hex; - use ln::peers::hkdf::derive; + use ln::peers::hkdf5869rfc::derive; // Test with SHA-256 and zero-length salt/info // Our implementation uses a zero-length info field and returns the first 64 octets. As a result, diff --git a/lightning/src/ln/peers/mod.rs b/lightning/src/ln/peers/mod.rs index 072950c0dba..aadef42d67d 100644 --- a/lightning/src/ln/peers/mod.rs +++ b/lightning/src/ln/peers/mod.rs @@ -5,7 +5,7 @@ mod chacha; pub mod handler; -mod hkdf; +mod hkdf5869rfc; #[cfg(feature = "fuzztarget")] pub mod conduit; From 8c6da109ef2284799e5d5322ced4d55c55d1f56e Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Wed, 2 Sep 2020 09:55:35 -0700 Subject: [PATCH 43/72] review: Explicitly set version in Act creation Don't rely on the initialized buffer filled with zeros. --- lightning/src/ln/peers/handshake/states.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lightning/src/ln/peers/handshake/states.rs b/lightning/src/ln/peers/handshake/states.rs index 13b392ba980..d40ebb5619a 100644 --- a/lightning/src/ln/peers/handshake/states.rs +++ b/lightning/src/ln/peers/handshake/states.rs @@ -313,6 +313,7 @@ impl IHandshakeState for InitiatorAwaitingActTwoState { let conduit = Conduit::new(sending_key, receiving_key, chaining_key); // 8. Send m = 0 || c || t + act_three[0] = 0; Ok(( Some(Act::Three(act_three)), HandshakeState::Complete(Some((conduit, responder_static_public_key))) @@ -446,6 +447,7 @@ fn calculate_act_message(local_private_ephemeral_key: &SecretKey, local_public_e let hash = concat_then_sha256!(hash, &act_out[34..]); // Send m = 0 || e.pub.serializeCompressed() || c + act_out[0] = 0; act_out[1..34].copy_from_slice(&serialized_local_public_key); (hash, chaining_key, temporary_key) From 19a0f42c87b490c3b81ffb9a02694fdf4ec9cd2b Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Wed, 2 Sep 2020 10:08:46 -0700 Subject: [PATCH 44/72] review: ActBuilder::fill() returns the number of bytes consumed Instead of returning a subslice of input for the caller to process, just return the number of bytes consume and let them do the accounting. --- lightning/src/ln/peers/handshake/acts.rs | 25 +++++++++++----------- lightning/src/ln/peers/handshake/states.rs | 24 ++++++++++----------- 2 files changed, 25 insertions(+), 24 deletions(-) diff --git a/lightning/src/ln/peers/handshake/acts.rs b/lightning/src/ln/peers/handshake/acts.rs index fa7a2ece5a6..2795ae621df 100644 --- a/lightning/src/ln/peers/handshake/acts.rs +++ b/lightning/src/ln/peers/handshake/acts.rs @@ -82,8 +82,8 @@ impl ActBuilder { } } - /// Fills the Act with bytes from input and returns the unprocessed bytes - pub(super) fn fill<'a>(&mut self, input: &'a [u8]) -> &'a [u8] { + /// Fills the Act with bytes from input and returns the number of bytes consumed from input. + pub(super) fn fill(&mut self, input: &[u8]) -> usize { // Simple fill implementation for both almost-identical structs to deduplicate code // $act: Act[One|Two|Three], $input: &[u8]; returns &[u8] of remaining input that was not processed macro_rules! fill_act_content { @@ -93,7 +93,7 @@ impl ActBuilder { $act[$write_pos..$write_pos + fill_amount].copy_from_slice(&$input[..fill_amount]); $write_pos += fill_amount; - &$input[fill_amount..] + fill_amount }} } @@ -125,11 +125,12 @@ mod tests { fn partial_fill() { let mut builder = ActBuilder::new(Act::One(EMPTY_ACT_ONE)); - let remaining = builder.fill(&[1, 2, 3]); + let input = [1, 2, 3]; + let bytes_read = builder.fill(&input); assert_eq!(builder.partial_act.len(), ACT_ONE_LENGTH); assert_eq!(builder.write_pos, 3); assert!(!builder.is_finished()); - assert_eq!(remaining, &[]); + assert_eq!(bytes_read, input.len()); } // Test bookkeeping of exact fill @@ -137,13 +138,13 @@ mod tests { fn exact_fill() { let mut builder = ActBuilder::new(Act::One(EMPTY_ACT_ONE)); - let input = [0; 50]; - let remaining = builder.fill(&input); + let input = [0; ACT_ONE_LENGTH]; + let bytes_read = builder.fill(&input); assert_eq!(builder.partial_act.len(), ACT_ONE_LENGTH); assert_eq!(builder.write_pos, ACT_ONE_LENGTH); assert!(builder.is_finished()); assert_eq!(Act::from(builder).as_ref(), &input[..]); - assert_eq!(remaining, &[]); + assert_eq!(bytes_read, input.len()); } // Test bookkeeping of overfill @@ -151,14 +152,14 @@ mod tests { fn over_fill() { let mut builder = ActBuilder::new(Act::One(EMPTY_ACT_ONE)); - let input = [0; 51]; - let remaining = builder.fill(&input); + let input = [0; ACT_ONE_LENGTH + 1]; + let bytes_read = builder.fill(&input); assert_eq!(builder.partial_act.len(), ACT_ONE_LENGTH); assert_eq!(builder.write_pos, ACT_ONE_LENGTH); assert!(builder.is_finished()); - assert_eq!(Act::from(builder).as_ref(), &input[..50]); - assert_eq!(remaining, &[0]); + assert_eq!(Act::from(builder).as_ref(), &input[..ACT_ONE_LENGTH]); + assert_eq!(bytes_read, ACT_ONE_LENGTH); } // Converting an unfinished ActBuilder panics diff --git a/lightning/src/ln/peers/handshake/states.rs b/lightning/src/ln/peers/handshake/states.rs index d40ebb5619a..6dbfe3cd764 100644 --- a/lightning/src/ln/peers/handshake/states.rs +++ b/lightning/src/ln/peers/handshake/states.rs @@ -181,17 +181,17 @@ impl IHandshakeState for ResponderAwaitingActOneState { // https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#act-two (sender) fn next(self, input: &[u8]) -> Result<(Option, HandshakeState), String> { let mut act_one_builder = self.act_one_builder; - let remaining = act_one_builder.fill(input); + let bytes_read = act_one_builder.fill(input); - // Any payload larger than ACT_ONE_LENGTH indicates a bad peer since initiator data - // is required to generate act3 (so it can't come before we transition) - if remaining.len() != 0 { + // Any payload that is not fully consumed by the builder indicates a bad peer since responder + // data is required to generate act3 (so it can't come before we transition) + if bytes_read < input.len() { return Err("Act One too large".to_string()); } // In the event of a partial fill, stay in the same state and wait for more data if !act_one_builder.is_finished() { - assert_eq!(remaining.len(), 0); + assert_eq!(bytes_read, input.len()); return Ok(( None, HandshakeState::ResponderAwaitingActOne(Self { @@ -247,17 +247,17 @@ impl IHandshakeState for InitiatorAwaitingActTwoState { // https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#act-three (sender) fn next(self, input: &[u8]) -> Result<(Option, HandshakeState), String> { let mut act_two_builder = self.act_two_builder; - let remaining = act_two_builder.fill(input); + let bytes_read = act_two_builder.fill(input); - // Any payload larger than ACT_TWO_LENGTH indicates a bad peer since responder data + // Any payload that is not fully consumed by the builder indicates a bad peer since responder data // is required to generate post-authentication messages (so it can't come before we transition) - if remaining.len() != 0 { + if bytes_read < input.len() { return Err("Act Two too large".to_string()); } // In the event of a partial fill, stay in the same state and wait for more data if !act_two_builder.is_finished() { - assert_eq!(remaining.len(), 0); + assert_eq!(bytes_read, input.len()); return Ok(( None, HandshakeState::InitiatorAwaitingActTwo(Self { @@ -325,11 +325,11 @@ impl IHandshakeState for ResponderAwaitingActThreeState { // https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md#act-three (receiver) fn next(self, input: &[u8]) -> Result<(Option, HandshakeState), String> { let mut act_three_builder = self.act_three_builder; - let remaining = act_three_builder.fill(input); + let bytes_read = act_three_builder.fill(input); // In the event of a partial fill, stay in the same state and wait for more data if !act_three_builder.is_finished() { - assert_eq!(remaining.len(), 0); + assert_eq!(bytes_read, input.len()); return Ok(( None, HandshakeState::ResponderAwaitingActThree(Self { @@ -392,7 +392,7 @@ impl IHandshakeState for ResponderAwaitingActThreeState { // Any remaining data in the read buffer would be encrypted, so transfer ownership // to the Conduit for future use. - conduit.read(remaining); + conduit.read(&input[bytes_read..]); Ok(( None, From 30286c0328f71ba0c47472d6320f3977eb584707 Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Fri, 18 Sep 2020 10:29:41 -0700 Subject: [PATCH 45/72] docs: Add license to new peers/ files --- lightning/src/ln/peers/chacha.rs | 9 +++++++++ lightning/src/ln/peers/conduit.rs | 9 +++++++++ lightning/src/ln/peers/handshake/acts.rs | 9 +++++++++ lightning/src/ln/peers/handshake/mod.rs | 9 +++++++++ lightning/src/ln/peers/handshake/states.rs | 9 +++++++++ lightning/src/ln/peers/hkdf5869rfc.rs | 9 +++++++++ lightning/src/ln/peers/mod.rs | 9 +++++++++ 7 files changed, 63 insertions(+) diff --git a/lightning/src/ln/peers/chacha.rs b/lightning/src/ln/peers/chacha.rs index 76742bad452..7d5ab45c50c 100644 --- a/lightning/src/ln/peers/chacha.rs +++ b/lightning/src/ln/peers/chacha.rs @@ -1,3 +1,12 @@ +// This file is Copyright its original authors, visible in version control +// history. +// +// This file is licensed under the Apache License, Version 2.0 or the MIT license +// , at your option. +// You may not use this file except in accordance with one or both of these +// licenses. + use util::byte_utils; use util::chacha20poly1305rfc::ChaCha20Poly1305RFC; diff --git a/lightning/src/ln/peers/conduit.rs b/lightning/src/ln/peers/conduit.rs index 5ffc1aa1e89..aa9ad87bc58 100644 --- a/lightning/src/ln/peers/conduit.rs +++ b/lightning/src/ln/peers/conduit.rs @@ -1,3 +1,12 @@ +// This file is Copyright its original authors, visible in version control +// history. +// +// This file is licensed under the Apache License, Version 2.0 or the MIT license +// , at your option. +// You may not use this file except in accordance with one or both of these +// licenses. + //! Handles all over the wire message encryption and decryption upon handshake completion. use ln::peers::{chacha, hkdf5869rfc}; diff --git a/lightning/src/ln/peers/handshake/acts.rs b/lightning/src/ln/peers/handshake/acts.rs index 2795ae621df..e95fe8d9cfb 100644 --- a/lightning/src/ln/peers/handshake/acts.rs +++ b/lightning/src/ln/peers/handshake/acts.rs @@ -1,3 +1,12 @@ +// This file is Copyright its original authors, visible in version control +// history. +// +// This file is licensed under the Apache License, Version 2.0 or the MIT license +// , at your option. +// You may not use this file except in accordance with one or both of these +// licenses. + //! Helper library for working with NOISE handshake Act data. Contains utilities for passing Act //! objects around and building them from received data. //! Act objects are thin wrappers about raw arrays for stack-based processing as well as convenient diff --git a/lightning/src/ln/peers/handshake/mod.rs b/lightning/src/ln/peers/handshake/mod.rs index d914b299518..f964c245a94 100644 --- a/lightning/src/ln/peers/handshake/mod.rs +++ b/lightning/src/ln/peers/handshake/mod.rs @@ -1,3 +1,12 @@ +// This file is Copyright its original authors, visible in version control +// history. +// +// This file is licensed under the Apache License, Version 2.0 or the MIT license +// , at your option. +// You may not use this file except in accordance with one or both of these +// licenses. + //! Execute handshakes for peer-to-peer connection establishment. //! Handshake states can be advanced automatically, or by manually calling the appropriate step. //! Once complete, returns an instance of Conduit. diff --git a/lightning/src/ln/peers/handshake/states.rs b/lightning/src/ln/peers/handshake/states.rs index 6dbfe3cd764..347620cbb8f 100644 --- a/lightning/src/ln/peers/handshake/states.rs +++ b/lightning/src/ln/peers/handshake/states.rs @@ -1,3 +1,12 @@ +// This file is Copyright its original authors, visible in version control +// history. +// +// This file is licensed under the Apache License, Version 2.0 or the MIT license +// , at your option. +// You may not use this file except in accordance with one or both of these +// licenses. + use bitcoin::secp256k1; use bitcoin::hashes::{Hash, HashEngine}; diff --git a/lightning/src/ln/peers/hkdf5869rfc.rs b/lightning/src/ln/peers/hkdf5869rfc.rs index 546e64e6fc7..1d9f66ab1cb 100644 --- a/lightning/src/ln/peers/hkdf5869rfc.rs +++ b/lightning/src/ln/peers/hkdf5869rfc.rs @@ -1,3 +1,12 @@ +// This file is Copyright its original authors, visible in version control +// history. +// +// This file is licensed under the Apache License, Version 2.0 or the MIT license +// , at your option. +// You may not use this file except in accordance with one or both of these +// licenses. + use bitcoin::hashes::{Hash, HashEngine, Hmac, HmacEngine}; use bitcoin::hashes::sha256::Hash as Sha256; diff --git a/lightning/src/ln/peers/mod.rs b/lightning/src/ln/peers/mod.rs index aadef42d67d..5c10e56657c 100644 --- a/lightning/src/ln/peers/mod.rs +++ b/lightning/src/ln/peers/mod.rs @@ -1,3 +1,12 @@ +// This file is Copyright its original authors, visible in version control +// history. +// +// This file is licensed under the Apache License, Version 2.0 or the MIT license +// , at your option. +// You may not use this file except in accordance with one or both of these +// licenses. + //! Everything that has to do with over-the-wire peer communication. //! The handshake module exposes mechanisms to conduct inbound and outbound handshakes. //! When a handshake completes, it returns an instance of Conduit. From e4b8a59e09570e702b6507449343b92e8cbab7e3 Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Tue, 1 Sep 2020 11:16:51 -0700 Subject: [PATCH 46/72] refactor: Abstract SocketDescriptor writing queue To enable better testing, put the code to enqueue data and flush it to a SocketDescriptor behind an object. This patch starts to implement traits for the various separate interface that each object needs. The idea is the consumer of an interface defines it and the dependency objects implement it. This will be useful when creating test doubles for unit tests and helps make the dependencies of each function more clear. Additional uses of trait-based contracts will be more clear in the next patches that start to add more testing. --- lightning/src/ln/peers/handler.rs | 117 +++++--- lightning/src/ln/peers/mod.rs | 1 + lightning/src/ln/peers/outbound_queue.rs | 359 +++++++++++++++++++++++ 3 files changed, 441 insertions(+), 36 deletions(-) create mode 100644 lightning/src/ln/peers/outbound_queue.rs diff --git a/lightning/src/ln/peers/handler.rs b/lightning/src/ln/peers/handler.rs index 25ecfcaaadd..939cd2ef2f5 100644 --- a/lightning/src/ln/peers/handler.rs +++ b/lightning/src/ln/peers/handler.rs @@ -29,7 +29,7 @@ use util::events::{MessageSendEvent, MessageSendEventsProvider}; use util::logger::Logger; use routing::network_graph::NetGraphMsgHandler; -use std::collections::{HashMap,hash_map,HashSet,LinkedList}; +use std::collections::{HashMap,hash_map,HashSet}; use std::sync::{Arc, Mutex}; use std::sync::atomic::{AtomicUsize, Ordering}; use std::{cmp,error,hash,fmt}; @@ -40,6 +40,9 @@ use bitcoin::hashes::sha256::HashEngine as Sha256Engine; use bitcoin::hashes::{HashEngine, Hash}; use ln::peers::handshake::PeerHandshake; use ln::peers::conduit::Conduit; +use ln::peers::outbound_queue::OutboundQueue; + +const MSG_BUFF_SIZE: usize = 10; /// Provides references to trait impls which handle different types of messages. pub struct MessageHandler where @@ -120,6 +123,64 @@ enum InitSyncTracker{ NodesSyncing(PublicKey), } +/// Trait representing a container that allows enqueuing of Vec<[u8]> +pub(super) trait PayloadQueuer { + /// Enqueue item to the queue + fn push_back(&mut self, item: Vec); + + /// Returns true if the queue is empty + fn is_empty(&self) -> bool; + + /// Returns the amount of available space in queue + fn queue_space(&self) -> usize; +} + +/// Implement &mut PayloadQueuer passthroughs +impl<'a, T> PayloadQueuer for &'a mut T where + T: PayloadQueuer { + fn push_back(&mut self, item: Vec) { + T::push_back(self, item) + } + + fn is_empty(&self) -> bool { + T::is_empty(self) + } + + fn queue_space(&self) -> usize { + T::queue_space(self) + } +} + +/// Trait representing a container that can try to flush data through a SocketDescriptor +pub(super) trait SocketDescriptorFlusher { + /// Write previously enqueued data to the SocketDescriptor. A return of false indicates the + /// underlying SocketDescriptor could not fulfill the send_data() call and the blocked state + /// has been set. Use unblock() when the SocketDescriptor may have more room. + fn try_flush_one(&mut self, descriptor: &mut impl SocketDescriptor) -> bool; + + /// Clear the blocked state caused when a previous write failed + fn unblock(&mut self); + + /// Check if the container is in a blocked state + fn is_blocked(&self) -> bool; +} + +/// Implement &mut Flushable passthroughs +impl<'a, T> SocketDescriptorFlusher for &'a mut T where + T: SocketDescriptorFlusher { + fn try_flush_one(&mut self, descriptor: &mut impl SocketDescriptor) -> bool { + T::try_flush_one(self, descriptor) + } + + fn unblock(&mut self) { + T::unblock(self) + } + + fn is_blocked(&self) -> bool { + T::is_blocked(self) + } +} + enum PeerState { Authenticating(PeerHandshake), Connected(Conduit), @@ -139,7 +200,7 @@ impl PeerState { } } - fn process_peer_data(&mut self, data: &[u8], mutable_response_buffer: &mut LinkedList>) -> PeerDataProcessingDecision { + fn process_peer_data(&mut self, data: &[u8], pending_outbound_buffer: &mut impl PayloadQueuer) -> PeerDataProcessingDecision { let (new_state_opt, decision) = match self { &mut PeerState::Authenticating(ref mut handshake) => { match handshake.process_act(data) { @@ -153,7 +214,7 @@ impl PeerState { // Any response generated by the handshake sequence is put into the response buffer if let Some(response_vec) = response_vec_option { - mutable_response_buffer.push_back(response_vec); + pending_outbound_buffer.push_back(response_vec); } // if process_act() returns the conduit and remote static public key (node id) @@ -186,9 +247,7 @@ struct Peer { their_node_id: Option, their_features: Option, - pending_outbound_buffer: LinkedList>, - pending_outbound_buffer_first_msg_offset: usize, - awaiting_write_event: bool, + pending_outbound_buffer: OutboundQueue, sync_status: InitSyncTracker, @@ -375,9 +434,7 @@ impl PeerManager PeerManager PeerManager 0 { match peer.sync_status { InitSyncTracker::NoSyncRequested => {}, InitSyncTracker::ChannelsSyncing(c) if c < 0xffff_ffff_ffff_ffff => { - let steps = ((MSG_BUFF_SIZE - peer.pending_outbound_buffer.len() + 2) / 3) as u8; + let steps = ((queue_space + 2) / 3) as u8; let all_messages = self.message_handler.route_handler.get_next_channel_announcements(c, steps); for &(ref announce, ref update_a_option, ref update_b_option) in all_messages.iter() { encode_and_send_msg!(announce); @@ -455,7 +511,7 @@ impl PeerManager { - let steps = (MSG_BUFF_SIZE - peer.pending_outbound_buffer.len()) as u8; + let steps = queue_space as u8; let all_messages = self.message_handler.route_handler.get_next_node_announcements(None, steps); for msg in all_messages.iter() { encode_and_send_msg!(msg); @@ -467,7 +523,7 @@ impl PeerManager unreachable!(), InitSyncTracker::NodesSyncing(key) => { - let steps = (MSG_BUFF_SIZE - peer.pending_outbound_buffer.len()) as u8; + let steps = queue_space as u8; let all_messages = self.message_handler.route_handler.get_next_node_announcements(Some(&key), steps); for msg in all_messages.iter() { encode_and_send_msg!(msg); @@ -480,23 +536,12 @@ impl PeerManager return, - Some(buff) => buff, - }; - - let should_be_reading = peer.pending_outbound_buffer.len() < MSG_BUFF_SIZE; - let pending = &next_buff[peer.pending_outbound_buffer_first_msg_offset..]; - let data_sent = descriptor.send_data(pending, should_be_reading); - peer.pending_outbound_buffer_first_msg_offset += data_sent; - if peer.pending_outbound_buffer_first_msg_offset == next_buff.len() { true } else { false } - } { - peer.pending_outbound_buffer_first_msg_offset = 0; - peer.pending_outbound_buffer.pop_front(); - } else { - peer.awaiting_write_event = true; + // No messages to send + if peer.pending_outbound_buffer.is_empty() { + break; } + + peer.pending_outbound_buffer.try_flush_one(descriptor); } } @@ -515,7 +560,7 @@ impl PeerManager panic!("Descriptor for write_event is not already known to PeerManager"), Some(peer) => { - peer.awaiting_write_event = false; + peer.pending_outbound_buffer.unblock(); self.do_attempt_write_data(descriptor, peer); } }; @@ -693,7 +738,7 @@ impl PeerManager 10 // pause_read + peer.pending_outbound_buffer.queue_space() == 0 // pause_read } }; diff --git a/lightning/src/ln/peers/mod.rs b/lightning/src/ln/peers/mod.rs index 5c10e56657c..0fc6122a5e2 100644 --- a/lightning/src/ln/peers/mod.rs +++ b/lightning/src/ln/peers/mod.rs @@ -15,6 +15,7 @@ mod chacha; pub mod handler; mod hkdf5869rfc; +mod outbound_queue; #[cfg(feature = "fuzztarget")] pub mod conduit; diff --git a/lightning/src/ln/peers/outbound_queue.rs b/lightning/src/ln/peers/outbound_queue.rs new file mode 100644 index 00000000000..d261e0f248a --- /dev/null +++ b/lightning/src/ln/peers/outbound_queue.rs @@ -0,0 +1,359 @@ +// This file is Copyright its original authors, visible in version control +// history. +// +// This file is licensed under the Apache License, Version 2.0 or the MIT license +// , at your option. +// You may not use this file except in accordance with one or both of these +// licenses. + +/// Abstracts the buffer used to write data through a SocketDescriptor handling partial writes and +/// flow control. + +use ln::peers::handler::{SocketDescriptor, PayloadQueuer, SocketDescriptorFlusher}; +use std::collections::LinkedList; +use std::cmp; + +pub(super) struct OutboundQueue { + blocked: bool, + soft_limit: usize, + buffer: LinkedList>, + buffer_first_msg_offset: usize, +} + +impl PayloadQueuer for OutboundQueue { + /// Unconditionally queue item. May increase queue above soft limit. + fn push_back(&mut self, item: Vec) { + self.buffer.push_back(item); + } + + /// Returns true if the queue is empty + fn is_empty(&self) -> bool { + self.buffer.is_empty() + } + + /// Returns the amount of free space in the queue before the soft limit + fn queue_space(&self) -> usize { + self.soft_limit - cmp::min(self.soft_limit, self.buffer.len()) + } +} +impl SocketDescriptorFlusher for OutboundQueue { + fn try_flush_one(&mut self, descriptor: &mut impl SocketDescriptor) -> bool { + // Exit early if a previous full write failed and haven't heard that there may be more + // room available + if self.blocked { + return false; + } + + let full_write_succeeded = match self.buffer.front() { + None => true, + Some(next_buff) => { + let should_be_reading = self.buffer.len() < self.soft_limit; + let pending = &next_buff[self.buffer_first_msg_offset..]; + let data_sent = descriptor.send_data(pending, should_be_reading); + self.buffer_first_msg_offset += data_sent; + self.buffer_first_msg_offset == next_buff.len() + } + }; + + if full_write_succeeded { + self.buffer_first_msg_offset = 0; + self.buffer.pop_front(); + } else { + self.blocked = true; + } + + full_write_succeeded + } + + fn unblock(&mut self) { + self.blocked = false; + } + + fn is_blocked(&self) -> bool { + self.blocked + } +} + +impl OutboundQueue { + + /// Create a new writer with a soft limit that is used to notify the SocketDescriptor when + /// it is OK to resume reading if it was paused + pub(super) fn new(soft_limit: usize) -> Self { + Self { + blocked: false, + soft_limit, + buffer: LinkedList::new(), + buffer_first_msg_offset: 0, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::rc::Rc; + use std::cell::RefCell; + use std::hash::Hash; + use std::cmp; + + /// Mock implementation of the SocketDescriptor trait that can be used in tests to finely control + /// the send_data() behavior. + /// + ///Additionally, records the actual calls to send_data() for later validation. + #[derive(Debug, Eq)] + struct SocketDescriptorMock { + /// If true, all send_data() calls will succeed + unbounded: Rc>, + + /// Amount of free space in the descriptor for send_data() bytes + free_space: Rc>, + + /// Vector of arguments and return values to send_data() used for validation + send_recording: Rc, bool)>>>, + } + + impl SocketDescriptorMock { + /// Basic unbounded implementation where send_data() will always succeed + fn new() -> Self { + Self { + unbounded: Rc::new(RefCell::new(true)), + send_recording: Rc::new(RefCell::new(Vec::new())), + free_space: Rc::new(RefCell::new(0)) + } + } + + /// Used for tests that want to return partial sends after a certain amount of data is sent through send_data() + fn with_fixed_size(limit: usize) -> Self { + let mut descriptor = Self::new(); + descriptor.unbounded = Rc::new(RefCell::new(false)); + descriptor.free_space = Rc::new(RefCell::new(limit)); + + descriptor + } + + /// Standard Mock api to verify actual vs. expected calls + fn assert_called_with(&self, expectation: Vec<(Vec, bool)>) { + assert_eq!(expectation.as_slice(), self.send_recording.borrow().as_slice()) + } + + /// Allow future send_data() calls to succeed for the next added_room bytes. Not valid for + /// unbounded mock descriptors + fn make_room(&mut self, added_room: usize) { + assert!(!*self.unbounded.borrow()); + let mut free_space = self.free_space.borrow_mut(); + + *free_space += added_room; + } + } + + impl SocketDescriptor for SocketDescriptorMock { + fn send_data(&mut self, data: &[u8], resume_read: bool) -> usize { + self.send_recording.borrow_mut().push((data.to_vec(), resume_read)); + + let mut free_space = self.free_space.borrow_mut(); + + // Unbounded just flush everything + return if *self.unbounded.borrow() { + data.len() + } + // Bounded flush up to the free_space limit + else { + let write_len = cmp::min(data.len(), *free_space); + *free_space -= write_len; + write_len + } + } + + fn disconnect_socket(&mut self) { + unimplemented!() + } + } + + impl Clone for SocketDescriptorMock { + fn clone(&self) -> Self { + Self { + unbounded: self.unbounded.clone(), + send_recording: self.send_recording.clone(), + free_space: self.free_space.clone() + } + } + } + + impl PartialEq for SocketDescriptorMock { + fn eq(&self, o: &Self) -> bool { + Rc::ptr_eq(&self.send_recording, &o.send_recording) + } + } + impl Hash for SocketDescriptorMock { + fn hash(&self, state: &mut H) { + self.send_recording.as_ptr().hash(state) + } + } + + // Test that a try_flush_one() call with no queued data doesn't write anything + #[test] + fn empty_does_not_write() { + let mut descriptor = SocketDescriptorMock::new(); + let mut empty = OutboundQueue::new(10); + + assert!(empty.try_flush_one(&mut descriptor)); + descriptor.assert_called_with(vec![]); + + } + + // Test that try_flush_one() sends the push_back + #[test] + fn push_back_drain() { + let mut descriptor = SocketDescriptorMock::new(); + let mut queue = OutboundQueue::new(10); + + queue.push_back(vec![1]); + assert!(queue.try_flush_one(&mut descriptor)); + + descriptor.assert_called_with(vec![(vec![1], true)]); + } + + // Test that try_flush_one() sends just first push_back + #[test] + fn push_back_push_back_drain_drain() { + let mut descriptor = SocketDescriptorMock::new(); + let mut queue = OutboundQueue::new(10); + + queue.push_back(vec![1]); + queue.push_back(vec![2]); + assert!(queue.try_flush_one(&mut descriptor)); + + descriptor.assert_called_with(vec![(vec![1], true)]); + } + + // Test that descriptor that can't write all bytes returns valid response + #[test] + fn push_back_drain_partial() { + let mut descriptor = SocketDescriptorMock::with_fixed_size(1); + let mut queue = OutboundQueue::new(10); + + queue.push_back(vec![1, 2, 3]); + assert!(!queue.try_flush_one(&mut descriptor)); + + descriptor.assert_called_with(vec![(vec![1, 2, 3], true)]); + } + + // Test the bookkeeping for multiple partial writes + #[test] + fn push_back_drain_partial_drain_partial_try_flush_one() { + let mut descriptor = SocketDescriptorMock::with_fixed_size(1); + let mut queue = OutboundQueue::new(10); + + queue.push_back(vec![1, 2, 3]); + assert!(!queue.try_flush_one(&mut descriptor)); + + descriptor.make_room(1); + queue.unblock(); + assert!(!queue.try_flush_one(&mut descriptor)); + + descriptor.make_room(1); + queue.unblock(); + assert!(queue.try_flush_one(&mut descriptor)); + + descriptor.assert_called_with(vec![(vec![1, 2, 3], true), (vec![2, 3], true), (vec![3], true)]); + } + + #[test] + fn push_back_drain_blocks() { + let mut descriptor = SocketDescriptorMock::with_fixed_size(0); + let mut queue = OutboundQueue::new(10); + + // Fail write and move to blocked state + queue.push_back(vec![1, 2]); + assert!(!queue.try_flush_one(&mut descriptor)); + descriptor.assert_called_with(vec![(vec![1, 2], true)]); + + // Make room but don't signal + descriptor.make_room(1); + assert!(!queue.try_flush_one(&mut descriptor)); + assert!(queue.is_blocked()); + descriptor.assert_called_with(vec![(vec![1, 2], true)]); + + // Unblock and try again + queue.unblock(); + + // Partial write will succeed, but still move to blocked + assert!(!queue.try_flush_one(&mut descriptor)); + assert!(queue.is_blocked()); + descriptor.assert_called_with(vec![(vec![1, 2], true), (vec![1, 2], true)]); + + // Make room and signal which will succeed in writing the final piece + descriptor.make_room(1); + queue.unblock(); + assert!(queue.try_flush_one(&mut descriptor)); + assert!(!queue.is_blocked()); + descriptor.assert_called_with(vec![(vec![1, 2], true), (vec![1, 2], true), (vec![2], true)]); + } + + // Test resume_reading argument to send_data when queue is above soft limit + #[test] + fn push_back_above_limit_resume_reading_false() { + let mut descriptor = SocketDescriptorMock::with_fixed_size(10); + let mut queue = OutboundQueue::new(1); + + queue.push_back(vec![1]); + assert!(queue.try_flush_one(&mut descriptor)); + descriptor.assert_called_with(vec![(vec![1], false)]); + } + + // Test that push_back works above soft limit, but send_read() is informed of the correct state + #[test] + fn push_back_above_limit_is_ok() { + let mut descriptor = SocketDescriptorMock::with_fixed_size(10); + let mut queue = OutboundQueue::new(2); + + queue.push_back(vec![1]); + queue.push_back(vec![2]); + queue.push_back(vec![3]); + assert!(queue.try_flush_one(&mut descriptor)); + assert!(queue.try_flush_one(&mut descriptor)); + assert!(queue.try_flush_one(&mut descriptor)); + descriptor.assert_called_with(vec![(vec![1], false), (vec![2], false), (vec![3], true)]); + } + + // Test is_empty() + #[test] + fn is_empty() { + let mut descriptor = SocketDescriptorMock::with_fixed_size(10); + let mut queue = OutboundQueue::new(1); + assert!(queue.is_empty()); + + queue.push_back(vec![1]); + assert!(!queue.is_empty()); + + assert!(queue.try_flush_one(&mut descriptor)); + assert!(queue.is_empty()); + } + + // Test queue_space() + #[test] + fn queue_space() { + let mut descriptor = SocketDescriptorMock::with_fixed_size(10); + let mut queue = OutboundQueue::new(1); + + // below soft limit + assert_eq!(queue.queue_space(), 1); + + // at soft limit + queue.push_back(vec![1]); + assert_eq!(queue.queue_space(), 0); + + // above soft limit + queue.push_back(vec![2]); + assert_eq!(queue.queue_space(), 0); + + // at soft limit + assert!(queue.try_flush_one(&mut descriptor)); + assert_eq!(queue.queue_space(), 0); + + // below soft limt + assert!(queue.try_flush_one(&mut descriptor)); + assert_eq!(queue.queue_space(), 1); + } +} \ No newline at end of file From bf9edf351d0ee6600bc3256eb954e3918dacb1eb Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Wed, 16 Sep 2020 12:10:54 -0700 Subject: [PATCH 47/72] review: Move IHandshakeState code move to separate patch This makes the Transport layer patch easier to review. --- lightning/src/ln/peers/handshake/mod.rs | 11 ++++++++++- lightning/src/ln/peers/handshake/states.rs | 8 +------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/lightning/src/ln/peers/handshake/mod.rs b/lightning/src/ln/peers/handshake/mod.rs index f964c245a94..e751fc5c1ff 100644 --- a/lightning/src/ln/peers/handshake/mod.rs +++ b/lightning/src/ln/peers/handshake/mod.rs @@ -14,11 +14,20 @@ use bitcoin::secp256k1::{PublicKey, SecretKey}; use ln::peers::conduit::Conduit; -use ln::peers::handshake::states::{HandshakeState, IHandshakeState}; +use ln::peers::handshake::acts::Act; +use ln::peers::handshake::states::HandshakeState; mod acts; mod states; +/// Interface used by PeerHandshake to interact with NOISE state machine. +/// State may transition to the same state in the event there are not yet enough bytes to move +/// forward with the handshake. +trait IHandshakeState { + /// Returns the next HandshakeState after processing the input bytes + fn next(self, input: &[u8]) -> Result<(Option, HandshakeState), String>; +} + /// Object for managing handshakes. /// Currently requires explicit ephemeral private key specification. pub struct PeerHandshake { diff --git a/lightning/src/ln/peers/handshake/states.rs b/lightning/src/ln/peers/handshake/states.rs index 347620cbb8f..a0ca5a847b4 100644 --- a/lightning/src/ln/peers/handshake/states.rs +++ b/lightning/src/ln/peers/handshake/states.rs @@ -16,6 +16,7 @@ use bitcoin::secp256k1::{SecretKey, PublicKey}; use ln::peers::{chacha, hkdf5869rfc}; use ln::peers::conduit::{Conduit, SymmetricKey}; use ln::peers::handshake::acts::{Act, ActBuilder, ACT_ONE_LENGTH, ACT_TWO_LENGTH, ACT_THREE_LENGTH, EMPTY_ACT_ONE, EMPTY_ACT_TWO, EMPTY_ACT_THREE}; +use ln::peers::handshake::IHandshakeState; // Alias type to help differentiate between temporary key and chaining key when passing bytes around type ChainingKey = [u8; 32]; @@ -39,13 +40,6 @@ pub(super) enum HandshakeState { Complete(Option<(Conduit, PublicKey)>), } -// Trait for all individual states to implement that ensure HandshakeState::next() can -// delegate to a common function signature. May transition to the same state in the event there are -// not yet enough bytes to move forward with the handshake. -pub(super) trait IHandshakeState { - fn next(self, input: &[u8]) -> Result<(Option, HandshakeState), String>; -} - // Enum dispatch for state machine. Single public interface can statically dispatch to all states impl HandshakeState { pub(super) fn new_initiator(initiator_static_private_key: &SecretKey, responder_static_public_key: &PublicKey, initiator_ephemeral_private_key: &SecretKey) -> Self { From 8bdb638c21cf14061f97467ddc4f4f894e235d4a Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Fri, 4 Sep 2020 14:32:46 -0700 Subject: [PATCH 48/72] refactor: Abstract Transport layer and test it Start moving the code that implements the Bolt8 transport layer into a separate testable module. This patch makes use of Rust's supported mocking pattern to create test doubles and leverage them to write simple unit tests for the public API of Transport. The end goal is a mockable Transport layer for easier testing of the PeerManager. --- fuzz/src/peer_crypt.rs | 1 + lightning/src/ln/peers/handler.rs | 489 ++++++++++------------- lightning/src/ln/peers/handshake/mod.rs | 34 +- lightning/src/ln/peers/mod.rs | 9 + lightning/src/ln/peers/outbound_queue.rs | 100 +---- lightning/src/ln/peers/test_util.rs | 214 ++++++++++ lightning/src/ln/peers/transport.rs | 266 ++++++++++++ 7 files changed, 714 insertions(+), 399 deletions(-) create mode 100644 lightning/src/ln/peers/test_util.rs create mode 100644 lightning/src/ln/peers/transport.rs diff --git a/fuzz/src/peer_crypt.rs b/fuzz/src/peer_crypt.rs index 7d832122584..6ceb3d9c014 100644 --- a/fuzz/src/peer_crypt.rs +++ b/fuzz/src/peer_crypt.rs @@ -15,6 +15,7 @@ use bitcoin::secp256k1; use bitcoin::secp256k1::key::{PublicKey,SecretKey}; use lightning::ln::peers::conduit::Conduit; use lightning::ln::peers::handshake::PeerHandshake; +use lightning::ln::peers::transport::IPeerHandshake; use utils::test_logger; // Test structure used to generate "random" values based on input data. It is used throughout diff --git a/lightning/src/ln/peers/handler.rs b/lightning/src/ln/peers/handler.rs index 939cd2ef2f5..990838abf21 100644 --- a/lightning/src/ln/peers/handler.rs +++ b/lightning/src/ln/peers/handler.rs @@ -21,7 +21,7 @@ use ln::features::InitFeatures; use ln::msgs; use ln::msgs::{ChannelMessageHandler, LightningError, RoutingMessageHandler}; use ln::channelmanager::{SimpleArcChannelManager, SimpleRefChannelManager}; -use util::ser::{VecWriter, Writeable}; +use util::ser::{Writeable}; use ln::wire; use ln::wire::Encode; use util::byte_utils; @@ -38,12 +38,76 @@ use std::ops::Deref; use bitcoin::hashes::sha256::Hash as Sha256; use bitcoin::hashes::sha256::HashEngine as Sha256Engine; use bitcoin::hashes::{HashEngine, Hash}; -use ln::peers::handshake::PeerHandshake; -use ln::peers::conduit::Conduit; use ln::peers::outbound_queue::OutboundQueue; +use ln::peers::transport::Transport; const MSG_BUFF_SIZE: usize = 10; +/// Interface PeerHandler uses to interact with the Transport object +pub(super) trait ITransport { + /// Instantiate the new outbound Transport + fn new_outbound(initiator_static_private_key: &SecretKey, responder_static_public_key: &PublicKey, initiator_ephemeral_private_key: &SecretKey) -> Self; + + /// Set up the Transport receiving any bytes that need to be sent to the peer + fn set_up_outbound(&mut self) -> Vec; + + /// Instantiate a new inbound Transport + fn new_inbound(responder_static_private_key: &SecretKey, responder_ephemeral_private_key: &SecretKey) -> Self; + + /// Process input data similar to reading it off a descriptor directly. + fn process_input(&mut self, input: &[u8], output_buffer: &mut impl PayloadQueuer) -> Result<(), String>; + + /// Returns true if the connection is established and encrypted messages can be sent. + fn is_ready_for_encryption(&self) -> bool; + + /// Encodes, encrypts, and enqueues a message to the outbound queue. Panics if the connection is + /// not established yet. + fn enqueue_message(&mut self, message: &M, output_buffer: &mut Q); +} + + +/// Trait representing a container that allows enqueuing of Vec<[u8]> +pub(super) trait PayloadQueuer { + /// Enqueue item to the queue + fn push_back(&mut self, item: Vec); + + /// Returns true if the queue is empty + fn is_empty(&self) -> bool; + + /// Returns the amount of available space in queue + fn queue_space(&self) -> usize; +} + +/// Implement &mut PayloadQueuer passthroughs +impl<'a, T> PayloadQueuer for &'a mut T where + T: PayloadQueuer { + fn push_back(&mut self, item: Vec) { + T::push_back(self, item) + } + + fn is_empty(&self) -> bool { + T::is_empty(self) + } + + fn queue_space(&self) -> usize { + T::queue_space(self) + } +} + +/// Trait representing a container that can try to flush data through a SocketDescriptor +pub(super) trait SocketDescriptorFlusher { + /// Write previously enqueued data to the SocketDescriptor. A return of false indicates the + /// underlying SocketDescriptor could not fulfill the send_data() call and the blocked state + /// has been set. Use unblock() when the SocketDescriptor may have more room. + fn try_flush_one(&mut self, descriptor: &mut impl SocketDescriptor) -> bool; + + /// Clear the blocked state caused when a previous write failed + fn unblock(&mut self); + + /// Check if the container is in a blocked state + fn is_blocked(&self) -> bool; +} + /// Provides references to trait impls which handle different types of messages. pub struct MessageHandler where CM::Target: ChannelMessageHandler, @@ -123,126 +187,8 @@ enum InitSyncTracker{ NodesSyncing(PublicKey), } -/// Trait representing a container that allows enqueuing of Vec<[u8]> -pub(super) trait PayloadQueuer { - /// Enqueue item to the queue - fn push_back(&mut self, item: Vec); - - /// Returns true if the queue is empty - fn is_empty(&self) -> bool; - - /// Returns the amount of available space in queue - fn queue_space(&self) -> usize; -} - -/// Implement &mut PayloadQueuer passthroughs -impl<'a, T> PayloadQueuer for &'a mut T where - T: PayloadQueuer { - fn push_back(&mut self, item: Vec) { - T::push_back(self, item) - } - - fn is_empty(&self) -> bool { - T::is_empty(self) - } - - fn queue_space(&self) -> usize { - T::queue_space(self) - } -} - -/// Trait representing a container that can try to flush data through a SocketDescriptor -pub(super) trait SocketDescriptorFlusher { - /// Write previously enqueued data to the SocketDescriptor. A return of false indicates the - /// underlying SocketDescriptor could not fulfill the send_data() call and the blocked state - /// has been set. Use unblock() when the SocketDescriptor may have more room. - fn try_flush_one(&mut self, descriptor: &mut impl SocketDescriptor) -> bool; - - /// Clear the blocked state caused when a previous write failed - fn unblock(&mut self); - - /// Check if the container is in a blocked state - fn is_blocked(&self) -> bool; -} - -/// Implement &mut Flushable passthroughs -impl<'a, T> SocketDescriptorFlusher for &'a mut T where - T: SocketDescriptorFlusher { - fn try_flush_one(&mut self, descriptor: &mut impl SocketDescriptor) -> bool { - T::try_flush_one(self, descriptor) - } - - fn unblock(&mut self) { - T::unblock(self) - } - - fn is_blocked(&self) -> bool { - T::is_blocked(self) - } -} - -enum PeerState { - Authenticating(PeerHandshake), - Connected(Conduit), -} - -enum PeerDataProcessingDecision { - CompleteHandshake(PublicKey), - Continue, - Disconnect(PeerHandleError) -} - -impl PeerState { - fn is_ready_for_encryption(&self) -> bool { - match self { - &PeerState::Connected(_) => true, - _ => false - } - } - - fn process_peer_data(&mut self, data: &[u8], pending_outbound_buffer: &mut impl PayloadQueuer) -> PeerDataProcessingDecision { - let (new_state_opt, decision) = match self { - &mut PeerState::Authenticating(ref mut handshake) => { - match handshake.process_act(data) { - // Any errors originating from the handshake sequence result in Disconnect - Err(_e) => { - (None, PeerDataProcessingDecision::Disconnect(PeerHandleError { no_connection_possible: false })) - }, - // Otherwise, handshake may or may not be complete depending on whether or not - // we receive a (conduit, pubkey) - Ok((response_vec_option, conduit_and_remote_static_public_key_option)) => { - - // Any response generated by the handshake sequence is put into the response buffer - if let Some(response_vec) = response_vec_option { - pending_outbound_buffer.push_back(response_vec); - } - - // if process_act() returns the conduit and remote static public key (node id) - // the handshake is complete - if let Some((conduit, remote_static_public_key)) = conduit_and_remote_static_public_key_option { - (Some(PeerState::Connected(conduit)), PeerDataProcessingDecision::CompleteHandshake(remote_static_public_key)) - } else { - (None, PeerDataProcessingDecision::Continue) - } - } - } - }, - &mut PeerState::Connected(ref mut conduit) => { - conduit.read(data); - (None, PeerDataProcessingDecision::Continue) - } - }; - - if let Some(new_state) = new_state_opt { - *self = new_state; - } - - decision - } -} - struct Peer { - encryptor: PeerState, + transport: Transport, outbound: bool, their_node_id: Option, their_features: Option, @@ -351,14 +297,6 @@ impl From for MessageHandlingError { } } -macro_rules! encode_msg { - ($msg: expr) => {{ - let mut buffer = VecWriter(Vec::new()); - wire::write($msg, &mut buffer).unwrap(); - buffer.0 - }} -} - /// Manages and reacts to connection events. You probably want to use file descriptors as PeerIds. /// PeerIds may repeat, but only after socket_disconnected() has been called. impl PeerManager where @@ -395,7 +333,7 @@ impl PeerManager Vec { let peers = self.peers.lock().unwrap(); peers.peers.values().filter_map(|p| { - if !p.encryptor.is_ready_for_encryption() || p.their_features.is_none() { + if !p.transport.is_ready_for_encryption() || p.their_features.is_none() { return None; } p.their_node_id @@ -424,12 +362,12 @@ impl PeerManager Result, PeerHandleError> { - let mut handshake = PeerHandshake::new_outbound(&self.our_node_secret, &their_node_id, &self.get_ephemeral_key()); - let initial_bytes = handshake.set_up_outbound(); - let mut peers = self.peers.lock().unwrap(); + let mut transport = Transport::new_outbound(&self.our_node_secret, &their_node_id, &self.get_ephemeral_key()); + let initial_bytes = transport.set_up_outbound(); + if peers.peers.insert(descriptor, Peer { - encryptor: PeerState::Authenticating(handshake), + transport, outbound: true, their_node_id: Some(their_node_id.clone()), their_features: None, @@ -455,11 +393,9 @@ impl PeerManager Result<(), PeerHandleError> { - let handshake = PeerHandshake::new_inbound(&self.our_node_secret, &self.get_ephemeral_key()); - let mut peers = self.peers.lock().unwrap(); if peers.peers.insert(descriptor, Peer { - encryptor: PeerState::Authenticating(handshake), + transport: Transport::new_inbound(&self.our_node_secret, &self.get_ephemeral_key()), outbound: false, their_node_id: None, their_features: None, @@ -476,14 +412,12 @@ impl PeerManager { { log_trace!(self.logger, "Encoding and sending sync update message of type {} to {}", $msg.type_id(), log_pubkey!(peer.their_node_id.unwrap())); - match peer.encryptor { - PeerState::Connected(ref mut conduit) => peer.pending_outbound_buffer.push_back(conduit.encrypt(&encode_msg!($msg)[..])), - _ => panic!("peer must be connected!") - } + assert!(peer.transport.is_ready_for_encryption()); + peer.transport.enqueue_message($msg, &mut peer.pending_outbound_buffer) } } } @@ -497,12 +431,12 @@ impl PeerManager PeerManager PeerManager PeerManager(&self, peers_needing_send: &mut HashSet, peer: &mut Peer, descriptor: Descriptor, message: &M) { - let mut buffer = VecWriter(Vec::new()); - wire::write(message, &mut buffer).unwrap(); // crash if the write failed - let encoded_message = buffer.0; - log_trace!(self.logger, "Enqueueing message of type {} to {}", message.type_id(), log_pubkey!(peer.their_node_id.unwrap())); - match peer.encryptor { - PeerState::Connected(ref mut conduit) => peer.pending_outbound_buffer.push_back(conduit.encrypt(&encoded_message[..])), - _ => panic!("peer must be connected!") - } + peer.transport.enqueue_message(message, &mut peer.pending_outbound_buffer); peers_needing_send.insert(descriptor); } @@ -611,89 +538,98 @@ impl PeerManager panic!("Descriptor for read_event is not already known to PeerManager"), Some(peer) => { - let data_processing_decision = peer.encryptor.process_peer_data(data, &mut peer.pending_outbound_buffer); - match data_processing_decision { - PeerDataProcessingDecision::Disconnect(e) => { - log_trace!(self.logger, "Invalid act message; disconnecting: {}", e); - return Err(e); - } - - PeerDataProcessingDecision::CompleteHandshake(remote_pubkey) => { - peer.their_node_id = Some(remote_pubkey); + match peer.transport.process_input(data, &mut peer.pending_outbound_buffer) { + Err(e) => { + log_trace!(self.logger, "Error while processing input: {}", e); + return Err(PeerHandleError { no_connection_possible: false }) + }, + Ok(_) => { + + // If the transport is newly connected, do the appropriate set up for the connection + if peer.transport.is_ready_for_encryption() { + let their_node_id = peer.transport.their_node_id.unwrap(); + + match peers.node_id_to_descriptor.entry(their_node_id.clone()) { + hash_map::Entry::Occupied(entry) => { + if entry.get() != peer_descriptor { + // Existing entry in map is from a different descriptor, this is a duplicate + log_trace!(self.logger, "Got second connection with {}, closing", log_pubkey!(&their_node_id)); + peer.their_node_id = None; + return Err(PeerHandleError { no_connection_possible: false }); + } else { + // read_event for existing peer + } + }, + hash_map::Entry::Vacant(entry) => { + log_trace!(self.logger, "Finished noise handshake for connection with {}", log_pubkey!(&their_node_id)); + peer.their_node_id = Some(their_node_id.clone()); + + if peer.outbound { + let mut features = InitFeatures::known(); + if !self.message_handler.route_handler.should_request_full_sync(&their_node_id) { + features.clear_initial_routing_sync(); + } - if peer.outbound { - let mut features = InitFeatures::known(); - if !self.message_handler.route_handler.should_request_full_sync(&peer.their_node_id.unwrap()) { - features.clear_initial_routing_sync(); + let resp = msgs::Init { features }; + self.enqueue_message(&mut peers.peers_needing_send, peer, peer_descriptor.clone(), &resp); + } + entry.insert(peer_descriptor.clone()); + } } - - let resp = msgs::Init { features }; - self.enqueue_message(&mut peers.peers_needing_send, peer, peer_descriptor.clone(), &resp); } - - // insert node id - match peers.node_id_to_descriptor.entry(peer.their_node_id.unwrap()) { - hash_map::Entry::Occupied(_) => { - log_trace!(self.logger, "Got second connection with {}, closing", log_pubkey!(peer.their_node_id.unwrap())); - peer.their_node_id = None; // Unset so that we don't generate a peer_disconnected event - return Err(PeerHandleError { no_connection_possible: false }); - } - hash_map::Entry::Vacant(entry) => { - log_trace!(self.logger, "Finished noise handshake for connection with {}", log_pubkey!(peer.their_node_id.unwrap())); - entry.insert(peer_descriptor.clone()) - } - }; } - _ => { } - }; + } let mut received_messages = vec![]; - if let &mut PeerState::Connected(ref mut conduit) = &mut peer.encryptor { - // Using Iterators that can error requires special handling - // The item returned from next() has type Option, String>> - // The Some wrapper is stripped for each item inside the loop - // There are 3 valid match cases: - // 1) Some(Ok(Some(msg_data))) => Indicates a valid decrypted msg accessed via msg_data - // 2) Some(Err(_)) => Indicates an error during decryption that should be handled - // 3) None -> Indicates there were no messages available to decrypt - // Invalid Cases - // 1) Some(Ok(None)) => Translated to None case above so users of iterators can stop correctly - for msg_data_result in &mut conduit.decryptor { - match msg_data_result { - Ok(Some(msg_data)) => { - let mut reader = ::std::io::Cursor::new(&msg_data[..]); - let message_result = wire::read(&mut reader); - let message = match message_result { - Ok(x) => x, - Err(e) => { - match e { - msgs::DecodeError::UnknownVersion => return Err(PeerHandleError { no_connection_possible: false }), - msgs::DecodeError::UnknownRequiredFeature => { - log_debug!(self.logger, "Got a channel/node announcement with an known required feature flag, you may want to update!"); - continue; - } - msgs::DecodeError::InvalidValue => { - log_debug!(self.logger, "Got an invalid value while deserializing message"); - return Err(PeerHandleError { no_connection_possible: false }); - } - msgs::DecodeError::ShortRead => { - log_debug!(self.logger, "Deserialization failed due to shortness of message"); - return Err(PeerHandleError { no_connection_possible: false }); + match peer.transport.conduit { + None => { } + Some(ref mut conduit) => { + // Using Iterators that can error requires special handling + // The item returned from next() has type Option, String>> + // The Some wrapper is stripped for each item inside the loop + // There are 3 valid match cases: + // 1) Some(Ok(Some(msg_data))) => Indicates a valid decrypted msg accessed via msg_data + // 2) Some(Err(_)) => Indicates an error during decryption that should be handled + // 3) None -> Indicates there were no messages available to decrypt + // Invalid Cases + // 1) Some(Ok(None)) => Translated to None case above so users of iterators can stop correctly + for msg_data_result in &mut conduit.decryptor { + match msg_data_result { + Ok(Some(msg_data)) => { + let mut reader = ::std::io::Cursor::new(&msg_data[..]); + let message_result = wire::read(&mut reader); + let message = match message_result { + Ok(x) => x, + Err(e) => { + match e { + msgs::DecodeError::UnknownVersion => return Err(PeerHandleError { no_connection_possible: false }), + msgs::DecodeError::UnknownRequiredFeature => { + log_debug!(self.logger, "Got a channel/node announcement with an known required feature flag, you may want to update!"); + continue; + } + msgs::DecodeError::InvalidValue => { + log_debug!(self.logger, "Got an invalid value while deserializing message"); + return Err(PeerHandleError { no_connection_possible: false }); + } + msgs::DecodeError::ShortRead => { + log_debug!(self.logger, "Deserialization failed due to shortness of message"); + return Err(PeerHandleError { no_connection_possible: false }); + } + msgs::DecodeError::BadLengthDescriptor => return Err(PeerHandleError { no_connection_possible: false }), + msgs::DecodeError::Io(_) => return Err(PeerHandleError { no_connection_possible: false }), } - msgs::DecodeError::BadLengthDescriptor => return Err(PeerHandleError { no_connection_possible: false }), - msgs::DecodeError::Io(_) => return Err(PeerHandleError { no_connection_possible: false }), } - } - }; + }; - received_messages.push(message); - }, - Err(e) => { - log_trace!(self.logger, "Message decryption failed due to: {}", e); - return Err(PeerHandleError { no_connection_possible: false }); - } - Ok(None) => { - panic!("Invalid behavior. Conduit iterator should never return this match.") + received_messages.push(message); + }, + Err(e) => { + log_trace!(self.logger, "Message decryption failed due to: {}", e); + return Err(PeerHandleError { no_connection_possible: false }); + } + Ok(None) => { + panic!("Invalid behavior. Conduit iterator should never return this match.") + } } } } @@ -979,8 +915,8 @@ impl PeerManager PeerManager PeerManager PeerManager PeerManager PeerManager PeerManager PeerManager PeerManager PeerManager PeerManager { log_trace!(self.logger, "Handling BroadcastChannelAnnouncement event in peer_handler for short channel id {}", msg.contents.short_channel_id); if self.message_handler.route_handler.handle_channel_announcement(msg).is_ok() && self.message_handler.route_handler.handle_channel_update(update_msg).is_ok() { - let encoded_msg = encode_msg!(msg); - let encoded_update_msg = encode_msg!(update_msg); - for (ref descriptor, ref mut peer) in peers.peers.iter_mut() { - if !peer.encryptor.is_ready_for_encryption() || peer.their_features.is_none() || + if !peer.transport.is_ready_for_encryption() || peer.their_features.is_none() || !peer.should_forward_channel_announcement(msg.contents.short_channel_id) { continue } @@ -1145,9 +1078,9 @@ impl PeerManager PeerManager { log_trace!(self.logger, "Handling BroadcastNodeAnnouncement event in peer_handler"); if self.message_handler.route_handler.handle_node_announcement(msg).is_ok() { - let encoded_msg = encode_msg!(msg); - for (ref descriptor, ref mut peer) in peers.peers.iter_mut() { - if !peer.encryptor.is_ready_for_encryption() || peer.their_features.is_none() || + if !peer.transport.is_ready_for_encryption() || peer.their_features.is_none() || !peer.should_forward_node_announcement(msg.contents.node_id) { continue } - if let PeerState::Connected(ref mut conduit) = peer.encryptor { - peer.pending_outbound_buffer.push_back(conduit.encrypt(&encoded_msg[..])); + if peer.transport.is_ready_for_encryption() { + peer.transport.enqueue_message(msg, &mut peer.pending_outbound_buffer); } self.do_attempt_write_data(&mut (*descriptor).clone(), peer); } @@ -1173,15 +1104,13 @@ impl PeerManager { log_trace!(self.logger, "Handling BroadcastChannelUpdate event in peer_handler for short channel id {}", msg.contents.short_channel_id); if self.message_handler.route_handler.handle_channel_update(msg).is_ok() { - let encoded_msg = encode_msg!(msg); - for (ref descriptor, ref mut peer) in peers.peers.iter_mut() { - if !peer.encryptor.is_ready_for_encryption() || peer.their_features.is_none() || + if !peer.transport.is_ready_for_encryption() || peer.their_features.is_none() || !peer.should_forward_channel_announcement(msg.contents.short_channel_id) { continue } - if let PeerState::Connected(ref mut conduit) = peer.encryptor { - peer.pending_outbound_buffer.push_back(conduit.encrypt(&encoded_msg[..])); + if peer.transport.is_ready_for_encryption() { + peer.transport.enqueue_message(msg, &mut peer.pending_outbound_buffer); } self.do_attempt_write_data(&mut (*descriptor).clone(), peer); } @@ -1200,8 +1129,8 @@ impl PeerManager PeerManager PeerManager Self { + fn new_outbound(initiator_static_private_key: &SecretKey, responder_static_public_key: &PublicKey, initiator_ephemeral_private_key: &SecretKey) -> Self { let state = HandshakeState::new_initiator(initiator_static_private_key, responder_static_public_key, initiator_ephemeral_private_key); Self { @@ -47,16 +48,8 @@ impl PeerHandshake { } } - /// Instantiate a handshake in anticipation of a peer's first handshake act - pub fn new_inbound(responder_static_private_key: &SecretKey, responder_ephemeral_private_key: &SecretKey) -> Self { - Self { - state: Some(HandshakeState::new_responder(responder_static_private_key, responder_ephemeral_private_key)), - ready_to_process: true, - } - } - /// Initializes the outbound handshake and provides the initial bytes to send to the responder - pub fn set_up_outbound(&mut self) -> Vec { + fn set_up_outbound(&mut self) -> Vec { assert!(!self.ready_to_process); self.ready_to_process = true; @@ -67,6 +60,14 @@ impl PeerHandshake { response_vec_option.unwrap() } + /// Instantiate a new handshake in anticipation of a peer's first handshake act + fn new_inbound(responder_static_private_key: &SecretKey, responder_ephemeral_private_key: &SecretKey) -> Self { + Self { + state: Some(HandshakeState::new_responder(responder_static_private_key, responder_ephemeral_private_key)), + ready_to_process: true, + } + } + /// Process act dynamically /// # Arguments /// `input`: Byte slice received from peer as part of the handshake protocol @@ -75,7 +76,7 @@ impl PeerHandshake { /// Returns a tuple with the following components: /// `.0`: Byte vector containing the next act to send back to the peer per the handshake protocol /// `.1`: Some(Conduit, PublicKey) if the handshake was just processed to completion and messages can now be encrypted and decrypted - pub fn process_act(&mut self, input: &[u8]) -> Result<(Option>, Option<(Conduit, PublicKey)>), String> { + fn process_act(&mut self, input: &[u8]) -> Result<(Option>, Option<(Conduit, PublicKey)>), String> { assert!(self.ready_to_process); let cur_state = self.state.take().unwrap(); @@ -138,15 +139,6 @@ mod test { } } - macro_rules! assert_matches { - ($e:expr, $state_match:pat) => { - match $e { - $state_match => (), - _ => panic!() - } - } - } - macro_rules! do_process_act_or_panic { ($handshake:expr, $input:expr) => { $handshake.process_act($input).unwrap().0.unwrap() diff --git a/lightning/src/ln/peers/mod.rs b/lightning/src/ln/peers/mod.rs index 0fc6122a5e2..1156d939264 100644 --- a/lightning/src/ln/peers/mod.rs +++ b/lightning/src/ln/peers/mod.rs @@ -12,6 +12,10 @@ //! When a handshake completes, it returns an instance of Conduit. //! Conduit enables message encryption and decryption, and automatically handles key rotation. +#[cfg(test)] +#[macro_use] +mod test_util; + mod chacha; pub mod handler; mod hkdf5869rfc; @@ -26,3 +30,8 @@ mod conduit; pub mod handshake; #[cfg(not(feature = "fuzztarget"))] mod handshake; + +#[cfg(feature = "fuzztarget")] +pub mod transport; +#[cfg(not(feature = "fuzztarget"))] +mod transport; diff --git a/lightning/src/ln/peers/outbound_queue.rs b/lightning/src/ln/peers/outbound_queue.rs index d261e0f248a..241c8bd9638 100644 --- a/lightning/src/ln/peers/outbound_queue.rs +++ b/lightning/src/ln/peers/outbound_queue.rs @@ -37,6 +37,7 @@ impl PayloadQueuer for OutboundQueue { self.soft_limit - cmp::min(self.soft_limit, self.buffer.len()) } } + impl SocketDescriptorFlusher for OutboundQueue { fn try_flush_one(&mut self, descriptor: &mut impl SocketDescriptor) -> bool { // Exit early if a previous full write failed and haven't heard that there may be more @@ -92,104 +93,7 @@ impl OutboundQueue { #[cfg(test)] mod tests { use super::*; - use std::rc::Rc; - use std::cell::RefCell; - use std::hash::Hash; - use std::cmp; - - /// Mock implementation of the SocketDescriptor trait that can be used in tests to finely control - /// the send_data() behavior. - /// - ///Additionally, records the actual calls to send_data() for later validation. - #[derive(Debug, Eq)] - struct SocketDescriptorMock { - /// If true, all send_data() calls will succeed - unbounded: Rc>, - - /// Amount of free space in the descriptor for send_data() bytes - free_space: Rc>, - - /// Vector of arguments and return values to send_data() used for validation - send_recording: Rc, bool)>>>, - } - - impl SocketDescriptorMock { - /// Basic unbounded implementation where send_data() will always succeed - fn new() -> Self { - Self { - unbounded: Rc::new(RefCell::new(true)), - send_recording: Rc::new(RefCell::new(Vec::new())), - free_space: Rc::new(RefCell::new(0)) - } - } - - /// Used for tests that want to return partial sends after a certain amount of data is sent through send_data() - fn with_fixed_size(limit: usize) -> Self { - let mut descriptor = Self::new(); - descriptor.unbounded = Rc::new(RefCell::new(false)); - descriptor.free_space = Rc::new(RefCell::new(limit)); - - descriptor - } - - /// Standard Mock api to verify actual vs. expected calls - fn assert_called_with(&self, expectation: Vec<(Vec, bool)>) { - assert_eq!(expectation.as_slice(), self.send_recording.borrow().as_slice()) - } - - /// Allow future send_data() calls to succeed for the next added_room bytes. Not valid for - /// unbounded mock descriptors - fn make_room(&mut self, added_room: usize) { - assert!(!*self.unbounded.borrow()); - let mut free_space = self.free_space.borrow_mut(); - - *free_space += added_room; - } - } - - impl SocketDescriptor for SocketDescriptorMock { - fn send_data(&mut self, data: &[u8], resume_read: bool) -> usize { - self.send_recording.borrow_mut().push((data.to_vec(), resume_read)); - - let mut free_space = self.free_space.borrow_mut(); - - // Unbounded just flush everything - return if *self.unbounded.borrow() { - data.len() - } - // Bounded flush up to the free_space limit - else { - let write_len = cmp::min(data.len(), *free_space); - *free_space -= write_len; - write_len - } - } - - fn disconnect_socket(&mut self) { - unimplemented!() - } - } - - impl Clone for SocketDescriptorMock { - fn clone(&self) -> Self { - Self { - unbounded: self.unbounded.clone(), - send_recording: self.send_recording.clone(), - free_space: self.free_space.clone() - } - } - } - - impl PartialEq for SocketDescriptorMock { - fn eq(&self, o: &Self) -> bool { - Rc::ptr_eq(&self.send_recording, &o.send_recording) - } - } - impl Hash for SocketDescriptorMock { - fn hash(&self, state: &mut H) { - self.send_recording.as_ptr().hash(state) - } - } + use ln::peers::test_util::*; // Test that a try_flush_one() call with no queued data doesn't write anything #[test] diff --git a/lightning/src/ln/peers/test_util.rs b/lightning/src/ln/peers/test_util.rs new file mode 100644 index 00000000000..9d18ad86ace --- /dev/null +++ b/lightning/src/ln/peers/test_util.rs @@ -0,0 +1,214 @@ +// This file is Copyright its original authors, visible in version control +// history. +// +// This file is licensed under the Apache License, Version 2.0 or the MIT license +// , at your option. +// You may not use this file except in accordance with one or both of these +// licenses. + +/// Test library for test doubles used in the various peers unit tests + +use bitcoin::secp256k1; +use bitcoin::secp256k1::key::{PublicKey, SecretKey}; + +use ln::peers::conduit::Conduit; +use ln::peers::handler::{SocketDescriptor, PayloadQueuer}; +use ln::peers::transport::IPeerHandshake; + +use std::rc::Rc; +use std::cell::RefCell; +use std::hash::Hash; +use std::cmp; + +macro_rules! assert_matches { + ($actual:expr, $expected:pat) => { + match $actual { + $expected => (), + _ => panic!() + } + } +} + +/// Stub implementation of IPeerHandshake that returns an error for process_act() +pub(super) struct PeerHandshakeTestStubFail { } + +impl IPeerHandshake for PeerHandshakeTestStubFail { + fn new_outbound(_initiator_static_private_key: &SecretKey, _responder_static_public_key: &PublicKey, _initiator_ephemeral_private_key: &SecretKey) -> Self { + PeerHandshakeTestStubFail { } + } + + fn set_up_outbound(&mut self) -> Vec { + vec![] + } + + fn new_inbound(_responder_static_private_key: &SecretKey, _responder_ephemeral_private_key: &SecretKey) -> Self { + PeerHandshakeTestStubFail { } + } + + fn process_act(&mut self, _input: &[u8]) -> Result<(Option>, Option<(Conduit, PublicKey)>), String> { + Err("Oh no!".to_string()) + } +} + +/// Stub implementation of IPeerHandshake that returns &[1] from process_act() +pub(super) struct PeerHandshakeTestStubBytes { } + +impl PeerHandshakeTestStubBytes { + pub(crate) const RETURNED_BYTES: [u8; 1] = [1]; +} + +impl IPeerHandshake for PeerHandshakeTestStubBytes { + + fn new_outbound(_initiator_static_private_key: &SecretKey, _responder_static_public_key: &PublicKey, _initiator_ephemeral_private_key: &SecretKey) -> Self { + PeerHandshakeTestStubBytes { } + } + + fn set_up_outbound(&mut self) -> Vec { + vec![] + } + + fn new_inbound(_responder_static_private_key: &SecretKey, _responder_ephemeral_private_key: &SecretKey) -> Self { + PeerHandshakeTestStubBytes { } + } + + fn process_act(&mut self, _input: &[u8]) -> Result<(Option>, Option<(Conduit, PublicKey)>), String> { + Ok((Some(Self::RETURNED_BYTES[..].to_vec()), None)) + } +} + +/// Stub implementation of IPeerhandshake that returns Some(Conduit, PublicKey) +pub(super) struct PeerHandshakeTestStubComplete { } + +impl IPeerHandshake for PeerHandshakeTestStubComplete { + fn new_outbound(_initiator_static_private_key: &SecretKey, _responder_static_public_key: &PublicKey, _initiator_ephemeral_private_key: &SecretKey) -> Self { + PeerHandshakeTestStubComplete { } + } + + fn set_up_outbound(&mut self) -> Vec { + vec![] + } + + fn new_inbound(_responder_static_private_key: &SecretKey, _responder_ephemeral_private_key: &SecretKey) -> Self { + PeerHandshakeTestStubComplete { } + } + + fn process_act(&mut self, _input: &[u8]) -> Result<(Option>, Option<(Conduit, PublicKey)>), String> { + let curve = secp256k1::Secp256k1::new(); + let private_key = SecretKey::from_slice(&[0x_21_u8; 32]).unwrap(); + let public_key = PublicKey::from_secret_key(&curve, &private_key); + let conduit = Conduit::new([0;32], [0;32], [0;32]); + + Ok((None, Some((conduit, public_key)))) + } +} + +/// Mock implementation of the SocketDescriptor trait that can be used in tests to finely control +/// the send_data() behavior. +/// +/// Additionally, records the actual calls to send_data() for later validation. +#[derive(Debug, Eq)] +pub(super) struct SocketDescriptorMock { + /// If true, all send_data() calls will succeed + unbounded: Rc>, + + /// Amount of free space in the descriptor for send_data() bytes + free_space: Rc>, + + /// Vector of arguments and return values to send_data() used for validation + send_recording: Rc, bool)>>>, +} + +impl SocketDescriptorMock { + /// Basic unbounded implementation where send_data() will always succeed + pub(super) fn new() -> Self { + Self { + unbounded: Rc::new(RefCell::new(true)), + send_recording: Rc::new(RefCell::new(Vec::new())), + free_space: Rc::new(RefCell::new(0)) + } + } + + /// Used for tests that want to return partial sends after a certain amount of data is sent through send_data() + pub(super) fn with_fixed_size(limit: usize) -> Self { + let mut descriptor = Self::new(); + descriptor.unbounded = Rc::new(RefCell::new(false)); + descriptor.free_space = Rc::new(RefCell::new(limit)); + + descriptor + } + + /// Standard Mock api to verify actual vs. expected calls + pub(super) fn assert_called_with(&self, expectation: Vec<(Vec, bool)>) { + assert_eq!(expectation.as_slice(), self.send_recording.borrow().as_slice()) + } + + /// Allow future send_data() calls to succeed for the next added_room bytes. Not valid for + /// unbounded mock descriptors + pub(super) fn make_room(&mut self, added_room: usize) { + assert!(!*self.unbounded.borrow()); + let mut free_space = self.free_space.borrow_mut(); + + *free_space += added_room; + } +} + +impl SocketDescriptor for SocketDescriptorMock { + fn send_data(&mut self, data: &[u8], resume_read: bool) -> usize { + self.send_recording.borrow_mut().push((data.to_vec(), resume_read)); + + let mut free_space = self.free_space.borrow_mut(); + + // Unbounded just flush everything + return if *self.unbounded.borrow() { + data.len() + } + // Bounded flush up to the free_space limit + else { + let write_len = cmp::min(data.len(), *free_space); + *free_space -= write_len; + write_len + } + } + + fn disconnect_socket(&mut self) { + unimplemented!() + } +} + +impl Clone for SocketDescriptorMock { + fn clone(&self) -> Self { + Self { + unbounded: self.unbounded.clone(), + send_recording: self.send_recording.clone(), + free_space: self.free_space.clone() + } + } +} + +impl PartialEq for SocketDescriptorMock { + fn eq(&self, o: &Self) -> bool { + Rc::ptr_eq(&self.send_recording, &o.send_recording) + } +} +impl Hash for SocketDescriptorMock { + fn hash(&self, state: &mut H) { + self.send_recording.as_ptr().hash(state) + } +} + +/// Implement PayloadQueuer for Vec> so it can be used as a Spy in tests +impl PayloadQueuer for Vec> { + fn push_back(&mut self, item: Vec) { + self.push(item) + } + + fn is_empty(&self) -> bool { + self.is_empty() + } + + fn queue_space(&self) -> usize { + unimplemented!() + } +} + diff --git a/lightning/src/ln/peers/transport.rs b/lightning/src/ln/peers/transport.rs new file mode 100644 index 00000000000..12a72007f1c --- /dev/null +++ b/lightning/src/ln/peers/transport.rs @@ -0,0 +1,266 @@ +// This file is Copyright its original authors, visible in version control +// history. +// +// This file is licensed under the Apache License, Version 2.0 or the MIT license +// , at your option. +// You may not use this file except in accordance with one or both of these +// licenses. + +/// Abstraction for the transport layer as described by Bolt #8 [https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md] + +use bitcoin::secp256k1::{SecretKey, PublicKey}; + +use ln::peers::conduit::Conduit; +use ln::peers::handler::{ITransport, PayloadQueuer}; +use ln::peers::handshake::PeerHandshake; +use ln::wire; +use ln::wire::Encode; + +use util::ser::{Writeable, VecWriter}; + +/// Interface used by Transport to interact with a handshake object +pub trait IPeerHandshake { + /// Instantiate a new outbound handshake + fn new_outbound(initiator_static_private_key: &SecretKey, responder_static_public_key: &PublicKey, initiator_ephemeral_private_key: &SecretKey) -> Self; + + /// Set up the handshake receiving any bytes that need to be sent to the peer + fn set_up_outbound(&mut self) -> Vec; + + /// Instantiate a new inbound handshake + fn new_inbound(responder_static_private_key: &SecretKey, responder_ephemeral_private_key: &SecretKey) -> Self; + + /// Progress the handshake given bytes received from the peer. Returns Some(Conduit, PublicKey) when the handshake + /// is complete. + fn process_act(&mut self, input: &[u8]) -> Result<(Option>, Option<(Conduit, PublicKey)>), String>; +} + +pub(super) struct Transport { + pub(super) conduit: Option, + handshake: PeerHandshakeImpl, + pub(super) their_node_id: Option, +} + +impl ITransport for Transport { + fn new_outbound(initiator_static_private_key: &SecretKey, responder_static_public_key: &PublicKey, initiator_ephemeral_private_key: &SecretKey) -> Self { + Self { + conduit: None, + handshake: PeerHandshakeImpl::new_outbound(initiator_static_private_key, responder_static_public_key, initiator_ephemeral_private_key), + their_node_id: None, + } + } + + fn set_up_outbound(&mut self) -> Vec { + self.handshake.set_up_outbound().to_vec() + } + + fn new_inbound(responder_static_private_key: &SecretKey, responder_ephemeral_private_key: &SecretKey) -> Self { + Self { + conduit: None, + handshake: PeerHandshakeImpl::new_inbound(responder_static_private_key, responder_ephemeral_private_key), + their_node_id: None, + } + } + + fn process_input(&mut self, input: &[u8], output_buffer: &mut impl PayloadQueuer) -> Result<(), String> { + match self.conduit { + // Continue handshake + None => { + let (response_option, conduit_and_remote_pubkey_option) = self.handshake.process_act(input)?; + + // Any response generated by the handshake sequence is put into the response buffer + if let Some(response) = response_option { + output_buffer.push_back(response.to_vec()); + } + + // If handshake is complete change the state + if let Some((conduit, remote_pubkey)) = conduit_and_remote_pubkey_option { + self.conduit = Some(conduit); + self.their_node_id = Some(remote_pubkey); + } + } + Some(ref mut conduit) => { + conduit.read(input); + } + }; + + Ok(()) + } + + fn is_ready_for_encryption(&self) -> bool { + self.conduit.is_some() + } + + fn enqueue_message(&mut self, message: &M, output_buffer: &mut Q) { + match self.conduit { + None => panic!("Enqueueing messages only supported after transport is connected"), + Some(ref mut conduit) => { + let mut buffer = VecWriter(Vec::new()); + wire::write(message, &mut buffer).unwrap(); + output_buffer.push_back(conduit.encrypt(&buffer.0)); + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use ln::peers::test_util::*; + + use bitcoin::secp256k1; + use bitcoin::secp256k1::key::{PublicKey, SecretKey}; + use ln::msgs; + + fn create_outbound_for_test() -> Transport { + let curve = secp256k1::Secp256k1::new(); + let outbound_static_private_key = SecretKey::from_slice(&[0x_11_u8; 32]).unwrap(); + let outbound_ephemeral_private_key = SecretKey::from_slice(&[0x_12_u8; 32]).unwrap(); + let inbound_static_private_key = SecretKey::from_slice(&[0x_21_u8; 32]).unwrap(); + let inbound_static_public_key = PublicKey::from_secret_key(&curve, &inbound_static_private_key); + + Transport::::new_outbound(&outbound_static_private_key, &inbound_static_public_key, &outbound_ephemeral_private_key) + } + + fn create_inbound_for_test() -> Transport { + let inbound_static_private_key = SecretKey::from_slice(&[0x_21_u8; 32]).unwrap(); + let inbound_ephemeral_private_key = SecretKey::from_slice(&[0x_22_u8; 32]).unwrap(); + + Transport::::new_inbound(&inbound_static_private_key, &inbound_ephemeral_private_key) + } + + // Test initial states start off unconnected + #[test] + fn inbound_unconnected() { + let transport = create_inbound_for_test::(); + + assert!(!transport.is_ready_for_encryption()); + } + + #[test] + fn outbound_unconnected() { + let mut transport = create_outbound_for_test::(); + transport.set_up_outbound(); + + assert!(!transport.is_ready_for_encryption()); + } + + // Test that errors in the handshake code are reraised through the transport + #[test] + fn inbound_handshake_error_reraises() { + let mut transport = create_inbound_for_test::(); + let mut spy = Vec::new(); + + assert_eq!(transport.process_input(&[], &mut spy).err(), Some("Oh no!".to_string())); + } + + #[test] + fn outbound_handshake_error_reraises() { + let mut transport = create_outbound_for_test::(); + let mut spy = Vec::new(); + + assert_eq!(transport.process_input(&[], &mut spy).err(), Some("Oh no!".to_string())); + } + + #[test] + fn inbound_handshake_data_goes_to_queue() { + let mut transport = create_inbound_for_test::(); + let mut spy = Vec::new(); + + transport.process_input(&[], &mut spy).unwrap(); + assert!(!transport.is_ready_for_encryption()); + + assert_matches!(&spy[..], [_]); + } + + // Test that data returned from the in-progress handshake code makes it through to the outbound buffer + #[test] + fn outbound_handshake_data_goes_to_queue() { + let mut transport = create_outbound_for_test::(); + let mut spy = Vec::new(); + + transport.process_input(&[], &mut spy).unwrap(); + assert!(!transport.is_ready_for_encryption()); + + assert_matches!(&spy[..], [_]); + } + + #[test] + fn inbound_handshake_complete_ready_for_encryption() { + let mut transport = create_inbound_for_test::(); + let mut spy = Vec::new(); + + transport.process_input(&[], &mut spy).unwrap(); + assert!(transport.is_ready_for_encryption()); + } + + // Test that when a handshake completes is_ready_for_encryption() is correct + #[test] + fn outbound_handshake_complete_ready_for_encryption() { + let mut transport = create_outbound_for_test::(); + let mut spy = Vec::new(); + + transport.process_input(&[], &mut spy).unwrap(); + assert!(transport.is_ready_for_encryption()); + } + + #[test] + #[should_panic(expected = "Enqueueing messages only supported after transport is connected")] + fn inbound_enqueue_message_panic() { + let mut transport = create_inbound_for_test::(); + let mut spy = Vec::new(); + + let ping = msgs::Ping { + ponglen: 0, + byteslen: 64, + }; + transport.enqueue_message(&ping, &mut spy); + } + + // Test that enqueue_message() panics in the wrong state + #[test] + #[should_panic(expected = "Enqueueing messages only supported after transport is connected")] + fn outbound_enqueue_message_panic() { + let mut transport = create_outbound_for_test::(); + let mut spy = Vec::new(); + + let ping = msgs::Ping { + ponglen: 0, + byteslen: 64, + }; + transport.enqueue_message(&ping, &mut spy); + } + + // Test that enqueue_message() puts something into the outbound buffer + #[test] + fn inbound_enqueue_message_encrypts() { + let mut transport = create_inbound_for_test::(); + let mut spy = Vec::new(); + + transport.process_input(&[], &mut spy).unwrap(); + + let ping = msgs::Ping { + ponglen: 0, + byteslen: 64, + }; + transport.enqueue_message(&ping, &mut spy); + + assert_matches!(&spy[..], [_]); + } + + #[test] + fn outbound_enqueue_message_encrypts() { + let mut transport = create_outbound_for_test::(); + let mut spy = Vec::new(); + + transport.process_input(&[], &mut spy).unwrap(); + + let ping = msgs::Ping { + ponglen: 0, + byteslen: 64, + }; + transport.enqueue_message(&ping, &mut spy); + + assert_matches!(&spy[..], [_]); + } +} \ No newline at end of file From 62789ff8b8a0af04e41efb886ce674bc04612fcb Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Tue, 1 Sep 2020 13:58:39 -0700 Subject: [PATCH 49/72] rename: s/is_ready_for_encryption/is_connected/g --- lightning/src/ln/peers/handler.rs | 50 ++++++++++++++--------------- lightning/src/ln/peers/transport.rs | 16 ++++----- 2 files changed, 33 insertions(+), 33 deletions(-) diff --git a/lightning/src/ln/peers/handler.rs b/lightning/src/ln/peers/handler.rs index 990838abf21..1b934f78c5a 100644 --- a/lightning/src/ln/peers/handler.rs +++ b/lightning/src/ln/peers/handler.rs @@ -58,7 +58,7 @@ pub(super) trait ITransport { fn process_input(&mut self, input: &[u8], output_buffer: &mut impl PayloadQueuer) -> Result<(), String>; /// Returns true if the connection is established and encrypted messages can be sent. - fn is_ready_for_encryption(&self) -> bool; + fn is_connected(&self) -> bool; /// Encodes, encrypts, and enqueues a message to the outbound queue. Panics if the connection is /// not established yet. @@ -333,7 +333,7 @@ impl PeerManager Vec { let peers = self.peers.lock().unwrap(); peers.peers.values().filter_map(|p| { - if !p.transport.is_ready_for_encryption() || p.their_features.is_none() { + if !p.transport.is_connected() || p.their_features.is_none() { return None; } p.their_node_id @@ -416,7 +416,7 @@ impl PeerManager { { log_trace!(self.logger, "Encoding and sending sync update message of type {} to {}", $msg.type_id(), log_pubkey!(peer.their_node_id.unwrap())); - assert!(peer.transport.is_ready_for_encryption()); + assert!(peer.transport.is_connected()); peer.transport.enqueue_message($msg, &mut peer.pending_outbound_buffer) } } @@ -546,7 +546,7 @@ impl PeerManager { // If the transport is newly connected, do the appropriate set up for the connection - if peer.transport.is_ready_for_encryption() { + if peer.transport.is_connected() { let their_node_id = peer.transport.their_node_id.unwrap(); match peers.node_id_to_descriptor.entry(their_node_id.clone()) { @@ -915,7 +915,7 @@ impl PeerManager PeerManager PeerManager PeerManager PeerManager PeerManager PeerManager PeerManager PeerManager PeerManager PeerManager PeerManager PeerManager PeerManager PeerManager PeerManager PeerManager PeerManager { // This can't actually happen as we should have hit - // is_ready_for_encryption() previously on this same peer. + // is_connected() previously on this same peer. unreachable!(); }, } @@ -1233,7 +1233,7 @@ impl PeerManager ITransport for Transport bool { + fn is_connected(&self) -> bool { self.conduit.is_some() } @@ -134,7 +134,7 @@ mod tests { fn inbound_unconnected() { let transport = create_inbound_for_test::(); - assert!(!transport.is_ready_for_encryption()); + assert!(!transport.is_connected()); } #[test] @@ -142,7 +142,7 @@ mod tests { let mut transport = create_outbound_for_test::(); transport.set_up_outbound(); - assert!(!transport.is_ready_for_encryption()); + assert!(!transport.is_connected()); } // Test that errors in the handshake code are reraised through the transport @@ -168,7 +168,7 @@ mod tests { let mut spy = Vec::new(); transport.process_input(&[], &mut spy).unwrap(); - assert!(!transport.is_ready_for_encryption()); + assert!(!transport.is_connected()); assert_matches!(&spy[..], [_]); } @@ -180,7 +180,7 @@ mod tests { let mut spy = Vec::new(); transport.process_input(&[], &mut spy).unwrap(); - assert!(!transport.is_ready_for_encryption()); + assert!(!transport.is_connected()); assert_matches!(&spy[..], [_]); } @@ -191,17 +191,17 @@ mod tests { let mut spy = Vec::new(); transport.process_input(&[], &mut spy).unwrap(); - assert!(transport.is_ready_for_encryption()); + assert!(transport.is_connected()); } - // Test that when a handshake completes is_ready_for_encryption() is correct + // Test that when a handshake completes is_connected() is correct #[test] fn outbound_handshake_complete_ready_for_encryption() { let mut transport = create_outbound_for_test::(); let mut spy = Vec::new(); transport.process_input(&[], &mut spy).unwrap(); - assert!(transport.is_ready_for_encryption()); + assert!(transport.is_connected()); } #[test] From d5954459dfe7890603f23885e3fd87b6a7bab695 Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Tue, 1 Sep 2020 14:11:42 -0700 Subject: [PATCH 50/72] refactor: Move drain_messages() behind Transport --- lightning/src/ln/peers/handler.rs | 60 ++----------------- lightning/src/ln/peers/transport.rs | 89 ++++++++++++++++++++++++++++- 2 files changed, 91 insertions(+), 58 deletions(-) diff --git a/lightning/src/ln/peers/handler.rs b/lightning/src/ln/peers/handler.rs index 1b934f78c5a..28d3afaee2a 100644 --- a/lightning/src/ln/peers/handler.rs +++ b/lightning/src/ln/peers/handler.rs @@ -23,7 +23,7 @@ use ln::msgs::{ChannelMessageHandler, LightningError, RoutingMessageHandler}; use ln::channelmanager::{SimpleArcChannelManager, SimpleRefChannelManager}; use util::ser::{Writeable}; use ln::wire; -use ln::wire::Encode; +use ln::wire::{Encode, Message}; use util::byte_utils; use util::events::{MessageSendEvent, MessageSendEventsProvider}; use util::logger::Logger; @@ -60,6 +60,9 @@ pub(super) trait ITransport { /// Returns true if the connection is established and encrypted messages can be sent. fn is_connected(&self) -> bool; + /// Returns all Messages that have been received and can be parsed by the Transport + fn drain_messages(&mut self, logger: L) -> Result, PeerHandleError> where L::Target: Logger; + /// Encodes, encrypts, and enqueues a message to the outbound queue. Panics if the connection is /// not established yet. fn enqueue_message(&mut self, message: &M, output_buffer: &mut Q); @@ -580,60 +583,7 @@ impl PeerManager { } - Some(ref mut conduit) => { - // Using Iterators that can error requires special handling - // The item returned from next() has type Option, String>> - // The Some wrapper is stripped for each item inside the loop - // There are 3 valid match cases: - // 1) Some(Ok(Some(msg_data))) => Indicates a valid decrypted msg accessed via msg_data - // 2) Some(Err(_)) => Indicates an error during decryption that should be handled - // 3) None -> Indicates there were no messages available to decrypt - // Invalid Cases - // 1) Some(Ok(None)) => Translated to None case above so users of iterators can stop correctly - for msg_data_result in &mut conduit.decryptor { - match msg_data_result { - Ok(Some(msg_data)) => { - let mut reader = ::std::io::Cursor::new(&msg_data[..]); - let message_result = wire::read(&mut reader); - let message = match message_result { - Ok(x) => x, - Err(e) => { - match e { - msgs::DecodeError::UnknownVersion => return Err(PeerHandleError { no_connection_possible: false }), - msgs::DecodeError::UnknownRequiredFeature => { - log_debug!(self.logger, "Got a channel/node announcement with an known required feature flag, you may want to update!"); - continue; - } - msgs::DecodeError::InvalidValue => { - log_debug!(self.logger, "Got an invalid value while deserializing message"); - return Err(PeerHandleError { no_connection_possible: false }); - } - msgs::DecodeError::ShortRead => { - log_debug!(self.logger, "Deserialization failed due to shortness of message"); - return Err(PeerHandleError { no_connection_possible: false }); - } - msgs::DecodeError::BadLengthDescriptor => return Err(PeerHandleError { no_connection_possible: false }), - msgs::DecodeError::Io(_) => return Err(PeerHandleError { no_connection_possible: false }), - } - } - }; - - received_messages.push(message); - }, - Err(e) => { - log_trace!(self.logger, "Message decryption failed due to: {}", e); - return Err(PeerHandleError { no_connection_possible: false }); - } - Ok(None) => { - panic!("Invalid behavior. Conduit iterator should never return this match.") - } - } - } - } - } + let received_messages = peer.transport.drain_messages(&*self.logger)?; for message in received_messages { macro_rules! try_potential_handleerror { diff --git a/lightning/src/ln/peers/transport.rs b/lightning/src/ln/peers/transport.rs index fe2cb884944..1408783a994 100644 --- a/lightning/src/ln/peers/transport.rs +++ b/lightning/src/ln/peers/transport.rs @@ -12,12 +12,14 @@ use bitcoin::secp256k1::{SecretKey, PublicKey}; use ln::peers::conduit::Conduit; -use ln::peers::handler::{ITransport, PayloadQueuer}; +use ln::peers::handler::{ITransport, PeerHandleError, PayloadQueuer}; use ln::peers::handshake::PeerHandshake; -use ln::wire; -use ln::wire::Encode; +use ln::{wire, msgs}; +use ln::wire::{Encode, Message}; use util::ser::{Writeable, VecWriter}; +use util::logger::Logger; +use std::ops::Deref; /// Interface used by Transport to interact with a handshake object pub trait IPeerHandshake { @@ -87,6 +89,68 @@ impl ITransport for Transport(&mut self, logger: L) -> Result, PeerHandleError> + where L::Target: Logger { + + let mut received_messages = vec![]; + + match self.conduit { + None => {} + Some(ref mut conduit) => { + // Using Iterators that can error requires special handling + // The item returned from next() has type Option, String>> + // The Some wrapper is stripped for each item inside the loop + // There are 3 valid match cases: + // 1) Some(Ok(Some(msg_data))) => Indicates a valid decrypted msg accessed via msg_data + // 2) Some(Err(_)) => Indicates an error during decryption that should be handled + // 3) None -> Indicates there were no messages available to decrypt + // Invalid Cases + // 1) Some(Ok(None)) => Translated to None case above so users of iterators can stop correctly + for msg_data_result in &mut conduit.decryptor { + match msg_data_result { + Ok(Some(msg_data)) => { + let mut reader = ::std::io::Cursor::new(&msg_data[..]); + let message_result = wire::read(&mut reader); + let message = match message_result { + Ok(x) => x, + Err(e) => { + match e { + msgs::DecodeError::UnknownVersion => return Err(PeerHandleError { no_connection_possible: false }), + msgs::DecodeError::UnknownRequiredFeature => { + log_debug!(logger, "Got a channel/node announcement with an known required feature flag, you may want to update!"); + continue; + } + msgs::DecodeError::InvalidValue => { + log_debug!(logger, "Got an invalid value while deserializing message"); + return Err(PeerHandleError { no_connection_possible: false }); + } + msgs::DecodeError::ShortRead => { + log_debug!(logger, "Deserialization failed due to shortness of message"); + return Err(PeerHandleError { no_connection_possible: false }); + } + msgs::DecodeError::BadLengthDescriptor => return Err(PeerHandleError { no_connection_possible: false }), + msgs::DecodeError::Io(_) => return Err(PeerHandleError { no_connection_possible: false }), + } + } + }; + + received_messages.push(message); + }, + Err(e) => { + log_trace!(logger, "Message decryption failed due to: {}", e); + return Err(PeerHandleError { no_connection_possible: false }); + } + Ok(None) => { + panic!("Invalid behavior. Conduit iterator should never return this match.") + } + } + } + } + } + + Ok(received_messages) + } + fn is_connected(&self) -> bool { self.conduit.is_some() } @@ -111,6 +175,7 @@ mod tests { use bitcoin::secp256k1; use bitcoin::secp256k1::key::{PublicKey, SecretKey}; use ln::msgs; + use util::test_utils::TestLogger; fn create_outbound_for_test() -> Transport { let curve = secp256k1::Secp256k1::new(); @@ -263,4 +328,22 @@ mod tests { assert_matches!(&spy[..], [_]); } + + #[test] + fn inbound_not_connected_empty() { + let logger = TestLogger::new(); + let mut transport = create_inbound_for_test::(); + + let messages = transport.drain_messages(&logger).unwrap(); + assert_eq!(messages.len(), 0); + } + + #[test] + fn outbound_not_connected_empty() { + let logger = TestLogger::new(); + let mut transport = create_outbound_for_test::(); + + let messages = transport.drain_messages(&logger).unwrap(); + assert_eq!(messages.len(), 0); + } } \ No newline at end of file From 22d3db2c57285b74cd346108d46806c143d587ec Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Sun, 30 Aug 2020 18:31:50 -0700 Subject: [PATCH 51/72] refactor: remove duplicate assert --- lightning/src/ln/peers/handler.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/lightning/src/ln/peers/handler.rs b/lightning/src/ln/peers/handler.rs index 28d3afaee2a..6c97d97efdd 100644 --- a/lightning/src/ln/peers/handler.rs +++ b/lightning/src/ln/peers/handler.rs @@ -419,7 +419,6 @@ impl PeerManager { { log_trace!(self.logger, "Encoding and sending sync update message of type {} to {}", $msg.type_id(), log_pubkey!(peer.their_node_id.unwrap())); - assert!(peer.transport.is_connected()); peer.transport.enqueue_message($msg, &mut peer.pending_outbound_buffer) } } From 05dc443e2ccd4dd5884865034868b3e7e2387919 Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Tue, 1 Sep 2020 14:36:10 -0700 Subject: [PATCH 52/72] refactor: Move all enqueue logging to Transport --- lightning/src/ln/peers/handler.rs | 82 +++++++++++++---------------- lightning/src/ln/peers/transport.rs | 18 +++++-- 2 files changed, 49 insertions(+), 51 deletions(-) diff --git a/lightning/src/ln/peers/handler.rs b/lightning/src/ln/peers/handler.rs index 6c97d97efdd..e2f57edb8b9 100644 --- a/lightning/src/ln/peers/handler.rs +++ b/lightning/src/ln/peers/handler.rs @@ -65,7 +65,7 @@ pub(super) trait ITransport { /// Encodes, encrypts, and enqueues a message to the outbound queue. Panics if the connection is /// not established yet. - fn enqueue_message(&mut self, message: &M, output_buffer: &mut Q); + fn enqueue_message(&mut self, message: &M, output_buffer: &mut Q, logger: L) where L::Target: Logger; } @@ -415,15 +415,6 @@ impl PeerManager { - { - log_trace!(self.logger, "Encoding and sending sync update message of type {} to {}", $msg.type_id(), log_pubkey!(peer.their_node_id.unwrap())); - peer.transport.enqueue_message($msg, &mut peer.pending_outbound_buffer) - } - } - } - while !peer.pending_outbound_buffer.is_blocked() { let queue_space = peer.pending_outbound_buffer.queue_space(); if queue_space > 0 { @@ -433,12 +424,12 @@ impl PeerManager PeerManager PeerManager PeerManager(&self, peers_needing_send: &mut HashSet, peer: &mut Peer, descriptor: Descriptor, message: &M) { - log_trace!(self.logger, "Enqueueing message of type {} to {}", message.type_id(), log_pubkey!(peer.their_node_id.unwrap())); - peer.transport.enqueue_message(message, &mut peer.pending_outbound_buffer); - peers_needing_send.insert(descriptor); + fn enqueue_message(&self, peers_needing_send: &mut HashSet, transport: &mut impl ITransport, output_buffer: &mut impl PayloadQueuer, descriptor: &Descriptor, message: &M) { + transport.enqueue_message(message, output_buffer, &*self.logger); + peers_needing_send.insert(descriptor.clone()); } fn do_read_event(&self, peer_descriptor: &mut Descriptor, data: &[u8]) -> Result { @@ -573,7 +563,7 @@ impl PeerManager PeerManager { log_trace!(self.logger, "Got Err handling message, sending Error message because {}", e.err); - self.enqueue_message(&mut peers.peers_needing_send, peer, peer_descriptor.clone(), &msg); + self.enqueue_message(&mut peers.peers_needing_send, &mut peer.transport, &mut peer.pending_outbound_buffer, peer_descriptor, &msg); continue; }, } @@ -684,7 +674,7 @@ impl PeerManager PeerManager { if msg.ponglen < 65532 { let resp = msgs::Pong { byteslen: msg.ponglen }; - self.enqueue_message(peers_needing_send, peer, peer_descriptor.clone(), &resp); + self.enqueue_message(peers_needing_send, &mut peer.transport, &mut peer.pending_outbound_buffer, &peer_descriptor, &resp); } }, wire::Message::Pong(_msg) => { @@ -865,7 +855,7 @@ impl PeerManager PeerManager PeerManager PeerManager PeerManager PeerManager PeerManager PeerManager PeerManager PeerManager PeerManager PeerManager PeerManager PeerManager PeerManager PeerManager PeerManager ITransport for Transport(&mut self, message: &M, output_buffer: &mut Q) { + fn enqueue_message(&mut self, message: &M, output_buffer: &mut Q, logger: L) + where L::Target: Logger { + match self.conduit { None => panic!("Enqueueing messages only supported after transport is connected"), Some(ref mut conduit) => { + log_trace!(logger, "Enqueueing message of type {} to {}", message.type_id(), log_pubkey!(self.their_node_id.unwrap())); + let mut buffer = VecWriter(Vec::new()); wire::write(message, &mut buffer).unwrap(); output_buffer.push_back(conduit.encrypt(&buffer.0)); @@ -272,6 +276,7 @@ mod tests { #[test] #[should_panic(expected = "Enqueueing messages only supported after transport is connected")] fn inbound_enqueue_message_panic() { + let logger = TestLogger::new(); let mut transport = create_inbound_for_test::(); let mut spy = Vec::new(); @@ -279,13 +284,14 @@ mod tests { ponglen: 0, byteslen: 64, }; - transport.enqueue_message(&ping, &mut spy); + transport.enqueue_message(&ping, &mut spy, &logger); } // Test that enqueue_message() panics in the wrong state #[test] #[should_panic(expected = "Enqueueing messages only supported after transport is connected")] fn outbound_enqueue_message_panic() { + let logger = TestLogger::new(); let mut transport = create_outbound_for_test::(); let mut spy = Vec::new(); @@ -293,12 +299,13 @@ mod tests { ponglen: 0, byteslen: 64, }; - transport.enqueue_message(&ping, &mut spy); + transport.enqueue_message(&ping, &mut spy, &logger); } // Test that enqueue_message() puts something into the outbound buffer #[test] fn inbound_enqueue_message_encrypts() { + let logger = TestLogger::new(); let mut transport = create_inbound_for_test::(); let mut spy = Vec::new(); @@ -308,13 +315,14 @@ mod tests { ponglen: 0, byteslen: 64, }; - transport.enqueue_message(&ping, &mut spy); + transport.enqueue_message(&ping, &mut spy, &logger); assert_matches!(&spy[..], [_]); } #[test] fn outbound_enqueue_message_encrypts() { + let logger = TestLogger::new(); let mut transport = create_outbound_for_test::(); let mut spy = Vec::new(); @@ -324,7 +332,7 @@ mod tests { ponglen: 0, byteslen: 64, }; - transport.enqueue_message(&ping, &mut spy); + transport.enqueue_message(&ping, &mut spy, &logger); assert_matches!(&spy[..], [_]); } From a67264998941ca1f5003fa294a7c8a78c28f4217 Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Fri, 4 Sep 2020 15:21:30 -0700 Subject: [PATCH 53/72] refactor: Make Transport the source of their_node_id This patch ontinues to separate state the exists before NOISE is complete and after it is complete to unlock future refactoring. Most callers immediately unwrapped the value from Peer and can just call Transport::get_their_node_id(). The duplicate connection disconnect path has been rewritten to determine whether or not to remove & send a disconnect event without needing to use a None value for Option All other users are in contexts where they either exit early or continue if !transport.is_connected() so it is also safe to call Transport::get_their_node_id() --- lightning/src/ln/peers/handler.rs | 101 +++++++++++++--------------- lightning/src/ln/peers/transport.rs | 44 +++++++++++- 2 files changed, 90 insertions(+), 55 deletions(-) diff --git a/lightning/src/ln/peers/handler.rs b/lightning/src/ln/peers/handler.rs index e2f57edb8b9..3871e5f6653 100644 --- a/lightning/src/ln/peers/handler.rs +++ b/lightning/src/ln/peers/handler.rs @@ -60,6 +60,9 @@ pub(super) trait ITransport { /// Returns true if the connection is established and encrypted messages can be sent. fn is_connected(&self) -> bool; + /// Returns the node_id of the remote node. Panics if not connected. + fn get_their_node_id(&self) -> PublicKey; + /// Returns all Messages that have been received and can be parsed by the Transport fn drain_messages(&mut self, logger: L) -> Result, PeerHandleError> where L::Target: Logger; @@ -193,7 +196,6 @@ enum InitSyncTracker{ struct Peer { transport: Transport, outbound: bool, - their_node_id: Option, their_features: Option, pending_outbound_buffer: OutboundQueue, @@ -339,7 +341,7 @@ impl PeerManager PeerManager PeerManager PeerManager { if entry.get() != peer_descriptor { // Existing entry in map is from a different descriptor, this is a duplicate log_trace!(self.logger, "Got second connection with {}, closing", log_pubkey!(&their_node_id)); - peer.their_node_id = None; return Err(PeerHandleError { no_connection_possible: false }); } else { // read_event for existing peer @@ -554,7 +553,6 @@ impl PeerManager { log_trace!(self.logger, "Finished noise handshake for connection with {}", log_pubkey!(&their_node_id)); - peer.their_node_id = Some(their_node_id.clone()); if peer.outbound { let mut features = InitFeatures::known(); @@ -625,12 +623,12 @@ impl PeerManager, peer: &mut Peer, peer_descriptor: Descriptor, message: wire::Message) -> Result<(), MessageHandlingError> { - log_trace!(self.logger, "Received message of type {} from {}", message.type_id(), log_pubkey!(peer.their_node_id.unwrap())); + log_trace!(self.logger, "Received message of type {} from {}", message.type_id(), log_pubkey!(peer.transport.get_their_node_id())); // Need an Init as first message if let wire::Message::Init(_) = message { } else if peer.their_features.is_none() { - log_trace!(self.logger, "Peer {} sent non-Init first message", log_pubkey!(peer.their_node_id.unwrap())); + log_trace!(self.logger, "Peer {} sent non-Init first message", log_pubkey!(peer.transport.get_their_node_id())); return Err(PeerHandleError{ no_connection_possible: false }.into()); } @@ -663,13 +661,13 @@ impl PeerManager PeerManager { @@ -690,11 +688,11 @@ impl PeerManager PeerManager { - self.message_handler.chan_handler.handle_open_channel(&peer.their_node_id.unwrap(), peer.their_features.clone().unwrap(), &msg); + self.message_handler.chan_handler.handle_open_channel(&peer.transport.get_their_node_id(), peer.their_features.clone().unwrap(), &msg); }, wire::Message::AcceptChannel(msg) => { - self.message_handler.chan_handler.handle_accept_channel(&peer.their_node_id.unwrap(), peer.their_features.clone().unwrap(), &msg); + self.message_handler.chan_handler.handle_accept_channel(&peer.transport.get_their_node_id(), peer.their_features.clone().unwrap(), &msg); }, wire::Message::FundingCreated(msg) => { - self.message_handler.chan_handler.handle_funding_created(&peer.their_node_id.unwrap(), &msg); + self.message_handler.chan_handler.handle_funding_created(&peer.transport.get_their_node_id(), &msg); }, wire::Message::FundingSigned(msg) => { - self.message_handler.chan_handler.handle_funding_signed(&peer.their_node_id.unwrap(), &msg); + self.message_handler.chan_handler.handle_funding_signed(&peer.transport.get_their_node_id(), &msg); }, wire::Message::FundingLocked(msg) => { - self.message_handler.chan_handler.handle_funding_locked(&peer.their_node_id.unwrap(), &msg); + self.message_handler.chan_handler.handle_funding_locked(&peer.transport.get_their_node_id(), &msg); }, wire::Message::Shutdown(msg) => { - self.message_handler.chan_handler.handle_shutdown(&peer.their_node_id.unwrap(), &msg); + self.message_handler.chan_handler.handle_shutdown(&peer.transport.get_their_node_id(), &msg); }, wire::Message::ClosingSigned(msg) => { - self.message_handler.chan_handler.handle_closing_signed(&peer.their_node_id.unwrap(), &msg); + self.message_handler.chan_handler.handle_closing_signed(&peer.transport.get_their_node_id(), &msg); }, // Commitment messages: wire::Message::UpdateAddHTLC(msg) => { - self.message_handler.chan_handler.handle_update_add_htlc(&peer.their_node_id.unwrap(), &msg); + self.message_handler.chan_handler.handle_update_add_htlc(&peer.transport.get_their_node_id(), &msg); }, wire::Message::UpdateFulfillHTLC(msg) => { - self.message_handler.chan_handler.handle_update_fulfill_htlc(&peer.their_node_id.unwrap(), &msg); + self.message_handler.chan_handler.handle_update_fulfill_htlc(&peer.transport.get_their_node_id(), &msg); }, wire::Message::UpdateFailHTLC(msg) => { - self.message_handler.chan_handler.handle_update_fail_htlc(&peer.their_node_id.unwrap(), &msg); + self.message_handler.chan_handler.handle_update_fail_htlc(&peer.transport.get_their_node_id(), &msg); }, wire::Message::UpdateFailMalformedHTLC(msg) => { - self.message_handler.chan_handler.handle_update_fail_malformed_htlc(&peer.their_node_id.unwrap(), &msg); + self.message_handler.chan_handler.handle_update_fail_malformed_htlc(&peer.transport.get_their_node_id(), &msg); }, wire::Message::CommitmentSigned(msg) => { - self.message_handler.chan_handler.handle_commitment_signed(&peer.their_node_id.unwrap(), &msg); + self.message_handler.chan_handler.handle_commitment_signed(&peer.transport.get_their_node_id(), &msg); }, wire::Message::RevokeAndACK(msg) => { - self.message_handler.chan_handler.handle_revoke_and_ack(&peer.their_node_id.unwrap(), &msg); + self.message_handler.chan_handler.handle_revoke_and_ack(&peer.transport.get_their_node_id(), &msg); }, wire::Message::UpdateFee(msg) => { - self.message_handler.chan_handler.handle_update_fee(&peer.their_node_id.unwrap(), &msg); + self.message_handler.chan_handler.handle_update_fee(&peer.transport.get_their_node_id(), &msg); }, wire::Message::ChannelReestablish(msg) => { - self.message_handler.chan_handler.handle_channel_reestablish(&peer.their_node_id.unwrap(), &msg); + self.message_handler.chan_handler.handle_channel_reestablish(&peer.transport.get_their_node_id(), &msg); }, // Routing messages: wire::Message::AnnouncementSignatures(msg) => { - self.message_handler.chan_handler.handle_announcement_signatures(&peer.their_node_id.unwrap(), &msg); + self.message_handler.chan_handler.handle_announcement_signatures(&peer.transport.get_their_node_id(), &msg); }, wire::Message::ChannelAnnouncement(msg) => { let should_forward = match self.message_handler.route_handler.handle_channel_announcement(&msg) { @@ -1009,13 +1007,10 @@ impl PeerManager continue, - Some(their_node_id) => { - if their_node_id == msg.contents.node_id_1 || their_node_id == msg.contents.node_id_2 { - continue - } - } + + let their_node_id = peer.transport.get_their_node_id(); + if their_node_id == msg.contents.node_id_1 || their_node_id == msg.contents.node_id_2 { + continue } if peer.transport.is_connected() { peer.transport.enqueue_message(msg, &mut peer.pending_outbound_buffer, &*self.logger); @@ -1128,12 +1123,17 @@ impl PeerManager panic!("Descriptor for disconnect_event is not already known to PeerManager"), Some(peer) => { - match peer.their_node_id { - Some(node_id) => { + if peer.transport.is_connected() { + let node_id = peer.transport.get_their_node_id(); + + if peers.node_id_to_descriptor.get(&node_id).unwrap() == descriptor { peers.node_id_to_descriptor.remove(&node_id); self.message_handler.chan_handler.peer_disconnected(&node_id, no_connection_possible); - }, - None => {} + } else { + // This must have been generated from a duplicate connection error + } + } else { + // Unconnected nodes never make it into node_id_to_descriptor } } }; @@ -1156,18 +1156,11 @@ impl PeerManager { - log_trace!(self.logger, "Disconnecting peer with id {} due to ping timeout", node_id); - node_id_to_descriptor.remove(&node_id); - self.message_handler.chan_handler.peer_disconnected(&node_id, false); - } - None => { - // This can't actually happen as we should have hit - // is_connected() previously on this same peer. - unreachable!(); - }, - } + let their_node_id = peer.transport.get_their_node_id(); + log_trace!(self.logger, "Disconnecting peer with id {} due to ping timeout", their_node_id); + node_id_to_descriptor.remove(&their_node_id); + self.message_handler.chan_handler.peer_disconnected(&their_node_id, false); + return false; } diff --git a/lightning/src/ln/peers/transport.rs b/lightning/src/ln/peers/transport.rs index cf416899cc5..4b058e6800c 100644 --- a/lightning/src/ln/peers/transport.rs +++ b/lightning/src/ln/peers/transport.rs @@ -40,7 +40,7 @@ pub trait IPeerHandshake { pub(super) struct Transport { pub(super) conduit: Option, handshake: PeerHandshakeImpl, - pub(super) their_node_id: Option, + their_node_id: Option, } impl ITransport for Transport { @@ -169,6 +169,11 @@ impl ITransport for Transport PublicKey { + assert!(self.is_connected(), "Retrieving the remote node_id is only supported after transport is connected"); + self.their_node_id.unwrap() + } } #[cfg(test)] @@ -263,6 +268,42 @@ mod tests { assert!(transport.is_connected()); } + // Test get_their_node_id() in unconnected and connected scenarios + #[test] + #[should_panic(expected = "Retrieving the remote node_id is only supported after transport is connected")] + fn inbound_unconnected_get_their_node_id_panics() { + let transport = create_inbound_for_test::(); + + let _should_panic = transport.get_their_node_id(); + } + + #[test] + #[should_panic(expected = "Retrieving the remote node_id is only supported after transport is connected")] + fn outbound_unconnected_get_their_node_id_panics() { + let mut transport = create_outbound_for_test::(); + transport.set_up_outbound(); + + let _should_panic = transport.get_their_node_id(); + } + + #[test] + fn inbound_unconnected_get_their_node_id() { + let mut transport = create_inbound_for_test::(); + let mut spy = Vec::new(); + + transport.process_input(&[], &mut spy).unwrap(); + let _no_panic = transport.get_their_node_id(); + } + + #[test] + fn outbound_unconnected_get_their_node_id() { + let mut transport = create_inbound_for_test::(); + let mut spy = Vec::new(); + + transport.process_input(&[], &mut spy).unwrap(); + let _no_panic = transport.get_their_node_id(); + } + // Test that when a handshake completes is_connected() is correct #[test] fn outbound_handshake_complete_ready_for_encryption() { @@ -271,6 +312,7 @@ mod tests { transport.process_input(&[], &mut spy).unwrap(); assert!(transport.is_connected()); + let _no_panic = transport.get_their_node_id(); } #[test] From ec07cbc92658a1bdaa280be7dfdfa75ace18e619 Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Fri, 4 Sep 2020 15:28:23 -0700 Subject: [PATCH 54/72] tests: Add unit tests for PeerManager Use the newly abstracted Transport layer to write unit tests for most of the PeerManager functionality. This uses dependency inversion with the PeerManager to pass in a Transport test double that is configured to exercise the interesting code paths. To enable this, a new struct PeerManagerImpl has been created that has all of the original functionality of PeerManager, but includes a type parameter for the Transport implementation that should be used. To keep this test-feature hidden from public docs, the PeerManager struct is now just a shim that delegates all the calls to PeerManagerImpl. This provides a nice balance of a clean public interface with the features needed to create isolated tests. The tests make use of the Spy and Stub test patterns to control input state and validate the output state. --- lightning/src/ln/features.rs | 4 + lightning/src/ln/msgs.rs | 1 + lightning/src/ln/peers/handler.rs | 2038 ++++++++++++++++- lightning/src/ln/peers/mod.rs | 4 + lightning/src/ln/peers/test_message_macros.rs | 278 +++ lightning/src/ln/peers/test_util.rs | 166 +- lightning/src/ln/wire.rs | 2 +- lightning/src/util/test_utils.rs | 266 ++- 8 files changed, 2657 insertions(+), 102 deletions(-) create mode 100644 lightning/src/ln/peers/test_message_macros.rs diff --git a/lightning/src/ln/features.rs b/lightning/src/ln/features.rs index f573ac4c43a..1e9f8341de4 100644 --- a/lightning/src/ln/features.rs +++ b/lightning/src/ln/features.rs @@ -491,6 +491,10 @@ impl Features { pub(crate) fn requires_static_remote_key(&self) -> bool { ::requires_feature(&self.flags) } + #[cfg(test)] + pub(crate) fn clear_requires_static_remote_key(&mut self) { + ::clear_bits(&mut self.flags) + } } impl Features { diff --git a/lightning/src/ln/msgs.rs b/lightning/src/ln/msgs.rs index 16ba08629fe..8ffff144b86 100644 --- a/lightning/src/ln/msgs.rs +++ b/lightning/src/ln/msgs.rs @@ -672,6 +672,7 @@ pub enum ErrorAction { } /// An Err type for failure to process messages. +#[derive(Clone)] pub struct LightningError { /// A human-readable message describing the error pub err: String, diff --git a/lightning/src/ln/peers/handler.rs b/lightning/src/ln/peers/handler.rs index 3871e5f6653..5cd10547425 100644 --- a/lightning/src/ln/peers/handler.rs +++ b/lightning/src/ln/peers/handler.rs @@ -193,8 +193,8 @@ enum InitSyncTracker{ NodesSyncing(PublicKey), } -struct Peer { - transport: Transport, +struct Peer { + transport: TransportImpl, outbound: bool, their_features: Option, @@ -205,7 +205,7 @@ struct Peer { awaiting_pong: bool, } -impl Peer { +impl Peer { /// Returns true if the channel announcements/updates for the given channel should be /// forwarded to this peer. /// If we are sending our routing table to this peer and we have not yet sent channel @@ -230,8 +230,8 @@ impl Peer { } } -struct PeerHolder { - peers: HashMap, +struct PeerHolder { + peers: HashMap>, /// Added to by do_read_event for cases where we pushed a message onto the send buffer but /// didn't call do_attempt_write_data to avoid reentrancy. Cleared in process_events() peers_needing_send: HashSet, @@ -269,11 +269,23 @@ pub type SimpleRefPeerManager<'a, 'b, 'c, 'd, 'e, 'f, 'g, SD, M, T, F, C, L> = P /// SimpleArcPeerManager when you require a PeerManager with a static lifetime, such as when /// you're using lightning-net-tokio. pub struct PeerManager where + CM::Target: ChannelMessageHandler, + RM::Target: RoutingMessageHandler, + L::Target: Logger { + inner: PeerManagerImpl, +} + +// Internal struct that mirrors the PeerManager interface, but can take in a Transport type parameter +// that is useful for testing. This enables the public docs for PeerManager to stay clean. +// +// All PeerManager calls just delegate to this struct directly and it is important to keep it that +// way to ensure full test coverage of the public APIs. +struct PeerManagerImpl where CM::Target: ChannelMessageHandler, RM::Target: RoutingMessageHandler, L::Target: Logger { message_handler: MessageHandler, - peers: Mutex>, + peers: Mutex>, our_node_secret: SecretKey, ephemeral_key_midstate: Sha256Engine, @@ -305,17 +317,122 @@ impl From for MessageHandlingError { /// Manages and reacts to connection events. You probably want to use file descriptors as PeerIds. /// PeerIds may repeat, but only after socket_disconnected() has been called. impl PeerManager where - CM::Target: ChannelMessageHandler, - RM::Target: RoutingMessageHandler, - L::Target: Logger { + CM::Target: ChannelMessageHandler, + RM::Target: RoutingMessageHandler, + L::Target: Logger { + /// Constructs a new PeerManager with the given message handlers and node_id secret key /// ephemeral_random_data is used to derive per-connection ephemeral keys and must be /// cryptographically secure random bytes. pub fn new(message_handler: MessageHandler, our_node_secret: SecretKey, ephemeral_random_data: &[u8; 32], logger: L) -> Self { + Self { + inner: PeerManagerImpl::new(message_handler, our_node_secret, ephemeral_random_data, logger) + } + } + + /// Get the list of node ids for peers which have completed the initial handshake. + /// + /// For outbound connections, this will be the same as the their_node_id parameter passed in to + /// new_outbound_connection, however entries will only appear once the initial handshake has + /// completed and we are sure the remote peer has the private key for the given node_id. + pub fn get_peer_node_ids(&self) -> Vec { + self.inner.get_peer_node_ids() + } + + /// Indicates a new outbound connection has been established to a node with the given node_id. + /// Note that if an Err is returned here you MUST NOT call socket_disconnected for the new + /// descriptor but must disconnect the connection immediately. + /// + /// Returns a small number of bytes to send to the remote node (currently always 50). + /// + /// Panics if descriptor is duplicative with some other descriptor which has not yet had a + /// socket_disconnected(). + pub fn new_outbound_connection(&self, their_node_id: PublicKey, descriptor: Descriptor) -> Result, PeerHandleError> { + self.inner.new_outbound_connection(their_node_id, descriptor) + } + + /// Indicates a new inbound connection has been established. + /// + /// May refuse the connection by returning an Err, but will never write bytes to the remote end + /// (outbound connector always speaks first). Note that if an Err is returned here you MUST NOT + /// call socket_disconnected for the new descriptor but must disconnect the connection + /// immediately. + /// + /// Panics if descriptor is duplicative with some other descriptor which has not yet had + /// socket_disconnected called. + pub fn new_inbound_connection(&self, descriptor: Descriptor) -> Result<(), PeerHandleError> { + self.inner.new_inbound_connection(descriptor) + } + + /// Indicates that there is room to write data to the given socket descriptor. + /// + /// May return an Err to indicate that the connection should be closed. + /// + /// Will most likely call send_data on the descriptor passed in (or the descriptor handed into + /// new_*\_connection) before returning. Thus, be very careful with reentrancy issues! The + /// invariants around calling write_buffer_space_avail in case a write did not fully complete + /// must still hold - be ready to call write_buffer_space_avail again if a write call generated + /// here isn't sufficient! Panics if the descriptor was not previously registered in a + /// new_\*_connection event. + pub fn write_buffer_space_avail(&self, descriptor: &mut Descriptor) -> Result<(), PeerHandleError> { + self.inner.write_buffer_space_avail(descriptor) + } + + /// Indicates that data was read from the given socket descriptor. + /// + /// May return an Err to indicate that the connection should be closed. + /// + /// Will *not* call back into send_data on any descriptors to avoid reentrancy complexity. + /// Thus, however, you almost certainly want to call process_events() after any read_event to + /// generate send_data calls to handle responses. + /// + /// If Ok(true) is returned, further read_events should not be triggered until a send_data call + /// on this file descriptor has resume_read set (preventing DoS issues in the send buffer). + /// + /// Panics if the descriptor was not previously registered in a new_*_connection event. + pub fn read_event(&self, peer_descriptor: &mut Descriptor, data: &[u8]) -> Result { + self.inner.read_event(peer_descriptor, data) + } + + /// Checks for any events generated by our handlers and processes them. Includes sending most + /// response messages as well as messages generated by calls to handler functions directly (eg + /// functions like ChannelManager::process_pending_htlc_forward or send_payment). + pub fn process_events(&self) { + self.inner.process_events(); + } + + /// Indicates that the given socket descriptor's connection is now closed. + /// + /// This must only be called if the socket has been disconnected by the peer or your own + /// decision to disconnect it and must NOT be called in any case where other parts of this + /// library (eg PeerHandleError, explicit disconnect_socket calls) instruct you to disconnect + /// the peer. + /// + /// Panics if the descriptor was not previously registered in a successful new_*_connection event. + pub fn socket_disconnected(&self, descriptor: &Descriptor) { + self.inner.socket_disconnected(descriptor) + } + + /// This function should be called roughly once every 30 seconds. + /// It will send pings to each peer and disconnect those which did not respond to the last round of pings. + + /// Will most likely call send_data on all of the registered descriptors, thus, be very careful with reentrancy issues! + pub fn timer_tick_occured(&self) { + self.inner.timer_tick_occured() + } +} + + +impl PeerManagerImpl where + CM::Target: ChannelMessageHandler, + RM::Target: RoutingMessageHandler, + L::Target: Logger { + + fn new(message_handler: MessageHandler, our_node_secret: SecretKey, ephemeral_random_data: &[u8; 32], logger: L) -> Self { let mut ephemeral_key_midstate = Sha256::engine(); ephemeral_key_midstate.input(ephemeral_random_data); - PeerManager { + PeerManagerImpl { message_handler, peers: Mutex::new(PeerHolder { peers: HashMap::new(), @@ -330,12 +447,7 @@ impl PeerManager Vec { + fn get_peer_node_ids(&self) -> Vec { let peers = self.peers.lock().unwrap(); peers.peers.values().filter_map(|p| { if !p.transport.is_connected() || p.their_features.is_none() { @@ -358,20 +470,16 @@ impl PeerManager Result, PeerHandleError> { + fn new_outbound_connection(&self, their_node_id: PublicKey, descriptor: Descriptor) -> Result, PeerHandleError> { + let transport = TransportImpl::new_outbound(&self.our_node_secret, &their_node_id, &self.get_ephemeral_key()); + self.new_outbound_connection_with_transport(descriptor, transport) + } + + fn new_outbound_connection_with_transport(&self, descriptor: Descriptor, mut transport: TransportImpl) -> Result, PeerHandleError> { let mut peers = self.peers.lock().unwrap(); - let mut transport = Transport::new_outbound(&self.our_node_secret, &their_node_id, &self.get_ephemeral_key()); let initial_bytes = transport.set_up_outbound(); - if peers.peers.insert(descriptor, Peer { + if peers.peers.insert(descriptor, Peer:: { transport, outbound: true, their_features: None, @@ -387,19 +495,15 @@ impl PeerManager Result<(), PeerHandleError> { + fn new_inbound_connection(&self, descriptor: Descriptor) -> Result<(), PeerHandleError> { + let transport = TransportImpl::new_inbound(&self.our_node_secret, &self.get_ephemeral_key()); + self.new_inbound_connection_with_transport(descriptor, transport) + } + + fn new_inbound_connection_with_transport(&self, descriptor: Descriptor, transport: TransportImpl) -> Result<(), PeerHandleError> { let mut peers = self.peers.lock().unwrap(); - if peers.peers.insert(descriptor, Peer { - transport: Transport::new_inbound(&self.our_node_secret, &self.get_ephemeral_key()), + if peers.peers.insert(descriptor, Peer:: { + transport, outbound: false, their_features: None, @@ -414,7 +518,7 @@ impl PeerManager) { while !peer.pending_outbound_buffer.is_blocked() { let queue_space = peer.pending_outbound_buffer.queue_space(); if queue_space > 0 { @@ -472,17 +576,7 @@ impl PeerManager Result<(), PeerHandleError> { + fn write_buffer_space_avail(&self, descriptor: &mut Descriptor) -> Result<(), PeerHandleError> { let mut peers = self.peers.lock().unwrap(); match peers.peers.get_mut(descriptor) { None => panic!("Descriptor for write_event is not already known to PeerManager"), @@ -494,19 +588,7 @@ impl PeerManager Result { + fn read_event(&self, peer_descriptor: &mut Descriptor, data: &[u8]) -> Result { match self.do_read_event(peer_descriptor, data) { Ok(res) => Ok(res), Err(e) => { @@ -622,7 +704,7 @@ impl PeerManager, peer: &mut Peer, peer_descriptor: Descriptor, message: wire::Message) -> Result<(), MessageHandlingError> { + fn handle_message(&self, peers_needing_send: &mut HashSet, peer: &mut Peer, peer_descriptor: Descriptor, message: wire::Message) -> Result<(), MessageHandlingError> { log_trace!(self.logger, "Received message of type {} from {}", message.type_id(), log_pubkey!(peer.transport.get_their_node_id())); // Need an Init as first message @@ -808,10 +890,7 @@ impl PeerManager PeerManager PeerManager PeerManager {{ + let their_node_secret = SecretKey::from_slice(&[0x_12_u8; 32]).unwrap(); + PublicKey::from_secret_key(&Secp256k1::new(), &their_node_secret) + }} + } + + // Container to store the test double objects and test constants that are passed to the + // PeerManager and referenced in the validation code. It supports type parameters so tests can + // use a variety of message handler test doubles with a common set up path. + struct TestCtx { + chan_handler: CM, + logger: TestLogger, + random_data: [u8; 32], + route_handler: RM, + their_node_id: PublicKey, + } + + impl TestCtx { + + // Basic TestCtx with default ChannelMessageHandlerTestSpy and RoutingMessageHandlerTestStub + fn new() -> TestCtx { + TestCtx::::with_channel_and_routing_handlers( + ChannelMessageHandlerTestSpy::new(), RoutingMessageHandlerTestStub::new()) + } + + // TestCtx creation for tests that need to override both default handlers + fn with_channel_and_routing_handlers(chan_handler: CM, route_handler: RM) -> Self { + Self { + chan_handler, + logger: TestLogger::new(), + random_data: [0; 32], + route_handler, + their_node_id: test_ctx_their_node_id!() + } + } + + // TestCtx creation for tests that need to override the routing handler + fn with_routing_handler(route_handler: RM) -> TestCtx { + TestCtx::::with_channel_and_routing_handlers( + ChannelMessageHandlerTestSpy::new(), route_handler) + } + + // TestCtx creation for tests that need to override the message handler + fn with_channel_handler(channel_handler: CM) -> TestCtx { + TestCtx::::with_channel_and_routing_handlers( + channel_handler, RoutingMessageHandlerTestStub::new()) + } + } + + // Convenience macro to hide the RefCell/Builder noise when creating an unconnected transport to + // make the tests more readable. + macro_rules! new_unconnected_transport { + () => {{ + RefCell::new(TransportStubBuilder::new().finish()) + }} + } + + // Convenience macro to hide the RefCell/Builder noise when creating a connected transport to + // make the tests more readable. + macro_rules! new_connected_transport { + ($test_ctx:expr) => {{ + RefCell::new(TransportStubBuilder::new().set_connected(&$test_ctx.their_node_id).finish()) + }} + } + + // Convenience macro to hide the type parameters for the PeerManagerImpl instantiation and test + // context set up. Makes the tests more readable. + macro_rules! new_peer_manager_for_test { + ($test_ctx:expr) => {{ + let our_node_secret = SecretKey::from_slice(&[0x_11_u8; 32]).unwrap(); + let message_handler = MessageHandler { + chan_handler: &$test_ctx.chan_handler, + route_handler: &$test_ctx.route_handler, + }; + PeerManagerImpl::<_, _, _, _, &RefCell>::new(message_handler, our_node_secret, &$test_ctx.random_data, &$test_ctx.logger) + }} + } + + // Generates a PeerManager & TransportStub for test that has already connected and parsed + // the Init message. To reduce test expansion, this only tests with an outbound connection with + // the understanding that after the init process, both connections are identical. + macro_rules! new_peer_manager_post_init { + ($test_ctx: expr, $descriptor: expr, $transport: expr) => {{ + let mut features = InitFeatures::known(); + features.clear_initial_routing_sync(); + + $transport.borrow_mut().add_incoming_message(Message::Init(Init { features })); + + let peer_manager = new_peer_manager_for_test!(&$test_ctx); + new_outbound!(peer_manager, $descriptor, $transport); + assert_matches!(peer_manager.read_event($descriptor, &[]), Ok(_)); + + // Drain pre-init data from descriptor in recording + $descriptor.clear_recording(); + + peer_manager + }} + } + + // Convenience macro to make the tests more readable when creating an outbound connection + macro_rules! new_outbound { + ($peer_manager: expr, $descriptor: expr, $transport: expr) => {{ + $peer_manager.new_outbound_connection_with_transport($descriptor.clone(), $transport).unwrap() + }} + } + + // Convenience macro to make the tests more readable when creating an inbound connection + macro_rules! new_inbound { + ($peer_manager: expr, $descriptor: expr, $transport: expr) => {{ + $peer_manager.new_inbound_connection_with_transport($descriptor.clone(), $transport).unwrap() + }} + } + + // Convenience macro to execute read_event() and assert the return value + macro_rules! assert_read_event_errors { + ($peer_manager: expr, $descriptor: expr, $no_connection_possible: expr) => {{ + assert_matches!($peer_manager.read_event($descriptor, &[]), Err(PeerHandleError { no_connection_possible: $no_connection_possible })) + }} + } + + // Assert that a given slice matches a Message pattern. The TransportTestStub places items on + // the outbound queue unencrypted, so this is used to decode the unencrypted data that makes + // it through the SocketDescriptor. + macro_rules! assert_matches_message { + ($bytes: expr, $message_pattern: pat) => {{ + let mut reader = ::std::io::Cursor::new($bytes); + let message_result = wire::read(&mut reader); + let message = message_result.unwrap(); + assert_matches!(message, $message_pattern) + }} + } + + // Convenience macro for returning the spy value by function name + macro_rules! channel_handler_called { + ($test_ctx:expr, $fn_name: ident) => {{ + $test_ctx.chan_handler.called.lock().unwrap().$fn_name + }} + } + + // Convenience macro for returning the spy value by function name + macro_rules! route_handler_called { + ($test_ctx:expr, $fn_name: ident) => {{ + $test_ctx.route_handler.called.lock().unwrap().$fn_name + }} + } + + // ____ _ _____ _ _____ _ _ + // | _ \ ___ __ _ __| | | ____|_ _____ _ __ | |_ |_ _|__ ___| |_(_)_ __ __ _ + // | |_) / _ \/ _` |/ _` | | _| \ \ / / _ \ '_ \| __| | |/ _ \/ __| __| | '_ \ / _` | + // | _ < __/ (_| | (_| | | |___ \ V / __/ | | | |_ | | __/\__ \ |_| | | | | (_| | + // |_| \_\___|\__,_|\__,_| |_____| \_/ \___|_| |_|\__| |_|\___||___/\__|_|_| |_|\__, | + // |___/ + + // Test that a new inbound connection: + // * get_peer_node_ids() does not contain the node_id + #[test] + fn new_inbound_not_in_get_peer_node_ids() { + let test_ctx = TestCtx::::new(); + let descriptor = SocketDescriptorMock::new(); + let mut transport = new_unconnected_transport!(); + let peer_manager = new_peer_manager_for_test!(&test_ctx); + + new_inbound!(peer_manager, descriptor, &mut transport); + + assert!(peer_manager.get_peer_node_ids().is_empty()); + } + + // Test that a new outbound connection: + // * get_peer_node_ids() does not contain the node_id + #[test] + fn new_outbound_not_in_get_peer_node_ids() { + let test_ctx = TestCtx::::new(); + let descriptor = SocketDescriptorMock::new(); + let mut transport = new_unconnected_transport!(); + let peer_manager = new_peer_manager_for_test!(&test_ctx); + + new_outbound!(peer_manager, descriptor, &mut transport); + + assert!(peer_manager.get_peer_node_ids().is_empty()); + } + + // Test that a new inbound connection: + // * read_event() returns errors from the Transport code + #[test] + fn new_inbound_transport_error_returns_error() { + let test_ctx = TestCtx::::new(); + let mut descriptor = SocketDescriptorMock::new(); + let mut transport = + RefCell::new(TransportStubBuilder::new().process_returns_error().finish()); + let peer_manager = new_peer_manager_for_test!(&test_ctx); + + new_inbound!(peer_manager, descriptor, &mut transport); + + assert_read_event_errors!(peer_manager, &mut descriptor, false); + } + + // Test that a new outbound connection: + // * read_event() returns errors from the Transport code + #[test] + fn new_outbound_transport_error_returns_error() { + let test_ctx = TestCtx::::new(); + let mut descriptor = SocketDescriptorMock::new(); + let mut transport = + RefCell::new(TransportStubBuilder::new().process_returns_error().finish()); + let peer_manager = new_peer_manager_for_test!(&test_ctx); + + new_outbound!(peer_manager, descriptor, &mut transport); + + assert_read_event_errors!(peer_manager, &mut descriptor, false); + } + + // Test that an inbound connection with a connected Transport, but no Init message + // * get_peer_node_ids() does not contain the node_id + // * process_events() does not send an Init message (must receive from Initiator first) + #[test] + fn inbound_connected_transport_not_in_get_peer_node_ids() { + let test_ctx = TestCtx::::new(); + let mut descriptor = SocketDescriptorMock::new(); + let mut transport = new_connected_transport!(&test_ctx); + let peer_manager = new_peer_manager_for_test!(&test_ctx); + + new_inbound!(peer_manager, descriptor, &mut transport); + assert_matches!(peer_manager.read_event(&mut descriptor, &[]), Ok(_)); + + assert!(peer_manager.get_peer_node_ids().is_empty()); + peer_manager.process_events(); + descriptor.assert_called_with(vec![]); + } + + // Test that an outbound connection with a connected Transport, but no Init message + // * read_event() does not call peer_disconnected callback if an error is returned from Transport + // XXXBUG: peer_connected is called after the Init message, but peer_disconnected is called + // with any error after the NOISE handshake is complete + #[test] + fn outbound_connected_transport_error_does_not_call_peer_disconnected_on_error() { + let test_ctx = TestCtx::::new(); + let mut descriptor = SocketDescriptorMock::new(); + let transport = new_connected_transport!(&test_ctx); + let peer_manager = new_peer_manager_for_test!(&test_ctx); + new_outbound!(peer_manager, descriptor, &transport); + assert_matches!(peer_manager.read_event(&mut descriptor, &[]), Ok(_)); + + // Signal the error in transport and ensure we don't send a dangling peer_disconnected + transport.borrow_mut().process_returns_error(); + assert_read_event_errors!(peer_manager, &mut descriptor, false); + assert!(!channel_handler_called!(&test_ctx, peer_connected)); + // assert!(!channel_handler_called!(&test_ctx, peer_disconnected)); + } + + // Test that an inbound connection with a connected Transport and queued Init message: + // * XXXBUG: does not send anything in read_event() + // * read_event() calls the peer_connected channel manager callback + // * process_events() sends an Init message + #[test] + fn inbound_connected_transport_responds_with_init() { + let test_ctx = TestCtx::::new(); + let mut descriptor = SocketDescriptorMock::new(); + let mut transport = new_connected_transport!(&test_ctx); + let peer_manager = new_peer_manager_for_test!(&test_ctx); + transport.borrow_mut().add_incoming_message( + Message::Init(Init { features: InitFeatures::known() })); + + new_inbound!(peer_manager, descriptor, &mut transport); + + assert_matches!(peer_manager.read_event(&mut descriptor, &[]), Ok(_)); + assert!(channel_handler_called!(&test_ctx, peer_connected)); + // descriptor.assert_called_with(vec![]); + + peer_manager.process_events(); + + let recording = descriptor.get_recording(); + assert_matches_message!(&recording[0].0, Message::Init(_)); + } + + // Test that an inbound connection with a connected Transport and queued Init message: + // * read_event() returns true if the outbound queue is full + // * read_event() returns false once room is made and write_buffer_space_avail is called + // Test leverages a 0 capacity SocketDescriptor and the initial routing sync from + // TestRoutingMessagehandler to fill the queue + #[test] + fn inbound_connected_transport_full_outbound_queue() { + let routing_handler = TestRoutingMessageHandler::new(); + let test_ctx = TestCtx::::with_routing_handler(routing_handler); + let mut descriptor = SocketDescriptorMock::with_fixed_size(0); + let mut transport = new_connected_transport!(&test_ctx); + let peer_manager = new_peer_manager_for_test!(&test_ctx); + transport.borrow_mut().add_incoming_message(Message::Init(Init { features: InitFeatures::known() })); + + new_inbound!(peer_manager, descriptor, &mut transport); + + assert_matches!(peer_manager.read_event(&mut descriptor, &[]), Ok(true)); + + // Call w/o write_buffer_space_avail still returns Ok(true) + assert_matches!(peer_manager.read_event(&mut descriptor, &[]), Ok(true)); + + // Call w/o more room in SocketDescriptor still returns Ok(true) + peer_manager.write_buffer_space_avail(&mut descriptor).unwrap(); + assert_matches!(peer_manager.read_event(&mut descriptor, &[]), Ok(true)); + + // Call after more room in SocketDescriptor returns Ok(false) + descriptor.make_room(100000); + peer_manager.write_buffer_space_avail(&mut descriptor).unwrap(); + assert_matches!(peer_manager.read_event(&mut descriptor, &[]), Ok(_)); + + peer_manager.process_events(); + + let recording = descriptor.get_recording(); + assert_matches_message!(&recording[0].0, Message::Init(_)); + assert!(channel_handler_called!(&test_ctx, peer_connected)); + } + + // Test that an outbound connection with a connected Transport: + // * get_peer_node_ids() does not contain the node_id + // * XXXBUG: does not send anything in read_event() + // * process_events() sends an Init message + #[test] + fn outbound_connected_transport_sends_init_in_process_events() { + let test_ctx = TestCtx::::new(); + let mut descriptor = SocketDescriptorMock::new(); + let mut transport = new_connected_transport!(&test_ctx); + let peer_manager = new_peer_manager_for_test!(&test_ctx); + + new_outbound!(peer_manager, descriptor, &mut transport); + assert_matches!(peer_manager.read_event(&mut descriptor, &[]), Ok(_)); + // XXXBUG: Should not call back into descriptor + // descriptor.assert_called_with(vec![]); + + assert!(peer_manager.get_peer_node_ids().is_empty()); + + peer_manager.process_events(); + let recording = descriptor.get_recording(); + assert_eq!(1, recording.len()); + + assert_matches_message!(&recording[0].0, Message::Init(_)); + } + + // Test that an outbound connection with a connected Transport: + // * read_event() errors when receiving a Non-Init message first + // * read_event() calls the peer_disconnected channel manager callback + #[test] + fn inbound_connected_transport_non_init_first_fails() { + let test_ctx = TestCtx::::new(); + let mut descriptor = SocketDescriptorMock::new(); + let mut transport = new_connected_transport!(&test_ctx); + let peer_manager = new_peer_manager_for_test!(&test_ctx); + transport.borrow_mut().add_incoming_message(Message::Ping(Ping { ponglen: 0, byteslen: 0 })); + + new_inbound!(peer_manager, descriptor, &mut transport); + assert_read_event_errors!(peer_manager, &mut descriptor, false); + assert!(channel_handler_called!(&test_ctx, peer_disconnected)); + } + + // Test that an outbound connection with a connected Transport: + // * read_event() errors when receiving a Non-Init message first + // * read_event() calls the peer_disconnected channel manager callback + #[test] + fn outbound_connected_transport_non_init_first_fails() { + let test_ctx = TestCtx::::new(); + let mut descriptor = SocketDescriptorMock::new(); + let mut transport = new_connected_transport!(&test_ctx); + let peer_manager = new_peer_manager_for_test!(&test_ctx); + transport.borrow_mut().add_incoming_message(Message::Ping(Ping { ponglen: 0, byteslen: 0 })); + + new_outbound!(peer_manager, descriptor, &mut transport); + assert_read_event_errors!(peer_manager, &mut descriptor, false); + assert!(channel_handler_called!(&test_ctx, peer_disconnected)); + } + + // Test that an inbound connection with a connected Transport: + // * read_event() errors out with no_connection_possible if an Init message contains requires_unknown_bits + // * read_event() calls the peer_disconnected channel manager callback + #[test] + fn inbound_connected_transport_init_with_required_unknown_first_fails() { + let test_ctx = TestCtx::::new(); + let mut descriptor = SocketDescriptorMock::new(); + let mut transport = new_connected_transport!(&test_ctx); + let peer_manager = new_peer_manager_for_test!(&test_ctx); + let mut features = InitFeatures::known(); + features.set_required_unknown_bits(); + transport.borrow_mut().add_incoming_message(Message::Init(Init { features })); + + new_inbound!(peer_manager, descriptor, &mut transport); + assert_read_event_errors!(peer_manager, &mut descriptor, true); + assert!(channel_handler_called!(&test_ctx, peer_disconnected)); + } + + // Test that an outbound connection with a connected Transport: + // * read_event() errors out with no_connection_possible if an Init message contains requires_unknown_bits + // * read_event() calls the peer_disconnected channel manager callback + #[test] + fn outbound_connected_transport_init_with_required_unknown_first_fails() { + let test_ctx = TestCtx::::new(); + let mut descriptor = SocketDescriptorMock::new(); + let mut transport = new_connected_transport!(&test_ctx); + let peer_manager = new_peer_manager_for_test!(&test_ctx); + let mut features = InitFeatures::known(); + features.set_required_unknown_bits(); + transport.borrow_mut().add_incoming_message(Message::Init(Init { features })); + + new_outbound!(peer_manager, descriptor, &mut transport); + assert_read_event_errors!(peer_manager, &mut descriptor, true); + assert!(channel_handler_called!(&test_ctx, peer_disconnected)); + } + + // Test that an inbound connection with a connected Transport: + // * read_event() errors out with no_connection_possible if an Init message does not contain requires_static_remote_key + // * read_event() calls the peer_disconnected channel manager callback + #[test] + fn inbound_connected_transport_init_with_clear_requires_static_remote_key() { + let test_ctx = TestCtx::::new(); + let mut descriptor = SocketDescriptorMock::new(); + let mut transport = new_connected_transport!(&test_ctx); + let peer_manager = new_peer_manager_for_test!(&test_ctx); + let mut features = InitFeatures::known(); + features.clear_requires_static_remote_key(); + transport.borrow_mut().add_incoming_message(Message::Init(Init { features })); + + new_inbound!(peer_manager, descriptor, &mut transport); + assert_read_event_errors!(peer_manager, &mut descriptor, true); + assert!(channel_handler_called!(&test_ctx, peer_disconnected)); + } + + // Test that an outbound connection with a connected Transport: + // * read_event() errors out with no_connection_possible if an Init message does not contain requires_static_remote_key + // * read_event() calls the peer_disconnected channel manager callback + #[test] + fn outbound_connected_transport_init_with_clear_requires_static_remote_key() { + let test_ctx = TestCtx::::new(); + let mut descriptor = SocketDescriptorMock::new(); + let mut transport = new_connected_transport!(&test_ctx); + let peer_manager = new_peer_manager_for_test!(&test_ctx); + let mut features = InitFeatures::known(); + features.clear_requires_static_remote_key(); + transport.borrow_mut().add_incoming_message(Message::Init(Init { features })); + + new_outbound!(peer_manager, descriptor, &mut transport); + assert_read_event_errors!(peer_manager, &mut descriptor, true); + assert!(channel_handler_called!(&test_ctx, peer_disconnected)); + } + + // Test that an inbound connection with a connected Transport and queued Init Message: + // * read_event() calls the peer_connected channel manager callback + // * get_peer_node_ids() contains the node_id + #[test] + fn inbound_connected_transport_after_init_in_get_peer_node_ids() { + let test_ctx = TestCtx::::new(); + let mut descriptor = SocketDescriptorMock::new(); + let mut transport = new_connected_transport!(&test_ctx); + let peer_manager = new_peer_manager_for_test!(&test_ctx); + transport.borrow_mut().add_incoming_message(Message::Init(Init { features: InitFeatures::known() })); + + new_inbound!(peer_manager, descriptor, &mut transport); + + assert_matches!(peer_manager.read_event(&mut descriptor, &[]), Ok(_)); + + assert!(peer_manager.get_peer_node_ids().contains(&test_ctx.their_node_id)); + assert!(channel_handler_called!(&test_ctx, peer_connected)); + } + + // Test that an outbound connection with a connected Transport and queued Init Message: + // * read_event() calls the peer_connected channel manager callback + // * get_peer_node_ids() contains the node_id + #[test] + fn outbound_connected_transport_after_init_in_get_peer_node_ids() { + let test_ctx = TestCtx::::new(); + let mut descriptor = SocketDescriptorMock::new(); + let mut transport = new_connected_transport!(&test_ctx); + let peer_manager = new_peer_manager_for_test!(&test_ctx); + transport.borrow_mut().add_incoming_message(Message::Init(Init { features: InitFeatures::known() })); + + new_outbound!(peer_manager, descriptor, &mut transport); + + assert_matches!(peer_manager.read_event(&mut descriptor, &[]), Ok(_)); + + assert!(peer_manager.get_peer_node_ids().contains(&test_ctx.their_node_id)); + assert!(channel_handler_called!(&test_ctx, peer_connected)); + } + + // Test that a post-Init connection: + // * read_event() propagates an error coming out of Transport + // * read_event() calls the peer_disconnected channel manager callback + // * get_peer_node_ids() does not contain the node_id + #[test] + fn post_init_connected_after_error_not_in_get_peer_node_ids() { + let test_ctx = TestCtx::::new(); + let mut descriptor = SocketDescriptorMock::new(); + let transport = new_connected_transport!(&test_ctx); + let peer_manager = new_peer_manager_post_init!(&test_ctx, &mut descriptor, &transport); + + // Set transport to return an error + transport.borrow_mut().process_returns_error(); + + // Verify errors out and removed from get_peer_node_ids() + assert_read_event_errors!(peer_manager, &mut descriptor, false); + assert!(channel_handler_called!(&test_ctx, peer_disconnected)); + assert!(peer_manager.get_peer_node_ids().is_empty()); + } + + // Test that a post-Init duplicate connection: + // * read_event() returns an error + // * get_peer_node_ids() contains the original node_id + #[test] + fn post_init_duplicate_connection_errors_and_original_keeps_existing() { + let test_ctx = TestCtx::::new(); + let mut descriptor = SocketDescriptorMock::new(); + let transport = new_connected_transport!(&test_ctx); + // Create a duplicate connection from the same node_id + let mut duplicate_connection_descriptor = SocketDescriptorMock::new(); + let mut duplicate_connection_transport = RefCell::new(TransportStubBuilder::new() + .set_connected(&test_ctx.their_node_id) + .finish()); + let peer_manager = new_peer_manager_post_init!(&test_ctx, &mut descriptor, &transport); + + assert!(peer_manager.get_peer_node_ids().contains(&test_ctx.their_node_id)); + + // Duplicate connection errors out + new_outbound!(peer_manager, duplicate_connection_descriptor, &mut duplicate_connection_transport); + assert_read_event_errors!(peer_manager, &mut duplicate_connection_descriptor, false); + + // And any queued messages such as an outgoing Init are never sent + peer_manager.process_events(); + duplicate_connection_descriptor.assert_called_with(vec![]); + + // But the original still exists in get_peer_node_ids() + assert!(peer_manager.get_peer_node_ids().contains(&test_ctx.their_node_id)); + } + + // __ __ _____ _ _ + // | \/ | ___ ___ ___ __ _ __ _ ___ |_ _|__ ___| |_(_)_ __ __ _ + // | |\/| |/ _ \/ __/ __|/ _` |/ _` |/ _ \ | |/ _ \/ __| __| | '_ \ / _` | + // | | | | __/\__ \__ \ (_| | (_| | __/ | | __/\__ \ |_| | | | | (_| | + // |_| |_|\___||___/___/\__,_|\__, |\___| |_|\___||___/\__|_|_| |_|\__, | + // |___/ |___/ + + // Test that a post-Init connection: + // * read_event() returns an error if it receives a second Init message + // * read_event() calls the peer_disconnected channel manager callback + #[test] + fn post_init_second_init_fails() { + let test_ctx = TestCtx::::new(); + let mut descriptor = SocketDescriptorMock::new(); + let transport = new_connected_transport!(&test_ctx); + let peer_manager = new_peer_manager_post_init!(&test_ctx, &mut descriptor, &transport); + + transport.borrow_mut().add_incoming_message(Message::Init(Init { features: InitFeatures::known() })); + + assert_read_event_errors!(peer_manager, &mut descriptor, false); + assert!(channel_handler_called!(&test_ctx, peer_disconnected)); + } + + // Test that a post-Init connection: + // * read_event() does not propagate an Error Message with no printable + // * read_event() calls the handle_error channel manager callback + #[test] + fn post_init_error_message_without_printable() { + let test_ctx = TestCtx::::new(); + let mut descriptor = SocketDescriptorMock::new(); + let transport = new_connected_transport!(&test_ctx); + let peer_manager = new_peer_manager_post_init!(&test_ctx, &mut descriptor, &transport); + + transport.borrow_mut().add_incoming_message(Message::Error(ErrorMessage { channel_id: [1; 32], data: "".to_string() })); + + assert_matches!(peer_manager.read_event(&mut descriptor, &[]), Ok(_)); + assert!(channel_handler_called!(&test_ctx, handle_error)); + } + + // Test that a post-Init connection: + // * read_event() does not propagate an Error Message with printable + // * read_event() calls the handle_error channel manager callback + #[test] + fn post_init_error_message_with_printable() { + let test_ctx = TestCtx::::new(); + let mut descriptor = SocketDescriptorMock::new(); + let transport = new_connected_transport!(&test_ctx); + let peer_manager = new_peer_manager_post_init!(&test_ctx, &mut descriptor, &transport); + + transport.borrow_mut().add_incoming_message(Message::Error(ErrorMessage { channel_id: [1; 32], data: "error".to_string() })); + + assert_matches!(peer_manager.read_event(&mut descriptor, &[]), Ok(_)); + assert!(channel_handler_called!(&test_ctx, handle_error)); + } + + // Test that a post-Init connection: + // * read_event() does not propagate an Error Message with a control character + // * read_event() calls the handle_error channel manager callback + #[test] + fn post_init_error_message_with_non_ascii_ignored() { + let test_ctx = TestCtx::::new(); + let mut descriptor = SocketDescriptorMock::new(); + let transport = new_connected_transport!(&test_ctx); + let peer_manager = new_peer_manager_post_init!(&test_ctx, &mut descriptor, &transport); + + transport.borrow_mut().add_incoming_message(Message::Error(ErrorMessage { channel_id: [1; 32], data: "\x00".to_string() })); + + assert_matches!(peer_manager.read_event(&mut descriptor, &[]), Ok(_)); + assert!(channel_handler_called!(&test_ctx, handle_error)); + } + + // Test that a post-Init connection: + // * read_event() returns an error when it receives an Error Message with a 0 channel_id + // * read_event() calls the handle_error channel manager callback + #[test] + fn post_init_error_message_with_zero_channel_id() { + let test_ctx = TestCtx::::new(); + let mut descriptor = SocketDescriptorMock::new(); + let transport = new_connected_transport!(&test_ctx); + let peer_manager = new_peer_manager_post_init!(&test_ctx, &mut descriptor, &transport); + + transport.borrow_mut().add_incoming_message(Message::Error(ErrorMessage { channel_id: [0; 32], data: "".to_string() })); + + assert_read_event_errors!(peer_manager, &mut descriptor, true); + assert!(channel_handler_called!(&test_ctx, handle_error)); + } + + // Test that a post-Init connection: + // * read_event() returns an error when it receives Message::Unknown (even) + #[test] + fn post_init_unknown_message_even() { + let test_ctx = TestCtx::::new(); + let mut descriptor = SocketDescriptorMock::new(); + let transport = new_connected_transport!(&test_ctx); + let peer_manager = new_peer_manager_post_init!(&test_ctx, &mut descriptor, &transport); + + transport.borrow_mut().add_incoming_message(Message::Unknown(wire::MessageType(254))); + + assert_read_event_errors!(peer_manager, &mut descriptor, true); + } + + // Test that a post-Init connection: + // * read_event() ignores Message::Unknown (odd) + #[test] + fn post_init_unknown_message_odd() { + let test_ctx = TestCtx::::new(); + let mut descriptor = SocketDescriptorMock::new(); + let transport = new_connected_transport!(&test_ctx); + let peer_manager = new_peer_manager_post_init!(&test_ctx, &mut descriptor, &transport); + + transport.borrow_mut().add_incoming_message(Message::Unknown(wire::MessageType(255))); + + assert_matches!(peer_manager.read_event(&mut descriptor, &[]), Ok(_)); + } + + // Test that a post-init connection: + // * read_event() calls the correct ChannelMessageHandler callback given the correct message type + macro_rules! generate_handle_message_test { + ($expected_cb: ident, $msg: expr) => { + #[test] + fn $expected_cb() { + let test_ctx = TestCtx::::new(); + let mut descriptor = SocketDescriptorMock::new(); + let transport = new_connected_transport!(&test_ctx); + let peer_manager = new_peer_manager_post_init!(&test_ctx, &mut descriptor, &transport); + transport.borrow_mut().add_incoming_message($msg); + + assert_matches!(peer_manager.read_event(&mut descriptor, &[]), Ok(_)); + assert!(channel_handler_called!(&test_ctx, $expected_cb)); + } + } + } + + generate_handle_message_test!(handle_open_channel, Message::OpenChannel(fake_open_channel_msg!())); + generate_handle_message_test!(handle_accept_channel, Message::AcceptChannel(fake_accept_channel_msg!())); + generate_handle_message_test!(handle_funding_created, Message::FundingCreated(fake_funding_created_msg!())); + generate_handle_message_test!(handle_funding_signed, Message::FundingSigned(fake_funding_signed_msg!())); + generate_handle_message_test!(handle_funding_locked, Message::FundingLocked(fake_funding_locked_msg!())); + generate_handle_message_test!(handle_shutdown, Message::Shutdown(fake_shutdown_msg!())); + generate_handle_message_test!(handle_closing_signed, Message::ClosingSigned(fake_closing_signed_msg!())); + generate_handle_message_test!(handle_update_add_htlc, Message::UpdateAddHTLC(fake_update_add_htlc_msg!())); + generate_handle_message_test!(handle_update_fulfill_htlc, Message::UpdateFulfillHTLC(fake_update_fulfill_htlc_msg!())); + generate_handle_message_test!(handle_update_fail_htlc, Message::UpdateFailHTLC(fake_update_fail_htlc_msg!())); + generate_handle_message_test!(handle_update_fail_malformed_htlc, Message::UpdateFailMalformedHTLC(fake_update_fail_malformed_htlc_msg!())); + generate_handle_message_test!(handle_commitment_signed, Message::CommitmentSigned(fake_commitment_signed_msg!())); + generate_handle_message_test!(handle_revoke_and_ack, Message::RevokeAndACK(fake_revoke_and_ack_msg!())); + generate_handle_message_test!(handle_update_fee, Message::UpdateFee(fake_update_fee_msg!())); + generate_handle_message_test!(handle_channel_reestablish, Message::ChannelReestablish(fake_channel_reestablish_msg!())); + generate_handle_message_test!(handle_announcement_signatures, Message::AnnouncementSignatures(fake_announcement_signatures_msg!())); + + // Test that a post-Init connection: + // * read_event() returns an error if a ChannelAnnouncement message is received and the routing handler + // returns ErrorAction::DisconnectPeer + // * read_event() calls the peer_disconnected channel manager callback + // * read_event() does not call disconnect_socket() is not called on the SocketDescriptor + #[test] + fn post_init_handle_channel_announcement_disconnect_peer() { + let mut routing_handler = RoutingMessageHandlerTestStub::new(); + routing_handler.handle_channel_announcement_return = Err(msgs::LightningError { err: "".to_string(), action: msgs::ErrorAction::DisconnectPeer { msg: None } }); + let test_ctx = TestCtx::::with_routing_handler(routing_handler); + let mut descriptor = SocketDescriptorMock::new(); + let transport = new_connected_transport!(&test_ctx); + let peer_manager = new_peer_manager_post_init!(&test_ctx, &mut descriptor, &transport); + + transport.borrow_mut().add_incoming_message(Message::ChannelAnnouncement(fake_channel_announcement_msg!(0, fake_public_key!(), fake_public_key!()))); + + assert_read_event_errors!(peer_manager, &mut descriptor, false); + assert!(peer_manager.get_peer_node_ids().is_empty()); + assert!(channel_handler_called!(&test_ctx, peer_disconnected)); + assert!(!descriptor.disconnect_called()); + } + + // Test generator macro to reduce duplication across the broadcast message cases. + // (test name, expression that returns a routing handler, message that will be queued, closure taking (peer_manager, descriptor) that does the validation) + macro_rules! generate_broadcast_message_test { + ($test_name: ident, $routing_handler: expr, $message: expr, $validation: tt) => { + #[test] + fn $test_name() { + let test_ctx = TestCtx::::with_routing_handler($routing_handler); + let mut descriptor = SocketDescriptorMock::new(); + let transport = new_connected_transport!(&test_ctx); + let peer_manager = new_peer_manager_post_init!(&test_ctx, &mut descriptor, &transport); + + transport.borrow_mut().add_incoming_message($message); + + $validation(peer_manager, descriptor) + } + } + } + + // Test that a post-Init connection: + // * read_event() returns Ok(_) if a ChannelAnnouncement message is received and the routing handler + // returns ErrorAction::IgnoreError + generate_broadcast_message_test!(post_init_handle_channel_announcement_ignore_error, { + let mut routing_handler = RoutingMessageHandlerTestStub::new(); + routing_handler.handle_channel_announcement_return = Err(msgs::LightningError { err: "".to_string(), action: msgs::ErrorAction::IgnoreError }); + routing_handler + }, + Message::ChannelAnnouncement(fake_channel_announcement_msg!(0, fake_public_key!(), fake_public_key!())), + (| peer_manager: PeerManagerImpl>, mut descriptor | + assert_matches!(peer_manager.read_event(&mut descriptor, &[]), Ok(_)) + )); + + // Test that a post-Init connection: + // * read_event() returns Ok(_) if a ChannelAnnouncement message is received and the routing handler + // returns ErrorAction::IgnoreError + // * process_events() sends an ErrorMessage + generate_broadcast_message_test!(post_init_handle_channel_announcement_send_error_message, { + let mut routing_handler = RoutingMessageHandlerTestStub::new(); + routing_handler.handle_channel_announcement_return = Err(msgs::LightningError { err: "".to_string(), action: msgs::ErrorAction::SendErrorMessage { msg: ErrorMessage { channel_id: [0; 32], data: "".to_string() } } }); + routing_handler + }, + Message::ChannelAnnouncement(fake_channel_announcement_msg!(0, fake_public_key!(), fake_public_key!())), + (| peer_manager: PeerManagerImpl>, mut descriptor | { + assert_matches!(peer_manager.read_event(&mut descriptor, &[]), Ok(_)); + let recording = descriptor.get_recording(); + assert_matches_message!(&recording[0].0, Message::Error(_)) + } + )); + + // Test that a post-Init connection: + // * read_event() returns Ok(_) if a ChannelAnnouncement message is received and the routing handler + // returns true + generate_broadcast_message_test!(post_init_handle_channel_announcement_should_forward, { + let mut routing_handler = RoutingMessageHandlerTestStub::new(); + routing_handler.handle_channel_announcement_return = Ok(true); + routing_handler + }, + Message::ChannelAnnouncement(fake_channel_announcement_msg!(0, fake_public_key!(), fake_public_key!())), + (| peer_manager: PeerManagerImpl>, mut descriptor | { + assert_matches!(peer_manager.read_event(&mut descriptor, &[]), Ok(_)); + } + )); + + // Test that a post-Init connection: + // * read_event() returns Ok(_) if a ChannelAnnouncement message is received and the routing handler + // returns false + generate_broadcast_message_test!(post_init_handle_channel_announcement_should_not_forward, { + let mut routing_handler = RoutingMessageHandlerTestStub::new(); + routing_handler.handle_channel_announcement_return = Ok(false); + routing_handler + }, + Message::ChannelAnnouncement(fake_channel_announcement_msg!(0, fake_public_key!(), fake_public_key!())), + (| peer_manager: PeerManagerImpl>, mut descriptor | { + assert_matches!(peer_manager.read_event(&mut descriptor, &[]), Ok(_)); + } + )); + + // Test that a post-Init connection: + // * read_event() returns Ok(_) if a NodeAnnouncement message is received and the routing handler + // returns ErrorAction::IgnoreError + generate_broadcast_message_test!(post_init_handle_node_announcement_ignore_error, { + let mut routing_handler = RoutingMessageHandlerTestStub::new(); + routing_handler.handle_node_announcement_return = Err(msgs::LightningError { err: "".to_string(), action: msgs::ErrorAction::IgnoreError }); + routing_handler + }, + Message::NodeAnnouncement(fake_node_announcement_msg!()), + (| peer_manager: PeerManagerImpl>, mut descriptor | + assert_matches!(peer_manager.read_event(&mut descriptor, &[]), Ok(_)) + )); + + // Test that a post-Init connection: + // * read_event() returns Ok(_) if a NodeAnnouncement message is received and the routing handler + // returns ErrorAction::IgnoreError + // * process_events() sends an ErrorMessage + generate_broadcast_message_test!(post_init_handle_node_announcement_send_error_message, { + let mut routing_handler = RoutingMessageHandlerTestStub::new(); + routing_handler.handle_node_announcement_return = Err(msgs::LightningError { err: "".to_string(), action: msgs::ErrorAction::SendErrorMessage { msg: ErrorMessage { channel_id: [0; 32], data: "".to_string() } } }); + routing_handler + }, + Message::NodeAnnouncement(fake_node_announcement_msg!()), + (| peer_manager: PeerManagerImpl>, mut descriptor | { + assert_matches!(peer_manager.read_event(&mut descriptor, &[]), Ok(_)); + let recording = descriptor.get_recording(); + assert_matches_message!(&recording[0].0, Message::Error(_)) + } + )); + + // Test that a post-Init connection: + // * read_event() returns Ok(_) if a NodeAnnouncement message is received and the routing handler + // returns true + generate_broadcast_message_test!(post_init_handle_node_announcement_should_forward, { + let mut routing_handler = RoutingMessageHandlerTestStub::new(); + routing_handler.handle_node_announcement_return = Ok(true); + routing_handler + }, + Message::NodeAnnouncement(fake_node_announcement_msg!()), + (| peer_manager: PeerManagerImpl>, mut descriptor | { + assert_matches!(peer_manager.read_event(&mut descriptor, &[]), Ok(_)); + } + )); + + // Test that a post-Init connection: + // * read_event() returns Ok(_) if a NodeAnnouncement message is received and the routing handler + // returns false + generate_broadcast_message_test!(post_init_handle_node_announcement_should_not_forward, { + let mut routing_handler = RoutingMessageHandlerTestStub::new(); + routing_handler.handle_node_announcement_return = Ok(false); + routing_handler + }, + Message::NodeAnnouncement(fake_node_announcement_msg!()), + (| peer_manager: PeerManagerImpl>, mut descriptor | { + assert_matches!(peer_manager.read_event(&mut descriptor, &[]), Ok(_)); + } + )); + + // Test that a post-Init connection: + // * read_event() returns Ok(_) if a ChannelUpdate message is received and the routing handler + // returns ErrorAction::IgnoreError + generate_broadcast_message_test!(post_init_handle_channel_update_ignore_error, { + let mut routing_handler = RoutingMessageHandlerTestStub::new(); + routing_handler.handle_channel_update_return = Err(msgs::LightningError { err: "".to_string(), action: msgs::ErrorAction::IgnoreError }); + routing_handler + }, + Message::ChannelUpdate(fake_channel_update_msg!()), + (| peer_manager: PeerManagerImpl>, mut descriptor | + assert_matches!(peer_manager.read_event(&mut descriptor, &[]), Ok(_)) + )); + + // Test that a post-Init connection: + // * read_event() returns Ok(_) if a ChannelUpdate message is received and the routing handler + // returns ErrorAction::IgnoreError + // * process_events() sends an ErrorMessage + generate_broadcast_message_test!(post_init_handle_channel_update_send_error_message, { + let mut routing_handler = RoutingMessageHandlerTestStub::new(); + routing_handler.handle_channel_update_return = Err(msgs::LightningError { err: "".to_string(), action: msgs::ErrorAction::SendErrorMessage { msg: ErrorMessage { channel_id: [0; 32], data: "".to_string() } } }); + routing_handler + }, + Message::ChannelUpdate(fake_channel_update_msg!()), + (| peer_manager: PeerManagerImpl>, mut descriptor | { + assert_matches!(peer_manager.read_event(&mut descriptor, &[]), Ok(_)); + let recording = descriptor.get_recording(); + assert_matches_message!(&recording[0].0, Message::Error(_)) + } + )); + + // Test that a post-Init connection: + // * read_event() returns Ok(_) if a ChannelUpdate message is received and the routing handler + // returns true + generate_broadcast_message_test!(post_init_handle_channel_update_should_forward, { + let mut routing_handler = RoutingMessageHandlerTestStub::new(); + routing_handler.handle_channel_update_return = Ok(true); + routing_handler + }, + Message::ChannelUpdate(fake_channel_update_msg!()), + (| peer_manager: PeerManagerImpl>, mut descriptor | { + assert_matches!(peer_manager.read_event(&mut descriptor, &[]), Ok(_)); + } + )); + + // Test that a post-Init connection: + // * read_event() returns Ok(_) if a ChannelUpdate message is received and the routing handler + // returns false + generate_broadcast_message_test!(post_init_handle_channel_update_should_not_forward, { + let mut routing_handler = RoutingMessageHandlerTestStub::new(); + routing_handler.handle_channel_update_return = Ok(false); + routing_handler + }, + Message::ChannelUpdate(fake_channel_update_msg!()), + (| peer_manager: PeerManagerImpl>, mut descriptor | { + assert_matches!(peer_manager.read_event(&mut descriptor, &[]), Ok(_)); + } + )); + + // _____ _ _____ _ _ + // | ____|_ _____ _ __ | |_ |_ _|__ ___| |_(_)_ __ __ _ + // | _| \ \ / / _ \ '_ \| __| | |/ _ \/ __| __| | '_ \ / _` | + // | |___ \ V / __/ | | | |_ | | __/\__ \ |_| | | | | (_| | + // |_____| \_/ \___|_| |_|\__| |_|\___||___/\__|_|_| |_|\__, | + // |___/ + + // To reduce test expansion, the unknown, unconnected, and connected variants are only run + // on one event type. All handlers use the same accessor to retrieve the connected node so one + // test of those paths should be sufficient. The initialized variant is run on all types which + // is where the interesting code is run. Once features are added to take action on unconnected + // nodes, this should be revisited. + + // Test that a post-Init connection: + // * process_events() does not send an OpenChannel message when it receives a SendOpenChannel + // event if the peer is unknown + #[test] + fn unknown_node_send_open_channel_event() { + let channel_handler = TestChannelMessageHandler::new(); + channel_handler.pending_events.lock().unwrap().push(MessageSendEvent::SendOpenChannel { + node_id: fake_public_key!(), + msg: fake_open_channel_msg!() + }); + let test_ctx = TestCtx::::with_channel_handler(channel_handler); + let mut descriptor = SocketDescriptorMock::new(); + let transport = new_connected_transport!(&test_ctx); + let peer_manager = new_peer_manager_post_init!(&test_ctx, &mut descriptor, &transport); + + peer_manager.process_events(); + assert!(descriptor.get_recording().is_empty()); + } + + // Test that a post-Init connection: + // * process_events() does not send an OpenChannel message when it receives a SendOpenChannel + // event if the peer is known, but the NOISE handshake is not complete + #[test] + fn unconnected_transport_send_open_channel_event() { + let channel_handler = TestChannelMessageHandler::new(); + channel_handler.pending_events.lock().unwrap().push(MessageSendEvent::SendOpenChannel { + node_id: fake_public_key!(), + msg: fake_open_channel_msg!() + }); + let test_ctx = TestCtx::::with_channel_handler(channel_handler); + let descriptor = SocketDescriptorMock::new(); + let mut transport = new_unconnected_transport!(); + let peer_manager = new_peer_manager_for_test!(&test_ctx); + new_outbound!(peer_manager, descriptor, &mut transport); + + peer_manager.process_events(); + + assert!(descriptor.get_recording().is_empty()); + } + + // Test that a post-Init connection: + // * process_events() does not send an OpenChannel message when it receives a SendOpenChannel + // event if the peer is known, but the Init message has not been received + #[test] + fn connected_transport_send_open_channel_event() { + let channel_handler = TestChannelMessageHandler::new(); + channel_handler.pending_events.lock().unwrap().push(MessageSendEvent::SendOpenChannel { + node_id: fake_public_key!(), + msg: fake_open_channel_msg!() + }); + let test_ctx = TestCtx::::with_channel_handler(channel_handler); + let descriptor = SocketDescriptorMock::new(); + let mut transport = new_connected_transport!(&test_ctx); + let peer_manager = new_peer_manager_for_test!(&test_ctx); + new_outbound!(peer_manager, descriptor, &mut transport); + + peer_manager.process_events(); + + assert!(descriptor.get_recording().is_empty()); + } + + // Test generator macro to reduce duplication across the event handlers that just enqueue a message + // (test name, event to send, expected sent message) + macro_rules! generate_event_handler_test { + ($test_name: ident, $event: expr, $expected_message: pat) => { + #[test] + fn $test_name() { + let channel_handler = TestChannelMessageHandler::new(); + channel_handler.pending_events.lock().unwrap().push($event); + + let test_ctx = TestCtx::::with_channel_handler(channel_handler); + let mut descriptor = SocketDescriptorMock::new(); + let transport = new_connected_transport!(&test_ctx); + let peer_manager = new_peer_manager_post_init!(&test_ctx, &mut descriptor, &transport); + + peer_manager.process_events(); + + let recording = descriptor.get_recording(); + assert_matches_message!(&recording[0].0, $expected_message); + } + } + } + + // Test that a post-Init connection: + // * process_events() sends an OpenChannel message when it receives a SendOpenChannel event for + // an initialized node + generate_event_handler_test!(post_init_send_open_channel, + SendOpenChannel { + node_id: test_ctx_their_node_id!(), + msg: fake_open_channel_msg!(), + }, + Message::OpenChannel(_) + ); + + // Test that a post-Init connection: + // * process_events() sends an AcceptChannel message when it receives a SendAcceptChannel event + // for an initialized node + generate_event_handler_test!(post_init_send_accept_channel, + SendAcceptChannel { + node_id: test_ctx_their_node_id!(), + msg: fake_accept_channel_msg!(), + }, + Message::AcceptChannel(_) + ); + + // Test that a post-Init connection: + // * process_events() sends an AcceptChannel message when it receives a SendAcceptChannel event + // for an initialized node + generate_event_handler_test!(post_init_send_funding_created, + SendFundingCreated { + node_id: test_ctx_their_node_id!(), + msg: fake_funding_created_msg!(), + }, + Message::FundingCreated(_) + ); + + // Test that a post-Init connection: + // * process_events() sends an FundingSigned message when it receives a SendFundingSigned event + // for an initialized node + generate_event_handler_test!(post_init_send_funding_signed, + SendFundingSigned { + node_id: test_ctx_their_node_id!(), + msg: fake_funding_signed_msg!(), + }, + Message::FundingSigned(_) + ); + + // Test that a post-Init connection: + // * process_events() sends an FundingLocked message when it receives a SendFundingLocked event + // for an initialized node + generate_event_handler_test!(post_init_send_funding_locked, + SendFundingLocked { + node_id: test_ctx_their_node_id!(), + msg: fake_funding_locked_msg!(), + }, + Message::FundingLocked(_) + ); + + // Test that a post-Init connection: + // * process_events() sends an AnnouncementSignatures message when it receives a + // SendAnnouncementSignatures event for an initialized node + generate_event_handler_test!(post_init_send_announcement_signatures, + SendAnnouncementSignatures { + node_id: test_ctx_their_node_id!(), + msg: fake_announcement_signatures_msg!(), + }, + Message::AnnouncementSignatures(_) + ); + + // Test that a post-Init connection: + // * process_events() sends an RevokeAndACK message when it receives a SendRevokeAndACK event + // for an initialized node + generate_event_handler_test!(post_init_send_revoke_ack, + SendRevokeAndACK { + node_id: test_ctx_their_node_id!(), + msg: fake_revoke_and_ack_msg!(), + }, + Message::RevokeAndACK(_) + ); + + // Test that a post-Init connection: + // * process_events() sends an ClosingSigned message when it receives a SendClosingSigned event + // for an initialized node + generate_event_handler_test!(post_init_send_closing_signed, + SendClosingSigned { + node_id: test_ctx_their_node_id!(), + msg: fake_closing_signed_msg!(), + }, + Message::ClosingSigned(_) + ); + + // Test that a post-Init connection: + // * process_events() sends an Shutdown message when it receives a Shutdown event for an + // initialized node + generate_event_handler_test!(post_init_send_shutdown, + SendShutdown { + node_id: test_ctx_their_node_id!(), + msg: fake_shutdown_msg!(), + }, + Message::Shutdown(_) + ); + + // Test that a post-Init connection: + // * process_events() sends an ChannelReestablish message when it receives a + // SendChannelReestablish event for an initialized node + generate_event_handler_test!(post_init_send_channel_reestablish, + SendChannelReestablish { + node_id: test_ctx_their_node_id!(), + msg: fake_channel_reestablish_msg!() + }, + Message::ChannelReestablish(_) + ); + + // Test that a post-Init connection: + // * process_events() sends relevant HTLC messages when it receives a UpdateHTLC event for an + // initialized node + #[test] + fn post_init_send_update_htlcs() { + let channel_handler = TestChannelMessageHandler::new(); + channel_handler.pending_events.lock().unwrap().push(UpdateHTLCs { + node_id: test_ctx_their_node_id!(), + updates: CommitmentUpdate { + update_add_htlcs: vec![fake_update_add_htlc_msg!()], + update_fulfill_htlcs: vec![fake_update_fulfill_htlc_msg!()], + update_fail_htlcs: vec![fake_update_fail_htlc_msg!()], + update_fail_malformed_htlcs: vec![fake_update_fail_malformed_htlc_msg!()], + update_fee: Some(fake_update_fee_msg!()), + commitment_signed: fake_commitment_signed_msg!() + } + }); + + let test_ctx = TestCtx::::with_channel_handler(channel_handler); + let mut descriptor = SocketDescriptorMock::new(); + let transport = new_connected_transport!(&test_ctx); + let peer_manager = new_peer_manager_post_init!(&test_ctx, &mut descriptor, &transport); + + peer_manager.process_events(); + + let recording = descriptor.get_recording(); + assert_matches_message!(&recording[0].0, Message::UpdateAddHTLC(_)); + assert_matches_message!(&recording[1].0, Message::UpdateFulfillHTLC(_)); + assert_matches_message!(&recording[2].0, Message::UpdateFailHTLC(_)); + assert_matches_message!(&recording[3].0, Message::UpdateFailMalformedHTLC(_)); + assert_matches_message!(&recording[4].0, Message::UpdateFee(_)); + assert_matches_message!(&recording[5].0, Message::CommitmentSigned(_)); + } + + // Test that a post-Init connection: + // * process_events() sends nothing when it receives a BroadcastChannelAnnouncement if the + // route_handler.handle_channel_announcement errors + #[test] + fn post_init_broadcast_channel_announcement_route_handler_handle_announcement_errors() { + let channel_handler = TestChannelMessageHandler::new(); + let mut routing_handler = RoutingMessageHandlerTestStub::new(); + routing_handler.handle_channel_announcement_return = Err(msgs::LightningError { err: "".to_string(), action: msgs::ErrorAction::IgnoreError }); + channel_handler.pending_events.lock().unwrap().push(BroadcastChannelAnnouncement { + msg: fake_channel_announcement_msg!(0, fake_public_key!(), fake_public_key!()), + update_msg: fake_channel_update_msg!() + }); + + let test_ctx = TestCtx::::with_channel_and_routing_handlers(channel_handler, routing_handler); + let mut descriptor = SocketDescriptorMock::new(); + let transport = new_connected_transport!(&test_ctx); + let peer_manager = new_peer_manager_post_init!(&test_ctx, &mut descriptor, &transport); + + peer_manager.process_events(); + assert!(descriptor.get_recording().is_empty()); + } + + // Test that a post-Init connection: + // * process_events() sends nothing when it receives a BroadcastChannelAnnouncement if the + // route_handler.handle_channel_update errors + #[test] + fn post_init_broadcast_channel_announcement_route_handler_handle_update_errors() { + let channel_handler = TestChannelMessageHandler::new(); + let mut routing_handler = RoutingMessageHandlerTestStub::new(); + routing_handler.handle_channel_update_return = Err(msgs::LightningError { err: "".to_string(), action: msgs::ErrorAction::IgnoreError }); + channel_handler.pending_events.lock().unwrap().push(BroadcastChannelAnnouncement { + msg: fake_channel_announcement_msg!(0, fake_public_key!(), fake_public_key!()), + update_msg: fake_channel_update_msg!() + }); + + let test_ctx = TestCtx::::with_channel_and_routing_handlers(channel_handler, routing_handler); + let mut descriptor = SocketDescriptorMock::new(); + let transport = new_connected_transport!(&test_ctx); + let peer_manager = new_peer_manager_post_init!(&test_ctx, &mut descriptor, &transport); + + peer_manager.process_events(); + assert!(descriptor.get_recording().is_empty()); + } + + // Test that a post-Init connection: + // * process_events() sends nothing when it receives a BroadcastChannelAnnouncement if the + // route_handler.handle_channel_announcement returns false + // XXXBUG: Implementation does not check return value of handle_channel_announcement, only that it didn't error + #[test] + fn post_init_broadcast_channel_announcement_route_handler_handle_announcement_returns_false() { + let channel_handler = TestChannelMessageHandler::new(); + let mut routing_handler = RoutingMessageHandlerTestStub::new(); + routing_handler.handle_channel_announcement_return = Ok(false); + channel_handler.pending_events.lock().unwrap().push(BroadcastChannelAnnouncement { + msg: fake_channel_announcement_msg!(0, fake_public_key!(), fake_public_key!()), + update_msg: fake_channel_update_msg!() + }); + + let test_ctx = TestCtx::::with_channel_and_routing_handlers(channel_handler, routing_handler); + let mut descriptor = SocketDescriptorMock::new(); + let transport = new_connected_transport!(&test_ctx); + let peer_manager = new_peer_manager_post_init!(&test_ctx, &mut descriptor, &transport); + + peer_manager.process_events(); + // assert!(descriptor.get_recording().is_empty()); + } + + // Test that a post-Init connection: + // * process_events() sends nothing when it receives a BroadcastChannelAnnouncement if the + // route_handler.handle_channel_update returns false + // XXXBUG: Implementation does not check return value of handle_channel_update, only that it didn't error + #[test] + fn post_init_broadcast_channel_announcement_route_handle_update_returns_false() { + let channel_handler = TestChannelMessageHandler::new(); + let mut routing_handler = RoutingMessageHandlerTestStub::new(); + routing_handler.handle_channel_update_return = Ok(false); + channel_handler.pending_events.lock().unwrap().push(BroadcastChannelAnnouncement { + msg: fake_channel_announcement_msg!(0, fake_public_key!(), fake_public_key!()), + update_msg: fake_channel_update_msg!() + }); + + let test_ctx = TestCtx::::with_channel_and_routing_handlers(channel_handler, routing_handler); + let mut descriptor = SocketDescriptorMock::new(); + let transport = new_connected_transport!(&test_ctx); + let peer_manager = new_peer_manager_post_init!(&test_ctx, &mut descriptor, &transport); + + peer_manager.process_events(); + // assert!(descriptor.get_recording().is_empty()); + } + + // To reduce test expansion, the unconnected and connected transport tests are only run on one + // broadcast variant. All broadcast implementations use the same API to determine whether or not + // the peer wants the announcement forwarded. + + // Test that a post-Init connection: + // * process_events() sends nothing when it receives a BroadcastChannelAnnouncement if the peer + // has not completed the NOISE handshake + #[test] + fn unconnected_transport_broadcast_channel_announcement() { + let channel_handler = TestChannelMessageHandler::new(); + let mut routing_handler = RoutingMessageHandlerTestStub::new(); + routing_handler.handle_channel_update_return = Ok(false); + channel_handler.pending_events.lock().unwrap().push(BroadcastChannelAnnouncement { + msg: fake_channel_announcement_msg!(0, fake_public_key!(), fake_public_key!()), + update_msg: fake_channel_update_msg!() + }); + + let test_ctx = TestCtx::::with_channel_and_routing_handlers(channel_handler, routing_handler); + let mut descriptor = SocketDescriptorMock::new(); + let transport = new_unconnected_transport!(); + let peer_manager = new_peer_manager_for_test!(&test_ctx); + new_outbound!(peer_manager, &mut descriptor, &transport); + + peer_manager.process_events(); + assert!(descriptor.get_recording().is_empty()); + } + + // Test that a post-Init connection: + // * process_events() sends nothing when it receives a BroadcastChannelAnnouncement if the peer + // has not received an Init message + #[test] + fn connected_transport_broadcast_channel_announcement() { + let channel_handler = TestChannelMessageHandler::new(); + let mut routing_handler = RoutingMessageHandlerTestStub::new(); + routing_handler.handle_channel_update_return = Ok(false); + channel_handler.pending_events.lock().unwrap().push(BroadcastChannelAnnouncement { + msg: fake_channel_announcement_msg!(0, fake_public_key!(), fake_public_key!()), + update_msg: fake_channel_update_msg!() + }); + + let test_ctx = TestCtx::::with_channel_and_routing_handlers(channel_handler, routing_handler); + let mut descriptor = SocketDescriptorMock::new(); + let transport = new_connected_transport!(&test_ctx); + let peer_manager = new_peer_manager_for_test!(&test_ctx); + new_outbound!(peer_manager, &mut descriptor, &transport); + + peer_manager.process_events(); + assert!(descriptor.get_recording().is_empty()); + } + + // Test that a post-Init connection: + // * process_events() sends nothing when it receives a BroadcastChannelAnnouncement if the peer + // is initialized, but Peer::should_forward_channel_announcement returns false + #[test] + fn connected_transport_broadcast_channel_announcement_short_channel_id_larger_than_current_sync() { + let channel_handler = TestChannelMessageHandler::new(); + let routing_handler = RoutingMessageHandlerTestStub::new(); + channel_handler.pending_events.lock().unwrap().push(BroadcastChannelAnnouncement { + msg: fake_channel_announcement_msg!(10000, fake_public_key!(), fake_public_key!()), + update_msg: fake_channel_update_msg!() + }); + + let test_ctx = TestCtx::::with_channel_and_routing_handlers(channel_handler, routing_handler); + let mut descriptor = SocketDescriptorMock::new(); + let transport = new_connected_transport!(&test_ctx); + let peer_manager = new_peer_manager_for_test!(&test_ctx); + new_outbound!(peer_manager, &mut descriptor, &transport); + + // Use an Init sequence with initial_routing_sync and use an arbitrarily high short + // channel_id in the fake_channel_announcement_msg to create the state. This test knows a bit too + // much and future refactoring can make this much better. + transport.borrow_mut().add_incoming_message(Message::Init(Init { features: InitFeatures::known() })); + peer_manager.process_events(); + assert!(descriptor.get_recording().is_empty()); + } + + // Test that a post-Init connection: + // * process_events() sends nothing when it receives a BroadcastChannelAnnouncement if the peer + // is node_id_1 + #[test] + fn post_init_broadcast_channel_announcement_skip_node_id_1() { + let channel_handler = TestChannelMessageHandler::new(); + channel_handler.pending_events.lock().unwrap().push(BroadcastChannelAnnouncement { + msg: fake_channel_announcement_msg!(0, test_ctx_their_node_id!(), fake_public_key!()), + update_msg: fake_channel_update_msg!() + }); + + let test_ctx = TestCtx::::with_channel_handler(channel_handler); + let mut descriptor = SocketDescriptorMock::new(); + let transport = new_connected_transport!(&test_ctx); + let peer_manager = new_peer_manager_post_init!(&test_ctx, &mut descriptor, &transport); + + peer_manager.process_events(); + assert!(descriptor.get_recording().is_empty()); + } + + // Test that a post-Init connection: + // * process_events() sends nothing when it receives a BroadcastChannelAnnouncement if the peer + // is node_id_2 + #[test] + fn post_init_broadcast_channel_announcement_skip_node_id_2() { + let channel_handler = TestChannelMessageHandler::new(); + channel_handler.pending_events.lock().unwrap().push(BroadcastChannelAnnouncement { + msg: fake_channel_announcement_msg!(0, fake_public_key!(), test_ctx_their_node_id!()), + update_msg: fake_channel_update_msg!() + }); + + let test_ctx = TestCtx::::with_channel_handler(channel_handler); + let mut descriptor = SocketDescriptorMock::new(); + let transport = new_connected_transport!(&test_ctx); + let peer_manager = new_peer_manager_post_init!(&test_ctx, &mut descriptor, &transport); + + peer_manager.process_events(); + assert!(descriptor.get_recording().is_empty()); + } + + // Test that a post-Init connection: + // * process_events() sends the relevant messages when it receives a BroadcastChannelAnnouncement + #[test] + fn post_init_broadcast_channel_announcement() { + let channel_handler = TestChannelMessageHandler::new(); + channel_handler.pending_events.lock().unwrap().push(BroadcastChannelAnnouncement { + msg: fake_channel_announcement_msg!(0, fake_public_key!(), fake_public_key!()), + update_msg: fake_channel_update_msg!() + }); + + let test_ctx = TestCtx::::with_channel_handler(channel_handler); + let mut descriptor = SocketDescriptorMock::new(); + let transport = new_connected_transport!(&test_ctx); + let peer_manager = new_peer_manager_post_init!(&test_ctx, &mut descriptor, &transport); + + peer_manager.process_events(); + + let recording = descriptor.get_recording(); + assert_matches_message!(&recording[0].0, Message::ChannelAnnouncement(_)); + assert_matches_message!(&recording[1].0, Message::ChannelUpdate(_)); + } + + // Test that a post-Init connection: + // * process_events() sends nothing when it receives a BroadcastNodeAnnouncement if the + // route_handler.handle_node_announcement errors + #[test] + fn post_init_broadcast_node_announcement_route_handler_handle_announcement_errors() { + let channel_handler = TestChannelMessageHandler::new(); + let mut routing_handler = RoutingMessageHandlerTestStub::new(); + routing_handler.handle_node_announcement_return = Err(msgs::LightningError { err: "".to_string(), action: msgs::ErrorAction::IgnoreError }); + channel_handler.pending_events.lock().unwrap().push(BroadcastNodeAnnouncement { + msg: fake_node_announcement_msg!() + }); + + let test_ctx = TestCtx::::with_channel_and_routing_handlers(channel_handler, routing_handler); + let mut descriptor = SocketDescriptorMock::new(); + let transport = new_connected_transport!(&test_ctx); + let peer_manager = new_peer_manager_post_init!(&test_ctx, &mut descriptor, &transport); + + peer_manager.process_events(); + assert!(descriptor.get_recording().is_empty()); + } + + // Test that a post-Init connection: + // * process_events() sends nothing when it receives a BroadcastNodeAnnouncement if the + // route_handler.handle_node_announcement returns false + // XXXBUG: Implementation does not check return value of handle_node_announcement, only that it didn't error + #[test] + fn post_init_broadcast_node_announcement_route_handler_handle_announcement_returns_false() { + let channel_handler = TestChannelMessageHandler::new(); + let mut routing_handler = RoutingMessageHandlerTestStub::new(); + routing_handler.handle_node_announcement_return = Ok(false); + channel_handler.pending_events.lock().unwrap().push(BroadcastNodeAnnouncement { + msg: fake_node_announcement_msg!() + }); + + let test_ctx = TestCtx::::with_channel_and_routing_handlers(channel_handler, routing_handler); + let mut descriptor = SocketDescriptorMock::new(); + let transport = new_connected_transport!(&test_ctx); + let peer_manager = new_peer_manager_post_init!(&test_ctx, &mut descriptor, &transport); + + peer_manager.process_events(); + // assert!(descriptor.get_recording().is_empty()); + } + + // Test that a post-Init connection: + // * process_events() sends the relevant messages when it receives a BroadcastNodeAnnouncement + #[test] + fn post_init_broadcast_node_announcement() { + let channel_handler = TestChannelMessageHandler::new(); + channel_handler.pending_events.lock().unwrap().push(BroadcastNodeAnnouncement { + msg: fake_node_announcement_msg!() + }); + + let test_ctx = TestCtx::::with_channel_handler(channel_handler); + let mut descriptor = SocketDescriptorMock::new(); + let transport = new_connected_transport!(&test_ctx); + let peer_manager = new_peer_manager_post_init!(&test_ctx, &mut descriptor, &transport); + + peer_manager.process_events(); + + let recording = descriptor.get_recording(); + assert_matches_message!(&recording[0].0, Message::NodeAnnouncement(_)); + } + + + // Test that a post-Init connection: + // * process_events() sends nothing when it receives a BroadcastChannelUpdate if the + // route_handler.handle_channel_update errors + #[test] + fn post_init_broadcast_channel_update_route_handler_handle_update_errors() { + let channel_handler = TestChannelMessageHandler::new(); + let mut routing_handler = RoutingMessageHandlerTestStub::new(); + routing_handler.handle_channel_update_return = Err(msgs::LightningError { err: "".to_string(), action: msgs::ErrorAction::IgnoreError }); + channel_handler.pending_events.lock().unwrap().push(BroadcastChannelUpdate { + msg: fake_channel_update_msg!() + }); + + let test_ctx = TestCtx::::with_channel_and_routing_handlers(channel_handler, routing_handler); + let mut descriptor = SocketDescriptorMock::new(); + let transport = new_connected_transport!(&test_ctx); + let peer_manager = new_peer_manager_post_init!(&test_ctx, &mut descriptor, &transport); + + peer_manager.process_events(); + assert!(descriptor.get_recording().is_empty()); + } + + // Test that a post-Init connection: + // * process_events() sends nothing when it receives a BroadcastChannelUpdate if the + // route_handler.handle_channel_update returns false + // XXXBUG: Implementation does not check return value of handle_node_announcement, only that it didn't error + #[test] + fn post_init_broadcast_channel_update_route_handler_handle_update_returns_false() { + let channel_handler = TestChannelMessageHandler::new(); + let mut routing_handler = RoutingMessageHandlerTestStub::new(); + routing_handler.handle_channel_update_return = Ok(false); + channel_handler.pending_events.lock().unwrap().push(BroadcastChannelUpdate { + msg: fake_channel_update_msg!() + }); + + let test_ctx = TestCtx::::with_channel_and_routing_handlers(channel_handler, routing_handler); + let mut descriptor = SocketDescriptorMock::new(); + let transport = new_connected_transport!(&test_ctx); + let peer_manager = new_peer_manager_post_init!(&test_ctx, &mut descriptor, &transport); + + peer_manager.process_events(); + // assert!(descriptor.get_recording().is_empty()); + } + + // Test that a post-Init connection: + // * process_events() sends the relevant messages when it receives a BroadcastChannelAnnouncement + #[test] + fn post_init_broadcast_channel_update() { + let channel_handler = TestChannelMessageHandler::new(); + channel_handler.pending_events.lock().unwrap().push(BroadcastChannelUpdate { + msg: fake_channel_update_msg!() + }); + + let test_ctx = TestCtx::::with_channel_handler(channel_handler); + let mut descriptor = SocketDescriptorMock::new(); + let transport = new_connected_transport!(&test_ctx); + let peer_manager = new_peer_manager_post_init!(&test_ctx, &mut descriptor, &transport); + + peer_manager.process_events(); + + let recording = descriptor.get_recording(); + assert_matches_message!(&recording[0].0, Message::ChannelUpdate(_)); + } + + // Test that a post-Init connection: + // * process_events() calls the correct route handler callback when it receives a + // PaymentFailureNetworkUpdate event + #[test] + fn post_init_payment_failure_network_update() { + let routing_handler = RoutingMessageHandlerTestSpy::new(); + let channel_handler = TestChannelMessageHandler::new(); + channel_handler.pending_events.lock().unwrap().push(PaymentFailureNetworkUpdate { + update: HTLCFailChannelUpdate::ChannelUpdateMessage { + msg: fake_channel_update_msg!() + } + }); + + let test_ctx = TestCtx::::with_channel_and_routing_handlers(channel_handler, routing_handler); + let mut descriptor = SocketDescriptorMock::new(); + let transport = new_connected_transport!(&test_ctx); + let peer_manager = new_peer_manager_post_init!(&test_ctx, &mut descriptor, &transport); + + peer_manager.process_events(); + + assert!(route_handler_called!(&test_ctx, handle_htlc_fail_channel_update)); + } + + // Test that a post-Init connection: + // * process_events() ignores a HandleErrorEvent::DisconnectPeer for an unknown peer + #[test] + fn post_init_handle_error_event_disconnect_unknown_peer() { + let channel_handler = TestChannelMessageHandler::new(); + channel_handler.pending_events.lock().unwrap().push(HandleError { + node_id: fake_public_key!(), + action: ErrorAction::DisconnectPeer { msg: None } + }); + + let test_ctx = TestCtx::::with_channel_handler(channel_handler); + let mut descriptor = SocketDescriptorMock::new(); + let transport = new_connected_transport!(&test_ctx); + let peer_manager = new_peer_manager_post_init!(&test_ctx, &mut descriptor, &transport); + + peer_manager.process_events(); + + assert!(!descriptor.disconnect_called()); + assert!(descriptor.get_recording().is_empty()); + assert!(!peer_manager.get_peer_node_ids().is_empty()); + } + + // Test that a post-Init connection: + // * When process_events() receives a HandleErrorEvent::DisconnectPeer for an initialized peer w/ no message + // * get_peer_node_id() does not contain node_id + // * process_events() calls socket_disconnected() on the SocketDescriptor + #[test] + fn post_init_handle_error_event_disconnect_no_message() { + let channel_handler = TestChannelMessageHandler::new(); + channel_handler.pending_events.lock().unwrap().push(HandleError { + node_id: test_ctx_their_node_id!(), + action: ErrorAction::DisconnectPeer { msg: None } + }); + + let test_ctx = TestCtx::::with_channel_handler(channel_handler); + let mut descriptor = SocketDescriptorMock::new(); + let transport = new_connected_transport!(&test_ctx); + let peer_manager = new_peer_manager_post_init!(&test_ctx, &mut descriptor, &transport); + + peer_manager.process_events(); + + assert!(descriptor.disconnect_called()); + assert!(descriptor.get_recording().is_empty()); + assert!(peer_manager.get_peer_node_ids().is_empty()); + } + + // Test that a post-Init connection: + // * When process_events() receives a HandleErrorEvent::DisconnectPeer for an initialized peer w/ a message + // * process_events() sends error message is sent through SocketDescriptor (attempted) + // * process_events() calls socket_disconnected() on the SocketDescriptor + // * get_peer_node_id() does not contain node_id + #[test] + fn post_init_handle_error_event_disconnect_message() { + let channel_handler = TestChannelMessageHandler::new(); + channel_handler.pending_events.lock().unwrap().push(HandleError { + node_id: test_ctx_their_node_id!(), + action: ErrorAction::DisconnectPeer { msg: Some(ErrorMessage { + channel_id: [0; 32], + data: "".to_string() + })} + }); + + let test_ctx = TestCtx::::with_channel_handler(channel_handler); + let mut descriptor = SocketDescriptorMock::new(); + let transport = new_connected_transport!(&test_ctx); + let peer_manager = new_peer_manager_post_init!(&test_ctx, &mut descriptor, &transport); + + peer_manager.process_events(); + + let recording = descriptor.get_recording(); + assert_matches_message!(&recording[0].0, Message::Error(_)); + assert!(descriptor.disconnect_called()); + assert!(peer_manager.get_peer_node_ids().is_empty()); + } + + // Test that a post-Init connection: + // * process_events() ignores a HandleErrorEvent::IgnoreError for an initialized peer + #[test] + fn post_init_handle_error_event_ignore_error() { + let channel_handler = TestChannelMessageHandler::new(); + channel_handler.pending_events.lock().unwrap().push(HandleError { + node_id: test_ctx_their_node_id!(), + action: ErrorAction::IgnoreError + }); + + let test_ctx = TestCtx::::with_channel_handler(channel_handler); + let mut descriptor = SocketDescriptorMock::new(); + let transport = new_connected_transport!(&test_ctx); + let peer_manager = new_peer_manager_post_init!(&test_ctx, &mut descriptor, &transport); + + peer_manager.process_events(); + + assert!(!descriptor.disconnect_called()); + assert!(descriptor.get_recording().is_empty()); + assert!(!peer_manager.get_peer_node_ids().is_empty()); + } + + // Test that a post-Init connection: + // * process_events() sends an error when it receives a HandleErrorEvent::SendErrorMessage for + // an initialized peer + #[test] + fn post_init_handle_error_event_send_error_message() { + let channel_handler = TestChannelMessageHandler::new(); + channel_handler.pending_events.lock().unwrap().push(HandleError { + node_id: test_ctx_their_node_id!(), + action: ErrorAction::SendErrorMessage { + msg: ErrorMessage { channel_id: [0; 32], data: "".to_string() } + } + }); + + let test_ctx = TestCtx::::with_channel_handler(channel_handler); + let mut descriptor = SocketDescriptorMock::new(); + let transport = new_connected_transport!(&test_ctx); + let peer_manager = new_peer_manager_post_init!(&test_ctx, &mut descriptor, &transport); + + peer_manager.process_events(); + + let recording = descriptor.get_recording(); + assert_matches_message!(&recording[0].0, Message::Error(_)); + assert!(!descriptor.disconnect_called()); + assert!(!peer_manager.get_peer_node_ids().is_empty()); + } + + // ____ _ _____ _ _ + // | _ \(_)_ __ __ _ |_ _|__ ___| |_(_)_ __ __ _ + // | |_) | | '_ \ / _` | | |/ _ \/ __| __| | '_ \ / _` | + // | __/| | | | | (_| | | | __/\__ \ |_| | | | | (_| | + // |_| |_|_| |_|\__, | |_|\___||___/\__|_|_| |_|\__, | + // |___/ |___/ + + // Test that a post-Init connection: + // * read_event()/process_events() sends a Pong when it receives a Ping + #[test] + fn post_init_ping_creates_pong() { + let test_ctx = TestCtx::::new(); + let mut descriptor = SocketDescriptorMock::new(); + let transport = new_connected_transport!(&test_ctx); + let peer_manager = new_peer_manager_post_init!(&test_ctx, &mut descriptor, &transport); + + transport.borrow_mut().add_incoming_message(Message::Ping(Ping { ponglen: 1, byteslen: 0 })); + + assert_matches!(peer_manager.read_event(&mut descriptor, &[]), Ok(_)); + + peer_manager.process_events(); + + let recording = descriptor.get_recording(); + assert_matches_message!(&recording[0].0, Message::Pong(Pong { byteslen: 1})); + } + + // Test that a post-Init connection: + // * read_event()/process_events() ignores a Pong with ponglen > 65531 + #[test] + fn post_init_ping_ignores_large_pong() { + let test_ctx = TestCtx::::new(); + let mut descriptor = SocketDescriptorMock::new(); + let transport = new_connected_transport!(&test_ctx); + let peer_manager = new_peer_manager_post_init!(&test_ctx, &mut descriptor, &transport); + + transport.borrow_mut().add_incoming_message(Message::Ping(Ping { ponglen: 65532, byteslen: 0 })); + assert_matches!(peer_manager.read_event(&mut descriptor, &[]), Ok(_)); + descriptor.assert_called_with(vec![]); + + peer_manager.process_events(); + descriptor.assert_called_with(vec![]); + } + + // Test that a post-Init connection: + // * timer_tick_occurred() generates a Ping + #[test] + fn post_init_timer_tick_occurred_generates_ping() { + let test_ctx = TestCtx::::new(); + let mut descriptor = SocketDescriptorMock::new(); + let transport = new_connected_transport!(&test_ctx); + let peer_manager = new_peer_manager_post_init!(&test_ctx, &mut descriptor, &transport); + + peer_manager.timer_tick_occured(); + + let recording = descriptor.get_recording(); + assert_matches_message!(&recording[0].0, Message::Ping(_)); + } + + // Test that a post-Init connection: + // * timer_tick_occurred() calls socket_disconnected() on the SocketDescriptor + // * timer_tick_occurred() calls the peer_disconnected channel manager callback + // * get_peer_node_ids() does not contain disconnected node_id + #[test] + fn post_init_ping_no_pong_disconnects() { + let test_ctx = TestCtx::::new(); + let mut descriptor = SocketDescriptorMock::new(); + let transport = new_connected_transport!(&test_ctx); + let peer_manager = new_peer_manager_post_init!(&test_ctx, &mut descriptor, &transport); + + peer_manager.timer_tick_occured(); + + // Elapsed time with no Pong + + peer_manager.timer_tick_occured(); + + assert!(descriptor.disconnect_called()); + assert!(channel_handler_called!(&test_ctx, peer_disconnected)); + assert!(!peer_manager.get_peer_node_ids().contains(&test_ctx.their_node_id)); + } + + // Test that a post-Init connection: + // * timer_tick_occurred() does not call socket_disconnected() if a Pong was received + // * get_peer_node_ids() contains node_id + #[test] + fn post_init_ping_with_pong() { + let test_ctx = TestCtx::::new(); + let mut descriptor = SocketDescriptorMock::new(); + let transport = new_connected_transport!(&test_ctx); + let peer_manager = new_peer_manager_post_init!(&test_ctx, &mut descriptor, &transport); + + peer_manager.timer_tick_occured(); + + transport.borrow_mut().add_incoming_message(Message::Pong(Pong { byteslen: 64 })); + assert_matches!(peer_manager.read_event(&mut descriptor, &[]), Ok(_)); + + // Should notice the Pong and resend Ping + peer_manager.timer_tick_occured(); + + let recording = descriptor.get_recording(); + assert_matches_message!(&recording[1].0, Message::Ping(_)); + assert!(peer_manager.get_peer_node_ids().contains(&test_ctx.their_node_id)); + } + + // Test that a post-Init connection: + // * socket_disconnected() removes the node_id from get_peer_node_ids() + // * socket_disconnected() does not call disconnect_socket() on the SocketDescriptor + // * socket_disconnected() calls the peer_disconnected channel manager callback + #[test] + fn post_init_socket_disconnected() { + let test_ctx = TestCtx::::new(); + let mut descriptor = SocketDescriptorMock::new(); + let transport = new_connected_transport!(&test_ctx); + let peer_manager = new_peer_manager_post_init!(&test_ctx, &mut descriptor, &transport); + + peer_manager.socket_disconnected(&descriptor); + + assert!(peer_manager.get_peer_node_ids().is_empty()); + assert!(!descriptor.disconnect_called()); + assert!(channel_handler_called!(&test_ctx, peer_disconnected)); + } +} + #[cfg(test)] mod tests { use ln::peers::handler::{PeerManager, MessageHandler, SocketDescriptor}; @@ -1266,7 +3114,7 @@ mod tests { fn establish_connection<'a>(peer_a: &PeerManager, peer_b: &PeerManager) -> (FileDescriptor, FileDescriptor) { let secp_ctx = Secp256k1::new(); - let a_id = PublicKey::from_secret_key(&secp_ctx, &peer_a.our_node_secret); + let a_id = PublicKey::from_secret_key(&secp_ctx, &peer_a.inner.our_node_secret); let mut fd_a = FileDescriptor { fd: 1, outbound_data: Arc::new(Mutex::new(Vec::new())) }; let mut fd_b = FileDescriptor { fd: 1, outbound_data: Arc::new(Mutex::new(Vec::new())) }; let initial_data = peer_b.new_outbound_connection(a_id, fd_b.clone()).unwrap(); @@ -1292,20 +3140,20 @@ mod tests { let chan_handler = test_utils::TestChannelMessageHandler::new(); let mut peers = create_network(2, &cfgs); establish_connection(&peers[0], &peers[1]); - assert_eq!(peers[0].peers.lock().unwrap().peers.len(), 1); + assert_eq!(peers[0].inner.peers.lock().unwrap().peers.len(), 1); let secp_ctx = Secp256k1::new(); - let their_id = PublicKey::from_secret_key(&secp_ctx, &peers[1].our_node_secret); + let their_id = PublicKey::from_secret_key(&secp_ctx, &peers[1].inner.our_node_secret); chan_handler.pending_events.lock().unwrap().push(events::MessageSendEvent::HandleError { node_id: their_id, action: msgs::ErrorAction::DisconnectPeer { msg: None }, }); assert_eq!(chan_handler.pending_events.lock().unwrap().len(), 1); - peers[0].message_handler.chan_handler = &chan_handler; + peers[0].inner.message_handler.chan_handler = &chan_handler; peers[0].process_events(); - assert_eq!(peers[0].peers.lock().unwrap().peers.len(), 0); + assert_eq!(peers[0].inner.peers.lock().unwrap().peers.len(), 0); } #[test] @@ -1314,15 +3162,15 @@ mod tests { let cfgs = create_peermgr_cfgs(2); let peers = create_network(2, &cfgs); establish_connection(&peers[0], &peers[1]); - assert_eq!(peers[0].peers.lock().unwrap().peers.len(), 1); + assert_eq!(peers[0].inner.peers.lock().unwrap().peers.len(), 1); // peers[0] awaiting_pong is set to true, but the Peer is still connected peers[0].timer_tick_occured(); - assert_eq!(peers[0].peers.lock().unwrap().peers.len(), 1); + assert_eq!(peers[0].inner.peers.lock().unwrap().peers.len(), 1); // Since timer_tick_occured() is called again when awaiting_pong is true, all Peers are disconnected peers[0].timer_tick_occured(); - assert_eq!(peers[0].peers.lock().unwrap().peers.len(), 0); + assert_eq!(peers[0].inner.peers.lock().unwrap().peers.len(), 0); } #[test] @@ -1362,8 +3210,8 @@ mod tests { let peers = create_network(2, &cfgs); let (fd_0_to_1, fd_1_to_0) = establish_connection_and_read_events(&peers[0], &peers[1]); - let peer_0 = peers[0].peers.lock().unwrap(); - let peer_1 = peers[1].peers.lock().unwrap(); + let peer_0 = peers[0].inner.peers.lock().unwrap(); + let peer_1 = peers[1].inner.peers.lock().unwrap(); let peer_0_features = peer_1.peers.get(&fd_1_to_0).unwrap().their_features.as_ref(); let peer_1_features = peer_0.peers.get(&fd_0_to_1).unwrap().their_features.as_ref(); @@ -1379,8 +3227,8 @@ mod tests { let peers = create_network(2, &cfgs); let (fd_0_to_1, fd_1_to_0) = establish_connection_and_read_events(&peers[0], &peers[1]); - let peer_0 = peers[0].peers.lock().unwrap(); - let peer_1 = peers[1].peers.lock().unwrap(); + let peer_0 = peers[0].inner.peers.lock().unwrap(); + let peer_1 = peers[1].inner.peers.lock().unwrap(); let peer_0_features = peer_1.peers.get(&fd_1_to_0).unwrap().their_features.as_ref(); let peer_1_features = peer_0.peers.get(&fd_0_to_1).unwrap().their_features.as_ref(); diff --git a/lightning/src/ln/peers/mod.rs b/lightning/src/ln/peers/mod.rs index 1156d939264..ce7f280964c 100644 --- a/lightning/src/ln/peers/mod.rs +++ b/lightning/src/ln/peers/mod.rs @@ -16,6 +16,10 @@ #[macro_use] mod test_util; +#[cfg(test)] +#[macro_use] +mod test_message_macros; + mod chacha; pub mod handler; mod hkdf5869rfc; diff --git a/lightning/src/ln/peers/test_message_macros.rs b/lightning/src/ln/peers/test_message_macros.rs new file mode 100644 index 00000000000..b5751d1511a --- /dev/null +++ b/lightning/src/ln/peers/test_message_macros.rs @@ -0,0 +1,278 @@ +// This file is Copyright its original authors, visible in version control +// history. +// +// This file is licensed under the Apache License, Version 2.0 or the MIT license +// , at your option. +// You may not use this file except in accordance with one or both of these +// licenses. + +/// Helper macros that construct fake Messages. Useful in tests that don't care about the contents. + +macro_rules! fake_public_key { + () => {{ + let their_node_secret = SecretKey::from_slice(&[0x_11_u8; 32]).unwrap(); + PublicKey::from_secret_key(&Secp256k1::new(), &their_node_secret) + }} +} + +macro_rules! fake_valid_sig { + () => {{ + use bitcoin::secp256k1::ffi::Signature as FFISignature; + Signature::from(FFISignature::new()) + }} +} + +macro_rules! fake_open_channel_msg { + () => {{ + OpenChannel { + chain_hash: Default::default(), + temporary_channel_id: [0; 32], + funding_satoshis: 0, + push_msat: 0, + dust_limit_satoshis: 0, + max_htlc_value_in_flight_msat: 0, + channel_reserve_satoshis: 0, + htlc_minimum_msat: 0, + feerate_per_kw: 0, + to_self_delay: 0, + max_accepted_htlcs: 0, + funding_pubkey: fake_public_key!(), + revocation_basepoint: fake_public_key!(), + delayed_payment_basepoint: fake_public_key!(), + htlc_basepoint: fake_public_key!(), + payment_point: fake_public_key!(), + first_per_commitment_point: fake_public_key!(), + channel_flags: 0, + shutdown_scriptpubkey: OptionalField::Absent + } + }} +} + +macro_rules! fake_accept_channel_msg { + () => {{ + AcceptChannel { + temporary_channel_id: [0; 32], + dust_limit_satoshis: 0, + max_htlc_value_in_flight_msat: 0, + channel_reserve_satoshis: 0, + htlc_minimum_msat: 0, + minimum_depth: 0, + to_self_delay: 0, + max_accepted_htlcs: 0, + funding_pubkey: fake_public_key!(), + revocation_basepoint: fake_public_key!(), + payment_point: fake_public_key!(), + delayed_payment_basepoint: fake_public_key!(), + htlc_basepoint: fake_public_key!(), + first_per_commitment_point: fake_public_key!(), + shutdown_scriptpubkey: OptionalField::Absent + } + }} +} +macro_rules! fake_funding_created_msg { + () => {{ + FundingCreated { + temporary_channel_id: [0; 32], + funding_txid: Default::default(), + funding_output_index: 0, + signature: fake_valid_sig!() + } + }} +} + +macro_rules! fake_funding_signed_msg { + () => {{ + FundingSigned { + channel_id: [0; 32], + signature: fake_valid_sig!() + } + }} +} + +macro_rules! fake_funding_locked_msg { + () => {{ + FundingLocked { + channel_id: [0; 32], + next_per_commitment_point: fake_public_key!() + } + }} +} + +macro_rules! fake_shutdown_msg { + () => {{ + Shutdown { + channel_id: [0; 32], + scriptpubkey: Default::default() + } + }} +} + +macro_rules! fake_closing_signed_msg { + () => {{ + ClosingSigned { + channel_id: [0; 32], + fee_satoshis: 0, + signature: fake_valid_sig!() + } + }} +} + +macro_rules! fake_update_add_htlc_msg { + () => {{ + UpdateAddHTLC { + channel_id: [0; 32], + htlc_id: 0, + amount_msat: 0, + payment_hash: PaymentHash([0; 32]), + cltv_expiry: 0, + onion_routing_packet: OnionPacket { + version: 0, + public_key: Ok(fake_public_key!()), + hop_data: [0; 1300], + hmac: [0; 32] + } + } + }} +} + +macro_rules! fake_update_fulfill_htlc_msg { + () => {{ + UpdateFulfillHTLC { + channel_id: [0; 32], + htlc_id: 0, + payment_preimage: PaymentPreimage([0; 32]) + } + }} +} + + +macro_rules! fake_update_fail_htlc_msg { + () => {{ + UpdateFailHTLC { + channel_id: [0; 32], + htlc_id: 0, + reason: OnionErrorPacket { data: vec![] } + } + }} +} + +macro_rules! fake_update_fail_malformed_htlc_msg { + () => { + UpdateFailMalformedHTLC { + channel_id: [0; 32], + htlc_id: 0, + sha256_of_onion: [0; 32], + failure_code: 0 + } + } +} + +macro_rules! fake_commitment_signed_msg { + () => {{ + CommitmentSigned { + channel_id: [0; 32], + signature: fake_valid_sig!(), + htlc_signatures: vec![] + } + }} +} + +macro_rules! fake_revoke_and_ack_msg { + () => {{ + RevokeAndACK { + channel_id: [0; 32], + per_commitment_secret: [0; 32], + next_per_commitment_point: fake_public_key!() + } + }} +} +macro_rules! fake_update_fee_msg { + () => {{ + UpdateFee { + channel_id: [0; 32], + feerate_per_kw: 0 + } + }} +} + +macro_rules! fake_channel_reestablish_msg { + () => {{ + ChannelReestablish { + channel_id: [0; 32], + next_local_commitment_number: 0, + next_remote_commitment_number: 0, + data_loss_protect: OptionalField::Absent + } + }} +} + +macro_rules! fake_announcement_signatures_msg { + () => {{ + AnnouncementSignatures { + channel_id: [0;32], + short_channel_id: 0, + node_signature: fake_valid_sig!(), + bitcoin_signature: fake_valid_sig!() + } + }} +} + +macro_rules! fake_channel_announcement_msg { + ($channel_id: expr, $node_id_1: expr, $node_id_2: expr) => {{ + ChannelAnnouncement { + node_signature_1: fake_valid_sig!(), + node_signature_2: fake_valid_sig!(), + bitcoin_signature_1: fake_valid_sig!(), + bitcoin_signature_2: fake_valid_sig!(), + contents: UnsignedChannelAnnouncement { + features: ChannelFeatures::empty(), + chain_hash: Default::default(), + short_channel_id: $channel_id, + node_id_1: $node_id_1, + node_id_2: $node_id_2, + bitcoin_key_1: fake_public_key!(), + bitcoin_key_2: fake_public_key!(), + excess_data: vec![] + } + } + }} +} + +macro_rules! fake_node_announcement_msg { + () => {{ + NodeAnnouncement { + signature: fake_valid_sig!(), + contents: UnsignedNodeAnnouncement { + features: NodeFeatures::empty(), + timestamp: 0, + node_id: fake_public_key!(), + rgb: [0; 3], + alias: [0; 32], + addresses: vec![], + excess_address_data: vec![], + excess_data: vec![] + } + } + }} +} + +macro_rules! fake_channel_update_msg { + () => {{ + ChannelUpdate { + signature: fake_valid_sig!(), + contents: UnsignedChannelUpdate { + chain_hash: Default::default(), + short_channel_id: 0, + timestamp: 0, + flags: 0, + cltv_expiry_delta: 0, + htlc_minimum_msat: 0, + htlc_maximum_msat: OptionalField::Absent, + fee_base_msat: 0, + fee_proportional_millionths: 0, + excess_data: vec![] + } + } + }} +} diff --git a/lightning/src/ln/peers/test_util.rs b/lightning/src/ln/peers/test_util.rs index 9d18ad86ace..69dafa54223 100644 --- a/lightning/src/ln/peers/test_util.rs +++ b/lightning/src/ln/peers/test_util.rs @@ -13,13 +13,18 @@ use bitcoin::secp256k1; use bitcoin::secp256k1::key::{PublicKey, SecretKey}; use ln::peers::conduit::Conduit; -use ln::peers::handler::{SocketDescriptor, PayloadQueuer}; +use ln::peers::handler::{SocketDescriptor, PayloadQueuer, ITransport, PeerHandleError}; use ln::peers::transport::IPeerHandshake; use std::rc::Rc; -use std::cell::RefCell; +use std::cell::{RefCell}; use std::hash::Hash; use std::cmp; +use ln::wire::{Message, Encode}; +use bitcoin::hashes::core::ops::Deref; +use util::logger::Logger; +use util::ser::{Writeable, VecWriter}; +use ln::wire; macro_rules! assert_matches { ($actual:expr, $expected:pat) => { @@ -117,6 +122,9 @@ pub(super) struct SocketDescriptorMock { /// Vector of arguments and return values to send_data() used for validation send_recording: Rc, bool)>>>, + + /// Record if disconnect() was called on the Mock + disconnect_called: Rc>, } impl SocketDescriptorMock { @@ -125,7 +133,8 @@ impl SocketDescriptorMock { Self { unbounded: Rc::new(RefCell::new(true)), send_recording: Rc::new(RefCell::new(Vec::new())), - free_space: Rc::new(RefCell::new(0)) + free_space: Rc::new(RefCell::new(0)), + disconnect_called: Rc::new(RefCell::new(false)) } } @@ -143,6 +152,15 @@ impl SocketDescriptorMock { assert_eq!(expectation.as_slice(), self.send_recording.borrow().as_slice()) } + /// Retrieve the underlying recording for use in pattern matching or more complex value validation + pub(super) fn get_recording(&self) -> Vec<(Vec, bool)> { + self.send_recording.borrow().clone() + } + + pub(super) fn disconnect_called(&self) -> bool { + *self.disconnect_called.borrow_mut() + } + /// Allow future send_data() calls to succeed for the next added_room bytes. Not valid for /// unbounded mock descriptors pub(super) fn make_room(&mut self, added_room: usize) { @@ -151,6 +169,11 @@ impl SocketDescriptorMock { *free_space += added_room; } + + /// Clear the saved recording. Useful for resetting state between test phases. + pub(super) fn clear_recording(&mut self) { + self.send_recording.borrow_mut().clear(); + } } impl SocketDescriptor for SocketDescriptorMock { @@ -172,7 +195,9 @@ impl SocketDescriptor for SocketDescriptorMock { } fn disconnect_socket(&mut self) { - unimplemented!() + let mut val = self.disconnect_called.borrow_mut(); + assert!(!*val, "disconnect_socket() was alraedy called"); + *val = true; } } @@ -181,7 +206,8 @@ impl Clone for SocketDescriptorMock { Self { unbounded: self.unbounded.clone(), send_recording: self.send_recording.clone(), - free_space: self.free_space.clone() + free_space: self.free_space.clone(), + disconnect_called: self.disconnect_called.clone(), } } } @@ -212,3 +238,133 @@ impl PayloadQueuer for Vec> { } } +// Builder for TransportTestStub that allows tests to easily construct the Transport layer they +// want to use for their test. +pub(super) struct TransportStubBuilder { + stub: TransportStub, +} + +impl TransportStubBuilder { + pub(super) fn new() -> Self { + Self { + stub: TransportStub { + is_connected: false, + messages: vec![], + process_returns_error: false, + their_node_id: None, + } + } + } + + pub(super) fn set_connected(mut self, their_node_id: &PublicKey) -> Self { + self.stub.is_connected = true; + self.stub.their_node_id = Some(their_node_id.clone()); + self + } + + pub(super) fn process_returns_error(mut self) -> Self { + self.stub.process_returns_error(); + self + } + + pub(super) fn finish(self) -> TransportStub { + self.stub + } +} + +pub(super) struct TransportStub { + is_connected: bool, + messages: Vec, + process_returns_error: bool, + their_node_id: Option, +} + +// &RefCell passthrough to allow unit tests to pass in a Transport to the PeerManager, but later +// modify it to fail, return messages, etc. +impl<'a> ITransport for &'a RefCell { + fn new_outbound(_initiator_static_private_key: &SecretKey, _responder_static_public_key: &PublicKey, _initiator_ephemeral_private_key: &SecretKey) -> Self { + unimplemented!() + } + + fn set_up_outbound(&mut self) -> Vec { + self.borrow_mut().set_up_outbound() + } + + fn new_inbound(_responder_static_private_key: &SecretKey, _responder_ephemeral_private_key: &SecretKey) -> Self { + unimplemented!() + } + + fn process_input(&mut self, input: &[u8], output_buffer: &mut impl PayloadQueuer) -> Result<(), String> { + self.borrow_mut().process_input(input, output_buffer) + } + + fn is_connected(&self) -> bool { + self.borrow().is_connected() + } + + fn get_their_node_id(&self) -> PublicKey { + self.borrow().get_their_node_id() + } + + fn drain_messages(&mut self, logger: L) -> Result, PeerHandleError> where L::Target: Logger { + self.borrow_mut().drain_messages(logger) + } + + fn enqueue_message(&mut self, message: &M, output_buffer: &mut Q, logger: L) where L::Target: Logger { + self.borrow_mut().enqueue_message(message, output_buffer, logger) + } +} + +impl TransportStub { + pub(super) fn process_returns_error(&mut self) { + self.process_returns_error = true; + } + + pub(super) fn add_incoming_message(&mut self, message: Message) { + assert!(self.is_connected, "Can't set messages on unconnected Transport"); + self.messages.push(message); + } +} + +// Stub implementation for ITransport with a fixed connection state. All enqueue_message() calls +// are placed directly into the PayloadQueuer unencrypted for easier test validation. +impl ITransport for TransportStub { + fn new_outbound(_initiator_static_private_key: &SecretKey, _responder_static_public_key: &PublicKey, _initiator_ephemeral_private_key: &SecretKey) -> Self { + unimplemented!() + } + + fn set_up_outbound(&mut self) -> Vec { + vec![] + } + + fn new_inbound(_responder_static_private_key: &SecretKey, _responder_ephemeral_private_key: &SecretKey) -> Self { + unimplemented!() + } + + fn process_input(&mut self, _input: &[u8], _output_buffer: &mut impl PayloadQueuer) -> Result<(), String> { + if self.process_returns_error { + Err("Oh no!".to_string()) + } else { + Ok(()) + } + } + + fn is_connected(&self) -> bool { + self.their_node_id.is_some() + } + + fn get_their_node_id(&self) -> PublicKey { + self.their_node_id.unwrap() + } + + fn drain_messages(&mut self, _logger: L) -> Result, PeerHandleError> where L::Target: Logger { + Ok(self.messages.drain(..).collect()) + } + + fn enqueue_message(&mut self, message: &M, output_buffer: &mut Q, _logger: L) + where L::Target: Logger { + let mut buffer = VecWriter(Vec::new()); + wire::write(message, &mut buffer).unwrap(); + output_buffer.push_back(buffer.0); + } +} \ No newline at end of file diff --git a/lightning/src/ln/wire.rs b/lightning/src/ln/wire.rs index 86d8bfdd494..432619bd659 100644 --- a/lightning/src/ln/wire.rs +++ b/lightning/src/ln/wire.rs @@ -61,7 +61,7 @@ pub enum Message { /// A number identifying a message to determine how it is encoded on the wire. #[derive(Clone, Copy)] -pub struct MessageType(u16); +pub struct MessageType(pub u16); impl Message { /// Returns the type that was used to decode the message payload. diff --git a/lightning/src/util/test_utils.rs b/lightning/src/util/test_utils.rs index 0370c0e1a40..03a0c3b0442 100644 --- a/lightning/src/util/test_utils.rs +++ b/lightning/src/util/test_utils.rs @@ -17,7 +17,7 @@ use chain::transaction::OutPoint; use chain::keysinterface; use ln::features::{ChannelFeatures, InitFeatures}; use ln::msgs; -use ln::msgs::OptionalField; +use ln::msgs::*; use util::enforcing_trait_impls::EnforcingChannelKeys; use util::events; use util::logger::{Logger, Level, Record}; @@ -143,6 +143,144 @@ impl chaininterface::BroadcasterInterface for TestBroadcaster { } } +/// Test Spy infrastructure for the ChannelMessageHandler trait that tests can use to validate +/// the correct callbacks were called. +macro_rules! generate_message_handler_called_fns { + ($($name: ident,)*) => { + pub struct ChannelMessageHandlerTestSpyCalledFns { + $( + pub $name: bool, + )* + } + impl ChannelMessageHandlerTestSpyCalledFns { + pub fn new() -> Self { + Self { + $( + $name: false, + )* + } + } + } + } +} +generate_message_handler_called_fns! { + handle_open_channel, + handle_accept_channel, + handle_funding_created, + handle_funding_signed, + handle_funding_locked, + handle_shutdown, + handle_closing_signed, + handle_update_add_htlc, + handle_update_fulfill_htlc, + handle_update_fail_htlc, + handle_update_fail_malformed_htlc, + handle_commitment_signed, + handle_revoke_and_ack, + handle_update_fee, + handle_announcement_signatures, + handle_channel_reestablish, + handle_error, + peer_connected, + peer_disconnected, +} + +pub struct ChannelMessageHandlerTestSpy { + pub called: Mutex +} + +impl ChannelMessageHandlerTestSpy { + pub fn new() -> Self { + Self { + called: Mutex::new(ChannelMessageHandlerTestSpyCalledFns::new()) + } + } +} + +impl msgs::ChannelMessageHandler for ChannelMessageHandlerTestSpy { + fn handle_open_channel(&self, _their_node_id: &PublicKey, _their_features: InitFeatures, _msg: &OpenChannel) { + self.called.lock().unwrap().handle_open_channel = true; + } + + fn handle_accept_channel(&self, _their_node_id: &PublicKey, _their_features: InitFeatures, _msg: &AcceptChannel) { + self.called.lock().unwrap().handle_accept_channel = true; + } + + fn handle_funding_created(&self, _their_node_id: &PublicKey, _msg: &FundingCreated) { + self.called.lock().unwrap().handle_funding_created = true; + } + + fn handle_funding_signed(&self, _their_node_id: &PublicKey, _msg: &FundingSigned) { + self.called.lock().unwrap().handle_funding_signed = true; + } + + fn handle_funding_locked(&self, _their_node_id: &PublicKey, _msg: &FundingLocked) { + self.called.lock().unwrap().handle_funding_locked = true; + } + + fn handle_shutdown(&self, _their_node_id: &PublicKey, _msg: &Shutdown) { + self.called.lock().unwrap().handle_shutdown = true; + } + + fn handle_closing_signed(&self, _their_node_id: &PublicKey, _msg: &ClosingSigned) { + self.called.lock().unwrap().handle_closing_signed = true; + } + + fn handle_update_add_htlc(&self, _their_node_id: &PublicKey, _msg: &UpdateAddHTLC) { + self.called.lock().unwrap().handle_update_add_htlc = true; + } + + fn handle_update_fulfill_htlc(&self, _their_node_id: &PublicKey, _msg: &UpdateFulfillHTLC) { + self.called.lock().unwrap().handle_update_fulfill_htlc = true; + } + + fn handle_update_fail_htlc(&self, _their_node_id: &PublicKey, _msg: &UpdateFailHTLC) { + self.called.lock().unwrap().handle_update_fail_htlc = true; + } + + fn handle_update_fail_malformed_htlc(&self, _their_node_id: &PublicKey, _msg: &UpdateFailMalformedHTLC) { + self.called.lock().unwrap().handle_update_fail_malformed_htlc = true; + } + + fn handle_commitment_signed(&self, _their_node_id: &PublicKey, _msg: &CommitmentSigned) { + self.called.lock().unwrap().handle_commitment_signed = true; + } + + fn handle_revoke_and_ack(&self, _their_node_id: &PublicKey, _msg: &RevokeAndACK) { + self.called.lock().unwrap().handle_revoke_and_ack = true; + } + + fn handle_update_fee(&self, _their_node_id: &PublicKey, _msg: &UpdateFee) { + self.called.lock().unwrap().handle_update_fee = true; + } + + fn handle_announcement_signatures(&self, _their_node_id: &PublicKey, _msg: &AnnouncementSignatures) { + self.called.lock().unwrap().handle_announcement_signatures = true; + } + + fn peer_disconnected(&self, _their_node_id: &PublicKey, _no_connection_possible: bool) { + self.called.lock().unwrap().peer_disconnected = true; + } + + fn peer_connected(&self, _their_node_id: &PublicKey, _msg: &Init) { + self.called.lock().unwrap().peer_connected = true; + } + + fn handle_channel_reestablish(&self, _their_node_id: &PublicKey, _msg: &ChannelReestablish) { + self.called.lock().unwrap().handle_channel_reestablish = true; + } + + fn handle_error(&self, _their_node_id: &PublicKey, _msg: &ErrorMessage) { + self.called.lock().unwrap().handle_error = true; + } +} + +impl events::MessageSendEventsProvider for ChannelMessageHandlerTestSpy { + fn get_and_clear_pending_msg_events(&self) -> Vec { + vec![] + } +} + pub struct TestChannelMessageHandler { pub pending_events: Mutex>, } @@ -234,6 +372,132 @@ fn get_dummy_channel_update(short_chan_id: u64) -> msgs::ChannelUpdate { } } +/// Test Stub for the RoutingMessageHandler. +pub struct RoutingMessageHandlerTestStub { + pub handle_node_announcement_return: Result, + pub handle_channel_announcement_return: Result, + pub handle_channel_update_return: Result, + pub should_request_full_sync_return: bool +} + +impl RoutingMessageHandlerTestStub { + pub fn new() -> Self { + Self { + handle_node_announcement_return: Ok(true), + handle_channel_announcement_return: Ok(true), + handle_channel_update_return: Ok(true), + should_request_full_sync_return: true + } + } +} + +impl RoutingMessageHandler for RoutingMessageHandlerTestStub { + fn handle_node_announcement(&self, _msg: &NodeAnnouncement) -> Result { + self.handle_node_announcement_return.clone() + } + + fn handle_channel_announcement(&self, _msg: &ChannelAnnouncement) -> Result { + self.handle_channel_announcement_return.clone() + } + + fn handle_channel_update(&self, _msg: &ChannelUpdate) -> Result { + self.handle_channel_update_return.clone() + } + + fn handle_htlc_fail_channel_update(&self, _update: &HTLCFailChannelUpdate) { } + + fn get_next_channel_announcements(&self, _starting_point: u64, _batch_amount: u8) -> Vec<(ChannelAnnouncement, Option, Option)> { + vec![] + } + + fn get_next_node_announcements(&self, _starting_point: Option<&PublicKey>, _batch_amount: u8) -> Vec { + vec![] + } + + fn should_request_full_sync(&self, _node_id: &PublicKey) -> bool { + self.should_request_full_sync_return + } +} + +/// Test Spy infrastructure for the RoutingMessageHandler trait that tests can use to validate +/// the correct callbacks were called. +macro_rules! generate_routing_handler_called_fns { + ($($name: ident,)*) => { + pub struct RoutingMessageHandlerTestSpyCalledFns { + $( + pub $name: bool, + )* + } + impl RoutingMessageHandlerTestSpyCalledFns { + pub fn new() -> Self { + Self { + $( + $name: false, + )* + } + } + } + } +} + +generate_routing_handler_called_fns!( + handle_node_announcement, + handle_channel_announcement, + handle_channel_update, + handle_htlc_fail_channel_update, + get_next_channel_announcements, + get_next_node_announcements, + should_request_full_sync, +); + +pub struct RoutingMessageHandlerTestSpy { + pub called: Mutex +} + +impl RoutingMessageHandlerTestSpy { + pub fn new() -> Self { + Self { + called: Mutex::new(RoutingMessageHandlerTestSpyCalledFns::new()) + } + } +} + +impl RoutingMessageHandler for RoutingMessageHandlerTestSpy { + fn handle_node_announcement(&self, _msg: &NodeAnnouncement) -> Result { + self.called.lock().unwrap().handle_node_announcement = true; + Ok(true) + } + + fn handle_channel_announcement(&self, _msg: &ChannelAnnouncement) -> Result { + self.called.lock().unwrap().handle_channel_announcement = true; + Ok(true) + } + + fn handle_channel_update(&self, _msg: &ChannelUpdate) -> Result { + self.called.lock().unwrap().handle_channel_update = true; + Ok(true) + } + + fn handle_htlc_fail_channel_update(&self, _update: &HTLCFailChannelUpdate) { + self.called.lock().unwrap().handle_htlc_fail_channel_update = true; + } + + fn get_next_channel_announcements(&self, _starting_point: u64, _batch_amount: u8) -> Vec<(ChannelAnnouncement, Option, Option)> { + self.called.lock().unwrap().get_next_channel_announcements = true; + vec![] + } + + fn get_next_node_announcements(&self, _starting_point: Option<&PublicKey>, _batch_amount: u8) -> Vec { + self.called.lock().unwrap().get_next_node_announcements = true; + vec![] + } + + fn should_request_full_sync(&self, _node_id: &PublicKey) -> bool { + self.called.lock().unwrap().should_request_full_sync = true; + true + } +} + pub struct TestRoutingMessageHandler { pub chan_upds_recvd: AtomicUsize, pub chan_anns_recvd: AtomicUsize, From 3218522cc7e95a7a2c009634bb4d86216b1266bd Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Sat, 5 Sep 2020 14:30:40 -0700 Subject: [PATCH 55/72] refactor: Destructure read_event() path Pass in all the in/out parameters individually instead of taking everything out of peer. The important thing to notice here is that the pending_outbound_buffer parameter MUST be an OutboundQueue and CANNOT be an 'impl PayloadQueuer'. This demonstrates that the read_event() function has the ability to flush() since it can use the SocketDescriptorFlusher interface. This should be illegal due to the invariant that read_event() never calls back into the SocketDescriptor. This is one of the motivations of using trait bounds in argument position as a defensive programing technique to prohibit functions from taking actions that shouldn't be allowed even if they have access to the object. Future patches will fix this bug and ensure the type system enforces this bug from happening again. This is a short-lived destructure that will be recombined once the dependencies are cleaned up. --- lightning/src/ln/peers/handler.rs | 336 ++++++++++++++++-------------- 1 file changed, 180 insertions(+), 156 deletions(-) diff --git a/lightning/src/ln/peers/handler.rs b/lightning/src/ln/peers/handler.rs index 5cd10547425..55919119cbe 100644 --- a/lightning/src/ln/peers/handler.rs +++ b/lightning/src/ln/peers/handler.rs @@ -518,61 +518,66 @@ impl) { - while !peer.pending_outbound_buffer.is_blocked() { - let queue_space = peer.pending_outbound_buffer.queue_space(); + fn do_attempt_write_data( + &self, + descriptor: &mut Descriptor, + sync_status: &mut InitSyncTracker, + transport: &mut impl ITransport, + pending_outbound_buffer: &mut Q) { + while !pending_outbound_buffer.is_blocked() { + let queue_space = pending_outbound_buffer.queue_space(); if queue_space > 0 { - match peer.sync_status { - InitSyncTracker::NoSyncRequested => {}, - InitSyncTracker::ChannelsSyncing(c) if c < 0xffff_ffff_ffff_ffff => { + match sync_status { + &mut InitSyncTracker::NoSyncRequested => {}, + &mut InitSyncTracker::ChannelsSyncing(c) if c < 0xffff_ffff_ffff_ffff => { let steps = ((queue_space + 2) / 3) as u8; let all_messages = self.message_handler.route_handler.get_next_channel_announcements(c, steps); for &(ref announce, ref update_a_option, ref update_b_option) in all_messages.iter() { - peer.transport.enqueue_message(announce, &mut peer.pending_outbound_buffer, &*self.logger); + transport.enqueue_message(announce, pending_outbound_buffer, &*self.logger); if let &Some(ref update_a) = update_a_option { - peer.transport.enqueue_message(update_a, &mut peer.pending_outbound_buffer, &*self.logger); + transport.enqueue_message(update_a, pending_outbound_buffer, &*self.logger); } if let &Some(ref update_b) = update_b_option { - peer.transport.enqueue_message(update_b, &mut peer.pending_outbound_buffer, &*self.logger); + transport.enqueue_message(update_b, pending_outbound_buffer, &*self.logger); } - peer.sync_status = InitSyncTracker::ChannelsSyncing(announce.contents.short_channel_id + 1); + *sync_status = InitSyncTracker::ChannelsSyncing(announce.contents.short_channel_id + 1); } if all_messages.is_empty() || all_messages.len() != steps as usize { - peer.sync_status = InitSyncTracker::ChannelsSyncing(0xffff_ffff_ffff_ffff); + *sync_status = InitSyncTracker::ChannelsSyncing(0xffff_ffff_ffff_ffff); } }, - InitSyncTracker::ChannelsSyncing(c) if c == 0xffff_ffff_ffff_ffff => { + &mut InitSyncTracker::ChannelsSyncing(c) if c == 0xffff_ffff_ffff_ffff => { let steps = queue_space as u8; let all_messages = self.message_handler.route_handler.get_next_node_announcements(None, steps); for msg in all_messages.iter() { - peer.transport.enqueue_message(msg, &mut peer.pending_outbound_buffer, &*self.logger); - peer.sync_status = InitSyncTracker::NodesSyncing(msg.contents.node_id); + transport.enqueue_message(msg, pending_outbound_buffer, &*self.logger); + *sync_status = InitSyncTracker::NodesSyncing(msg.contents.node_id); } if all_messages.is_empty() || all_messages.len() != steps as usize { - peer.sync_status = InitSyncTracker::NoSyncRequested; + *sync_status = InitSyncTracker::NoSyncRequested; } }, - InitSyncTracker::ChannelsSyncing(_) => unreachable!(), - InitSyncTracker::NodesSyncing(key) => { + &mut InitSyncTracker::ChannelsSyncing(_) => unreachable!(), + &mut InitSyncTracker::NodesSyncing(key) => { let steps = queue_space as u8; let all_messages = self.message_handler.route_handler.get_next_node_announcements(Some(&key), steps); for msg in all_messages.iter() { - peer.transport.enqueue_message(msg, &mut peer.pending_outbound_buffer, &*self.logger); - peer.sync_status = InitSyncTracker::NodesSyncing(msg.contents.node_id); + transport.enqueue_message(msg, pending_outbound_buffer, &*self.logger); + *sync_status = InitSyncTracker::NodesSyncing(msg.contents.node_id); } if all_messages.is_empty() || all_messages.len() != steps as usize { - peer.sync_status = InitSyncTracker::NoSyncRequested; + *sync_status = InitSyncTracker::NoSyncRequested; } }, } } // No messages to send - if peer.pending_outbound_buffer.is_empty() { + if pending_outbound_buffer.is_empty() { break; } - peer.pending_outbound_buffer.try_flush_one(descriptor); + pending_outbound_buffer.try_flush_one(descriptor); } } @@ -582,14 +587,23 @@ impl panic!("Descriptor for write_event is not already known to PeerManager"), Some(peer) => { peer.pending_outbound_buffer.unblock(); - self.do_attempt_write_data(descriptor, peer); + self.do_attempt_write_data(descriptor, &mut peer.sync_status, &mut peer.transport, &mut peer.pending_outbound_buffer); } }; Ok(()) } fn read_event(&self, peer_descriptor: &mut Descriptor, data: &[u8]) -> Result { - match self.do_read_event(peer_descriptor, data) { + let result = { + let peers = &mut *self.peers.lock().unwrap(); + let peer = match peers.peers.get_mut(peer_descriptor) { + None => panic!("Descriptor for read_event is not already known to PeerManager"), + Some(peer) => peer + }; + self.do_read_event(peer_descriptor, data, peer.outbound, &mut peer.sync_status, &mut peer.awaiting_pong, &mut peer.their_features, &mut peer.transport, &mut peer.pending_outbound_buffer, &mut peers.node_id_to_descriptor, &mut peers.peers_needing_send) + }; + + match result { Ok(res) => Ok(res), Err(e) => { self.disconnect_event_internal(peer_descriptor, e.no_connection_possible); @@ -604,113 +618,123 @@ impl Result { + fn do_read_event(&self, + peer_descriptor: &mut Descriptor, + data: &[u8], + outbound: bool, + sync_status: &mut InitSyncTracker, + awaiting_pong: &mut bool, + their_features: &mut Option, + transport: &mut impl ITransport, + pending_outbound_buffer: &mut OutboundQueue, + node_id_to_descriptor: &mut HashMap, + peers_needing_send: &mut HashSet) -> Result { let pause_read = { - let mut peers_lock = self.peers.lock().unwrap(); - let peers = &mut *peers_lock; - let pause_read = match peers.peers.get_mut(peer_descriptor) { - None => panic!("Descriptor for read_event is not already known to PeerManager"), - Some(peer) => { - - match peer.transport.process_input(data, &mut peer.pending_outbound_buffer) { - Err(e) => { - log_trace!(self.logger, "Error while processing input: {}", e); - return Err(PeerHandleError { no_connection_possible: false }) - }, - Ok(_) => { - - // If the transport is newly connected, do the appropriate set up for the connection - if peer.transport.is_connected() { - let their_node_id = peer.transport.get_their_node_id(); - - match peers.node_id_to_descriptor.entry(their_node_id.clone()) { - hash_map::Entry::Occupied(entry) => { - if entry.get() != peer_descriptor { - // Existing entry in map is from a different descriptor, this is a duplicate - log_trace!(self.logger, "Got second connection with {}, closing", log_pubkey!(&their_node_id)); - return Err(PeerHandleError { no_connection_possible: false }); - } else { - // read_event for existing peer - } - }, - hash_map::Entry::Vacant(entry) => { - log_trace!(self.logger, "Finished noise handshake for connection with {}", log_pubkey!(&their_node_id)); - - if peer.outbound { - let mut features = InitFeatures::known(); - if !self.message_handler.route_handler.should_request_full_sync(&their_node_id) { - features.clear_initial_routing_sync(); - } + match transport.process_input(data, pending_outbound_buffer) { + Err(e) => { + log_trace!(self.logger, "Error while processing input: {}", e); + return Err(PeerHandleError { no_connection_possible: false }) + }, + Ok(_) => { + + // If the transport is newly connected, do the appropriate set up for the connection + if transport.is_connected() { + let their_node_id = transport.get_their_node_id(); + + match node_id_to_descriptor.entry(their_node_id.clone()) { + hash_map::Entry::Occupied(entry) => { + if entry.get() != peer_descriptor { + // Existing entry in map is from a different descriptor, this is a duplicate + log_trace!(self.logger, "Got second connection with {}, closing", log_pubkey!(&their_node_id)); + return Err(PeerHandleError { no_connection_possible: false }); + } else { + // read_event for existing peer + } + }, + hash_map::Entry::Vacant(entry) => { + log_trace!(self.logger, "Finished noise handshake for connection with {}", log_pubkey!(&their_node_id)); - let resp = msgs::Init { features }; - self.enqueue_message(&mut peers.peers_needing_send, &mut peer.transport, &mut peer.pending_outbound_buffer, peer_descriptor, &resp); - } - entry.insert(peer_descriptor.clone()); + if outbound { + let mut features = InitFeatures::known(); + if !self.message_handler.route_handler.should_request_full_sync(&their_node_id) { + features.clear_initial_routing_sync(); } + + let resp = msgs::Init { features }; + self.enqueue_message(peers_needing_send, transport, pending_outbound_buffer, peer_descriptor, &resp); } + entry.insert(peer_descriptor.clone()); } } } + } + } - let received_messages = peer.transport.drain_messages(&*self.logger)?; - - for message in received_messages { - macro_rules! try_potential_handleerror { - ($thing: expr) => { - match $thing { - Ok(x) => x, - Err(e) => { - match e.action { - msgs::ErrorAction::DisconnectPeer { msg: _ } => { - //TODO: Try to push msg - log_trace!(self.logger, "Got Err handling message, disconnecting peer because {}", e.err); - return Err(PeerHandleError{ no_connection_possible: false }); - }, - msgs::ErrorAction::IgnoreError => { - log_trace!(self.logger, "Got Err handling message, ignoring because {}", e.err); - continue; - }, - msgs::ErrorAction::SendErrorMessage { msg } => { - log_trace!(self.logger, "Got Err handling message, sending Error message because {}", e.err); - self.enqueue_message(&mut peers.peers_needing_send, &mut peer.transport, &mut peer.pending_outbound_buffer, peer_descriptor, &msg); - continue; - }, - } - } - }; - } - } - - if let Err(handling_error) = self.handle_message(&mut peers.peers_needing_send, peer, peer_descriptor.clone(), message){ - match handling_error { - MessageHandlingError::PeerHandleError(e) => { return Err(e) }, - MessageHandlingError::LightningError(e) => { - try_potential_handleerror!(Err(e)); - }, + let received_messages = transport.drain_messages(&*self.logger)?; + + for message in received_messages { + macro_rules! try_potential_handleerror { + ($thing: expr) => { + match $thing { + Ok(x) => x, + Err(e) => { + match e.action { + msgs::ErrorAction::DisconnectPeer { msg: _ } => { + //TODO: Try to push msg + log_trace!(self.logger, "Got Err handling message, disconnecting peer because {}", e.err); + return Err(PeerHandleError{ no_connection_possible: false }); + }, + msgs::ErrorAction::IgnoreError => { + log_trace!(self.logger, "Got Err handling message, ignoring because {}", e.err); + continue; + }, + msgs::ErrorAction::SendErrorMessage { msg } => { + log_trace!(self.logger, "Got Err handling message, sending Error message because {}", e.err); + self.enqueue_message(peers_needing_send, transport, pending_outbound_buffer, peer_descriptor, &msg); + continue; + }, + } } - } + }; } + } - self.do_attempt_write_data(peer_descriptor, peer); - - peer.pending_outbound_buffer.queue_space() == 0 // pause_read + if let Err(handling_error) = self.handle_message(peers_needing_send, peer_descriptor, message, outbound, sync_status, awaiting_pong, their_features, transport, pending_outbound_buffer) { + match handling_error { + MessageHandlingError::PeerHandleError(e) => { return Err(e) }, + MessageHandlingError::LightningError(e) => { + try_potential_handleerror!(Err(e)); + }, + } } - }; + } + + self.do_attempt_write_data(peer_descriptor, sync_status, transport, pending_outbound_buffer); - pause_read + pending_outbound_buffer.queue_space() == 0 // pause_read }; Ok(pause_read) } /// Process an incoming message and return a decision (ok, lightning error, peer handling error) regarding the next action with the peer - fn handle_message(&self, peers_needing_send: &mut HashSet, peer: &mut Peer, peer_descriptor: Descriptor, message: wire::Message) -> Result<(), MessageHandlingError> { - log_trace!(self.logger, "Received message of type {} from {}", message.type_id(), log_pubkey!(peer.transport.get_their_node_id())); + fn handle_message(&self, + peers_needing_send: &mut HashSet, + peer_descriptor: &mut Descriptor, + message: wire::Message, + outbound: bool, + sync_status: &mut InitSyncTracker, + awaiting_pong: &mut bool, + their_features: &mut Option, + transport: &mut impl ITransport, + pending_outbound_buffer: &mut OutboundQueue + ) -> Result<(), MessageHandlingError> { + log_trace!(self.logger, "Received message of type {} from {}", message.type_id(), log_pubkey!(transport.get_their_node_id())); // Need an Init as first message if let wire::Message::Init(_) = message { - } else if peer.their_features.is_none() { - log_trace!(self.logger, "Peer {} sent non-Init first message", log_pubkey!(peer.transport.get_their_node_id())); + } else if their_features.is_none() { + log_trace!(self.logger, "Peer {} sent non-Init first message", log_pubkey!(transport.get_their_node_id())); return Err(PeerHandleError{ no_connection_possible: false }.into()); } @@ -725,7 +749,7 @@ impl { let mut data_is_printable = true; @@ -770,11 +794,11 @@ impl { if msg.ponglen < 65532 { let resp = msgs::Pong { byteslen: msg.ponglen }; - self.enqueue_message(peers_needing_send, &mut peer.transport, &mut peer.pending_outbound_buffer, &peer_descriptor, &resp); + self.enqueue_message(peers_needing_send, transport, pending_outbound_buffer, &peer_descriptor, &resp); } }, wire::Message::Pong(_msg) => { - peer.awaiting_pong = false; + *awaiting_pong = false; }, // Channel messages: wire::Message::OpenChannel(msg) => { - self.message_handler.chan_handler.handle_open_channel(&peer.transport.get_their_node_id(), peer.their_features.clone().unwrap(), &msg); + self.message_handler.chan_handler.handle_open_channel(&transport.get_their_node_id(), their_features.clone().unwrap(), &msg); }, wire::Message::AcceptChannel(msg) => { - self.message_handler.chan_handler.handle_accept_channel(&peer.transport.get_their_node_id(), peer.their_features.clone().unwrap(), &msg); + self.message_handler.chan_handler.handle_accept_channel(&transport.get_their_node_id(), their_features.clone().unwrap(), &msg); }, wire::Message::FundingCreated(msg) => { - self.message_handler.chan_handler.handle_funding_created(&peer.transport.get_their_node_id(), &msg); + self.message_handler.chan_handler.handle_funding_created(&transport.get_their_node_id(), &msg); }, wire::Message::FundingSigned(msg) => { - self.message_handler.chan_handler.handle_funding_signed(&peer.transport.get_their_node_id(), &msg); + self.message_handler.chan_handler.handle_funding_signed(&transport.get_their_node_id(), &msg); }, wire::Message::FundingLocked(msg) => { - self.message_handler.chan_handler.handle_funding_locked(&peer.transport.get_their_node_id(), &msg); + self.message_handler.chan_handler.handle_funding_locked(&transport.get_their_node_id(), &msg); }, wire::Message::Shutdown(msg) => { - self.message_handler.chan_handler.handle_shutdown(&peer.transport.get_their_node_id(), &msg); + self.message_handler.chan_handler.handle_shutdown(&transport.get_their_node_id(), &msg); }, wire::Message::ClosingSigned(msg) => { - self.message_handler.chan_handler.handle_closing_signed(&peer.transport.get_their_node_id(), &msg); + self.message_handler.chan_handler.handle_closing_signed(&transport.get_their_node_id(), &msg); }, // Commitment messages: wire::Message::UpdateAddHTLC(msg) => { - self.message_handler.chan_handler.handle_update_add_htlc(&peer.transport.get_their_node_id(), &msg); + self.message_handler.chan_handler.handle_update_add_htlc(&transport.get_their_node_id(), &msg); }, wire::Message::UpdateFulfillHTLC(msg) => { - self.message_handler.chan_handler.handle_update_fulfill_htlc(&peer.transport.get_their_node_id(), &msg); + self.message_handler.chan_handler.handle_update_fulfill_htlc(&transport.get_their_node_id(), &msg); }, wire::Message::UpdateFailHTLC(msg) => { - self.message_handler.chan_handler.handle_update_fail_htlc(&peer.transport.get_their_node_id(), &msg); + self.message_handler.chan_handler.handle_update_fail_htlc(&transport.get_their_node_id(), &msg); }, wire::Message::UpdateFailMalformedHTLC(msg) => { - self.message_handler.chan_handler.handle_update_fail_malformed_htlc(&peer.transport.get_their_node_id(), &msg); + self.message_handler.chan_handler.handle_update_fail_malformed_htlc(&transport.get_their_node_id(), &msg); }, wire::Message::CommitmentSigned(msg) => { - self.message_handler.chan_handler.handle_commitment_signed(&peer.transport.get_their_node_id(), &msg); + self.message_handler.chan_handler.handle_commitment_signed(&transport.get_their_node_id(), &msg); }, wire::Message::RevokeAndACK(msg) => { - self.message_handler.chan_handler.handle_revoke_and_ack(&peer.transport.get_their_node_id(), &msg); + self.message_handler.chan_handler.handle_revoke_and_ack(&transport.get_their_node_id(), &msg); }, wire::Message::UpdateFee(msg) => { - self.message_handler.chan_handler.handle_update_fee(&peer.transport.get_their_node_id(), &msg); + self.message_handler.chan_handler.handle_update_fee(&transport.get_their_node_id(), &msg); }, wire::Message::ChannelReestablish(msg) => { - self.message_handler.chan_handler.handle_channel_reestablish(&peer.transport.get_their_node_id(), &msg); + self.message_handler.chan_handler.handle_channel_reestablish(&transport.get_their_node_id(), &msg); }, // Routing messages: wire::Message::AnnouncementSignatures(msg) => { - self.message_handler.chan_handler.handle_announcement_signatures(&peer.transport.get_their_node_id(), &msg); + self.message_handler.chan_handler.handle_announcement_signatures(&transport.get_their_node_id(), &msg); }, wire::Message::ChannelAnnouncement(msg) => { let should_forward = match self.message_handler.route_handler.handle_channel_announcement(&msg) { @@ -934,7 +958,7 @@ impl { log_trace!(self.logger, "Handling SendOpenChannel event in peer_handler for node {} for channel {}", @@ -946,7 +970,7 @@ impl { log_trace!(self.logger, "Handling SendFundingCreated event in peer_handler for node {} for channel {} (which becomes {})", @@ -960,7 +984,7 @@ impl { log_trace!(self.logger, "Handling SendFundingSigned event in peer_handler for node {} for channel {}", @@ -973,7 +997,7 @@ impl { log_trace!(self.logger, "Handling SendFundingLocked event in peer_handler for node {} for channel {}", @@ -985,7 +1009,7 @@ impl { log_trace!(self.logger, "Handling SendAnnouncementSignatures event in peer_handler for node {} for channel {})", @@ -998,7 +1022,7 @@ impl { log_trace!(self.logger, "Handling UpdateHTLCs event in peer_handler for node {} with {} adds, {} fulfills, {} fails for channel {}", @@ -1028,7 +1052,7 @@ impl { log_trace!(self.logger, "Handling SendRevokeAndACK event in peer_handler for node {} for channel {}", @@ -1040,7 +1064,7 @@ impl { log_trace!(self.logger, "Handling SendClosingSigned event in peer_handler for node {} for channel {}", @@ -1052,7 +1076,7 @@ impl { log_trace!(self.logger, "Handling Shutdown event in peer_handler for node {} for channel {}", @@ -1064,7 +1088,7 @@ impl { log_trace!(self.logger, "Handling SendChannelReestablish event in peer_handler for node {} for channel {}", @@ -1076,7 +1100,7 @@ impl { log_trace!(self.logger, "Handling BroadcastChannelAnnouncement event in peer_handler for short channel id {}", msg.contents.short_channel_id); @@ -1095,7 +1119,7 @@ impl self.do_attempt_write_data(&mut descriptor, peer), + Some(peer) => self.do_attempt_write_data(&mut descriptor, &mut peer.sync_status, &mut peer.transport, &mut peer.pending_outbound_buffer), None => panic!("Inconsistent peers set state!"), } } @@ -1243,7 +1267,7 @@ impl Date: Sat, 5 Sep 2020 14:46:54 -0700 Subject: [PATCH 56/72] fix: Fix reentrancy bug in read_event() Per the external documentation, there is an invariant that read_event() will not call back into the SocketDescriptor, but the actual code flushed the outbound queue. Fix the bug and use the type system to guarantee this behavior in the future. By changing the function parameters to use `impl PayloadQueuer` the function will only have access to functions on that interface throughout the execution, but still be able to pass the OutboundQueue object since it satisfies the trait bounds. Functions such as enqueue_message() also take a `impl PayloadQueuer` so they can be called from that context, but functions that need the SocketDescriptorFlusher interface will fail at compile-time and point to the issue before any tests need to be run. This also fixes up tests and the tokio implementation that did not implement the proper behavior and relied on read_event() calling send_data(). --- lightning-net-tokio/src/lib.rs | 23 +++-- lightning/src/ln/peers/handler.rs | 163 +++++++++++++++--------------- lightning/src/util/test_utils.rs | 85 ++++++++++++++++ 3 files changed, 177 insertions(+), 94 deletions(-) diff --git a/lightning-net-tokio/src/lib.rs b/lightning-net-tokio/src/lib.rs index 8e02b11e719..40022e8c133 100644 --- a/lightning-net-tokio/src/lib.rs +++ b/lightning-net-tokio/src/lib.rs @@ -190,17 +190,20 @@ impl Connection { Ok(len) => { prepare_read_write_call!(); let read_res = peer_manager.read_event(&mut our_descriptor, &buf[0..len]); - let mut us_lock = us.lock().unwrap(); - match read_res { - Ok(pause_read) => { - if pause_read { - us_lock.read_paused = true; - } - Self::event_trigger(&mut us_lock); - }, - Err(e) => shutdown_socket!(e, Disconnect::CloseConnection), + { + let mut us_lock = us.lock().unwrap(); + match read_res { + Ok(pause_read) => { + if pause_read { + us_lock.read_paused = true; + } + Self::event_trigger(&mut us_lock); + }, + Err(e) => shutdown_socket!(e, Disconnect::CloseConnection), + } + us_lock.block_disconnect_socket = false; } - us_lock.block_disconnect_socket = false; + peer_manager.process_events(); }, Err(e) => shutdown_socket!(e, Disconnect::PeerDisconnected), }, diff --git a/lightning/src/ln/peers/handler.rs b/lightning/src/ln/peers/handler.rs index 55919119cbe..998321c6364 100644 --- a/lightning/src/ln/peers/handler.rs +++ b/lightning/src/ln/peers/handler.rs @@ -626,7 +626,7 @@ impl, transport: &mut impl ITransport, - pending_outbound_buffer: &mut OutboundQueue, + pending_outbound_buffer: &mut impl PayloadQueuer, node_id_to_descriptor: &mut HashMap, peers_needing_send: &mut HashSet) -> Result { let pause_read = { @@ -666,6 +666,12 @@ impl, transport: &mut impl ITransport, - pending_outbound_buffer: &mut OutboundQueue + pending_outbound_buffer: &mut impl PayloadQueuer ) -> Result<(), MessageHandlingError> { log_trace!(self.logger, "Received message of type {} from {}", message.type_id(), log_pubkey!(transport.get_their_node_id())); @@ -1401,6 +1405,7 @@ mod unit_tests { assert_matches!(peer_manager.read_event($descriptor, &[]), Ok(_)); // Drain pre-init data from descriptor in recording + peer_manager.process_events(); $descriptor.clear_recording(); peer_manager @@ -1558,7 +1563,7 @@ mod unit_tests { } // Test that an inbound connection with a connected Transport and queued Init message: - // * XXXBUG: does not send anything in read_event() + // * read_event() does not send anything in read_event() // * read_event() calls the peer_connected channel manager callback // * process_events() sends an Init message #[test] @@ -1574,7 +1579,7 @@ mod unit_tests { assert_matches!(peer_manager.read_event(&mut descriptor, &[]), Ok(_)); assert!(channel_handler_called!(&test_ctx, peer_connected)); - // descriptor.assert_called_with(vec![]); + descriptor.assert_called_with(vec![]); peer_manager.process_events(); @@ -1598,7 +1603,9 @@ mod unit_tests { new_inbound!(peer_manager, descriptor, &mut transport); - assert_matches!(peer_manager.read_event(&mut descriptor, &[]), Ok(true)); + // Process Init through to outbound queue + assert_matches!(peer_manager.read_event(&mut descriptor, &[]), Ok(false)); + peer_manager.process_events(); // Call w/o write_buffer_space_avail still returns Ok(true) assert_matches!(peer_manager.read_event(&mut descriptor, &[]), Ok(true)); @@ -1612,8 +1619,6 @@ mod unit_tests { peer_manager.write_buffer_space_avail(&mut descriptor).unwrap(); assert_matches!(peer_manager.read_event(&mut descriptor, &[]), Ok(_)); - peer_manager.process_events(); - let recording = descriptor.get_recording(); assert_matches_message!(&recording[0].0, Message::Init(_)); assert!(channel_handler_called!(&test_ctx, peer_connected)); @@ -1621,7 +1626,7 @@ mod unit_tests { // Test that an outbound connection with a connected Transport: // * get_peer_node_ids() does not contain the node_id - // * XXXBUG: does not send anything in read_event() + // * read_event() does not send anything // * process_events() sends an Init message #[test] fn outbound_connected_transport_sends_init_in_process_events() { @@ -1632,8 +1637,7 @@ mod unit_tests { new_outbound!(peer_manager, descriptor, &mut transport); assert_matches!(peer_manager.read_event(&mut descriptor, &[]), Ok(_)); - // XXXBUG: Should not call back into descriptor - // descriptor.assert_called_with(vec![]); + descriptor.assert_called_with(vec![]); assert!(peer_manager.get_peer_node_ids().is_empty()); @@ -2008,7 +2012,7 @@ mod unit_tests { } // Test generator macro to reduce duplication across the broadcast message cases. - // (test name, expression that returns a routing handler, message that will be queued, closure taking (peer_manager, descriptor) that does the validation) + // (test name, expression that returns a routing handler, message that will be queued, closure taking (descriptor) that does the validation) macro_rules! generate_broadcast_message_test { ($test_name: ident, $routing_handler: expr, $message: expr, $validation: tt) => { #[test] @@ -2019,8 +2023,10 @@ mod unit_tests { let peer_manager = new_peer_manager_post_init!(&test_ctx, &mut descriptor, &transport); transport.borrow_mut().add_incoming_message($message); + assert_matches!(peer_manager.read_event(&mut descriptor, &[]), Ok(_)); + peer_manager.process_events(); - $validation(peer_manager, descriptor) + $validation(descriptor) } } } @@ -2034,9 +2040,8 @@ mod unit_tests { routing_handler }, Message::ChannelAnnouncement(fake_channel_announcement_msg!(0, fake_public_key!(), fake_public_key!())), - (| peer_manager: PeerManagerImpl>, mut descriptor | - assert_matches!(peer_manager.read_event(&mut descriptor, &[]), Ok(_)) - )); + (| _descriptor | { }) + ); // Test that a post-Init connection: // * read_event() returns Ok(_) if a ChannelAnnouncement message is received and the routing handler @@ -2048,8 +2053,7 @@ mod unit_tests { routing_handler }, Message::ChannelAnnouncement(fake_channel_announcement_msg!(0, fake_public_key!(), fake_public_key!())), - (| peer_manager: PeerManagerImpl>, mut descriptor | { - assert_matches!(peer_manager.read_event(&mut descriptor, &[]), Ok(_)); + (| descriptor: SocketDescriptorMock | { let recording = descriptor.get_recording(); assert_matches_message!(&recording[0].0, Message::Error(_)) } @@ -2064,10 +2068,8 @@ mod unit_tests { routing_handler }, Message::ChannelAnnouncement(fake_channel_announcement_msg!(0, fake_public_key!(), fake_public_key!())), - (| peer_manager: PeerManagerImpl>, mut descriptor | { - assert_matches!(peer_manager.read_event(&mut descriptor, &[]), Ok(_)); - } - )); + (| _descriptor | { }) + ); // Test that a post-Init connection: // * read_event() returns Ok(_) if a ChannelAnnouncement message is received and the routing handler @@ -2078,10 +2080,8 @@ mod unit_tests { routing_handler }, Message::ChannelAnnouncement(fake_channel_announcement_msg!(0, fake_public_key!(), fake_public_key!())), - (| peer_manager: PeerManagerImpl>, mut descriptor | { - assert_matches!(peer_manager.read_event(&mut descriptor, &[]), Ok(_)); - } - )); + (| _descriptor | { }) + ); // Test that a post-Init connection: // * read_event() returns Ok(_) if a NodeAnnouncement message is received and the routing handler @@ -2092,8 +2092,7 @@ mod unit_tests { routing_handler }, Message::NodeAnnouncement(fake_node_announcement_msg!()), - (| peer_manager: PeerManagerImpl>, mut descriptor | - assert_matches!(peer_manager.read_event(&mut descriptor, &[]), Ok(_)) + (| _descriptor | { } )); // Test that a post-Init connection: @@ -2106,8 +2105,7 @@ mod unit_tests { routing_handler }, Message::NodeAnnouncement(fake_node_announcement_msg!()), - (| peer_manager: PeerManagerImpl>, mut descriptor | { - assert_matches!(peer_manager.read_event(&mut descriptor, &[]), Ok(_)); + (| descriptor: SocketDescriptorMock | { let recording = descriptor.get_recording(); assert_matches_message!(&recording[0].0, Message::Error(_)) } @@ -2122,10 +2120,8 @@ mod unit_tests { routing_handler }, Message::NodeAnnouncement(fake_node_announcement_msg!()), - (| peer_manager: PeerManagerImpl>, mut descriptor | { - assert_matches!(peer_manager.read_event(&mut descriptor, &[]), Ok(_)); - } - )); + (| _descriptor | { }) + ); // Test that a post-Init connection: // * read_event() returns Ok(_) if a NodeAnnouncement message is received and the routing handler @@ -2136,10 +2132,8 @@ mod unit_tests { routing_handler }, Message::NodeAnnouncement(fake_node_announcement_msg!()), - (| peer_manager: PeerManagerImpl>, mut descriptor | { - assert_matches!(peer_manager.read_event(&mut descriptor, &[]), Ok(_)); - } - )); + (| _descriptor | { }) + ); // Test that a post-Init connection: // * read_event() returns Ok(_) if a ChannelUpdate message is received and the routing handler @@ -2150,9 +2144,8 @@ mod unit_tests { routing_handler }, Message::ChannelUpdate(fake_channel_update_msg!()), - (| peer_manager: PeerManagerImpl>, mut descriptor | - assert_matches!(peer_manager.read_event(&mut descriptor, &[]), Ok(_)) - )); + (| _descriptor | { }) + ); // Test that a post-Init connection: // * read_event() returns Ok(_) if a ChannelUpdate message is received and the routing handler @@ -2164,8 +2157,7 @@ mod unit_tests { routing_handler }, Message::ChannelUpdate(fake_channel_update_msg!()), - (| peer_manager: PeerManagerImpl>, mut descriptor | { - assert_matches!(peer_manager.read_event(&mut descriptor, &[]), Ok(_)); + (| descriptor: SocketDescriptorMock | { let recording = descriptor.get_recording(); assert_matches_message!(&recording[0].0, Message::Error(_)) } @@ -2180,10 +2172,8 @@ mod unit_tests { routing_handler }, Message::ChannelUpdate(fake_channel_update_msg!()), - (| peer_manager: PeerManagerImpl>, mut descriptor | { - assert_matches!(peer_manager.read_event(&mut descriptor, &[]), Ok(_)); - } - )); + (| _descriptor | { }) + ); // Test that a post-Init connection: // * read_event() returns Ok(_) if a ChannelUpdate message is received and the routing handler @@ -2194,9 +2184,7 @@ mod unit_tests { routing_handler }, Message::ChannelUpdate(fake_channel_update_msg!()), - (| peer_manager: PeerManagerImpl>, mut descriptor | { - assert_matches!(peer_manager.read_event(&mut descriptor, &[]), Ok(_)); - } + (| _descriptor | { } )); // _____ _ _____ _ _ @@ -2280,13 +2268,13 @@ mod unit_tests { #[test] fn $test_name() { let channel_handler = TestChannelMessageHandler::new(); - channel_handler.pending_events.lock().unwrap().push($event); - - let test_ctx = TestCtx::::with_channel_handler(channel_handler); + let test_ctx = TestCtx::<&TestChannelMessageHandler, RoutingMessageHandlerTestStub>::with_channel_handler(&channel_handler); let mut descriptor = SocketDescriptorMock::new(); let transport = new_connected_transport!(&test_ctx); let peer_manager = new_peer_manager_post_init!(&test_ctx, &mut descriptor, &transport); + channel_handler.pending_events.lock().unwrap().push($event); + peer_manager.process_events(); let recording = descriptor.get_recording(); @@ -2411,6 +2399,11 @@ mod unit_tests { #[test] fn post_init_send_update_htlcs() { let channel_handler = TestChannelMessageHandler::new(); + let test_ctx = TestCtx::<&TestChannelMessageHandler, RoutingMessageHandlerTestStub>::with_channel_handler(&channel_handler); + let mut descriptor = SocketDescriptorMock::new(); + let transport = new_connected_transport!(&test_ctx); + let peer_manager = new_peer_manager_post_init!(&test_ctx, &mut descriptor, &transport); + channel_handler.pending_events.lock().unwrap().push(UpdateHTLCs { node_id: test_ctx_their_node_id!(), updates: CommitmentUpdate { @@ -2423,11 +2416,6 @@ mod unit_tests { } }); - let test_ctx = TestCtx::::with_channel_handler(channel_handler); - let mut descriptor = SocketDescriptorMock::new(); - let transport = new_connected_transport!(&test_ctx); - let peer_manager = new_peer_manager_post_init!(&test_ctx, &mut descriptor, &transport); - peer_manager.process_events(); let recording = descriptor.get_recording(); @@ -2650,16 +2638,16 @@ mod unit_tests { #[test] fn post_init_broadcast_channel_announcement() { let channel_handler = TestChannelMessageHandler::new(); + let test_ctx = TestCtx::<&TestChannelMessageHandler, RoutingMessageHandlerTestStub>::with_channel_handler(&channel_handler); + let mut descriptor = SocketDescriptorMock::new(); + let transport = new_connected_transport!(&test_ctx); + let peer_manager = new_peer_manager_post_init!(&test_ctx, &mut descriptor, &transport); + channel_handler.pending_events.lock().unwrap().push(BroadcastChannelAnnouncement { msg: fake_channel_announcement_msg!(0, fake_public_key!(), fake_public_key!()), update_msg: fake_channel_update_msg!() }); - let test_ctx = TestCtx::::with_channel_handler(channel_handler); - let mut descriptor = SocketDescriptorMock::new(); - let transport = new_connected_transport!(&test_ctx); - let peer_manager = new_peer_manager_post_init!(&test_ctx, &mut descriptor, &transport); - peer_manager.process_events(); let recording = descriptor.get_recording(); @@ -2715,15 +2703,15 @@ mod unit_tests { #[test] fn post_init_broadcast_node_announcement() { let channel_handler = TestChannelMessageHandler::new(); - channel_handler.pending_events.lock().unwrap().push(BroadcastNodeAnnouncement { - msg: fake_node_announcement_msg!() - }); - - let test_ctx = TestCtx::::with_channel_handler(channel_handler); + let test_ctx = TestCtx::<&TestChannelMessageHandler, RoutingMessageHandlerTestStub>::with_channel_handler(&channel_handler); let mut descriptor = SocketDescriptorMock::new(); let transport = new_connected_transport!(&test_ctx); let peer_manager = new_peer_manager_post_init!(&test_ctx, &mut descriptor, &transport); + channel_handler.pending_events.lock().unwrap().push(BroadcastNodeAnnouncement { + msg: fake_node_announcement_msg!() + }); + peer_manager.process_events(); let recording = descriptor.get_recording(); @@ -2779,15 +2767,15 @@ mod unit_tests { #[test] fn post_init_broadcast_channel_update() { let channel_handler = TestChannelMessageHandler::new(); - channel_handler.pending_events.lock().unwrap().push(BroadcastChannelUpdate { - msg: fake_channel_update_msg!() - }); - - let test_ctx = TestCtx::::with_channel_handler(channel_handler); + let test_ctx = TestCtx::<&TestChannelMessageHandler, RoutingMessageHandlerTestStub>::with_channel_handler(&channel_handler); let mut descriptor = SocketDescriptorMock::new(); let transport = new_connected_transport!(&test_ctx); let peer_manager = new_peer_manager_post_init!(&test_ctx, &mut descriptor, &transport); + channel_handler.pending_events.lock().unwrap().push(BroadcastChannelUpdate { + msg: fake_channel_update_msg!() + }); + peer_manager.process_events(); let recording = descriptor.get_recording(); @@ -2871,6 +2859,11 @@ mod unit_tests { #[test] fn post_init_handle_error_event_disconnect_message() { let channel_handler = TestChannelMessageHandler::new(); + let test_ctx = TestCtx::<&TestChannelMessageHandler, RoutingMessageHandlerTestStub>::with_channel_handler(&channel_handler); + let mut descriptor = SocketDescriptorMock::new(); + let transport = new_connected_transport!(&test_ctx); + let peer_manager = new_peer_manager_post_init!(&test_ctx, &mut descriptor, &transport); + channel_handler.pending_events.lock().unwrap().push(HandleError { node_id: test_ctx_their_node_id!(), action: ErrorAction::DisconnectPeer { msg: Some(ErrorMessage { @@ -2879,11 +2872,6 @@ mod unit_tests { })} }); - let test_ctx = TestCtx::::with_channel_handler(channel_handler); - let mut descriptor = SocketDescriptorMock::new(); - let transport = new_connected_transport!(&test_ctx); - let peer_manager = new_peer_manager_post_init!(&test_ctx, &mut descriptor, &transport); - peer_manager.process_events(); let recording = descriptor.get_recording(); @@ -2920,6 +2908,11 @@ mod unit_tests { #[test] fn post_init_handle_error_event_send_error_message() { let channel_handler = TestChannelMessageHandler::new(); + let test_ctx = TestCtx::<&TestChannelMessageHandler, RoutingMessageHandlerTestStub>::with_channel_handler(&channel_handler); + let mut descriptor = SocketDescriptorMock::new(); + let transport = new_connected_transport!(&test_ctx); + let peer_manager = new_peer_manager_post_init!(&test_ctx, &mut descriptor, &transport); + channel_handler.pending_events.lock().unwrap().push(HandleError { node_id: test_ctx_their_node_id!(), action: ErrorAction::SendErrorMessage { @@ -2927,11 +2920,6 @@ mod unit_tests { } }); - let test_ctx = TestCtx::::with_channel_handler(channel_handler); - let mut descriptor = SocketDescriptorMock::new(); - let transport = new_connected_transport!(&test_ctx); - let peer_manager = new_peer_manager_post_init!(&test_ctx, &mut descriptor, &transport); - peer_manager.process_events(); let recording = descriptor.get_recording(); @@ -3144,15 +3132,20 @@ mod tests { let initial_data = peer_b.new_outbound_connection(a_id, fd_b.clone()).unwrap(); peer_a.new_inbound_connection(fd_a.clone()).unwrap(); assert_eq!(peer_a.read_event(&mut fd_a, &initial_data).unwrap(), false); + peer_a.process_events(); assert_eq!(peer_b.read_event(&mut fd_b, &fd_a.outbound_data.lock().unwrap().split_off(0)).unwrap(), false); + peer_b.process_events(); assert_eq!(peer_a.read_event(&mut fd_a, &fd_b.outbound_data.lock().unwrap().split_off(0)).unwrap(), false); + peer_a.process_events(); (fd_a.clone(), fd_b.clone()) } fn establish_connection_and_read_events<'a>(peer_a: &PeerManager, peer_b: &PeerManager) -> (FileDescriptor, FileDescriptor) { let (mut fd_a, mut fd_b) = establish_connection(peer_a, peer_b); assert_eq!(peer_b.read_event(&mut fd_b, &fd_a.outbound_data.lock().unwrap().split_off(0)).unwrap(), false); + peer_b.process_events(); assert_eq!(peer_a.read_event(&mut fd_a, &fd_b.outbound_data.lock().unwrap().split_off(0)).unwrap(), false); + peer_a.process_events(); (fd_a.clone(), fd_b.clone()) } @@ -3215,7 +3208,9 @@ mod tests { // Make each peer to read the messages that the other peer just wrote to them. peers[1].read_event(&mut fd_b, &fd_a.outbound_data.lock().unwrap().split_off(0)).unwrap(); + peers[1].process_events(); peers[0].read_event(&mut fd_a, &fd_b.outbound_data.lock().unwrap().split_off(0)).unwrap(); + peers[0].process_events(); // Check that each peer has received the expected number of channel updates and channel // announcements. diff --git a/lightning/src/util/test_utils.rs b/lightning/src/util/test_utils.rs index 03a0c3b0442..16f62544fb2 100644 --- a/lightning/src/util/test_utils.rs +++ b/lightning/src/util/test_utils.rs @@ -324,6 +324,91 @@ impl events::MessageSendEventsProvider for TestChannelMessageHandler { } } +// &TestChannelMessageHandler passthroughs so TypeParameters can be either a value or ref in test object containers +impl<'a> ChannelMessageHandler for &'a TestChannelMessageHandler { + fn handle_open_channel(&self, their_node_id: &PublicKey, their_features: InitFeatures, msg: &OpenChannel) { + TestChannelMessageHandler::handle_open_channel(self, their_node_id, their_features, msg); + } + + fn handle_accept_channel(&self, their_node_id: &PublicKey, their_features: InitFeatures, msg: &AcceptChannel) { + TestChannelMessageHandler::handle_accept_channel(self, their_node_id, their_features, msg); + } + + fn handle_funding_created(&self, their_node_id: &PublicKey, msg: &FundingCreated) { + TestChannelMessageHandler::handle_funding_created(self, their_node_id, msg); + } + + fn handle_funding_signed(&self, their_node_id: &PublicKey, msg: &FundingSigned) { + TestChannelMessageHandler::handle_funding_signed(self, their_node_id, msg); + } + + fn handle_funding_locked(&self, their_node_id: &PublicKey, msg: &FundingLocked) { + TestChannelMessageHandler::handle_funding_locked(self, their_node_id, msg); + } + + fn handle_shutdown(&self, their_node_id: &PublicKey, msg: &Shutdown) { + TestChannelMessageHandler::handle_shutdown(self, their_node_id, msg); + } + + fn handle_closing_signed(&self, their_node_id: &PublicKey, msg: &ClosingSigned) { + TestChannelMessageHandler::handle_closing_signed(self, their_node_id, msg); + } + + fn handle_update_add_htlc(&self, their_node_id: &PublicKey, msg: &UpdateAddHTLC) { + TestChannelMessageHandler::handle_update_add_htlc(self, their_node_id, msg); + } + + fn handle_update_fulfill_htlc(&self, their_node_id: &PublicKey, msg: &UpdateFulfillHTLC) { + TestChannelMessageHandler::handle_update_fulfill_htlc(self, their_node_id, msg); + } + + fn handle_update_fail_htlc(&self, their_node_id: &PublicKey, msg: &UpdateFailHTLC) { + TestChannelMessageHandler::handle_update_fail_htlc(self, their_node_id, msg); + } + + fn handle_update_fail_malformed_htlc(&self, their_node_id: &PublicKey, msg: &UpdateFailMalformedHTLC) { + TestChannelMessageHandler::handle_update_fail_malformed_htlc(self, their_node_id, msg); + } + + fn handle_commitment_signed(&self, their_node_id: &PublicKey, msg: &CommitmentSigned) { + TestChannelMessageHandler::handle_commitment_signed(self, their_node_id, msg); + } + + fn handle_revoke_and_ack(&self, their_node_id: &PublicKey, msg: &RevokeAndACK) { + TestChannelMessageHandler::handle_revoke_and_ack(self, their_node_id, msg); + } + + fn handle_update_fee(&self, their_node_id: &PublicKey, msg: &UpdateFee) { + TestChannelMessageHandler::handle_update_fee(self, their_node_id, msg); + } + + fn handle_announcement_signatures(&self, their_node_id: &PublicKey, msg: &AnnouncementSignatures) { + TestChannelMessageHandler::handle_announcement_signatures(self, their_node_id, msg); + } + + fn peer_disconnected(&self, their_node_id: &PublicKey, no_connection_possible: bool) { + TestChannelMessageHandler::peer_disconnected(self, their_node_id, no_connection_possible); + } + + fn peer_connected(&self, their_node_id: &PublicKey, msg: &Init) { + TestChannelMessageHandler::peer_connected(self, their_node_id, msg); + } + + fn handle_channel_reestablish(&self, their_node_id: &PublicKey, msg: &ChannelReestablish) { + TestChannelMessageHandler::handle_channel_reestablish(self, their_node_id, msg); + } + + fn handle_error(&self, their_node_id: &PublicKey, msg: &ErrorMessage) { + TestChannelMessageHandler::handle_error(self, their_node_id, msg); + } +} + +impl<'a> events::MessageSendEventsProvider for &'a TestChannelMessageHandler { + fn get_and_clear_pending_msg_events(&self) -> Vec { + TestChannelMessageHandler::get_and_clear_pending_msg_events(self, ) + } +} + fn get_dummy_channel_announcement(short_chan_id: u64) -> msgs::ChannelAnnouncement { use bitcoin::secp256k1::ffi::Signature as FFISignature; let secp_ctx = Secp256k1::new(); From 8ac07371486b3227d8bb6c383feb54f8fcef85fc Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Wed, 16 Sep 2020 12:33:28 -0700 Subject: [PATCH 57/72] refactor: Continue constricting interfaces in message queue path Introduce the MessageQueuer interface used by Transport to queue messages for send and use it in all the existing places where messages are queued. --- lightning/src/ln/peers/handler.rs | 87 +++++++++++++----------- lightning/src/ln/peers/outbound_queue.rs | 2 +- lightning/src/ln/peers/test_util.rs | 6 +- lightning/src/ln/peers/transport.rs | 14 ++-- 4 files changed, 60 insertions(+), 49 deletions(-) diff --git a/lightning/src/ln/peers/handler.rs b/lightning/src/ln/peers/handler.rs index 998321c6364..6e3a27a67d0 100644 --- a/lightning/src/ln/peers/handler.rs +++ b/lightning/src/ln/peers/handler.rs @@ -43,8 +43,8 @@ use ln::peers::transport::Transport; const MSG_BUFF_SIZE: usize = 10; -/// Interface PeerHandler uses to interact with the Transport object -pub(super) trait ITransport { +/// Interface PeerManager uses to interact with the Transport object +pub(super) trait ITransport: MessageQueuer { /// Instantiate the new outbound Transport fn new_outbound(initiator_static_private_key: &SecretKey, responder_static_public_key: &PublicKey, initiator_ephemeral_private_key: &SecretKey) -> Self; @@ -65,7 +65,11 @@ pub(super) trait ITransport { /// Returns all Messages that have been received and can be parsed by the Transport fn drain_messages(&mut self, logger: L) -> Result, PeerHandleError> where L::Target: Logger; +} +/// Interface PeerManager uses to queue message to send. Used primarily to restrict the interface in +/// specific contexts. e.g. Only queueing during read_event(). No flushing allowed. +pub(super) trait MessageQueuer { /// Encodes, encrypts, and enqueues a message to the outbound queue. Panics if the connection is /// not established yet. fn enqueue_message(&mut self, message: &M, output_buffer: &mut Q, logger: L) where L::Target: Logger; @@ -520,10 +524,10 @@ impl( &self, - descriptor: &mut Descriptor, - sync_status: &mut InitSyncTracker, - transport: &mut impl ITransport, - pending_outbound_buffer: &mut Q) { + descriptor: &mut Descriptor, + sync_status: &mut InitSyncTracker, + message_queuer: &mut impl MessageQueuer, + pending_outbound_buffer: &mut Q) { while !pending_outbound_buffer.is_blocked() { let queue_space = pending_outbound_buffer.queue_space(); if queue_space > 0 { @@ -533,12 +537,12 @@ impl(&self, peers_needing_send: &mut HashSet, transport: &mut impl ITransport, output_buffer: &mut impl PayloadQueuer, descriptor: &Descriptor, message: &M) { - transport.enqueue_message(message, output_buffer, &*self.logger); + fn enqueue_message(&self, peers_needing_send: &mut HashSet, message_queuer: &mut impl MessageQueuer, output_buffer: &mut impl PayloadQueuer, descriptor: &Descriptor, message: &M) { + message_queuer.enqueue_message(message, output_buffer, &*self.logger); peers_needing_send.insert(descriptor.clone()); } @@ -705,7 +709,7 @@ impl { return Err(e) }, MessageHandlingError::LightningError(e) => { @@ -730,15 +734,16 @@ impl, - transport: &mut impl ITransport, + their_node_id: PublicKey, + message_queuer: &mut impl MessageQueuer, pending_outbound_buffer: &mut impl PayloadQueuer ) -> Result<(), MessageHandlingError> { - log_trace!(self.logger, "Received message of type {} from {}", message.type_id(), log_pubkey!(transport.get_their_node_id())); + log_trace!(self.logger, "Received message of type {} from {}", message.type_id(), log_pubkey!(&their_node_id)); // Need an Init as first message if let wire::Message::Init(_) = message { } else if their_features.is_none() { - log_trace!(self.logger, "Peer {} sent non-Init first message", log_pubkey!(transport.get_their_node_id())); + log_trace!(self.logger, "Peer {} sent non-Init first message", log_pubkey!(&their_node_id)); return Err(PeerHandleError{ no_connection_possible: false }.into()); } @@ -771,21 +776,21 @@ impl { @@ -798,11 +803,11 @@ impl { if msg.ponglen < 65532 { let resp = msgs::Pong { byteslen: msg.ponglen }; - self.enqueue_message(peers_needing_send, transport, pending_outbound_buffer, &peer_descriptor, &resp); + self.enqueue_message(peers_needing_send, message_queuer, pending_outbound_buffer, &peer_descriptor, &resp); } }, wire::Message::Pong(_msg) => { @@ -820,59 +825,59 @@ impl { - self.message_handler.chan_handler.handle_open_channel(&transport.get_their_node_id(), their_features.clone().unwrap(), &msg); + self.message_handler.chan_handler.handle_open_channel(&their_node_id, their_features.clone().unwrap(), &msg); }, wire::Message::AcceptChannel(msg) => { - self.message_handler.chan_handler.handle_accept_channel(&transport.get_their_node_id(), their_features.clone().unwrap(), &msg); + self.message_handler.chan_handler.handle_accept_channel(&their_node_id, their_features.clone().unwrap(), &msg); }, wire::Message::FundingCreated(msg) => { - self.message_handler.chan_handler.handle_funding_created(&transport.get_their_node_id(), &msg); + self.message_handler.chan_handler.handle_funding_created(&their_node_id, &msg); }, wire::Message::FundingSigned(msg) => { - self.message_handler.chan_handler.handle_funding_signed(&transport.get_their_node_id(), &msg); + self.message_handler.chan_handler.handle_funding_signed(&their_node_id, &msg); }, wire::Message::FundingLocked(msg) => { - self.message_handler.chan_handler.handle_funding_locked(&transport.get_their_node_id(), &msg); + self.message_handler.chan_handler.handle_funding_locked(&their_node_id, &msg); }, wire::Message::Shutdown(msg) => { - self.message_handler.chan_handler.handle_shutdown(&transport.get_their_node_id(), &msg); + self.message_handler.chan_handler.handle_shutdown(&their_node_id, &msg); }, wire::Message::ClosingSigned(msg) => { - self.message_handler.chan_handler.handle_closing_signed(&transport.get_their_node_id(), &msg); + self.message_handler.chan_handler.handle_closing_signed(&their_node_id, &msg); }, // Commitment messages: wire::Message::UpdateAddHTLC(msg) => { - self.message_handler.chan_handler.handle_update_add_htlc(&transport.get_their_node_id(), &msg); + self.message_handler.chan_handler.handle_update_add_htlc(&their_node_id, &msg); }, wire::Message::UpdateFulfillHTLC(msg) => { - self.message_handler.chan_handler.handle_update_fulfill_htlc(&transport.get_their_node_id(), &msg); + self.message_handler.chan_handler.handle_update_fulfill_htlc(&their_node_id, &msg); }, wire::Message::UpdateFailHTLC(msg) => { - self.message_handler.chan_handler.handle_update_fail_htlc(&transport.get_their_node_id(), &msg); + self.message_handler.chan_handler.handle_update_fail_htlc(&their_node_id, &msg); }, wire::Message::UpdateFailMalformedHTLC(msg) => { - self.message_handler.chan_handler.handle_update_fail_malformed_htlc(&transport.get_their_node_id(), &msg); + self.message_handler.chan_handler.handle_update_fail_malformed_htlc(&their_node_id, &msg); }, wire::Message::CommitmentSigned(msg) => { - self.message_handler.chan_handler.handle_commitment_signed(&transport.get_their_node_id(), &msg); + self.message_handler.chan_handler.handle_commitment_signed(&their_node_id, &msg); }, wire::Message::RevokeAndACK(msg) => { - self.message_handler.chan_handler.handle_revoke_and_ack(&transport.get_their_node_id(), &msg); + self.message_handler.chan_handler.handle_revoke_and_ack(&their_node_id, &msg); }, wire::Message::UpdateFee(msg) => { - self.message_handler.chan_handler.handle_update_fee(&transport.get_their_node_id(), &msg); + self.message_handler.chan_handler.handle_update_fee(&their_node_id, &msg); }, wire::Message::ChannelReestablish(msg) => { - self.message_handler.chan_handler.handle_channel_reestablish(&transport.get_their_node_id(), &msg); + self.message_handler.chan_handler.handle_channel_reestablish(&their_node_id, &msg); }, // Routing messages: wire::Message::AnnouncementSignatures(msg) => { - self.message_handler.chan_handler.handle_announcement_signatures(&transport.get_their_node_id(), &msg); + self.message_handler.chan_handler.handle_announcement_signatures(&their_node_id, &msg); }, wire::Message::ChannelAnnouncement(msg) => { let should_forward = match self.message_handler.route_handler.handle_channel_announcement(&msg) { diff --git a/lightning/src/ln/peers/outbound_queue.rs b/lightning/src/ln/peers/outbound_queue.rs index 241c8bd9638..c6c211880f4 100644 --- a/lightning/src/ln/peers/outbound_queue.rs +++ b/lightning/src/ln/peers/outbound_queue.rs @@ -10,7 +10,7 @@ /// Abstracts the buffer used to write data through a SocketDescriptor handling partial writes and /// flow control. -use ln::peers::handler::{SocketDescriptor, PayloadQueuer, SocketDescriptorFlusher}; +use ln::peers::handler::{PayloadQueuer, SocketDescriptor, SocketDescriptorFlusher}; use std::collections::LinkedList; use std::cmp; diff --git a/lightning/src/ln/peers/test_util.rs b/lightning/src/ln/peers/test_util.rs index 69dafa54223..e92bf85f7d7 100644 --- a/lightning/src/ln/peers/test_util.rs +++ b/lightning/src/ln/peers/test_util.rs @@ -13,7 +13,7 @@ use bitcoin::secp256k1; use bitcoin::secp256k1::key::{PublicKey, SecretKey}; use ln::peers::conduit::Conduit; -use ln::peers::handler::{SocketDescriptor, PayloadQueuer, ITransport, PeerHandleError}; +use ln::peers::handler::{SocketDescriptor, ITransport, PeerHandleError, MessageQueuer, PayloadQueuer}; use ln::peers::transport::IPeerHandshake; use std::rc::Rc; @@ -309,7 +309,9 @@ impl<'a> ITransport for &'a RefCell { fn drain_messages(&mut self, logger: L) -> Result, PeerHandleError> where L::Target: Logger { self.borrow_mut().drain_messages(logger) } +} +impl<'a> MessageQueuer for &'a RefCell { fn enqueue_message(&mut self, message: &M, output_buffer: &mut Q, logger: L) where L::Target: Logger { self.borrow_mut().enqueue_message(message, output_buffer, logger) } @@ -360,7 +362,9 @@ impl ITransport for TransportStub { fn drain_messages(&mut self, _logger: L) -> Result, PeerHandleError> where L::Target: Logger { Ok(self.messages.drain(..).collect()) } +} +impl MessageQueuer for TransportStub { fn enqueue_message(&mut self, message: &M, output_buffer: &mut Q, _logger: L) where L::Target: Logger { let mut buffer = VecWriter(Vec::new()); diff --git a/lightning/src/ln/peers/transport.rs b/lightning/src/ln/peers/transport.rs index 4b058e6800c..e5f838e3e46 100644 --- a/lightning/src/ln/peers/transport.rs +++ b/lightning/src/ln/peers/transport.rs @@ -12,7 +12,7 @@ use bitcoin::secp256k1::{SecretKey, PublicKey}; use ln::peers::conduit::Conduit; -use ln::peers::handler::{ITransport, PeerHandleError, PayloadQueuer}; +use ln::peers::handler::{ITransport, PeerHandleError, MessageQueuer, PayloadQueuer}; use ln::peers::handshake::PeerHandshake; use ln::{wire, msgs}; use ln::wire::{Encode, Message}; @@ -155,6 +155,13 @@ impl ITransport for Transport PublicKey { + assert!(self.is_connected(), "Retrieving the remote node_id is only supported after transport is connected"); + self.their_node_id.unwrap() + } +} + +impl MessageQueuer for Transport { fn enqueue_message(&mut self, message: &M, output_buffer: &mut Q, logger: L) where L::Target: Logger { @@ -169,11 +176,6 @@ impl ITransport for Transport PublicKey { - assert!(self.is_connected(), "Retrieving the remote node_id is only supported after transport is connected"); - self.their_node_id.unwrap() - } } #[cfg(test)] From 9de58d86f8b2bbab24ab6e139b3a0083355d28db Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Wed, 16 Sep 2020 12:34:09 -0700 Subject: [PATCH 58/72] review: Move PayloadQueuer trait move to a separate commit Improves readability during review. --- lightning/src/ln/peers/handler.rs | 31 +----------------------- lightning/src/ln/peers/outbound_queue.rs | 3 ++- lightning/src/ln/peers/test_util.rs | 4 +-- lightning/src/ln/peers/transport.rs | 14 ++++++++++- 4 files changed, 18 insertions(+), 34 deletions(-) diff --git a/lightning/src/ln/peers/handler.rs b/lightning/src/ln/peers/handler.rs index 6e3a27a67d0..91e75b0a33f 100644 --- a/lightning/src/ln/peers/handler.rs +++ b/lightning/src/ln/peers/handler.rs @@ -39,7 +39,7 @@ use bitcoin::hashes::sha256::Hash as Sha256; use bitcoin::hashes::sha256::HashEngine as Sha256Engine; use bitcoin::hashes::{HashEngine, Hash}; use ln::peers::outbound_queue::OutboundQueue; -use ln::peers::transport::Transport; +use ln::peers::transport::{PayloadQueuer, Transport}; const MSG_BUFF_SIZE: usize = 10; @@ -75,35 +75,6 @@ pub(super) trait MessageQueuer { fn enqueue_message(&mut self, message: &M, output_buffer: &mut Q, logger: L) where L::Target: Logger; } - -/// Trait representing a container that allows enqueuing of Vec<[u8]> -pub(super) trait PayloadQueuer { - /// Enqueue item to the queue - fn push_back(&mut self, item: Vec); - - /// Returns true if the queue is empty - fn is_empty(&self) -> bool; - - /// Returns the amount of available space in queue - fn queue_space(&self) -> usize; -} - -/// Implement &mut PayloadQueuer passthroughs -impl<'a, T> PayloadQueuer for &'a mut T where - T: PayloadQueuer { - fn push_back(&mut self, item: Vec) { - T::push_back(self, item) - } - - fn is_empty(&self) -> bool { - T::is_empty(self) - } - - fn queue_space(&self) -> usize { - T::queue_space(self) - } -} - /// Trait representing a container that can try to flush data through a SocketDescriptor pub(super) trait SocketDescriptorFlusher { /// Write previously enqueued data to the SocketDescriptor. A return of false indicates the diff --git a/lightning/src/ln/peers/outbound_queue.rs b/lightning/src/ln/peers/outbound_queue.rs index c6c211880f4..4ad8e8b3da9 100644 --- a/lightning/src/ln/peers/outbound_queue.rs +++ b/lightning/src/ln/peers/outbound_queue.rs @@ -10,7 +10,8 @@ /// Abstracts the buffer used to write data through a SocketDescriptor handling partial writes and /// flow control. -use ln::peers::handler::{PayloadQueuer, SocketDescriptor, SocketDescriptorFlusher}; +use ln::peers::handler::{SocketDescriptor, SocketDescriptorFlusher}; +use ln::peers::transport::PayloadQueuer; use std::collections::LinkedList; use std::cmp; diff --git a/lightning/src/ln/peers/test_util.rs b/lightning/src/ln/peers/test_util.rs index e92bf85f7d7..5803fab4a14 100644 --- a/lightning/src/ln/peers/test_util.rs +++ b/lightning/src/ln/peers/test_util.rs @@ -13,8 +13,8 @@ use bitcoin::secp256k1; use bitcoin::secp256k1::key::{PublicKey, SecretKey}; use ln::peers::conduit::Conduit; -use ln::peers::handler::{SocketDescriptor, ITransport, PeerHandleError, MessageQueuer, PayloadQueuer}; -use ln::peers::transport::IPeerHandshake; +use ln::peers::handler::{SocketDescriptor, ITransport, PeerHandleError, MessageQueuer}; +use ln::peers::transport::{IPeerHandshake, PayloadQueuer}; use std::rc::Rc; use std::cell::{RefCell}; diff --git a/lightning/src/ln/peers/transport.rs b/lightning/src/ln/peers/transport.rs index e5f838e3e46..81bd99afb6e 100644 --- a/lightning/src/ln/peers/transport.rs +++ b/lightning/src/ln/peers/transport.rs @@ -12,7 +12,7 @@ use bitcoin::secp256k1::{SecretKey, PublicKey}; use ln::peers::conduit::Conduit; -use ln::peers::handler::{ITransport, PeerHandleError, MessageQueuer, PayloadQueuer}; +use ln::peers::handler::{ITransport, PeerHandleError, MessageQueuer}; use ln::peers::handshake::PeerHandshake; use ln::{wire, msgs}; use ln::wire::{Encode, Message}; @@ -37,6 +37,18 @@ pub trait IPeerHandshake { fn process_act(&mut self, input: &[u8]) -> Result<(Option>, Option<(Conduit, PublicKey)>), String>; } +/// Trait representing a container that allows enqueuing of Vec<[u8]> +pub(super) trait PayloadQueuer { + /// Enqueue item to the queue + fn push_back(&mut self, item: Vec); + + /// Returns true if the queue is empty + fn is_empty(&self) -> bool; + + /// Returns the amount of available space in queue + fn queue_space(&self) -> usize; +} + pub(super) struct Transport { pub(super) conduit: Option, handshake: PeerHandshakeImpl, From 3a9eb140a411c65a587afc51cc85df7b5b0be4c6 Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Sat, 5 Sep 2020 17:49:06 -0700 Subject: [PATCH 59/72] refactor: Clean up event handling path w.r.t connected state Many of the is_connected() checks were duplicative due to the macro continuing out of the match statement if the peer referenced by the node_id had not seen an Init message yet. Remove the macro in favor of a function on PeerHolder that will return an Option<> and use it instead. Split out a new helper function Peer::is_initialized() that will return true if the peer has seen an Init message. --- lightning/src/ln/peers/handler.rs | 190 ++++++++++++++---------------- 1 file changed, 90 insertions(+), 100 deletions(-) diff --git a/lightning/src/ln/peers/handler.rs b/lightning/src/ln/peers/handler.rs index 91e75b0a33f..961dafd0e2d 100644 --- a/lightning/src/ln/peers/handler.rs +++ b/lightning/src/ln/peers/handler.rs @@ -181,6 +181,13 @@ struct Peer { } impl Peer { + + /// Returns true if an INIT message has been received from this peer. Implies that this node + /// can send and receive encrypted messages. + fn is_initialized(&self) -> bool { + self.their_features.is_some() + } + /// Returns true if the channel announcements/updates for the given channel should be /// forwarded to this peer. /// If we are sending our routing table to this peer and we have not yet sent channel @@ -214,6 +221,30 @@ struct PeerHolder { node_id_to_descriptor: HashMap, } +impl PeerHolder { + fn initialized_peer_by_node_id(&mut self, node_id: &PublicKey) -> Option<(Descriptor, &mut Peer)> { + match self.node_id_to_descriptor.get_mut(node_id) { + None => None, + Some(descriptor) => { + assert!(self.peers.contains_key(descriptor), "Invalid PeerHolder state"); + + match self.peers.get_mut(&descriptor) { + None => panic!("Invalid PeerHolder state!"), + Some(peer) => { + + // their_features is set after receiving an Init message + if !peer.is_initialized() { + None + } else { + Some((descriptor.clone(), peer)) + } + } + } + } + } + } +} + #[cfg(not(any(target_pointer_width = "32", target_pointer_width = "64")))] fn _check_usize_is_32_or_64() { // See below, less than 32 bit pointers may be unsafe here! @@ -425,7 +456,7 @@ impl Vec { let peers = self.peers.lock().unwrap(); peers.peers.values().filter_map(|p| { - if !p.transport.is_connected() || p.their_features.is_none() { + if !p.is_initialized() { return None; } Some(p.transport.get_their_node_id()) @@ -904,105 +935,76 @@ impl { - { - let descriptor = match peers.node_id_to_descriptor.get($node_id) { - Some(descriptor) => descriptor.clone(), - None => { - $handle_no_such_peer; - continue; - }, - }; - match peers.peers.get_mut(&descriptor) { - Some(peer) => { - if peer.their_features.is_none() { - $handle_no_such_peer; - continue; - } - (descriptor, peer) - }, - None => panic!("Inconsistent peers set state!"), - } - } - } - } match event { MessageSendEvent::SendAcceptChannel { ref node_id, ref msg } => { log_trace!(self.logger, "Handling SendAcceptChannel event in peer_handler for node {} for channel {}", log_pubkey!(node_id), log_bytes!(msg.temporary_channel_id)); - let (mut descriptor, peer) = get_peer_for_forwarding!(node_id, { - //TODO: Drop the pending channel? (or just let it timeout, but that sucks) - }); - if peer.transport.is_connected() { + if let Some((mut descriptor, peer)) = peers.initialized_peer_by_node_id(node_id) { peer.transport.enqueue_message(msg, &mut peer.pending_outbound_buffer, &*self.logger); + self.do_attempt_write_data(&mut descriptor, &mut peer.sync_status, &mut peer.transport, &mut peer.pending_outbound_buffer); + } else { + //TODO: Drop the pending channel? (or just let it timeout, but that sucks) } - self.do_attempt_write_data(&mut descriptor, &mut peer.sync_status, &mut peer.transport, &mut peer.pending_outbound_buffer); }, MessageSendEvent::SendOpenChannel { ref node_id, ref msg } => { log_trace!(self.logger, "Handling SendOpenChannel event in peer_handler for node {} for channel {}", log_pubkey!(node_id), log_bytes!(msg.temporary_channel_id)); - let (mut descriptor, peer) = get_peer_for_forwarding!(node_id, { - //TODO: Drop the pending channel? (or just let it timeout, but that sucks) - }); - if peer.transport.is_connected() { + if let Some((mut descriptor, peer)) = peers.initialized_peer_by_node_id(node_id) { peer.transport.enqueue_message(msg, &mut peer.pending_outbound_buffer, &*self.logger); + self.do_attempt_write_data(&mut descriptor, &mut peer.sync_status, &mut peer.transport, &mut peer.pending_outbound_buffer); + } else { + //TODO: Drop the pending channel? (or just let it timeout, but that sucks) } - self.do_attempt_write_data(&mut descriptor, &mut peer.sync_status, &mut peer.transport, &mut peer.pending_outbound_buffer); }, MessageSendEvent::SendFundingCreated { ref node_id, ref msg } => { log_trace!(self.logger, "Handling SendFundingCreated event in peer_handler for node {} for channel {} (which becomes {})", log_pubkey!(node_id), log_bytes!(msg.temporary_channel_id), log_funding_channel_id!(msg.funding_txid, msg.funding_output_index)); - let (mut descriptor, peer) = get_peer_for_forwarding!(node_id, { - //TODO: generate a DiscardFunding event indicating to the wallet that - //they should just throw away this funding transaction - }); - if peer.transport.is_connected() { + if let Some((mut descriptor, peer)) = peers.initialized_peer_by_node_id(node_id) { peer.transport.enqueue_message(msg, &mut peer.pending_outbound_buffer, &*self.logger); + self.do_attempt_write_data(&mut descriptor, &mut peer.sync_status, &mut peer.transport, &mut peer.pending_outbound_buffer); + } else { + //TODO: generate a DiscardFunding event indicating to the wallet that + //they should just throw away this funding transaction } - self.do_attempt_write_data(&mut descriptor, &mut peer.sync_status, &mut peer.transport, &mut peer.pending_outbound_buffer); }, MessageSendEvent::SendFundingSigned { ref node_id, ref msg } => { log_trace!(self.logger, "Handling SendFundingSigned event in peer_handler for node {} for channel {}", log_pubkey!(node_id), log_bytes!(msg.channel_id)); - let (mut descriptor, peer) = get_peer_for_forwarding!(node_id, { - //TODO: generate a DiscardFunding event indicating to the wallet that - //they should just throw away this funding transaction - }); - if peer.transport.is_connected() { + if let Some((mut descriptor, peer)) = peers.initialized_peer_by_node_id(node_id) { peer.transport.enqueue_message(msg, &mut peer.pending_outbound_buffer, &*self.logger); + self.do_attempt_write_data(&mut descriptor, &mut peer.sync_status, &mut peer.transport, &mut peer.pending_outbound_buffer); + } else { + //TODO: generate a DiscardFunding event indicating to the wallet that + //they should just throw away this funding transaction } - self.do_attempt_write_data(&mut descriptor, &mut peer.sync_status, &mut peer.transport, &mut peer.pending_outbound_buffer); }, MessageSendEvent::SendFundingLocked { ref node_id, ref msg } => { log_trace!(self.logger, "Handling SendFundingLocked event in peer_handler for node {} for channel {}", log_pubkey!(node_id), log_bytes!(msg.channel_id)); - let (mut descriptor, peer) = get_peer_for_forwarding!(node_id, { - //TODO: Do whatever we're gonna do for handling dropped messages - }); - if peer.transport.is_connected() { + if let Some((mut descriptor, peer)) = peers.initialized_peer_by_node_id(node_id) { peer.transport.enqueue_message(msg, &mut peer.pending_outbound_buffer, &*self.logger); + self.do_attempt_write_data(&mut descriptor, &mut peer.sync_status, &mut peer.transport, &mut peer.pending_outbound_buffer); + } else { + //TODO: Do whatever we're gonna do for handling dropped messages } - self.do_attempt_write_data(&mut descriptor, &mut peer.sync_status, &mut peer.transport, &mut peer.pending_outbound_buffer); }, MessageSendEvent::SendAnnouncementSignatures { ref node_id, ref msg } => { log_trace!(self.logger, "Handling SendAnnouncementSignatures event in peer_handler for node {} for channel {})", log_pubkey!(node_id), log_bytes!(msg.channel_id)); - let (mut descriptor, peer) = get_peer_for_forwarding!(node_id, { - //TODO: generate a DiscardFunding event indicating to the wallet that - //they should just throw away this funding transaction - }); - if peer.transport.is_connected() { + if let Some((mut descriptor, peer)) = peers.initialized_peer_by_node_id(node_id) { peer.transport.enqueue_message(msg, &mut peer.pending_outbound_buffer, &*self.logger); + self.do_attempt_write_data(&mut descriptor, &mut peer.sync_status, &mut peer.transport, &mut peer.pending_outbound_buffer); + } else { + //TODO: generate a DiscardFunding event indicating to the wallet that + //they should just throw away this funding transaction } - self.do_attempt_write_data(&mut descriptor, &mut peer.sync_status, &mut peer.transport, &mut peer.pending_outbound_buffer); }, MessageSendEvent::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 update_fee, ref commitment_signed } } => { log_trace!(self.logger, "Handling UpdateHTLCs event in peer_handler for node {} with {} adds, {} fulfills, {} fails for channel {}", @@ -1011,10 +1013,7 @@ impl { log_trace!(self.logger, "Handling SendRevokeAndACK event in peer_handler for node {} for channel {}", log_pubkey!(node_id), log_bytes!(msg.channel_id)); - let (mut descriptor, peer) = get_peer_for_forwarding!(node_id, { - //TODO: Do whatever we're gonna do for handling dropped messages - }); - if peer.transport.is_connected() { + if let Some((mut descriptor, peer)) = peers.initialized_peer_by_node_id(node_id) { peer.transport.enqueue_message(msg, &mut peer.pending_outbound_buffer, &*self.logger); + self.do_attempt_write_data(&mut descriptor, &mut peer.sync_status, &mut peer.transport, &mut peer.pending_outbound_buffer); + } else { + //TODO: Do whatever we're gonna do for handling dropped messages } - self.do_attempt_write_data(&mut descriptor, &mut peer.sync_status, &mut peer.transport, &mut peer.pending_outbound_buffer); }, MessageSendEvent::SendClosingSigned { ref node_id, ref msg } => { log_trace!(self.logger, "Handling SendClosingSigned event in peer_handler for node {} for channel {}", log_pubkey!(node_id), log_bytes!(msg.channel_id)); - let (mut descriptor, peer) = get_peer_for_forwarding!(node_id, { - //TODO: Do whatever we're gonna do for handling dropped messages - }); - if peer.transport.is_connected() { + if let Some((mut descriptor, peer)) = peers.initialized_peer_by_node_id(node_id) { peer.transport.enqueue_message(msg, &mut peer.pending_outbound_buffer, &*self.logger); + self.do_attempt_write_data(&mut descriptor, &mut peer.sync_status, &mut peer.transport, &mut peer.pending_outbound_buffer); + } else { + //TODO: Do whatever we're gonna do for handling dropped messages } - self.do_attempt_write_data(&mut descriptor, &mut peer.sync_status, &mut peer.transport, &mut peer.pending_outbound_buffer); }, MessageSendEvent::SendShutdown { ref node_id, ref msg } => { log_trace!(self.logger, "Handling Shutdown event in peer_handler for node {} for channel {}", log_pubkey!(node_id), log_bytes!(msg.channel_id)); - let (mut descriptor, peer) = get_peer_for_forwarding!(node_id, { - //TODO: Do whatever we're gonna do for handling dropped messages - }); - if peer.transport.is_connected() { + if let Some((mut descriptor, peer)) = peers.initialized_peer_by_node_id(node_id) { peer.transport.enqueue_message(msg, &mut peer.pending_outbound_buffer, &*self.logger); + self.do_attempt_write_data(&mut descriptor, &mut peer.sync_status, &mut peer.transport, &mut peer.pending_outbound_buffer); + } else { + //TODO: Do whatever we're gonna do for handling dropped messages } - self.do_attempt_write_data(&mut descriptor, &mut peer.sync_status, &mut peer.transport, &mut peer.pending_outbound_buffer); }, MessageSendEvent::SendChannelReestablish { ref node_id, ref msg } => { log_trace!(self.logger, "Handling SendChannelReestablish event in peer_handler for node {} for channel {}", log_pubkey!(node_id), log_bytes!(msg.channel_id)); - let (mut descriptor, peer) = get_peer_for_forwarding!(node_id, { - //TODO: Do whatever we're gonna do for handling dropped messages - }); - if peer.transport.is_connected() { + if let Some((mut descriptor, peer)) = peers.initialized_peer_by_node_id(node_id) { peer.transport.enqueue_message(msg, &mut peer.pending_outbound_buffer, &*self.logger); + self.do_attempt_write_data(&mut descriptor, &mut peer.sync_status, &mut peer.transport, &mut peer.pending_outbound_buffer); + } else { + //TODO: Do whatever we're gonna do for handling dropped messages } - self.do_attempt_write_data(&mut descriptor, &mut peer.sync_status, &mut peer.transport, &mut peer.pending_outbound_buffer); }, MessageSendEvent::BroadcastChannelAnnouncement { ref msg, ref update_msg } => { log_trace!(self.logger, "Handling BroadcastChannelAnnouncement event in peer_handler for short channel id {}", msg.contents.short_channel_id); if self.message_handler.route_handler.handle_channel_announcement(msg).is_ok() && self.message_handler.route_handler.handle_channel_update(update_msg).is_ok() { for (ref descriptor, ref mut peer) in peers.peers.iter_mut() { - if !peer.transport.is_connected() || peer.their_features.is_none() || + if !peer.is_initialized() || !peer.should_forward_channel_announcement(msg.contents.short_channel_id) { continue } @@ -1095,10 +1092,8 @@ impl Date: Mon, 7 Sep 2020 15:45:51 -0700 Subject: [PATCH 60/72] refactor: Introduce PostInitState Encapsulate the Peer information that is only valid after an Init message has been seen. This makes the accesses to sync_status, ping information, and their_features cleaner w.r.t. the Peer initialization state. --- lightning/src/ln/peers/handler.rs | 348 +++++++++++++++++------------- 1 file changed, 198 insertions(+), 150 deletions(-) diff --git a/lightning/src/ln/peers/handler.rs b/lightning/src/ln/peers/handler.rs index 961dafd0e2d..1a8e9d683db 100644 --- a/lightning/src/ln/peers/handler.rs +++ b/lightning/src/ln/peers/handler.rs @@ -168,24 +168,44 @@ enum InitSyncTracker{ NodesSyncing(PublicKey), } +// Container for all state only valid after an Init message is seen +struct PostInitState { + awaiting_pong: bool, + sync_status: InitSyncTracker, + their_features: InitFeatures, +} + +impl PostInitState { + fn new(sync_status: InitSyncTracker, their_features: InitFeatures) -> Self { + Self { + awaiting_pong: false, + sync_status, + their_features + } + } +} + struct Peer { - transport: TransportImpl, outbound: bool, - their_features: Option, - pending_outbound_buffer: OutboundQueue, - - sync_status: InitSyncTracker, - - awaiting_pong: bool, + post_init_state: Option, + transport: TransportImpl, } impl Peer { + fn new(outbound: bool, transport: TransportImpl) -> Self { + Self { + outbound, + pending_outbound_buffer: OutboundQueue::new(MSG_BUFF_SIZE), + post_init_state: None, + transport + } + } /// Returns true if an INIT message has been received from this peer. Implies that this node /// can send and receive encrypted messages. fn is_initialized(&self) -> bool { - self.their_features.is_some() + self.post_init_state.is_some() } /// Returns true if the channel announcements/updates for the given channel should be @@ -194,20 +214,30 @@ impl Peer { /// announcements/updates for the given channel_id then we will send it when we get to that /// point and we shouldn't send it yet to avoid sending duplicate updates. If we've already /// sent the old versions, we should send the update, and so return true here. - fn should_forward_channel_announcement(&self, channel_id: u64)->bool{ - match self.sync_status { - InitSyncTracker::NoSyncRequested => true, - InitSyncTracker::ChannelsSyncing(i) => i < channel_id, - InitSyncTracker::NodesSyncing(_) => true, + fn should_forward_channel_announcement(&self, channel_id: u64) -> bool{ + match &self.post_init_state { + None => panic!("should_forward_channel_announcement() only valid on an uninitialized peer"), + Some(state) => { + match state.sync_status { + InitSyncTracker::NoSyncRequested => true, + InitSyncTracker::ChannelsSyncing(i) => i < channel_id, + InitSyncTracker::NodesSyncing(_) => true, + } + } } } /// Similar to the above, but for node announcements indexed by node_id. fn should_forward_node_announcement(&self, node_id: PublicKey) -> bool { - match self.sync_status { - InitSyncTracker::NoSyncRequested => true, - InitSyncTracker::ChannelsSyncing(_) => false, - InitSyncTracker::NodesSyncing(pk) => pk < node_id, + match &self.post_init_state { + None => panic!("should_forward_channel_announcement() only valid on an uninitialized peer"), + Some(state) => { + match state.sync_status { + InitSyncTracker::NoSyncRequested => true, + InitSyncTracker::ChannelsSyncing(_) => false, + InitSyncTracker::NodesSyncing(pk) => pk < node_id, + } + } } } } @@ -485,17 +515,7 @@ impl { - transport, - outbound: true, - their_features: None, - - pending_outbound_buffer: OutboundQueue::new(MSG_BUFF_SIZE), - - sync_status: InitSyncTracker::NoSyncRequested, - - awaiting_pong: false, - }).is_some() { + if peers.peers.insert(descriptor, Peer::::new(true, transport)).is_some() { panic!("PeerManager driver duplicated descriptors!"); }; Ok(initial_bytes) @@ -508,74 +528,80 @@ impl Result<(), PeerHandleError> { let mut peers = self.peers.lock().unwrap(); - if peers.peers.insert(descriptor, Peer:: { - transport, - outbound: false, - their_features: None, - - pending_outbound_buffer: OutboundQueue::new(MSG_BUFF_SIZE), - - sync_status: InitSyncTracker::NoSyncRequested, - - awaiting_pong: false, - }).is_some() { + if peers.peers.insert(descriptor, Peer::::new(false, transport)).is_some() { panic!("PeerManager driver duplicated descriptors!"); }; Ok(()) } - fn do_attempt_write_data( + // Fill remaining slots in output queue with sync messages, updating the sync state when + // appropriate + fn fill_message_queue_with_sync( &self, - descriptor: &mut Descriptor, sync_status: &mut InitSyncTracker, message_queuer: &mut impl MessageQueuer, pending_outbound_buffer: &mut Q) { - while !pending_outbound_buffer.is_blocked() { - let queue_space = pending_outbound_buffer.queue_space(); - if queue_space > 0 { - match sync_status { - &mut InitSyncTracker::NoSyncRequested => {}, - &mut InitSyncTracker::ChannelsSyncing(c) if c < 0xffff_ffff_ffff_ffff => { - let steps = ((queue_space + 2) / 3) as u8; - let all_messages = self.message_handler.route_handler.get_next_channel_announcements(c, steps); - for &(ref announce, ref update_a_option, ref update_b_option) in all_messages.iter() { - message_queuer.enqueue_message(announce, pending_outbound_buffer, &*self.logger); - if let &Some(ref update_a) = update_a_option { - message_queuer.enqueue_message(update_a, pending_outbound_buffer, &*self.logger); - } - if let &Some(ref update_b) = update_b_option { - message_queuer.enqueue_message(update_b, pending_outbound_buffer, &*self.logger); - } - *sync_status = InitSyncTracker::ChannelsSyncing(announce.contents.short_channel_id + 1); - } - if all_messages.is_empty() || all_messages.len() != steps as usize { - *sync_status = InitSyncTracker::ChannelsSyncing(0xffff_ffff_ffff_ffff); - } - }, - &mut InitSyncTracker::ChannelsSyncing(c) if c == 0xffff_ffff_ffff_ffff => { - let steps = queue_space as u8; - let all_messages = self.message_handler.route_handler.get_next_node_announcements(None, steps); - for msg in all_messages.iter() { - message_queuer.enqueue_message(msg, pending_outbound_buffer, &*self.logger); - *sync_status = InitSyncTracker::NodesSyncing(msg.contents.node_id); - } - if all_messages.is_empty() || all_messages.len() != steps as usize { - *sync_status = InitSyncTracker::NoSyncRequested; - } - }, - &mut InitSyncTracker::ChannelsSyncing(_) => unreachable!(), - &mut InitSyncTracker::NodesSyncing(key) => { - let steps = queue_space as u8; - let all_messages = self.message_handler.route_handler.get_next_node_announcements(Some(&key), steps); - for msg in all_messages.iter() { - message_queuer.enqueue_message(msg, pending_outbound_buffer, &*self.logger); - *sync_status = InitSyncTracker::NodesSyncing(msg.contents.node_id); + + let queue_space = pending_outbound_buffer.queue_space(); + if queue_space > 0 { + match sync_status { + &mut InitSyncTracker::NoSyncRequested => {}, + &mut InitSyncTracker::ChannelsSyncing(c) if c < 0xffff_ffff_ffff_ffff => { + let steps = ((queue_space + 2) / 3) as u8; + let all_messages = self.message_handler.route_handler.get_next_channel_announcements(c, steps); + for &(ref announce, ref update_a_option, ref update_b_option) in all_messages.iter() { + message_queuer.enqueue_message(announce, pending_outbound_buffer, &*self.logger); + if let &Some(ref update_a) = update_a_option { + message_queuer.enqueue_message(update_a, pending_outbound_buffer, &*self.logger); } - if all_messages.is_empty() || all_messages.len() != steps as usize { - *sync_status = InitSyncTracker::NoSyncRequested; + if let &Some(ref update_b) = update_b_option { + message_queuer.enqueue_message(update_b, pending_outbound_buffer, &*self.logger); } - }, - } + *sync_status = InitSyncTracker::ChannelsSyncing(announce.contents.short_channel_id + 1); + } + if all_messages.is_empty() || all_messages.len() != steps as usize { + *sync_status = InitSyncTracker::ChannelsSyncing(0xffff_ffff_ffff_ffff); + } + }, + &mut InitSyncTracker::ChannelsSyncing(c) if c == 0xffff_ffff_ffff_ffff => { + let steps = queue_space as u8; + let all_messages = self.message_handler.route_handler.get_next_node_announcements(None, steps); + for msg in all_messages.iter() { + message_queuer.enqueue_message(msg, pending_outbound_buffer, &*self.logger); + *sync_status = InitSyncTracker::NodesSyncing(msg.contents.node_id); + } + if all_messages.is_empty() || all_messages.len() != steps as usize { + *sync_status = InitSyncTracker::NoSyncRequested; + } + }, + &mut InitSyncTracker::ChannelsSyncing(_) => unreachable!(), + &mut InitSyncTracker::NodesSyncing(key) => { + let steps = queue_space as u8; + let all_messages = self.message_handler.route_handler.get_next_node_announcements(Some(&key), steps); + for msg in all_messages.iter() { + message_queuer.enqueue_message(msg, pending_outbound_buffer, &*self.logger); + *sync_status = InitSyncTracker::NodesSyncing(msg.contents.node_id); + } + if all_messages.is_empty() || all_messages.len() != steps as usize { + *sync_status = InitSyncTracker::NoSyncRequested; + } + }, + } + } + } + + fn do_attempt_write_data( + &self, + descriptor: &mut Descriptor, + post_init_state: &mut Option, + message_queuer: &mut impl MessageQueuer, + pending_outbound_buffer: &mut Q) { + + while !pending_outbound_buffer.is_blocked() { + // If connected, fill output queue with sync messages + match post_init_state { + None => {}, + &mut Some(ref mut state) => self.fill_message_queue_with_sync(&mut state.sync_status, message_queuer, pending_outbound_buffer) } // No messages to send @@ -593,7 +619,7 @@ impl panic!("Descriptor for write_event is not already known to PeerManager"), Some(peer) => { peer.pending_outbound_buffer.unblock(); - self.do_attempt_write_data(descriptor, &mut peer.sync_status, &mut peer.transport, &mut peer.pending_outbound_buffer); + self.do_attempt_write_data(descriptor, &mut peer.post_init_state, &mut peer.transport, &mut peer.pending_outbound_buffer); } }; Ok(()) @@ -606,7 +632,7 @@ impl panic!("Descriptor for read_event is not already known to PeerManager"), Some(peer) => peer }; - self.do_read_event(peer_descriptor, data, peer.outbound, &mut peer.sync_status, &mut peer.awaiting_pong, &mut peer.their_features, &mut peer.transport, &mut peer.pending_outbound_buffer, &mut peers.node_id_to_descriptor, &mut peers.peers_needing_send) + self.do_read_event(peer_descriptor, data, peer.outbound, &mut peer.post_init_state, &mut peer.transport, &mut peer.pending_outbound_buffer, &mut peers.node_id_to_descriptor, &mut peers.peers_needing_send) }; match result { @@ -628,9 +654,7 @@ impl, + post_init_state: &mut Option, transport: &mut impl ITransport, pending_outbound_buffer: &mut impl PayloadQueuer, node_id_to_descriptor: &mut HashMap, @@ -711,7 +735,7 @@ impl { return Err(e) }, MessageHandlingError::LightningError(e) => { @@ -733,9 +757,7 @@ impl, + post_init_state: &mut Option, their_node_id: PublicKey, message_queuer: &mut impl MessageQueuer, pending_outbound_buffer: &mut impl PayloadQueuer @@ -744,7 +766,7 @@ impl { let mut data_is_printable = true; @@ -822,15 +849,29 @@ impl { - *awaiting_pong = false; + if let Some(ref mut state) = post_init_state { + state.awaiting_pong = false; + } else { + panic!("Received Pong before Init and didn't catch it earlier!"); + } }, // Channel messages: wire::Message::OpenChannel(msg) => { - self.message_handler.chan_handler.handle_open_channel(&their_node_id, their_features.clone().unwrap(), &msg); + match post_init_state { + None => panic!("Received OpenChannel before Init and didn't catch it earlier!"), + Some(state) => { + self.message_handler.chan_handler.handle_open_channel(&their_node_id, state.their_features.clone(), &msg); + } + } }, wire::Message::AcceptChannel(msg) => { - self.message_handler.chan_handler.handle_accept_channel(&their_node_id, their_features.clone().unwrap(), &msg); + match post_init_state { + None => panic!("Received AcceptChannel before Init and didn't catch it earlier!"), + Some(state) => { + self.message_handler.chan_handler.handle_accept_channel(&their_node_id, state.their_features.clone(), &msg); + } + } }, wire::Message::FundingCreated(msg) => { @@ -942,7 +983,7 @@ impl self.do_attempt_write_data(&mut descriptor, &mut peer.sync_status, &mut peer.transport, &mut peer.pending_outbound_buffer), + Some(peer) => self.do_attempt_write_data(&mut descriptor, &mut peer.post_init_state, &mut peer.transport, &mut peer.pending_outbound_buffer), None => panic!("Inconsistent peers set state!"), } } @@ -1214,34 +1255,41 @@ impl return true, // retain + Some(ref mut post_init_state) => { + if post_init_state.awaiting_pong { + peers_needing_send.remove(descriptor); + descriptors_needing_disconnect.push(descriptor.clone()); + let their_node_id = peer.transport.get_their_node_id(); + log_trace!(self.logger, "Disconnecting peer with id {} due to ping timeout", their_node_id); + node_id_to_descriptor.remove(&their_node_id); + self.message_handler.chan_handler.peer_disconnected(&their_node_id, false); + + return false; // retain + } - let mut needs_to_write_data = false; - if peer.transport.is_connected() { - let ping = msgs::Ping { - ponglen: 0, - byteslen: 64, - }; - peer.transport.enqueue_message(&ping, &mut peer.pending_outbound_buffer, &*self.logger); - needs_to_write_data = true; - } + if peer.transport.is_connected() { + let ping = msgs::Ping { + ponglen: 0, + byteslen: 64, + }; + peer.transport.enqueue_message(&ping, &mut peer.pending_outbound_buffer, &*self.logger); + post_init_state.awaiting_pong = true; + + true // needs_to_write_data + } else { + false // !needs_to_write_data + } + } + }; if needs_to_write_data { let mut descriptor_clone = descriptor.clone(); - self.do_attempt_write_data(&mut descriptor_clone, &mut peer.sync_status, &mut peer.transport, &mut peer.pending_outbound_buffer); + self.do_attempt_write_data(&mut descriptor_clone, &mut peer.post_init_state, &mut peer.transport, &mut peer.pending_outbound_buffer); } - peer.awaiting_pong = true; - true + true // retain }); for mut descriptor in descriptors_needing_disconnect.drain(..) { @@ -3198,11 +3246,11 @@ mod tests { let peer_0 = peers[0].inner.peers.lock().unwrap(); let peer_1 = peers[1].inner.peers.lock().unwrap(); - let peer_0_features = peer_1.peers.get(&fd_1_to_0).unwrap().their_features.as_ref(); - let peer_1_features = peer_0.peers.get(&fd_0_to_1).unwrap().their_features.as_ref(); + let peer_0_features = peer_1.peers.get(&fd_1_to_0).unwrap().post_init_state.as_ref().unwrap(); + let peer_1_features = peer_0.peers.get(&fd_0_to_1).unwrap().post_init_state.as_ref().unwrap(); - assert!(peer_0_features.unwrap().initial_routing_sync()); - assert!(!peer_1_features.unwrap().initial_routing_sync()); + assert!(peer_0_features.their_features.initial_routing_sync()); + assert!(!peer_1_features.their_features.initial_routing_sync()); } // Outbound peer 1 requests initial_routing_sync, but inbound peer 0 does not. @@ -3215,11 +3263,11 @@ mod tests { let peer_0 = peers[0].inner.peers.lock().unwrap(); let peer_1 = peers[1].inner.peers.lock().unwrap(); - let peer_0_features = peer_1.peers.get(&fd_1_to_0).unwrap().their_features.as_ref(); - let peer_1_features = peer_0.peers.get(&fd_0_to_1).unwrap().their_features.as_ref(); + let peer_0_features = peer_1.peers.get(&fd_1_to_0).unwrap().post_init_state.as_ref().unwrap(); + let peer_1_features = peer_0.peers.get(&fd_0_to_1).unwrap().post_init_state.as_ref().unwrap(); - assert!(!peer_0_features.unwrap().initial_routing_sync()); - assert!(peer_1_features.unwrap().initial_routing_sync()); + assert!(!peer_0_features.their_features.initial_routing_sync()); + assert!(peer_1_features.their_features.initial_routing_sync()); } } } From c44d42b6cc66c6ed01c2462cb3168956f764f992 Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Mon, 7 Sep 2020 17:58:22 -0700 Subject: [PATCH 61/72] fix: Don't allow peer_disconnect before peer_connected callbacks The previous implementation would send a peer_disconnect callback for any error after the NOISE handshake completed, but only send a peer_connected callback after the Init message was received. This could lead to a dangling peer_disconnected callback that didn't have a paired peer_connected. This patch cleans up the state differentiation to match the following cases: unconnected (!transport.is_connected() && peer.post_init_info.is_none()) * Know about the peer, but are still in the process of the NOISE handshake connected (transport.is_connected() && peer.post_init_info.is_none()) * NOISE handshake completed, but haven't received an Init message initialized (transport.is_connected() && peer.post_init_info.is_some()) * The NOISE handshake has completed and the Init message has been received and processed With the 3 conceptual states, the read_event() path now only inserts a Peer into the node_id_to_descriptor map after a Peer enters the initialized state. This fixes the asymmetry between peer_disconnected & peer_connected as well as simplifies the disconnect code. --- lightning/src/ln/peers/handler.rs | 257 +++++++++++++++------------- lightning/src/ln/peers/test_util.rs | 16 +- lightning/src/ln/peers/transport.rs | 26 +-- 3 files changed, 162 insertions(+), 137 deletions(-) diff --git a/lightning/src/ln/peers/handler.rs b/lightning/src/ln/peers/handler.rs index 1a8e9d683db..94852d4cd4c 100644 --- a/lightning/src/ln/peers/handler.rs +++ b/lightning/src/ln/peers/handler.rs @@ -29,7 +29,7 @@ use util::events::{MessageSendEvent, MessageSendEventsProvider}; use util::logger::Logger; use routing::network_graph::NetGraphMsgHandler; -use std::collections::{HashMap,hash_map,HashSet}; +use std::collections::{HashMap,HashSet}; use std::sync::{Arc, Mutex}; use std::sync::atomic::{AtomicUsize, Ordering}; use std::{cmp,error,hash,fmt}; @@ -55,7 +55,7 @@ pub(super) trait ITransport: MessageQueuer { fn new_inbound(responder_static_private_key: &SecretKey, responder_ephemeral_private_key: &SecretKey) -> Self; /// Process input data similar to reading it off a descriptor directly. - fn process_input(&mut self, input: &[u8], output_buffer: &mut impl PayloadQueuer) -> Result<(), String>; + fn process_input(&mut self, input: &[u8], output_buffer: &mut impl PayloadQueuer) -> Result; /// Returns true if the connection is established and encrypted messages can be sent. fn is_connected(&self) -> bool; @@ -247,7 +247,7 @@ struct PeerHolder { /// Added to by do_read_event for cases where we pushed a message onto the send buffer but /// didn't call do_attempt_write_data to avoid reentrancy. Cleared in process_events() peers_needing_send: HashSet, - /// Only add to this set when noise completes: + /// Peers in this map have completed the NOISE handshake and received an Init message node_id_to_descriptor: HashMap, } @@ -650,6 +650,85 @@ impl Result { + if init_message.features.requires_unknown_bits() { + log_info!(self.logger, "Peer global features required unknown version bits"); + return Err(PeerHandleError { no_connection_possible: true }.into()); + } + if init_message.features.requires_unknown_bits() { + log_info!(self.logger, "Peer local features required unknown version bits"); + return Err(PeerHandleError { no_connection_possible: true }.into()); + } + + log_info!( + self.logger, "Received peer Init message: data_loss_protect: {}, initial_routing_sync: {}, upfront_shutdown_script: {}, static_remote_key: {}, unknown flags (local and global): {}", + if init_message.features.supports_data_loss_protect() { "supported" } else { "not supported"}, + if init_message.features.initial_routing_sync() { "requested" } else { "not requested" }, + if init_message.features.supports_upfront_shutdown_script() { "supported" } else { "not supported"}, + if init_message.features.supports_static_remote_key() { "supported" } else { "not supported"}, + if init_message.features.supports_unknown_bits() { "present" } else { "none" } + ); + + let sync_status = if init_message.features.initial_routing_sync() { + InitSyncTracker::ChannelsSyncing(0) + } else { + InitSyncTracker::NoSyncRequested + }; + + if !init_message.features.supports_static_remote_key() { + log_debug!(self.logger, "Peer {} does not support static remote key, disconnecting with no_connection_possible", log_pubkey!(their_node_id)); + return Err(PeerHandleError { no_connection_possible: true }.into()); + } + + Ok(PostInitState::new(sync_status, init_message.features.clone())) + } + + // Add an Init message to the outbound queue + fn queue_init_message(&self, peers_needing_send: &mut HashSet, message_queuer: &mut impl MessageQueuer, pending_outbound_buffer: &mut impl PayloadQueuer, descriptor: &Descriptor, should_request_full_sync: bool) { + let mut features = InitFeatures::known(); + if !should_request_full_sync { + features.clear_initial_routing_sync(); + } + + let resp = msgs::Init { features }; + self.enqueue_message(peers_needing_send, message_queuer, pending_outbound_buffer, descriptor, &resp); + } + + // Process an incoming Init message and set Peer and PeerManager state accordingly + fn process_init_message(&self, message: Message, descriptor: &Descriptor, peers_needing_send: &mut HashSet, node_id_to_descriptor: &mut HashMap, pending_outbound_buffer: &mut impl PayloadQueuer, outbound: bool, transport: &mut impl ITransport, post_init_state: &mut Option) -> Result<(), PeerHandleError> { + let their_node_id = transport.get_their_node_id(); + + match message { + Message::Init(ref init_message) => { + log_trace!(self.logger, "Received Init message from {}", log_pubkey!(&their_node_id)); + if node_id_to_descriptor.contains_key(&their_node_id) { + log_trace!(self.logger, "Got second connection with {}, closing", log_pubkey!(&their_node_id)); + return Err(PeerHandleError { no_connection_possible: false }); + } + + let new_post_init_state = self.post_init_state_from_init_message(init_message, &their_node_id)?; + + if let InitSyncTracker::ChannelsSyncing(_) = new_post_init_state.sync_status { + peers_needing_send.insert(descriptor.clone()); + } + + if !outbound { + self.queue_init_message(peers_needing_send, transport, pending_outbound_buffer, descriptor, self.message_handler.route_handler.should_request_full_sync(&their_node_id)); + } + node_id_to_descriptor.insert(their_node_id.clone(), descriptor.clone()); + self.message_handler.chan_handler.peer_connected(&their_node_id, init_message); + *post_init_state = Some(new_post_init_state); + } + _ => { + log_trace!(self.logger, "Peer {} sent non-Init first message", log_pubkey!(&their_node_id)); + return Err(PeerHandleError { no_connection_possible: false }) + }, + } + + Ok(()) + } + fn do_read_event(&self, peer_descriptor: &mut Descriptor, data: &[u8], @@ -665,48 +744,29 @@ impl { - - // If the transport is newly connected, do the appropriate set up for the connection - if transport.is_connected() { - let their_node_id = transport.get_their_node_id(); - - match node_id_to_descriptor.entry(their_node_id.clone()) { - hash_map::Entry::Occupied(entry) => { - if entry.get() != peer_descriptor { - // Existing entry in map is from a different descriptor, this is a duplicate - log_trace!(self.logger, "Got second connection with {}, closing", log_pubkey!(&their_node_id)); - return Err(PeerHandleError { no_connection_possible: false }); - } else { - // read_event for existing peer - } - }, - hash_map::Entry::Vacant(entry) => { - log_trace!(self.logger, "Finished noise handshake for connection with {}", log_pubkey!(&their_node_id)); + Ok(newly_connected) => { + if newly_connected { + log_trace!(self.logger, "Finished noise handshake for connection with {}", log_pubkey!(&transport.get_their_node_id())); + } - if outbound { - let mut features = InitFeatures::known(); - if !self.message_handler.route_handler.should_request_full_sync(&their_node_id) { - features.clear_initial_routing_sync(); - } + if newly_connected && outbound { + self.queue_init_message(peers_needing_send, transport, pending_outbound_buffer, peer_descriptor, self.message_handler.route_handler.should_request_full_sync(&transport.get_their_node_id())); + } - let resp = msgs::Init { features }; - self.enqueue_message(peers_needing_send, transport, pending_outbound_buffer, peer_descriptor, &resp); - } - entry.insert(peer_descriptor.clone()); - } - } - } else { - // If the transport layer placed items in the outbound queue, we need - // to schedule ourselves for flush during the next process_events() - if !pending_outbound_buffer.is_empty() { - peers_needing_send.insert(peer_descriptor.clone()); - } + // If the transport layer placed items in the outbound queue, we need + // to schedule ourselves for flush during the next process_events() + if !pending_outbound_buffer.is_empty() { + peers_needing_send.insert(peer_descriptor.clone()); } } } - let received_messages = transport.drain_messages(&*self.logger)?; + let mut received_messages = transport.drain_messages(&*self.logger)?; + + if transport.is_connected() && post_init_state.is_none() && received_messages.len() > 0 { + let init_message = received_messages.remove(0); + self.process_init_message(init_message, peer_descriptor, peers_needing_send, node_id_to_descriptor, pending_outbound_buffer, outbound, transport, post_init_state)?; + } for message in received_messages { macro_rules! try_potential_handleerror { @@ -735,7 +795,7 @@ impl { return Err(e) }, MessageHandlingError::LightningError(e) => { @@ -756,7 +816,6 @@ impl, peer_descriptor: &mut Descriptor, message: wire::Message, - outbound: bool, post_init_state: &mut Option, their_node_id: PublicKey, message_queuer: &mut impl MessageQueuer, @@ -764,63 +823,11 @@ impl Result<(), MessageHandlingError> { log_trace!(self.logger, "Received message of type {} from {}", message.type_id(), log_pubkey!(&their_node_id)); - // Need an Init as first message - if let wire::Message::Init(_) = message { - } else if post_init_state.is_none() { - log_trace!(self.logger, "Peer {} sent non-Init first message", log_pubkey!(&their_node_id)); - return Err(PeerHandleError{ no_connection_possible: false }.into()); - } - match message { // Setup and Control messages: - wire::Message::Init(msg) => { - if msg.features.requires_unknown_bits() { - log_info!(self.logger, "Peer global features required unknown version bits"); - return Err(PeerHandleError{ no_connection_possible: true }.into()); - } - if msg.features.requires_unknown_bits() { - log_info!(self.logger, "Peer local features required unknown version bits"); - return Err(PeerHandleError{ no_connection_possible: true }.into()); - } - if post_init_state.is_some() { - return Err(PeerHandleError{ no_connection_possible: false }.into()); - } - - log_info!( - self.logger, "Received peer Init message: data_loss_protect: {}, initial_routing_sync: {}, upfront_shutdown_script: {}, static_remote_key: {}, unknown flags (local and global): {}", - if msg.features.supports_data_loss_protect() { "supported" } else { "not supported"}, - if msg.features.initial_routing_sync() { "requested" } else { "not requested" }, - if msg.features.supports_upfront_shutdown_script() { "supported" } else { "not supported"}, - if msg.features.supports_static_remote_key() { "supported" } else { "not supported"}, - if msg.features.supports_unknown_bits() { "present" } else { "none" } - ); - - let sync_status = if msg.features.initial_routing_sync() { - peers_needing_send.insert(peer_descriptor.clone()); - InitSyncTracker::ChannelsSyncing(0) - } else { - InitSyncTracker::NoSyncRequested - }; - - if !msg.features.supports_static_remote_key() { - log_debug!(self.logger, "Peer {} does not support static remote key, disconnecting with no_connection_possible", log_pubkey!(&their_node_id)); - return Err(PeerHandleError{ no_connection_possible: true }.into()); - } - - if !outbound { - let mut features = InitFeatures::known(); - if !self.message_handler.route_handler.should_request_full_sync(&their_node_id) { - features.clear_initial_routing_sync(); - } - - let resp = msgs::Init { features }; - self.enqueue_message(peers_needing_send, message_queuer, pending_outbound_buffer, &peer_descriptor, &resp); - } - - self.message_handler.chan_handler.peer_connected(&their_node_id, &msg); - - // Initialize the post_init_state now that the Init has been received - *post_init_state = Some(PostInitState::new(sync_status, msg.features)); + wire::Message::Init(_) => { + // 1st Init message handled before handle_message() so this must be a non-first + return Err(PeerHandleError{ no_connection_possible: false }.into()); }, wire::Message::Error(msg) => { let mut data_is_printable = true; @@ -1229,17 +1236,16 @@ impl panic!("Descriptor for disconnect_event is not already known to PeerManager"), Some(peer) => { - if peer.transport.is_connected() { - let node_id = peer.transport.get_their_node_id(); - - if peers.node_id_to_descriptor.get(&node_id).unwrap() == descriptor { - peers.node_id_to_descriptor.remove(&node_id); - self.message_handler.chan_handler.peer_disconnected(&node_id, no_connection_possible); - } else { - // This must have been generated from a duplicate connection error + if peer.is_initialized() { + let their_node_id = peer.transport.get_their_node_id(); + + match peers.node_id_to_descriptor.remove(&their_node_id) { + None => { panic!("Initialized peer must be in node_id_to_descriptor")} + Some(_) => { + peers.node_id_to_descriptor.remove(&their_node_id); + self.message_handler.chan_handler.peer_disconnected(&their_node_id, no_connection_possible); + } } - } else { - // Unconnected nodes never make it into node_id_to_descriptor } } }; @@ -1558,8 +1564,6 @@ mod unit_tests { // Test that an outbound connection with a connected Transport, but no Init message // * read_event() does not call peer_disconnected callback if an error is returned from Transport - // XXXBUG: peer_connected is called after the Init message, but peer_disconnected is called - // with any error after the NOISE handshake is complete #[test] fn outbound_connected_transport_error_does_not_call_peer_disconnected_on_error() { let test_ctx = TestCtx::::new(); @@ -1573,7 +1577,7 @@ mod unit_tests { transport.borrow_mut().process_returns_error(); assert_read_event_errors!(peer_manager, &mut descriptor, false); assert!(!channel_handler_called!(&test_ctx, peer_connected)); - // assert!(!channel_handler_called!(&test_ctx, peer_disconnected)); + assert!(!channel_handler_called!(&test_ctx, peer_disconnected)); } // Test that an inbound connection with a connected Transport and queued Init message: @@ -1664,7 +1668,7 @@ mod unit_tests { // Test that an outbound connection with a connected Transport: // * read_event() errors when receiving a Non-Init message first - // * read_event() calls the peer_disconnected channel manager callback + // * read_event() does not call the peer_connected/peer_disconnected callbacks #[test] fn inbound_connected_transport_non_init_first_fails() { let test_ctx = TestCtx::::new(); @@ -1675,12 +1679,13 @@ mod unit_tests { new_inbound!(peer_manager, descriptor, &mut transport); assert_read_event_errors!(peer_manager, &mut descriptor, false); - assert!(channel_handler_called!(&test_ctx, peer_disconnected)); + assert!(!channel_handler_called!(&test_ctx, peer_connected)); + assert!(!channel_handler_called!(&test_ctx, peer_disconnected)); } // Test that an outbound connection with a connected Transport: // * read_event() errors when receiving a Non-Init message first - // * read_event() calls the peer_disconnected channel manager callback + // * read_event() does not call the peer_connected/peer_disconnected callbacks #[test] fn outbound_connected_transport_non_init_first_fails() { let test_ctx = TestCtx::::new(); @@ -1691,12 +1696,13 @@ mod unit_tests { new_outbound!(peer_manager, descriptor, &mut transport); assert_read_event_errors!(peer_manager, &mut descriptor, false); - assert!(channel_handler_called!(&test_ctx, peer_disconnected)); + assert!(!channel_handler_called!(&test_ctx, peer_connected)); + assert!(!channel_handler_called!(&test_ctx, peer_disconnected)); } // Test that an inbound connection with a connected Transport: // * read_event() errors out with no_connection_possible if an Init message contains requires_unknown_bits - // * read_event() calls the peer_disconnected channel manager callback + // * read_event() does not call the peer_connected/peer_disconnected callbacks #[test] fn inbound_connected_transport_init_with_required_unknown_first_fails() { let test_ctx = TestCtx::::new(); @@ -1709,12 +1715,13 @@ mod unit_tests { new_inbound!(peer_manager, descriptor, &mut transport); assert_read_event_errors!(peer_manager, &mut descriptor, true); - assert!(channel_handler_called!(&test_ctx, peer_disconnected)); + assert!(!channel_handler_called!(&test_ctx, peer_connected)); + assert!(!channel_handler_called!(&test_ctx, peer_disconnected)); } // Test that an outbound connection with a connected Transport: // * read_event() errors out with no_connection_possible if an Init message contains requires_unknown_bits - // * read_event() calls the peer_disconnected channel manager callback + // * read_event() does not call the peer_connected/peer_disconnected callbacks #[test] fn outbound_connected_transport_init_with_required_unknown_first_fails() { let test_ctx = TestCtx::::new(); @@ -1727,12 +1734,13 @@ mod unit_tests { new_outbound!(peer_manager, descriptor, &mut transport); assert_read_event_errors!(peer_manager, &mut descriptor, true); - assert!(channel_handler_called!(&test_ctx, peer_disconnected)); + assert!(!channel_handler_called!(&test_ctx, peer_connected)); + assert!(!channel_handler_called!(&test_ctx, peer_disconnected)); } // Test that an inbound connection with a connected Transport: // * read_event() errors out with no_connection_possible if an Init message does not contain requires_static_remote_key - // * read_event() calls the peer_disconnected channel manager callback + // * read_event() does not call the peer_connected/peer_disconnected callbacks #[test] fn inbound_connected_transport_init_with_clear_requires_static_remote_key() { let test_ctx = TestCtx::::new(); @@ -1745,12 +1753,13 @@ mod unit_tests { new_inbound!(peer_manager, descriptor, &mut transport); assert_read_event_errors!(peer_manager, &mut descriptor, true); - assert!(channel_handler_called!(&test_ctx, peer_disconnected)); + assert!(!channel_handler_called!(&test_ctx, peer_connected)); + assert!(!channel_handler_called!(&test_ctx, peer_disconnected)); } // Test that an outbound connection with a connected Transport: // * read_event() errors out with no_connection_possible if an Init message does not contain requires_static_remote_key - // * read_event() calls the peer_disconnected channel manager callback + // * read_event() does not call the peer_connected/peer_disconnected callbacks #[test] fn outbound_connected_transport_init_with_clear_requires_static_remote_key() { let test_ctx = TestCtx::::new(); @@ -1763,7 +1772,8 @@ mod unit_tests { new_outbound!(peer_manager, descriptor, &mut transport); assert_read_event_errors!(peer_manager, &mut descriptor, true); - assert!(channel_handler_called!(&test_ctx, peer_disconnected)); + assert!(!channel_handler_called!(&test_ctx, peer_connected)); + assert!(!channel_handler_called!(&test_ctx, peer_disconnected)); } // Test that an inbound connection with a connected Transport and queued Init Message: @@ -1832,11 +1842,14 @@ mod unit_tests { let test_ctx = TestCtx::::new(); let mut descriptor = SocketDescriptorMock::new(); let transport = new_connected_transport!(&test_ctx); - // Create a duplicate connection from the same node_id + + // Create a duplicate connection from the same node_id w/ pending Init message let mut duplicate_connection_descriptor = SocketDescriptorMock::new(); let mut duplicate_connection_transport = RefCell::new(TransportStubBuilder::new() .set_connected(&test_ctx.their_node_id) .finish()); + duplicate_connection_transport.borrow_mut().add_incoming_message(Message::Init(Init { features: InitFeatures::known() })); + let peer_manager = new_peer_manager_post_init!(&test_ctx, &mut descriptor, &transport); assert!(peer_manager.get_peer_node_ids().contains(&test_ctx.their_node_id)); diff --git a/lightning/src/ln/peers/test_util.rs b/lightning/src/ln/peers/test_util.rs index 5803fab4a14..3c07a7c85de 100644 --- a/lightning/src/ln/peers/test_util.rs +++ b/lightning/src/ln/peers/test_util.rs @@ -251,6 +251,7 @@ impl TransportStubBuilder { is_connected: false, messages: vec![], process_returns_error: false, + returned_newly_connected: false, their_node_id: None, } } @@ -276,6 +277,7 @@ pub(super) struct TransportStub { is_connected: bool, messages: Vec, process_returns_error: bool, + returned_newly_connected: bool, their_node_id: Option, } @@ -294,7 +296,7 @@ impl<'a> ITransport for &'a RefCell { unimplemented!() } - fn process_input(&mut self, input: &[u8], output_buffer: &mut impl PayloadQueuer) -> Result<(), String> { + fn process_input(&mut self, input: &[u8], output_buffer: &mut impl PayloadQueuer) -> Result { self.borrow_mut().process_input(input, output_buffer) } @@ -343,11 +345,19 @@ impl ITransport for TransportStub { unimplemented!() } - fn process_input(&mut self, _input: &[u8], _output_buffer: &mut impl PayloadQueuer) -> Result<(), String> { + fn process_input(&mut self, _input: &[u8], _output_buffer: &mut impl PayloadQueuer) -> Result { if self.process_returns_error { Err("Oh no!".to_string()) } else { - Ok(()) + if self.is_connected() { + + // Simulate the newly_connected behavior by returning Ok(true) on the first call + if !self.returned_newly_connected { + self.returned_newly_connected = true; + return Ok(true) + } + } + return Ok(false) } } diff --git a/lightning/src/ln/peers/transport.rs b/lightning/src/ln/peers/transport.rs index 81bd99afb6e..f8829942fee 100644 --- a/lightning/src/ln/peers/transport.rs +++ b/lightning/src/ln/peers/transport.rs @@ -76,7 +76,7 @@ impl ITransport for Transport Result<(), String> { + fn process_input(&mut self, input: &[u8], output_buffer: &mut impl PayloadQueuer) -> Result { match self.conduit { // Continue handshake None => { @@ -91,14 +91,16 @@ impl ITransport for Transport { conduit.read(input); + Ok(false) // newly connected } - }; - - Ok(()) + } } fn drain_messages(&mut self, logger: L) -> Result, PeerHandleError> @@ -255,7 +257,7 @@ mod tests { let mut transport = create_inbound_for_test::(); let mut spy = Vec::new(); - transport.process_input(&[], &mut spy).unwrap(); + assert_eq!(transport.process_input(&[], &mut spy), Ok(false)); assert!(!transport.is_connected()); assert_matches!(&spy[..], [_]); @@ -267,7 +269,7 @@ mod tests { let mut transport = create_outbound_for_test::(); let mut spy = Vec::new(); - transport.process_input(&[], &mut spy).unwrap(); + assert_eq!(transport.process_input(&[], &mut spy), Ok(false)); assert!(!transport.is_connected()); assert_matches!(&spy[..], [_]); @@ -278,7 +280,7 @@ mod tests { let mut transport = create_inbound_for_test::(); let mut spy = Vec::new(); - transport.process_input(&[], &mut spy).unwrap(); + assert_eq!(transport.process_input(&[], &mut spy), Ok(true)); assert!(transport.is_connected()); } @@ -305,7 +307,7 @@ mod tests { let mut transport = create_inbound_for_test::(); let mut spy = Vec::new(); - transport.process_input(&[], &mut spy).unwrap(); + assert_eq!(transport.process_input(&[], &mut spy), Ok(true)); let _no_panic = transport.get_their_node_id(); } @@ -314,7 +316,7 @@ mod tests { let mut transport = create_inbound_for_test::(); let mut spy = Vec::new(); - transport.process_input(&[], &mut spy).unwrap(); + assert_eq!(transport.process_input(&[], &mut spy), Ok(true)); let _no_panic = transport.get_their_node_id(); } @@ -324,7 +326,7 @@ mod tests { let mut transport = create_outbound_for_test::(); let mut spy = Vec::new(); - transport.process_input(&[], &mut spy).unwrap(); + assert_eq!(transport.process_input(&[], &mut spy), Ok(true)); assert!(transport.is_connected()); let _no_panic = transport.get_their_node_id(); } @@ -365,7 +367,7 @@ mod tests { let mut transport = create_inbound_for_test::(); let mut spy = Vec::new(); - transport.process_input(&[], &mut spy).unwrap(); + assert_eq!(transport.process_input(&[], &mut spy), Ok(true)); let ping = msgs::Ping { ponglen: 0, @@ -382,7 +384,7 @@ mod tests { let mut transport = create_outbound_for_test::(); let mut spy = Vec::new(); - transport.process_input(&[], &mut spy).unwrap(); + assert_eq!(transport.process_input(&[], &mut spy), Ok(true)); let ping = msgs::Ping { ponglen: 0, From a5acccdf46beb0e680a66afa27cd80bb2c3d2461 Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Mon, 7 Sep 2020 19:17:51 -0700 Subject: [PATCH 62/72] refactor: Clean up the previously destructured read_event() path Now that the Init handling has been moved and the dependencies are more clear, deduplicate the parameters of the read_event() and helper functions to make use of Peer. The overall pattern remains the same, read_event() does the locking and passes in the separate items (peer, peers_needing_send, node_id_to_descriptor) to do_read_event(). The handle_message() path is also cleaned up now that post_init_state is guaranteed to be valid at that point. --- lightning/src/ln/peers/handler.rs | 87 ++++++++++++------------------- 1 file changed, 33 insertions(+), 54 deletions(-) diff --git a/lightning/src/ln/peers/handler.rs b/lightning/src/ln/peers/handler.rs index 94852d4cd4c..547008fd2a8 100644 --- a/lightning/src/ln/peers/handler.rs +++ b/lightning/src/ln/peers/handler.rs @@ -632,7 +632,7 @@ impl panic!("Descriptor for read_event is not already known to PeerManager"), Some(peer) => peer }; - self.do_read_event(peer_descriptor, data, peer.outbound, &mut peer.post_init_state, &mut peer.transport, &mut peer.pending_outbound_buffer, &mut peers.node_id_to_descriptor, &mut peers.peers_needing_send) + self.do_read_event(peer_descriptor, peer, &mut peers.peers_needing_send, &mut peers.node_id_to_descriptor, data) }; match result { @@ -685,19 +685,19 @@ impl, message_queuer: &mut impl MessageQueuer, pending_outbound_buffer: &mut impl PayloadQueuer, descriptor: &Descriptor, should_request_full_sync: bool) { + fn queue_init_message(&self, descriptor: &Descriptor, peer: &mut Peer, peers_needing_send: &mut HashSet) { let mut features = InitFeatures::known(); - if !should_request_full_sync { + if !self.message_handler.route_handler.should_request_full_sync(&peer.transport.get_their_node_id()) { features.clear_initial_routing_sync(); } let resp = msgs::Init { features }; - self.enqueue_message(peers_needing_send, message_queuer, pending_outbound_buffer, descriptor, &resp); + self.enqueue_message(peers_needing_send, &mut peer.transport, &mut peer.pending_outbound_buffer, descriptor, &resp); } // Process an incoming Init message and set Peer and PeerManager state accordingly - fn process_init_message(&self, message: Message, descriptor: &Descriptor, peers_needing_send: &mut HashSet, node_id_to_descriptor: &mut HashMap, pending_outbound_buffer: &mut impl PayloadQueuer, outbound: bool, transport: &mut impl ITransport, post_init_state: &mut Option) -> Result<(), PeerHandleError> { - let their_node_id = transport.get_their_node_id(); + fn process_init_message(&self, message: Message, descriptor: &Descriptor, peer: &mut Peer, peers_needing_send: &mut HashSet, node_id_to_descriptor: &mut HashMap) -> Result<(), PeerHandleError> { + let their_node_id = peer.transport.get_their_node_id(); match message { Message::Init(ref init_message) => { @@ -713,12 +713,14 @@ impl { log_trace!(self.logger, "Peer {} sent non-Init first message", log_pubkey!(&their_node_id)); @@ -729,43 +731,35 @@ impl, - transport: &mut impl ITransport, - pending_outbound_buffer: &mut impl PayloadQueuer, - node_id_to_descriptor: &mut HashMap, - peers_needing_send: &mut HashSet) -> Result { + fn do_read_event(&self, peer_descriptor: &mut Descriptor, peer: &mut Peer, peers_needing_send: &mut HashSet, node_id_to_descriptor: &mut HashMap, data: &[u8]) -> Result { let pause_read = { - match transport.process_input(data, pending_outbound_buffer) { + match peer.transport.process_input(data, &mut peer.pending_outbound_buffer) { Err(e) => { log_trace!(self.logger, "Error while processing input: {}", e); return Err(PeerHandleError { no_connection_possible: false }) }, Ok(newly_connected) => { if newly_connected { - log_trace!(self.logger, "Finished noise handshake for connection with {}", log_pubkey!(&transport.get_their_node_id())); + log_trace!(self.logger, "Finished noise handshake for connection with {}", log_pubkey!(&peer.transport.get_their_node_id())); } - if newly_connected && outbound { - self.queue_init_message(peers_needing_send, transport, pending_outbound_buffer, peer_descriptor, self.message_handler.route_handler.should_request_full_sync(&transport.get_their_node_id())); + if newly_connected && peer.outbound { + self.queue_init_message(peer_descriptor, peer, peers_needing_send); } // If the transport layer placed items in the outbound queue, we need // to schedule ourselves for flush during the next process_events() - if !pending_outbound_buffer.is_empty() { + if !peer.pending_outbound_buffer.is_empty() { peers_needing_send.insert(peer_descriptor.clone()); } } } - let mut received_messages = transport.drain_messages(&*self.logger)?; + let mut received_messages = peer.transport.drain_messages(&*self.logger)?; - if transport.is_connected() && post_init_state.is_none() && received_messages.len() > 0 { + if peer.transport.is_connected() && peer.post_init_state.is_none() && received_messages.len() > 0 { let init_message = received_messages.remove(0); - self.process_init_message(init_message, peer_descriptor, peers_needing_send, node_id_to_descriptor, pending_outbound_buffer, outbound, transport, post_init_state)?; + self.process_init_message(init_message, peer_descriptor, peer, peers_needing_send, node_id_to_descriptor)?; } for message in received_messages { @@ -786,7 +780,7 @@ impl { log_trace!(self.logger, "Got Err handling message, sending Error message because {}", e.err); - self.enqueue_message(peers_needing_send, transport, pending_outbound_buffer, peer_descriptor, &msg); + self.enqueue_message(peers_needing_send, &mut peer.transport, &mut peer.pending_outbound_buffer, peer_descriptor, &msg); continue; }, } @@ -795,7 +789,7 @@ impl { return Err(e) }, MessageHandlingError::LightningError(e) => { @@ -805,7 +799,7 @@ impl, - peer_descriptor: &mut Descriptor, message: wire::Message, - post_init_state: &mut Option, - their_node_id: PublicKey, - message_queuer: &mut impl MessageQueuer, - pending_outbound_buffer: &mut impl PayloadQueuer - ) -> Result<(), MessageHandlingError> { + peer_descriptor: &mut Descriptor, + peer: &mut Peer, + peers_needing_send: &mut HashSet) -> Result<(), MessageHandlingError> { + + let their_node_id = peer.transport.get_their_node_id(); + let post_init_state = peer.post_init_state.as_mut().unwrap(); log_trace!(self.logger, "Received message of type {} from {}", message.type_id(), log_pubkey!(&their_node_id)); match message { @@ -852,33 +845,19 @@ impl { if msg.ponglen < 65532 { let resp = msgs::Pong { byteslen: msg.ponglen }; - self.enqueue_message(peers_needing_send, message_queuer, pending_outbound_buffer, &peer_descriptor, &resp); + self.enqueue_message(peers_needing_send, &mut peer.transport, &mut peer.pending_outbound_buffer, &peer_descriptor, &resp); } }, wire::Message::Pong(_msg) => { - if let Some(ref mut state) = post_init_state { - state.awaiting_pong = false; - } else { - panic!("Received Pong before Init and didn't catch it earlier!"); - } + post_init_state.awaiting_pong = false; }, // Channel messages: wire::Message::OpenChannel(msg) => { - match post_init_state { - None => panic!("Received OpenChannel before Init and didn't catch it earlier!"), - Some(state) => { - self.message_handler.chan_handler.handle_open_channel(&their_node_id, state.their_features.clone(), &msg); - } - } + self.message_handler.chan_handler.handle_open_channel(&their_node_id, post_init_state.their_features.clone(), &msg); }, wire::Message::AcceptChannel(msg) => { - match post_init_state { - None => panic!("Received AcceptChannel before Init and didn't catch it earlier!"), - Some(state) => { - self.message_handler.chan_handler.handle_accept_channel(&their_node_id, state.their_features.clone(), &msg); - } - } + self.message_handler.chan_handler.handle_accept_channel(&their_node_id, post_init_state.their_features.clone(), &msg); }, wire::Message::FundingCreated(msg) => { From 0359955c06dd4669817215a4c08c9dfa70668f17 Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Mon, 7 Sep 2020 19:21:58 -0700 Subject: [PATCH 63/72] refactor: Remove scoping from do_read_event() Now that the locking and destructuring is done in read_event(), the workarounds for the pre-NLL borrow checker can go away. --- lightning/src/ln/peers/handler.rs | 113 +++++++++++++++--------------- 1 file changed, 55 insertions(+), 58 deletions(-) diff --git a/lightning/src/ln/peers/handler.rs b/lightning/src/ln/peers/handler.rs index 547008fd2a8..7bb4d86a75e 100644 --- a/lightning/src/ln/peers/handler.rs +++ b/lightning/src/ln/peers/handler.rs @@ -732,77 +732,74 @@ impl, peers_needing_send: &mut HashSet, node_id_to_descriptor: &mut HashMap, data: &[u8]) -> Result { - let pause_read = { - match peer.transport.process_input(data, &mut peer.pending_outbound_buffer) { - Err(e) => { - log_trace!(self.logger, "Error while processing input: {}", e); - return Err(PeerHandleError { no_connection_possible: false }) - }, - Ok(newly_connected) => { - if newly_connected { - log_trace!(self.logger, "Finished noise handshake for connection with {}", log_pubkey!(&peer.transport.get_their_node_id())); - } - if newly_connected && peer.outbound { - self.queue_init_message(peer_descriptor, peer, peers_needing_send); - } + match peer.transport.process_input(data, &mut peer.pending_outbound_buffer) { + Err(e) => { + log_trace!(self.logger, "Error while processing input: {}", e); + return Err(PeerHandleError { no_connection_possible: false }) + }, + Ok(newly_connected) => { + if newly_connected { + log_trace!(self.logger, "Finished noise handshake for connection with {}", log_pubkey!(&peer.transport.get_their_node_id())); + } - // If the transport layer placed items in the outbound queue, we need - // to schedule ourselves for flush during the next process_events() - if !peer.pending_outbound_buffer.is_empty() { - peers_needing_send.insert(peer_descriptor.clone()); - } + if newly_connected && peer.outbound { + self.queue_init_message(peer_descriptor, peer, peers_needing_send); + } + + // If the transport layer placed items in the outbound queue, we need + // to schedule ourselves for flush during the next process_events() + if !peer.pending_outbound_buffer.is_empty() { + peers_needing_send.insert(peer_descriptor.clone()); } } + } - let mut received_messages = peer.transport.drain_messages(&*self.logger)?; + let mut received_messages = peer.transport.drain_messages(&*self.logger)?; - if peer.transport.is_connected() && peer.post_init_state.is_none() && received_messages.len() > 0 { - let init_message = received_messages.remove(0); - self.process_init_message(init_message, peer_descriptor, peer, peers_needing_send, node_id_to_descriptor)?; - } + if peer.transport.is_connected() && peer.post_init_state.is_none() && received_messages.len() > 0 { + let init_message = received_messages.remove(0); + self.process_init_message(init_message, peer_descriptor, peer, peers_needing_send, node_id_to_descriptor)?; + } - for message in received_messages { - macro_rules! try_potential_handleerror { - ($thing: expr) => { - match $thing { - Ok(x) => x, - Err(e) => { - match e.action { - msgs::ErrorAction::DisconnectPeer { msg: _ } => { - //TODO: Try to push msg - log_trace!(self.logger, "Got Err handling message, disconnecting peer because {}", e.err); - return Err(PeerHandleError{ no_connection_possible: false }); - }, - msgs::ErrorAction::IgnoreError => { - log_trace!(self.logger, "Got Err handling message, ignoring because {}", e.err); - continue; - }, - msgs::ErrorAction::SendErrorMessage { msg } => { - log_trace!(self.logger, "Got Err handling message, sending Error message because {}", e.err); - self.enqueue_message(peers_needing_send, &mut peer.transport, &mut peer.pending_outbound_buffer, peer_descriptor, &msg); - continue; - }, - } + for message in received_messages { + macro_rules! try_potential_handleerror { + ($thing: expr) => { + match $thing { + Ok(x) => x, + Err(e) => { + match e.action { + msgs::ErrorAction::DisconnectPeer { msg: _ } => { + //TODO: Try to push msg + log_trace!(self.logger, "Got Err handling message, disconnecting peer because {}", e.err); + return Err(PeerHandleError{ no_connection_possible: false }); + }, + msgs::ErrorAction::IgnoreError => { + log_trace!(self.logger, "Got Err handling message, ignoring because {}", e.err); + continue; + }, + msgs::ErrorAction::SendErrorMessage { msg } => { + log_trace!(self.logger, "Got Err handling message, sending Error message because {}", e.err); + self.enqueue_message(peers_needing_send, &mut peer.transport, &mut peer.pending_outbound_buffer, peer_descriptor, &msg); + continue; + }, } - }; - } + } + }; } + } - if let Err(handling_error) = self.handle_message(message, peer_descriptor, peer, peers_needing_send) { - match handling_error { - MessageHandlingError::PeerHandleError(e) => { return Err(e) }, - MessageHandlingError::LightningError(e) => { - try_potential_handleerror!(Err(e)); - }, - } + if let Err(handling_error) = self.handle_message(message, peer_descriptor, peer, peers_needing_send) { + match handling_error { + MessageHandlingError::PeerHandleError(e) => { return Err(e) }, + MessageHandlingError::LightningError(e) => { + try_potential_handleerror!(Err(e)); + }, } } + } - peer.pending_outbound_buffer.queue_space() == 0 // pause_read - }; - - Ok(pause_read) + Ok(peer.pending_outbound_buffer.queue_space() == 0) // pause_read } /// Process an incoming message and return a decision (ok, lightning error, peer handling error) regarding the next action with the peer From 51a34600f1434e15f95b82fec9139059e0e267fb Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Tue, 8 Sep 2020 10:44:50 -0700 Subject: [PATCH 64/72] refactor: Clean up the PeerHolder access/iteration/remove This patch expands the PeerHolder API allowing for encapsulation of the peer state from the code that needs to iterate over initialized peers. This cleans up peer iteration/removal allowing for a much simpler design. 1) Introduce new APIs for PeerHolder: * initialized_peers_mut() * initialized_peer_node_ids() * remove_peer_by_descriptor() 2) Clean up event handler iteration using new APIs 3) Unify the timer_tick and DisconnectPeer event disconnect path using new APIs 4) Convert get_peer_node_ids() to use new API --- lightning/src/ln/peers/handler.rs | 203 ++++++++++++++---------------- 1 file changed, 97 insertions(+), 106 deletions(-) diff --git a/lightning/src/ln/peers/handler.rs b/lightning/src/ln/peers/handler.rs index 7bb4d86a75e..04def083327 100644 --- a/lightning/src/ln/peers/handler.rs +++ b/lightning/src/ln/peers/handler.rs @@ -40,6 +40,8 @@ use bitcoin::hashes::sha256::HashEngine as Sha256Engine; use bitcoin::hashes::{HashEngine, Hash}; use ln::peers::outbound_queue::OutboundQueue; use ln::peers::transport::{PayloadQueuer, Transport}; +use std::collections::hash_map::IterMut; +use std::iter::Filter; const MSG_BUFF_SIZE: usize = 10; @@ -252,7 +254,7 @@ struct PeerHolder { } impl PeerHolder { - fn initialized_peer_by_node_id(&mut self, node_id: &PublicKey) -> Option<(Descriptor, &mut Peer)> { + fn initialized_peer_by_node_id_mut(&mut self, node_id: &PublicKey) -> Option<(Descriptor, &mut Peer)> { match self.node_id_to_descriptor.get_mut(node_id) { None => None, Some(descriptor) => { @@ -263,7 +265,7 @@ impl PeerHolder { // their_features is set after receiving an Init message - if !peer.is_initialized() { + if peer.post_init_state.is_none() { None } else { Some((descriptor.clone(), peer)) @@ -273,6 +275,40 @@ impl PeerHolder(&'a mut self) -> Filter>, fn(&(&'a Descriptor, &'a mut Peer)) -> bool> { + self.peers.iter_mut().filter(| entry | { + entry.1.post_init_state.is_some() + }) + } + + // Returns the node id's of the subset of peers that are initialized + fn initialized_peer_node_ids(&self) -> Vec { + self.node_id_to_descriptor.keys().cloned().collect() + } + + // Removes all associated metadata for descriptor and returns the Peer object associated with it + fn remove_peer_by_descriptor(&mut self, descriptor: &Descriptor) -> Peer { + // may or may not be in this set depending on in-flight messages + self.peers_needing_send.remove(descriptor); + + let peer_option = self.peers.remove(descriptor); + match peer_option { + None => panic!("Descriptor for disconnect_event is not already known to PeerManager"), + Some(peer) => { + if peer.post_init_state.is_some() { + let their_node_id = peer.transport.get_their_node_id(); + + match self.node_id_to_descriptor.remove(&their_node_id) { + None => { panic!("Initialized peer must be in node_id_to_descriptor")} + Some(removed_descriptor) => { assert!(&removed_descriptor == descriptor, "Invalid PeerHolder state. Descriptors do not match!") } + } + } + peer + } + } + } } #[cfg(not(any(target_pointer_width = "32", target_pointer_width = "64")))] @@ -485,12 +521,7 @@ impl Vec { let peers = self.peers.lock().unwrap(); - peers.peers.values().filter_map(|p| { - if !p.is_initialized() { - return None; - } - Some(p.transport.get_their_node_id()) - }).collect() + peers.initialized_peer_node_ids() } fn get_ephemeral_key(&self) -> SecretKey { @@ -626,8 +657,8 @@ impl Result { - let result = { - let peers = &mut *self.peers.lock().unwrap(); + let peers = &mut *self.peers.lock().unwrap(); + let result = { let peer = match peers.peers.get_mut(peer_descriptor) { None => panic!("Descriptor for read_event is not already known to PeerManager"), Some(peer) => peer @@ -638,7 +669,7 @@ impl Ok(res), Err(e) => { - self.disconnect_event_internal(peer_descriptor, e.no_connection_possible); + self.disconnect_event_internal(peer_descriptor, e.no_connection_possible, peers); Err(e) } } @@ -964,7 +995,7 @@ impl { log_trace!(self.logger, "Handling BroadcastChannelAnnouncement event in peer_handler for short channel id {}", msg.contents.short_channel_id); if self.message_handler.route_handler.handle_channel_announcement(msg).is_ok() && self.message_handler.route_handler.handle_channel_update(update_msg).is_ok() { - for (ref descriptor, ref mut peer) in peers.peers.iter_mut() { - if !peer.is_initialized() || - !peer.should_forward_channel_announcement(msg.contents.short_channel_id) { + for (descriptor, peer) in peers.initialized_peers_mut() { + if !peer.should_forward_channel_announcement(msg.contents.short_channel_id) { continue } @@ -1125,9 +1155,8 @@ impl { log_trace!(self.logger, "Handling BroadcastNodeAnnouncement event in peer_handler"); if self.message_handler.route_handler.handle_node_announcement(msg).is_ok() { - for (ref descriptor, ref mut peer) in peers.peers.iter_mut() { - if !peer.is_initialized() || - !peer.should_forward_node_announcement(msg.contents.node_id) { + for (descriptor, peer) in peers.initialized_peers_mut() { + if !peer.should_forward_node_announcement(msg.contents.node_id) { continue } peer.transport.enqueue_message(msg, &mut peer.pending_outbound_buffer, &*self.logger); @@ -1138,9 +1167,8 @@ impl { log_trace!(self.logger, "Handling BroadcastChannelUpdate event in peer_handler for short channel id {}", msg.contents.short_channel_id); if self.message_handler.route_handler.handle_channel_update(msg).is_ok() { - for (ref descriptor, ref mut peer) in peers.peers.iter_mut() { - if !peer.is_initialized() || - !peer.should_forward_channel_announcement(msg.contents.short_channel_id) { + for (descriptor, peer) in peers.initialized_peers_mut() { + if !peer.should_forward_channel_announcement(msg.contents.short_channel_id) { continue } peer.transport.enqueue_message(msg, &mut peer.pending_outbound_buffer, &*self.logger); @@ -1154,25 +1182,28 @@ impl { match *action { msgs::ErrorAction::DisconnectPeer { ref msg } => { - if let Some(mut descriptor) = peers.node_id_to_descriptor.remove(node_id) { - peers.peers_needing_send.remove(&descriptor); - if let Some(mut peer) = peers.peers.remove(&descriptor) { + let descriptor_to_disconnect_option = match peers.initialized_peer_by_node_id_mut(node_id) { + None => { None /* ignore unknown or uninitialized peers */ } + Some((mut descriptor, peer)) => { if let Some(ref msg) = *msg { log_trace!(self.logger, "Handling DisconnectPeer HandleError event in peer_handler for node {} with message {}", log_pubkey!(node_id), msg.data); - if peer.transport.is_connected() { - peer.transport.enqueue_message(msg, &mut peer.pending_outbound_buffer, &*self.logger); - } + peer.transport.enqueue_message(msg, &mut peer.pending_outbound_buffer, &*self.logger); + // This isn't guaranteed to work, but if there is enough free // room in the send buffer, put the error message there... self.do_attempt_write_data(&mut descriptor, &mut peer.post_init_state, &mut peer.transport, &mut peer.pending_outbound_buffer); } else { log_trace!(self.logger, "Handling DisconnectPeer HandleError event in peer_handler for node {} with no message", log_pubkey!(node_id)); } + Some(descriptor) } + }; + + if let Some(mut descriptor) = descriptor_to_disconnect_option { + self.disconnect_event_internal(&descriptor, false, peers); descriptor.disconnect_socket(); - self.message_handler.chan_handler.peer_disconnected(&node_id, false); } }, msgs::ErrorAction::IgnoreError => {}, @@ -1180,7 +1211,7 @@ impl panic!("Descriptor for disconnect_event is not already known to PeerManager"), - Some(peer) => { - if peer.is_initialized() { - let their_node_id = peer.transport.get_their_node_id(); + fn disconnect_event_internal(&self, descriptor: &Descriptor, no_connection_possible: bool, peers: &mut PeerHolder) { + let peer = peers.remove_peer_by_descriptor(descriptor); - match peers.node_id_to_descriptor.remove(&their_node_id) { - None => { panic!("Initialized peer must be in node_id_to_descriptor")} - Some(_) => { - peers.node_id_to_descriptor.remove(&their_node_id); - self.message_handler.chan_handler.peer_disconnected(&their_node_id, no_connection_possible); - } - } - } - } - }; + if peer.is_initialized() { + self.message_handler.chan_handler.peer_disconnected(&peer.transport.get_their_node_id(), no_connection_possible); + } } fn timer_tick_occured(&self) { - let mut peers_lock = self.peers.lock().unwrap(); - { - let peers = &mut *peers_lock; - let peers_needing_send = &mut peers.peers_needing_send; - let node_id_to_descriptor = &mut peers.node_id_to_descriptor; - let peers = &mut peers.peers; - let mut descriptors_needing_disconnect = Vec::new(); - - peers.retain(|descriptor, peer| { - let needs_to_write_data = match peer.post_init_state { - None => return true, // retain - Some(ref mut post_init_state) => { - if post_init_state.awaiting_pong { - peers_needing_send.remove(descriptor); - descriptors_needing_disconnect.push(descriptor.clone()); - let their_node_id = peer.transport.get_their_node_id(); - log_trace!(self.logger, "Disconnecting peer with id {} due to ping timeout", their_node_id); - node_id_to_descriptor.remove(&their_node_id); - self.message_handler.chan_handler.peer_disconnected(&their_node_id, false); - - return false; // retain - } - - if peer.transport.is_connected() { - let ping = msgs::Ping { - ponglen: 0, - byteslen: 64, - }; - peer.transport.enqueue_message(&ping, &mut peer.pending_outbound_buffer, &*self.logger); - post_init_state.awaiting_pong = true; - - true // needs_to_write_data - } else { - false // !needs_to_write_data - } - } + let peers = &mut *self.peers.lock().unwrap(); + let mut descriptors_needing_disconnect = Vec::new(); + + for (descriptor, peer) in peers.initialized_peers_mut() { + // Mark peers that haven't sent a pong for disconnect + if peer.post_init_state.as_ref().unwrap().awaiting_pong { + log_trace!(self.logger, "Disconnecting peer with id {} due to ping timeout", &peer.transport.get_their_node_id()); + descriptors_needing_disconnect.push(descriptor.clone()); + } else { + let ping = msgs::Ping { + ponglen: 0, + byteslen: 64, }; - - if needs_to_write_data { - let mut descriptor_clone = descriptor.clone(); - self.do_attempt_write_data(&mut descriptor_clone, &mut peer.post_init_state, &mut peer.transport, &mut peer.pending_outbound_buffer); - } - - true // retain - }); - - for mut descriptor in descriptors_needing_disconnect.drain(..) { - descriptor.disconnect_socket(); + peer.transport.enqueue_message(&ping, &mut peer.pending_outbound_buffer, &*self.logger); + self.do_attempt_write_data(&mut descriptor.clone(), &mut peer.post_init_state, &mut peer.transport, &mut peer.pending_outbound_buffer); + peer.post_init_state.as_mut().unwrap().awaiting_pong = true; } } + + for mut descriptor in descriptors_needing_disconnect.drain(..) { + self.disconnect_event_internal(&descriptor, false, peers); + descriptor.disconnect_socket(); + } } } From e5d6c2f122fe48c3d2cce340ca593be96853fe23 Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Tue, 8 Sep 2020 11:13:29 -0700 Subject: [PATCH 65/72] fix: Handle Ok(false) from route_handler callbacks in event path The broadcast event handling code did not catch Ok(false) returned from the route handler when deciding whether or not to broadcast messages. Instead, it only checked if the return value was an Error. Fix it up and enable the regression tests. --- lightning/src/ln/peers/handler.rs | 70 +++++++++++++++++++++---------- 1 file changed, 47 insertions(+), 23 deletions(-) diff --git a/lightning/src/ln/peers/handler.rs b/lightning/src/ln/peers/handler.rs index 04def083327..005c2bc1b69 100644 --- a/lightning/src/ln/peers/handler.rs +++ b/lightning/src/ln/peers/handler.rs @@ -1136,7 +1136,23 @@ impl { log_trace!(self.logger, "Handling BroadcastChannelAnnouncement event in peer_handler for short channel id {}", msg.contents.short_channel_id); - if self.message_handler.route_handler.handle_channel_announcement(msg).is_ok() && self.message_handler.route_handler.handle_channel_update(update_msg).is_ok() { + let route_handler_wants_broadcast = match self.message_handler.route_handler.handle_channel_announcement(msg) { + Err(e) => { + log_trace!(self.logger, "Ignoring because handle_channel_announcement returned error: {:?}", e); + false + } + Ok(false) => false, + Ok(true) => { + match self.message_handler.route_handler.handle_channel_update(update_msg) { + Err(e) => { + log_trace!(self.logger, "Ignoring because handle_channel_update returned error: {:?}", e); + false + }, + Ok(result) => result + } + } + }; + if route_handler_wants_broadcast { for (descriptor, peer) in peers.initialized_peers_mut() { if !peer.should_forward_channel_announcement(msg.contents.short_channel_id) { continue @@ -1154,26 +1170,38 @@ impl { log_trace!(self.logger, "Handling BroadcastNodeAnnouncement event in peer_handler"); - if self.message_handler.route_handler.handle_node_announcement(msg).is_ok() { - for (descriptor, peer) in peers.initialized_peers_mut() { - if !peer.should_forward_node_announcement(msg.contents.node_id) { - continue + match self.message_handler.route_handler.handle_node_announcement(msg) { + Err(e) => { + log_trace!(self.logger, "Ignoring because handle_node_announcement returned error: {:?}", e); + }, + Ok(true) => { + for (descriptor, peer) in peers.initialized_peers_mut() { + if !peer.should_forward_node_announcement(msg.contents.node_id) { + continue + } + peer.transport.enqueue_message(msg, &mut peer.pending_outbound_buffer, &*self.logger); + self.do_attempt_write_data(&mut (*descriptor).clone(), &mut peer.post_init_state, &mut peer.transport, &mut peer.pending_outbound_buffer); } - peer.transport.enqueue_message(msg, &mut peer.pending_outbound_buffer, &*self.logger); - self.do_attempt_write_data(&mut (*descriptor).clone(), &mut peer.post_init_state, &mut peer.transport, &mut peer.pending_outbound_buffer); - } + }, + Ok(false) => { } } }, MessageSendEvent::BroadcastChannelUpdate { ref msg } => { log_trace!(self.logger, "Handling BroadcastChannelUpdate event in peer_handler for short channel id {}", msg.contents.short_channel_id); - if self.message_handler.route_handler.handle_channel_update(msg).is_ok() { - for (descriptor, peer) in peers.initialized_peers_mut() { - if !peer.should_forward_channel_announcement(msg.contents.short_channel_id) { - continue + match self.message_handler.route_handler.handle_channel_update(msg) { + Err(e) => { + log_trace!(self.logger, "Ignoring because handle_channel_update returned error: {:?}", e); + }, + Ok(true) => { + for (descriptor, peer) in peers.initialized_peers_mut() { + if !peer.should_forward_channel_announcement(msg.contents.short_channel_id) { + continue + } + peer.transport.enqueue_message(msg, &mut peer.pending_outbound_buffer, &*self.logger); + self.do_attempt_write_data(&mut (*descriptor).clone(), &mut peer.post_init_state, &mut peer.transport, &mut peer.pending_outbound_buffer); } - peer.transport.enqueue_message(msg, &mut peer.pending_outbound_buffer, &*self.logger); - self.do_attempt_write_data(&mut (*descriptor).clone(), &mut peer.post_init_state, &mut peer.transport, &mut peer.pending_outbound_buffer); - } + }, + Ok(false) => { } } }, MessageSendEvent::PaymentFailureNetworkUpdate { ref update } => { @@ -2468,7 +2496,6 @@ mod unit_tests { // Test that a post-Init connection: // * process_events() sends nothing when it receives a BroadcastChannelAnnouncement if the // route_handler.handle_channel_announcement returns false - // XXXBUG: Implementation does not check return value of handle_channel_announcement, only that it didn't error #[test] fn post_init_broadcast_channel_announcement_route_handler_handle_announcement_returns_false() { let channel_handler = TestChannelMessageHandler::new(); @@ -2485,13 +2512,12 @@ mod unit_tests { let peer_manager = new_peer_manager_post_init!(&test_ctx, &mut descriptor, &transport); peer_manager.process_events(); - // assert!(descriptor.get_recording().is_empty()); + assert!(descriptor.get_recording().is_empty()); } // Test that a post-Init connection: // * process_events() sends nothing when it receives a BroadcastChannelAnnouncement if the // route_handler.handle_channel_update returns false - // XXXBUG: Implementation does not check return value of handle_channel_update, only that it didn't error #[test] fn post_init_broadcast_channel_announcement_route_handle_update_returns_false() { let channel_handler = TestChannelMessageHandler::new(); @@ -2508,7 +2534,7 @@ mod unit_tests { let peer_manager = new_peer_manager_post_init!(&test_ctx, &mut descriptor, &transport); peer_manager.process_events(); - // assert!(descriptor.get_recording().is_empty()); + assert!(descriptor.get_recording().is_empty()); } // To reduce test expansion, the unconnected and connected transport tests are only run on one @@ -2673,7 +2699,6 @@ mod unit_tests { // Test that a post-Init connection: // * process_events() sends nothing when it receives a BroadcastNodeAnnouncement if the // route_handler.handle_node_announcement returns false - // XXXBUG: Implementation does not check return value of handle_node_announcement, only that it didn't error #[test] fn post_init_broadcast_node_announcement_route_handler_handle_announcement_returns_false() { let channel_handler = TestChannelMessageHandler::new(); @@ -2689,7 +2714,7 @@ mod unit_tests { let peer_manager = new_peer_manager_post_init!(&test_ctx, &mut descriptor, &transport); peer_manager.process_events(); - // assert!(descriptor.get_recording().is_empty()); + assert!(descriptor.get_recording().is_empty()); } // Test that a post-Init connection: @@ -2737,7 +2762,6 @@ mod unit_tests { // Test that a post-Init connection: // * process_events() sends nothing when it receives a BroadcastChannelUpdate if the // route_handler.handle_channel_update returns false - // XXXBUG: Implementation does not check return value of handle_node_announcement, only that it didn't error #[test] fn post_init_broadcast_channel_update_route_handler_handle_update_returns_false() { let channel_handler = TestChannelMessageHandler::new(); @@ -2753,7 +2777,7 @@ mod unit_tests { let peer_manager = new_peer_manager_post_init!(&test_ctx, &mut descriptor, &transport); peer_manager.process_events(); - // assert!(descriptor.get_recording().is_empty()); + assert!(descriptor.get_recording().is_empty()); } // Test that a post-Init connection: From e3161388bc1b8068f1874dae0608a214dabb6a8d Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Tue, 8 Sep 2020 11:30:46 -0700 Subject: [PATCH 66/72] fix: Use a separate lock for the ephemeral_key_midstate Use a separate lock to generate the SecretKey instead of overloading the PeerHolder mutex. Refactoring PeerManager removed this hidden constraint and this should make it more robust in the future. --- lightning/src/ln/peers/handler.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lightning/src/ln/peers/handler.rs b/lightning/src/ln/peers/handler.rs index 005c2bc1b69..c5905c2f1fe 100644 --- a/lightning/src/ln/peers/handler.rs +++ b/lightning/src/ln/peers/handler.rs @@ -359,7 +359,7 @@ struct PeerManagerImpl, peers: Mutex>, our_node_secret: SecretKey, - ephemeral_key_midstate: Sha256Engine, + ephemeral_key_midstate: Mutex, // Usize needs to be at least 32 bits to avoid overflowing both low and high. If usize is 64 // bits we will never realistically count into high: @@ -512,7 +512,7 @@ impl SecretKey { - let mut ephemeral_hash = self.ephemeral_key_midstate.clone(); + let mut ephemeral_hash = self.ephemeral_key_midstate.lock().unwrap().clone(); let low = self.peer_counter_low.fetch_add(1, Ordering::AcqRel); let high = if low == 0 { self.peer_counter_high.fetch_add(1, Ordering::AcqRel) From 8d5611c8f49bc554d20ca153b4861175a29017c0 Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Tue, 8 Sep 2020 13:56:20 -0700 Subject: [PATCH 67/72] rename: s/pending_outbound_buffer/outbound_queue/ s/fill_message_queue_with_sync/fill_outbound_queue_with_sync/ s/queue_init_message/enqueue_init_message/ --- lightning/src/ln/peers/handler.rs | 134 +++++++++++++++--------------- 1 file changed, 67 insertions(+), 67 deletions(-) diff --git a/lightning/src/ln/peers/handler.rs b/lightning/src/ln/peers/handler.rs index c5905c2f1fe..142db090859 100644 --- a/lightning/src/ln/peers/handler.rs +++ b/lightning/src/ln/peers/handler.rs @@ -189,7 +189,7 @@ impl PostInitState { struct Peer { outbound: bool, - pending_outbound_buffer: OutboundQueue, + outbound_queue: OutboundQueue, post_init_state: Option, transport: TransportImpl, } @@ -198,7 +198,7 @@ impl Peer { fn new(outbound: bool, transport: TransportImpl) -> Self { Self { outbound, - pending_outbound_buffer: OutboundQueue::new(MSG_BUFF_SIZE), + outbound_queue: OutboundQueue::new(MSG_BUFF_SIZE), post_init_state: None, transport } @@ -567,13 +567,13 @@ impl( + fn fill_outbound_queue_with_sync( &self, sync_status: &mut InitSyncTracker, message_queuer: &mut impl MessageQueuer, - pending_outbound_buffer: &mut Q) { + outbound_queue: &mut Q) { - let queue_space = pending_outbound_buffer.queue_space(); + let queue_space = outbound_queue.queue_space(); if queue_space > 0 { match sync_status { &mut InitSyncTracker::NoSyncRequested => {}, @@ -581,12 +581,12 @@ impl, message_queuer: &mut impl MessageQueuer, - pending_outbound_buffer: &mut Q) { + outbound_queue: &mut Q) { - while !pending_outbound_buffer.is_blocked() { + while !outbound_queue.is_blocked() { // If connected, fill output queue with sync messages match post_init_state { None => {}, - &mut Some(ref mut state) => self.fill_message_queue_with_sync(&mut state.sync_status, message_queuer, pending_outbound_buffer) + &mut Some(ref mut state) => self.fill_outbound_queue_with_sync(&mut state.sync_status, message_queuer, outbound_queue) } // No messages to send - if pending_outbound_buffer.is_empty() { + if outbound_queue.is_empty() { break; } - pending_outbound_buffer.try_flush_one(descriptor); + outbound_queue.try_flush_one(descriptor); } } @@ -649,8 +649,8 @@ impl panic!("Descriptor for write_event is not already known to PeerManager"), Some(peer) => { - peer.pending_outbound_buffer.unblock(); - self.do_attempt_write_data(descriptor, &mut peer.post_init_state, &mut peer.transport, &mut peer.pending_outbound_buffer); + peer.outbound_queue.unblock(); + self.do_attempt_write_data(descriptor, &mut peer.post_init_state, &mut peer.transport, &mut peer.outbound_queue); } }; Ok(()) @@ -716,14 +716,14 @@ impl, peers_needing_send: &mut HashSet) { + fn enqueue_init_message(&self, descriptor: &Descriptor, peer: &mut Peer, peers_needing_send: &mut HashSet) { let mut features = InitFeatures::known(); if !self.message_handler.route_handler.should_request_full_sync(&peer.transport.get_their_node_id()) { features.clear_initial_routing_sync(); } let resp = msgs::Init { features }; - self.enqueue_message(peers_needing_send, &mut peer.transport, &mut peer.pending_outbound_buffer, descriptor, &resp); + self.enqueue_message(peers_needing_send, &mut peer.transport, &mut peer.outbound_queue, descriptor, &resp); } // Process an incoming Init message and set Peer and PeerManager state accordingly @@ -745,7 +745,7 @@ impl, peers_needing_send: &mut HashSet, node_id_to_descriptor: &mut HashMap, data: &[u8]) -> Result { - match peer.transport.process_input(data, &mut peer.pending_outbound_buffer) { + match peer.transport.process_input(data, &mut peer.outbound_queue) { Err(e) => { log_trace!(self.logger, "Error while processing input: {}", e); return Err(PeerHandleError { no_connection_possible: false }) @@ -775,12 +775,12 @@ impl { log_trace!(self.logger, "Got Err handling message, sending Error message because {}", e.err); - self.enqueue_message(peers_needing_send, &mut peer.transport, &mut peer.pending_outbound_buffer, peer_descriptor, &msg); + self.enqueue_message(peers_needing_send, &mut peer.transport, &mut peer.outbound_queue, peer_descriptor, &msg); continue; }, } @@ -830,7 +830,7 @@ impl { if msg.ponglen < 65532 { let resp = msgs::Pong { byteslen: msg.ponglen }; - self.enqueue_message(peers_needing_send, &mut peer.transport, &mut peer.pending_outbound_buffer, &peer_descriptor, &resp); + self.enqueue_message(peers_needing_send, &mut peer.transport, &mut peer.outbound_queue, &peer_descriptor, &resp); } }, wire::Message::Pong(_msg) => { @@ -996,8 +996,8 @@ impl { } @@ -1197,8 +1197,8 @@ impl { } @@ -1217,11 +1217,11 @@ impl self.do_attempt_write_data(&mut descriptor, &mut peer.post_init_state, &mut peer.transport, &mut peer.pending_outbound_buffer), + Some(peer) => self.do_attempt_write_data(&mut descriptor, &mut peer.post_init_state, &mut peer.transport, &mut peer.outbound_queue), None => panic!("Inconsistent peers set state!"), } } @@ -1287,8 +1287,8 @@ impl Date: Thu, 10 Sep 2020 08:29:12 -0700 Subject: [PATCH 68/72] Remove peers_needing_send set Motivated by rust-bitcoin/rust-lightning#456, remove the peers_needing_send set in favor of just scanning the peers during process_events() and attempting to send data for those peers that have items in their outbound queue or pending sync items to be sent out. --- lightning/src/ln/peers/handler.rs | 76 ++++++++++++++----------------- 1 file changed, 33 insertions(+), 43 deletions(-) diff --git a/lightning/src/ln/peers/handler.rs b/lightning/src/ln/peers/handler.rs index 142db090859..0c584de989e 100644 --- a/lightning/src/ln/peers/handler.rs +++ b/lightning/src/ln/peers/handler.rs @@ -29,7 +29,7 @@ use util::events::{MessageSendEvent, MessageSendEventsProvider}; use util::logger::Logger; use routing::network_graph::NetGraphMsgHandler; -use std::collections::{HashMap,HashSet}; +use std::collections::HashMap; use std::sync::{Arc, Mutex}; use std::sync::atomic::{AtomicUsize, Ordering}; use std::{cmp,error,hash,fmt}; @@ -246,9 +246,6 @@ impl Peer { struct PeerHolder { peers: HashMap>, - /// Added to by do_read_event for cases where we pushed a message onto the send buffer but - /// didn't call do_attempt_write_data to avoid reentrancy. Cleared in process_events() - peers_needing_send: HashSet, /// Peers in this map have completed the NOISE handshake and received an Init message node_id_to_descriptor: HashMap, } @@ -290,9 +287,6 @@ impl PeerHolder Peer { - // may or may not be in this set depending on in-flight messages - self.peers_needing_send.remove(descriptor); - let peer_option = self.peers.remove(descriptor); match peer_option { None => panic!("Descriptor for disconnect_event is not already known to PeerManager"), @@ -309,6 +303,23 @@ impl PeerHolder(&'a mut self) -> Filter>, fn(&(&'a Descriptor, &'a mut Peer)) -> bool> { + self.peers.iter_mut().filter(|(_, peer)| { + let has_outbound_sync = match &peer.post_init_state { + None => false, + Some(post_init_state) => match &post_init_state.sync_status { + InitSyncTracker::NoSyncRequested => false, + InitSyncTracker::ChannelsSyncing(_) => true, + InitSyncTracker::NodesSyncing(_) => true, + } + }; + + has_outbound_sync || !peer.outbound_queue.is_empty() + }) + } } #[cfg(not(any(target_pointer_width = "32", target_pointer_width = "64")))] @@ -508,7 +519,6 @@ impl panic!("Descriptor for read_event is not already known to PeerManager"), Some(peer) => peer }; - self.do_read_event(peer_descriptor, peer, &mut peers.peers_needing_send, &mut peers.node_id_to_descriptor, data) + self.do_read_event(peer_descriptor, peer, &mut peers.node_id_to_descriptor, data) }; match result { @@ -675,12 +685,6 @@ impl(&self, peers_needing_send: &mut HashSet, message_queuer: &mut impl MessageQueuer, output_buffer: &mut impl PayloadQueuer, descriptor: &Descriptor, message: &M) { - message_queuer.enqueue_message(message, output_buffer, &*self.logger); - peers_needing_send.insert(descriptor.clone()); - } - // Returns a valid PostInitState given a Init message fn post_init_state_from_init_message(&self, init_message: &msgs::Init, their_node_id: &PublicKey) -> Result { if init_message.features.requires_unknown_bits() { @@ -716,18 +720,18 @@ impl, peers_needing_send: &mut HashSet) { + fn enqueue_init_message(&self, peer: &mut Peer) { let mut features = InitFeatures::known(); if !self.message_handler.route_handler.should_request_full_sync(&peer.transport.get_their_node_id()) { features.clear_initial_routing_sync(); } let resp = msgs::Init { features }; - self.enqueue_message(peers_needing_send, &mut peer.transport, &mut peer.outbound_queue, descriptor, &resp); + peer.transport.enqueue_message(&resp, &mut peer.outbound_queue, &*self.logger); } // Process an incoming Init message and set Peer and PeerManager state accordingly - fn process_init_message(&self, message: Message, descriptor: &Descriptor, peer: &mut Peer, peers_needing_send: &mut HashSet, node_id_to_descriptor: &mut HashMap) -> Result<(), PeerHandleError> { + fn process_init_message(&self, message: Message, descriptor: &Descriptor, peer: &mut Peer, node_id_to_descriptor: &mut HashMap) -> Result<(), PeerHandleError> { let their_node_id = peer.transport.get_their_node_id(); match message { @@ -740,13 +744,10 @@ impl, peers_needing_send: &mut HashSet, node_id_to_descriptor: &mut HashMap, data: &[u8]) -> Result { + fn do_read_event(&self, peer_descriptor: &mut Descriptor, peer: &mut Peer, node_id_to_descriptor: &mut HashMap, data: &[u8]) -> Result { match peer.transport.process_input(data, &mut peer.outbound_queue) { Err(e) => { @@ -775,13 +776,7 @@ impl 0 { let init_message = received_messages.remove(0); - self.process_init_message(init_message, peer_descriptor, peer, peers_needing_send, node_id_to_descriptor)?; + self.process_init_message(init_message, peer_descriptor, peer, node_id_to_descriptor)?; } for message in received_messages { @@ -811,7 +806,7 @@ impl { log_trace!(self.logger, "Got Err handling message, sending Error message because {}", e.err); - self.enqueue_message(peers_needing_send, &mut peer.transport, &mut peer.outbound_queue, peer_descriptor, &msg); + peer.transport.enqueue_message(&msg, &mut peer.outbound_queue, &*self.logger); continue; }, } @@ -820,7 +815,7 @@ impl { return Err(e) }, MessageHandlingError::LightningError(e) => { @@ -836,9 +831,7 @@ impl, - peers_needing_send: &mut HashSet) -> Result<(), MessageHandlingError> { + peer: &mut Peer) -> Result<(), MessageHandlingError> { let their_node_id = peer.transport.get_their_node_id(); let post_init_state = peer.post_init_state.as_mut().unwrap(); @@ -873,7 +866,7 @@ impl { if msg.ponglen < 65532 { let resp = msgs::Pong { byteslen: msg.ponglen }; - self.enqueue_message(peers_needing_send, &mut peer.transport, &mut peer.outbound_queue, &peer_descriptor, &resp); + peer.transport.enqueue_message(&resp, &mut peer.outbound_queue, &*self.logger); } }, wire::Message::Pong(_msg) => { @@ -1251,11 +1244,8 @@ impl self.do_attempt_write_data(&mut descriptor, &mut peer.post_init_state, &mut peer.transport, &mut peer.outbound_queue), - None => panic!("Inconsistent peers set state!"), - } + for (descriptor, peer) in peers.peers_needing_send() { + self.do_attempt_write_data(&mut descriptor.clone(), &mut peer.post_init_state, &mut peer.transport, &mut peer.outbound_queue); } } } From 4aababb5293c80d3d3d42a528fb895813dcb49b4 Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Wed, 16 Sep 2020 12:56:28 -0700 Subject: [PATCH 69/72] review: Small nits, documentation, and fixups Small review items that are noncontroversial and can be rolled into a single commit. --- lightning/src/ln/peers/handler.rs | 34 +++++++++++++++++++++-------- lightning/src/ln/peers/transport.rs | 12 ++++++---- 2 files changed, 33 insertions(+), 13 deletions(-) diff --git a/lightning/src/ln/peers/handler.rs b/lightning/src/ln/peers/handler.rs index 0c584de989e..438ec36aa24 100644 --- a/lightning/src/ln/peers/handler.rs +++ b/lightning/src/ln/peers/handler.rs @@ -43,7 +43,8 @@ use ln::peers::transport::{PayloadQueuer, Transport}; use std::collections::hash_map::IterMut; use std::iter::Filter; -const MSG_BUFF_SIZE: usize = 10; +// Number of items that can exist in the OutboundQueue before Sync message flow control is triggered +const OUTBOUND_QUEUE_SIZE: usize = 10; /// Interface PeerManager uses to interact with the Transport object pub(super) trait ITransport: MessageQueuer { @@ -56,7 +57,8 @@ pub(super) trait ITransport: MessageQueuer { /// Instantiate a new inbound Transport fn new_inbound(responder_static_private_key: &SecretKey, responder_ephemeral_private_key: &SecretKey) -> Self; - /// Process input data similar to reading it off a descriptor directly. + /// Process input data similar to reading it off a descriptor directly. Returns true on the first call + /// that results in the transport being newly connected. fn process_input(&mut self, input: &[u8], output_buffer: &mut impl PayloadQueuer) -> Result; /// Returns true if the connection is established and encrypted messages can be sent. @@ -65,19 +67,20 @@ pub(super) trait ITransport: MessageQueuer { /// Returns the node_id of the remote node. Panics if not connected. fn get_their_node_id(&self) -> PublicKey; - /// Returns all Messages that have been received and can be parsed by the Transport + /// Returns all Messages that have been received and can be successfully parsed by the Transport fn drain_messages(&mut self, logger: L) -> Result, PeerHandleError> where L::Target: Logger; } -/// Interface PeerManager uses to queue message to send. Used primarily to restrict the interface in -/// specific contexts. e.g. Only queueing during read_event(). No flushing allowed. +/// Interface PeerManager uses to queue message to send. Implemented by Transport to handle +/// encryption/decryption post-NOISE. pub(super) trait MessageQueuer { /// Encodes, encrypts, and enqueues a message to the outbound queue. Panics if the connection is /// not established yet. fn enqueue_message(&mut self, message: &M, output_buffer: &mut Q, logger: L) where L::Target: Logger; } -/// Trait representing a container that can try to flush data through a SocketDescriptor +/// Trait representing a container that can try to flush data through a SocketDescriptor. Used by the +/// PeerManager to handle flushing the outbound queue and flow control. pub(super) trait SocketDescriptorFlusher { /// Write previously enqueued data to the SocketDescriptor. A return of false indicates the /// underlying SocketDescriptor could not fulfill the send_data() call and the blocked state @@ -198,14 +201,14 @@ impl Peer { fn new(outbound: bool, transport: TransportImpl) -> Self { Self { outbound, - outbound_queue: OutboundQueue::new(MSG_BUFF_SIZE), + outbound_queue: OutboundQueue::new(OUTBOUND_QUEUE_SIZE), post_init_state: None, transport } } /// Returns true if an INIT message has been received from this peer. Implies that this node - /// can send and receive encrypted messages. + /// can send and receive encrypted messages (self.transport.is_connected() == true). fn is_initialized(&self) -> bool { self.post_init_state.is_some() } @@ -251,6 +254,9 @@ struct PeerHolder { } impl PeerHolder { + + // Returns an Option<(Descriptor, Peer)> for a node by node_id. A node is initialized after it + // has completed the NOISE handshake AND received an INIT message. fn initialized_peer_by_node_id_mut(&mut self, node_id: &PublicKey) -> Option<(Descriptor, &mut Peer)> { match self.node_id_to_descriptor.get_mut(node_id) { None => None, @@ -730,7 +736,17 @@ impl, node_id_to_descriptor: &mut HashMap) -> Result<(), PeerHandleError> { let their_node_id = peer.transport.get_their_node_id(); diff --git a/lightning/src/ln/peers/transport.rs b/lightning/src/ln/peers/transport.rs index f8829942fee..ccf309b2d18 100644 --- a/lightning/src/ln/peers/transport.rs +++ b/lightning/src/ln/peers/transport.rs @@ -37,7 +37,8 @@ pub trait IPeerHandshake { fn process_act(&mut self, input: &[u8]) -> Result<(Option>, Option<(Conduit, PublicKey)>), String>; } -/// Trait representing a container that allows enqueuing of Vec<[u8]> +/// Trait representing a container that allows enqueuing of Vec<[u8]>. Used by Transport to enqueue +/// NOISE handshake messages and post-NOISE encrypted Lightning Messages to be sent to a peer. pub(super) trait PayloadQueuer { /// Enqueue item to the queue fn push_back(&mut self, item: Vec); @@ -49,6 +50,8 @@ pub(super) trait PayloadQueuer { fn queue_space(&self) -> usize; } +/// Used by the PeerManager to work with a BOLT8 authenticated transport layer. Abstracts the NOISE +/// handshake as well as post-NOISE encrypted Lightning Message encryption/decryption. pub(super) struct Transport { pub(super) conduit: Option, handshake: PeerHandshakeImpl, @@ -76,6 +79,7 @@ impl ITransport for Transport Result { match self.conduit { // Continue handshake @@ -91,14 +95,14 @@ impl ITransport for Transport { conduit.read(input); - Ok(false) // newly connected + Ok(false) } } } From 93a70cefd15bc2bb1483ab479e6d9aa0992c3126 Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Wed, 16 Sep 2020 21:01:30 -0700 Subject: [PATCH 70/72] review: Catch duplicate connection after NOISE before INIT Catch a duplication connection from a peer with the same node_id at the completion of the NOISE handshake instead of the processing of the INIT message. --- lightning/src/ln/peers/handler.rs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/lightning/src/ln/peers/handler.rs b/lightning/src/ln/peers/handler.rs index 438ec36aa24..60e9b6bdb5d 100644 --- a/lightning/src/ln/peers/handler.rs +++ b/lightning/src/ln/peers/handler.rs @@ -740,9 +740,6 @@ impl { log_trace!(self.logger, "Received Init message from {}", log_pubkey!(&their_node_id)); - if node_id_to_descriptor.contains_key(&their_node_id) { - log_trace!(self.logger, "Got second connection with {}, closing", log_pubkey!(&their_node_id)); - return Err(PeerHandleError { no_connection_possible: false }); - } + let new_post_init_state = self.post_init_state_from_init_message(init_message, &their_node_id)?; @@ -788,7 +782,14 @@ impl { if newly_connected { - log_trace!(self.logger, "Finished noise handshake for connection with {}", log_pubkey!(&peer.transport.get_their_node_id())); + let their_node_id = peer.transport.get_their_node_id(); + log_trace!(self.logger, "Finished noise handshake for connection with {}", log_pubkey!(&their_node_id)); + + // Check for a duplicate connection at the completion of the NOISE handshake + if node_id_to_descriptor.contains_key(&their_node_id) { + log_trace!(self.logger, "Got second connection with {}, closing", log_pubkey!(&their_node_id)); + return Err(PeerHandleError { no_connection_possible: false }); + } } if newly_connected && peer.outbound { From e5268bb90d5ee67494b6797ca1717b285d4345b1 Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Wed, 16 Sep 2020 21:09:43 -0700 Subject: [PATCH 71/72] review: Remove legacy (local/global) feature flag notation * Remove local/global naming when processing INIT message contents * Remove duplicate requires_unknown_bits() check --- lightning/src/ln/peers/handler.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/lightning/src/ln/peers/handler.rs b/lightning/src/ln/peers/handler.rs index 60e9b6bdb5d..8356bcac1ac 100644 --- a/lightning/src/ln/peers/handler.rs +++ b/lightning/src/ln/peers/handler.rs @@ -694,16 +694,12 @@ impl Result { if init_message.features.requires_unknown_bits() { - log_info!(self.logger, "Peer global features required unknown version bits"); - return Err(PeerHandleError { no_connection_possible: true }.into()); - } - if init_message.features.requires_unknown_bits() { - log_info!(self.logger, "Peer local features required unknown version bits"); + log_info!(self.logger, "Peer features required unknown version bits"); return Err(PeerHandleError { no_connection_possible: true }.into()); } log_info!( - self.logger, "Received peer Init message: data_loss_protect: {}, initial_routing_sync: {}, upfront_shutdown_script: {}, static_remote_key: {}, unknown flags (local and global): {}", + self.logger, "Received peer Init message: data_loss_protect: {}, initial_routing_sync: {}, upfront_shutdown_script: {}, static_remote_key: {}, unknown flags: {}", if init_message.features.supports_data_loss_protect() { "supported" } else { "not supported"}, if init_message.features.initial_routing_sync() { "requested" } else { "not requested" }, if init_message.features.supports_upfront_shutdown_script() { "supported" } else { "not supported"}, From bf76e7cb1ef8f4074e13a60b1435ff1f2519ea79 Mon Sep 17 00:00:00 2001 From: Julian Knutsen Date: Fri, 2 Oct 2020 12:37:13 -0700 Subject: [PATCH 72/72] review: Collapse OutboundQueue/Transport traits Reduce trait complexity in the OutboundQueue and Transport objects. Splitting the objects into separate traits allowed for cleaner separation of responsibilities. But in practice, this led to complexity in the PeerManager function signatures and confusion in why they needed to be separated. To move this module to a more maintainable state for the core development team, collapse PayloadQueuer/SocketDescriptorFlusher into a single IOutboundQueue trait. Also, remove MessageQueuer in favor of just passing in the entire Transport object. This makes the code look more similar to the rest of the codebase while still leveraging traits in the OutboundQueue and Transport layer that allow for test doubles and real unit tests. --- lightning/src/ln/peers/handler.rs | 72 ++++++++++++++++-------- lightning/src/ln/peers/outbound_queue.rs | 36 +++++------- lightning/src/ln/peers/test_util.rs | 53 +++++++++-------- lightning/src/ln/peers/transport.rs | 51 ++++++----------- 4 files changed, 110 insertions(+), 102 deletions(-) diff --git a/lightning/src/ln/peers/handler.rs b/lightning/src/ln/peers/handler.rs index 8356bcac1ac..244b885ac86 100644 --- a/lightning/src/ln/peers/handler.rs +++ b/lightning/src/ln/peers/handler.rs @@ -39,7 +39,7 @@ use bitcoin::hashes::sha256::Hash as Sha256; use bitcoin::hashes::sha256::HashEngine as Sha256Engine; use bitcoin::hashes::{HashEngine, Hash}; use ln::peers::outbound_queue::OutboundQueue; -use ln::peers::transport::{PayloadQueuer, Transport}; +use ln::peers::transport::Transport; use std::collections::hash_map::IterMut; use std::iter::Filter; @@ -47,7 +47,7 @@ use std::iter::Filter; const OUTBOUND_QUEUE_SIZE: usize = 10; /// Interface PeerManager uses to interact with the Transport object -pub(super) trait ITransport: MessageQueuer { +pub(super) trait ITransport { /// Instantiate the new outbound Transport fn new_outbound(initiator_static_private_key: &SecretKey, responder_static_public_key: &PublicKey, initiator_ephemeral_private_key: &SecretKey) -> Self; @@ -59,7 +59,7 @@ pub(super) trait ITransport: MessageQueuer { /// Process input data similar to reading it off a descriptor directly. Returns true on the first call /// that results in the transport being newly connected. - fn process_input(&mut self, input: &[u8], output_buffer: &mut impl PayloadQueuer) -> Result; + fn process_input(&mut self, input: &[u8], outbound_queue: &mut impl IOutboundQueue) -> Result; /// Returns true if the connection is established and encrypted messages can be sent. fn is_connected(&self) -> bool; @@ -67,21 +67,43 @@ pub(super) trait ITransport: MessageQueuer { /// Returns the node_id of the remote node. Panics if not connected. fn get_their_node_id(&self) -> PublicKey; + /// Encodes, encrypts, and enqueues a message to the outbound queue. Panics if the connection is + /// not established yet. + fn enqueue_message(&mut self, message: &M, outbound_queue: &mut impl IOutboundQueue, logger: L) where L::Target: Logger; + /// Returns all Messages that have been received and can be successfully parsed by the Transport fn drain_messages(&mut self, logger: L) -> Result, PeerHandleError> where L::Target: Logger; } -/// Interface PeerManager uses to queue message to send. Implemented by Transport to handle -/// encryption/decryption post-NOISE. -pub(super) trait MessageQueuer { - /// Encodes, encrypts, and enqueues a message to the outbound queue. Panics if the connection is - /// not established yet. - fn enqueue_message(&mut self, message: &M, output_buffer: &mut Q, logger: L) where L::Target: Logger; -} +/// The OutboundQueue is a container for unencrypted payloads during the NOISE handshake and +/// encrypted Messages post-NOISE. This trait abstracts the behavior to push items to a queue, flush +/// them through a SocketDescriptor, and handle flow control. Each Peer owns a separate OutboundQueue. +/// +/// A trait is used to enable tests to use test doubles that implement a subset of the api with +/// cleaner test validation. +pub(super) trait IOutboundQueue { + + // ____ _ __ __ _ _ _ + // | _ \ _ _ ___| |__ | \/ | ___| |_| |__ ___ __| |___ + // | |_) | | | / __| '_ \ | |\/| |/ _ \ __| '_ \ / _ \ / _` / __| + // | __/| |_| \__ \ | | | | | | | __/ |_| | | | (_) | (_| \__ \ + // |_| \__,_|___/_| |_| |_| |_|\___|\__|_| |_|\___/ \__,_|___/ + + /// Unconditionally queue item. May increase queue above soft limit. + fn push_back(&mut self, item: Vec); + + /// Returns true if the queue is empty + fn is_empty(&self) -> bool; + + /// Returns the amount of free space in the queue before the soft limit + fn queue_space(&self) -> usize; + + // _____ _ _ __ __ _ _ _ + // | ___| |_ _ ___| |__ | \/ | ___| |_| |__ ___ __| |___ + // | |_ | | | | / __| '_ \ | |\/| |/ _ \ __| '_ \ / _ \ / _` / __| + // | _| | | |_| \__ \ | | | | | | | __/ |_| | | | (_) | (_| \__ \ + // |_| |_|\__,_|___/_| |_| |_| |_|\___|\__|_| |_|\___/ \__,_|___/ -/// Trait representing a container that can try to flush data through a SocketDescriptor. Used by the -/// PeerManager to handle flushing the outbound queue and flow control. -pub(super) trait SocketDescriptorFlusher { /// Write previously enqueued data to the SocketDescriptor. A return of false indicates the /// underlying SocketDescriptor could not fulfill the send_data() call and the blocked state /// has been set. Use unblock() when the SocketDescriptor may have more room. @@ -583,11 +605,11 @@ impl( + fn fill_outbound_queue_with_sync( &self, sync_status: &mut InitSyncTracker, - message_queuer: &mut impl MessageQueuer, - outbound_queue: &mut Q) { + transport: &mut TransportImpl, + outbound_queue: &mut OutboundQueue) { let queue_space = outbound_queue.queue_space(); if queue_space > 0 { @@ -597,12 +619,12 @@ impl( + fn do_attempt_write_data( &self, descriptor: &mut Descriptor, post_init_state: &mut Option, - message_queuer: &mut impl MessageQueuer, - outbound_queue: &mut Q) { + transport: &mut TransportImpl, + outbound_queue: &mut OutboundQueue) { while !outbound_queue.is_blocked() { // If connected, fill output queue with sync messages match post_init_state { None => {}, - &mut Some(ref mut state) => self.fill_outbound_queue_with_sync(&mut state.sync_status, message_queuer, outbound_queue) + &mut Some(ref mut state) => self.fill_outbound_queue_with_sync(&mut state.sync_status, transport, outbound_queue) } // No messages to send diff --git a/lightning/src/ln/peers/outbound_queue.rs b/lightning/src/ln/peers/outbound_queue.rs index 4ad8e8b3da9..ce6efc1c1e6 100644 --- a/lightning/src/ln/peers/outbound_queue.rs +++ b/lightning/src/ln/peers/outbound_queue.rs @@ -10,8 +10,7 @@ /// Abstracts the buffer used to write data through a SocketDescriptor handling partial writes and /// flow control. -use ln::peers::handler::{SocketDescriptor, SocketDescriptorFlusher}; -use ln::peers::transport::PayloadQueuer; +use ln::peers::handler::{IOutboundQueue, SocketDescriptor}; use std::collections::LinkedList; use std::cmp; @@ -22,24 +21,31 @@ pub(super) struct OutboundQueue { buffer_first_msg_offset: usize, } -impl PayloadQueuer for OutboundQueue { - /// Unconditionally queue item. May increase queue above soft limit. +impl OutboundQueue { + pub(super) fn new(soft_limit: usize) -> Self { + Self { + blocked: false, + soft_limit, + buffer: LinkedList::new(), + buffer_first_msg_offset: 0, + } + } +} + +impl IOutboundQueue for OutboundQueue { + fn push_back(&mut self, item: Vec) { self.buffer.push_back(item); } - /// Returns true if the queue is empty fn is_empty(&self) -> bool { self.buffer.is_empty() } - /// Returns the amount of free space in the queue before the soft limit fn queue_space(&self) -> usize { self.soft_limit - cmp::min(self.soft_limit, self.buffer.len()) } -} -impl SocketDescriptorFlusher for OutboundQueue { fn try_flush_one(&mut self, descriptor: &mut impl SocketDescriptor) -> bool { // Exit early if a previous full write failed and haven't heard that there may be more // room available @@ -77,20 +83,6 @@ impl SocketDescriptorFlusher for OutboundQueue { } } -impl OutboundQueue { - - /// Create a new writer with a soft limit that is used to notify the SocketDescriptor when - /// it is OK to resume reading if it was paused - pub(super) fn new(soft_limit: usize) -> Self { - Self { - blocked: false, - soft_limit, - buffer: LinkedList::new(), - buffer_first_msg_offset: 0, - } - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/lightning/src/ln/peers/test_util.rs b/lightning/src/ln/peers/test_util.rs index 3c07a7c85de..d79d1615f6b 100644 --- a/lightning/src/ln/peers/test_util.rs +++ b/lightning/src/ln/peers/test_util.rs @@ -13,8 +13,8 @@ use bitcoin::secp256k1; use bitcoin::secp256k1::key::{PublicKey, SecretKey}; use ln::peers::conduit::Conduit; -use ln::peers::handler::{SocketDescriptor, ITransport, PeerHandleError, MessageQueuer}; -use ln::peers::transport::{IPeerHandshake, PayloadQueuer}; +use ln::peers::handler::{SocketDescriptor, IOutboundQueue, ITransport, PeerHandleError}; +use ln::peers::transport::IPeerHandshake; use std::rc::Rc; use std::cell::{RefCell}; @@ -223,8 +223,9 @@ impl Hash for SocketDescriptorMock { } } -/// Implement PayloadQueuer for Vec> so it can be used as a Spy in tests -impl PayloadQueuer for Vec> { +/// Implement IOutboundQueue for Vec> so it can be used as a Spy in tests. This only implements +/// a subset of the push methods needed for the tests. +impl IOutboundQueue for Vec> { fn push_back(&mut self, item: Vec) { self.push(item) } @@ -236,6 +237,18 @@ impl PayloadQueuer for Vec> { fn queue_space(&self) -> usize { unimplemented!() } + + fn try_flush_one(&mut self, _descriptor: &mut impl SocketDescriptor) -> bool { + unimplemented!() + } + + fn unblock(&mut self) { + unimplemented!() + } + + fn is_blocked(&self) -> bool { + unimplemented!() + } } // Builder for TransportTestStub that allows tests to easily construct the Transport layer they @@ -296,8 +309,8 @@ impl<'a> ITransport for &'a RefCell { unimplemented!() } - fn process_input(&mut self, input: &[u8], output_buffer: &mut impl PayloadQueuer) -> Result { - self.borrow_mut().process_input(input, output_buffer) + fn process_input(&mut self, input: &[u8], outbound_queue: &mut impl IOutboundQueue) -> Result { + self.borrow_mut().process_input(input, outbound_queue) } fn is_connected(&self) -> bool { @@ -308,14 +321,12 @@ impl<'a> ITransport for &'a RefCell { self.borrow().get_their_node_id() } - fn drain_messages(&mut self, logger: L) -> Result, PeerHandleError> where L::Target: Logger { - self.borrow_mut().drain_messages(logger) + fn enqueue_message(&mut self, message: &M, outbound_queue: &mut impl IOutboundQueue, logger: L) where L::Target: Logger { + self.borrow_mut().enqueue_message(message, outbound_queue, logger) } -} -impl<'a> MessageQueuer for &'a RefCell { - fn enqueue_message(&mut self, message: &M, output_buffer: &mut Q, logger: L) where L::Target: Logger { - self.borrow_mut().enqueue_message(message, output_buffer, logger) + fn drain_messages(&mut self, logger: L) -> Result, PeerHandleError> where L::Target: Logger { + self.borrow_mut().drain_messages(logger) } } @@ -345,7 +356,7 @@ impl ITransport for TransportStub { unimplemented!() } - fn process_input(&mut self, _input: &[u8], _output_buffer: &mut impl PayloadQueuer) -> Result { + fn process_input(&mut self, _input: &[u8], _outbound_queue: &mut impl IOutboundQueue) -> Result { if self.process_returns_error { Err("Oh no!".to_string()) } else { @@ -369,16 +380,14 @@ impl ITransport for TransportStub { self.their_node_id.unwrap() } - fn drain_messages(&mut self, _logger: L) -> Result, PeerHandleError> where L::Target: Logger { - Ok(self.messages.drain(..).collect()) - } -} - -impl MessageQueuer for TransportStub { - fn enqueue_message(&mut self, message: &M, output_buffer: &mut Q, _logger: L) + fn enqueue_message(&mut self, message: &M, outbound_queue: &mut impl IOutboundQueue, _logger: L) where L::Target: Logger { let mut buffer = VecWriter(Vec::new()); wire::write(message, &mut buffer).unwrap(); - output_buffer.push_back(buffer.0); + outbound_queue.push_back(buffer.0); + } + + fn drain_messages(&mut self, _logger: L) -> Result, PeerHandleError> where L::Target: Logger { + Ok(self.messages.drain(..).collect()) } -} \ No newline at end of file +} diff --git a/lightning/src/ln/peers/transport.rs b/lightning/src/ln/peers/transport.rs index ccf309b2d18..b1d2878fc6e 100644 --- a/lightning/src/ln/peers/transport.rs +++ b/lightning/src/ln/peers/transport.rs @@ -12,7 +12,7 @@ use bitcoin::secp256k1::{SecretKey, PublicKey}; use ln::peers::conduit::Conduit; -use ln::peers::handler::{ITransport, PeerHandleError, MessageQueuer}; +use ln::peers::handler::{IOutboundQueue, ITransport, PeerHandleError}; use ln::peers::handshake::PeerHandshake; use ln::{wire, msgs}; use ln::wire::{Encode, Message}; @@ -37,19 +37,6 @@ pub trait IPeerHandshake { fn process_act(&mut self, input: &[u8]) -> Result<(Option>, Option<(Conduit, PublicKey)>), String>; } -/// Trait representing a container that allows enqueuing of Vec<[u8]>. Used by Transport to enqueue -/// NOISE handshake messages and post-NOISE encrypted Lightning Messages to be sent to a peer. -pub(super) trait PayloadQueuer { - /// Enqueue item to the queue - fn push_back(&mut self, item: Vec); - - /// Returns true if the queue is empty - fn is_empty(&self) -> bool; - - /// Returns the amount of available space in queue - fn queue_space(&self) -> usize; -} - /// Used by the PeerManager to work with a BOLT8 authenticated transport layer. Abstracts the NOISE /// handshake as well as post-NOISE encrypted Lightning Message encryption/decryption. pub(super) struct Transport { @@ -80,7 +67,7 @@ impl ITransport for Transport Result { + fn process_input(&mut self, input: &[u8], outbound_queue: &mut impl IOutboundQueue) -> Result { match self.conduit { // Continue handshake None => { @@ -88,7 +75,7 @@ impl ITransport for Transport ITransport for Transport(&mut self, message: &M, outbound_queue: &mut impl IOutboundQueue, logger: L) + where L::Target: Logger { + + match self.conduit { + None => panic!("Enqueueing messages only supported after transport is connected"), + Some(ref mut conduit) => { + log_trace!(logger, "Enqueueing message of type {} to {}", message.type_id(), log_pubkey!(self.their_node_id.unwrap())); + + let mut buffer = VecWriter(Vec::new()); + wire::write(message, &mut buffer).unwrap(); + outbound_queue.push_back(conduit.encrypt(&buffer.0)); + } + } + } + fn drain_messages(&mut self, logger: L) -> Result, PeerHandleError> where L::Target: Logger { @@ -179,23 +181,6 @@ impl ITransport for Transport MessageQueuer for Transport { - fn enqueue_message(&mut self, message: &M, output_buffer: &mut Q, logger: L) - where L::Target: Logger { - - match self.conduit { - None => panic!("Enqueueing messages only supported after transport is connected"), - Some(ref mut conduit) => { - log_trace!(logger, "Enqueueing message of type {} to {}", message.type_id(), log_pubkey!(self.their_node_id.unwrap())); - - let mut buffer = VecWriter(Vec::new()); - wire::write(message, &mut buffer).unwrap(); - output_buffer.push_back(conduit.encrypt(&buffer.0)); - } - } - } -} - #[cfg(test)] mod tests { use super::*;