diff --git a/nano/core_test/message.cpp b/nano/core_test/message.cpp index 15288e59..fcec2417 100644 --- a/nano/core_test/message.cpp +++ b/nano/core_test/message.cpp @@ -464,4 +464,173 @@ TEST (message, asc_pull_ack_serialization_account_info) ASSERT_EQ (original_payload.account_conf_height, message_payload.account_conf_height); ASSERT_TRUE (nano::at_end (stream)); +} + +TEST (message, node_id_handshake_query_serialization) +{ + nano::node_id_handshake::query_payload query{}; + query.cookie = 7; + nano::node_id_handshake original{ nano::dev::network_params.network, query }; + + // Serialize + std::vector bytes; + { + nano::vectorstream stream{ bytes }; + original.serialize (stream); + } + nano::bufferstream stream{ bytes.data (), bytes.size () }; + + // Header + bool error = false; + nano::message_header header (error, stream); + ASSERT_FALSE (error); + ASSERT_EQ (nano::message_type::node_id_handshake, header.type); + + // Message + nano::node_id_handshake message{ error, stream, header }; + ASSERT_FALSE (error); + ASSERT_TRUE (message.query); + ASSERT_FALSE (message.response); + + ASSERT_EQ (original.query->cookie, message.query->cookie); + + ASSERT_TRUE (nano::at_end (stream)); +} + +TEST (message, node_id_handshake_response_serialization) +{ + nano::node_id_handshake::response_payload response{}; + response.node_id = nano::account{ 7 }; + response.signature = nano::signature{ 11 }; + nano::node_id_handshake original{ nano::dev::network_params.network, std::nullopt, response }; + + // Serialize + std::vector bytes; + { + nano::vectorstream stream{ bytes }; + original.serialize (stream); + } + nano::bufferstream stream{ bytes.data (), bytes.size () }; + + // Header + bool error = false; + nano::message_header header (error, stream); + ASSERT_FALSE (error); + ASSERT_EQ (nano::message_type::node_id_handshake, header.type); + + // Message + nano::node_id_handshake message{ error, stream, header }; + ASSERT_FALSE (error); + ASSERT_FALSE (message.query); + ASSERT_TRUE (message.response); + ASSERT_FALSE (message.response->v2); + + ASSERT_EQ (original.response->node_id, message.response->node_id); + ASSERT_EQ (original.response->signature, message.response->signature); + + ASSERT_TRUE (nano::at_end (stream)); +} + +TEST (message, node_id_handshake_response_v2_serialization) +{ + nano::node_id_handshake::response_payload response{}; + response.node_id = nano::account{ 7 }; + response.signature = nano::signature{ 11 }; + nano::node_id_handshake::response_payload::v2_payload v2_pld{}; + v2_pld.salt = 17; + v2_pld.genesis = nano::block_hash{ 13 }; + response.v2 = v2_pld; + + nano::node_id_handshake original{ nano::dev::network_params.network, std::nullopt, response }; + + // Serialize + std::vector bytes; + { + nano::vectorstream stream{ bytes }; + original.serialize (stream); + } + nano::bufferstream stream{ bytes.data (), bytes.size () }; + + // Header + bool error = false; + nano::message_header header (error, stream); + ASSERT_FALSE (error); + ASSERT_EQ (nano::message_type::node_id_handshake, header.type); + + // Message + nano::node_id_handshake message{ error, stream, header }; + ASSERT_FALSE (error); + ASSERT_FALSE (message.query); + ASSERT_TRUE (message.response); + ASSERT_TRUE (message.response->v2); + + ASSERT_EQ (original.response->node_id, message.response->node_id); + ASSERT_EQ (original.response->signature, message.response->signature); + ASSERT_EQ (original.response->v2->salt, message.response->v2->salt); + ASSERT_EQ (original.response->v2->genesis, message.response->v2->genesis); + + ASSERT_TRUE (nano::at_end (stream)); +} + +TEST (handshake, signature) +{ + nano::keypair node_id{}; + nano::keypair node_id_2{}; + auto cookie = nano::random_pool::generate (); + auto cookie_2 = nano::random_pool::generate (); + + nano::node_id_handshake::response_payload response{}; + response.node_id = node_id.pub; + response.sign (cookie, node_id); + ASSERT_TRUE (response.validate (cookie)); + + // Invalid cookie + ASSERT_FALSE (response.validate (cookie_2)); + + // Invalid node id + response.node_id = node_id_2.pub; + ASSERT_FALSE (response.validate (cookie)); +} + +TEST (handshake, signature_v2) +{ + nano::keypair node_id{}; + nano::keypair node_id_2{}; + auto cookie = nano::random_pool::generate (); + auto cookie_2 = nano::random_pool::generate (); + + nano::node_id_handshake::response_payload original{}; + original.node_id = node_id.pub; + original.v2 = nano::node_id_handshake::response_payload::v2_payload{}; + original.v2->genesis = nano::test::random_hash (); + original.v2->salt = nano::random_pool::generate (); + original.sign (cookie, node_id); + ASSERT_TRUE (original.validate (cookie)); + + // Invalid cookie + ASSERT_FALSE (original.validate (cookie_2)); + + // Invalid node id + { + auto message = original; + ASSERT_TRUE (message.validate (cookie)); + message.node_id = node_id_2.pub; + ASSERT_FALSE (message.validate (cookie)); + } + + // Invalid genesis + { + auto message = original; + ASSERT_TRUE (message.validate (cookie)); + message.v2->genesis = nano::test::random_hash (); + ASSERT_FALSE (message.validate (cookie)); + } + + // Invalid salt + { + auto message = original; + ASSERT_TRUE (message.validate (cookie)); + message.v2->salt = nano::random_pool::generate (); + ASSERT_FALSE (message.validate (cookie)); + } } \ No newline at end of file diff --git a/nano/core_test/telemetry.cpp b/nano/core_test/telemetry.cpp index 1a19a443..aca0b169 100644 --- a/nano/core_test/telemetry.cpp +++ b/nano/core_test/telemetry.cpp @@ -509,7 +509,8 @@ TEST (telemetry, ongoing_broadcasts) ASSERT_TIMELY (5s, node2.stats.count (nano::stat::type::telemetry, nano::stat::detail::process) >= 3) } -TEST (telemetry, mismatched_genesis) +// TODO: With handshake V2, nodes with mismatched genesis will refuse to connect while setting up the system +TEST (telemetry, DISABLED_mismatched_genesis) { // Only second node will broadcast telemetry nano::test::system system; diff --git a/nano/crypto_lib/random_pool.hpp b/nano/crypto_lib/random_pool.hpp index e3b8b6fe..b2b3f347 100644 --- a/nano/crypto_lib/random_pool.hpp +++ b/nano/crypto_lib/random_pool.hpp @@ -19,6 +19,22 @@ public: static uint64_t generate_word64 (uint64_t min, uint64_t max); static unsigned char generate_byte (); + /** Fills variable with random data */ + template + static void generate (T & out) + { + generate_block (reinterpret_cast (&out), sizeof (T)); + } + /** Returns variable with random data */ + template + static T generate () + { + T t; + generate (t); + return t; + } + +public: random_pool () = delete; random_pool (random_pool const &) = delete; random_pool & operator= (random_pool const &) = delete; diff --git a/nano/lib/stats_enums.hpp b/nano/lib/stats_enums.hpp index 34da901a..f69f1b21 100644 --- a/nano/lib/stats_enums.hpp +++ b/nano/lib/stats_enums.hpp @@ -43,6 +43,7 @@ enum class type : uint8_t unchecked, election_scheduler, optimistic_scheduler, + handshake, _last // Must be the last enum }; @@ -53,6 +54,7 @@ enum class detail : uint8_t all = 0, // common + ok, loop, total, process, @@ -273,6 +275,11 @@ enum class detail : uint8_t insert_priority_success, erase_oldest, + // handshake + invalid_node_id, + missing_cookie, + invalid_genesis, + _last // Must be the last enum }; diff --git a/nano/node/messages.cpp b/nano/node/messages.cpp index 42b1bbaf..d98c02f6 100644 --- a/nano/node/messages.cpp +++ b/nano/node/messages.cpp @@ -203,11 +203,11 @@ void nano::message_header::count_set (uint8_t count_a) extensions |= std::bitset<16> (static_cast (count_a) << 12); } -void nano::message_header::flag_set (uint8_t flag_a) +void nano::message_header::flag_set (uint8_t flag_a, bool enable) { // Flags from 8 are block_type & count debug_assert (flag_a < 8); - extensions.set (flag_a, true); + extensions.set (flag_a, enable); } bool nano::message_header::bulk_pull_is_count_present () const @@ -1327,10 +1327,12 @@ nano::node_id_handshake::node_id_handshake (nano::network_constants const & cons if (query) { header.flag_set (query_flag); + header.flag_set (v2_flag); // Always indicate support for V2 handshake when querying, old peers will just ignore it } if (response) { header.flag_set (response_flag); + header.flag_set (v2_flag, response->v2.has_value ()); // We only use V2 handshake when replying to peers that indicated support for it } } @@ -1363,7 +1365,7 @@ bool nano::node_id_handshake::deserialize (nano::stream & stream) if (is_response (header)) { response_payload pld{}; - pld.deserialize (stream); + pld.deserialize (stream, header); response = pld; } } @@ -1388,6 +1390,18 @@ bool nano::node_id_handshake::is_response (nano::message_header const & header) return result; } +bool nano::node_id_handshake::is_v2 (nano::message_header const & header) +{ + debug_assert (header.type == nano::message_type::node_id_handshake); + bool result = header.extensions.test (v2_flag); + return result; +} + +bool nano::node_id_handshake::is_v2 () const +{ + return is_v2 (header); +} + void nano::node_id_handshake::visit (nano::message_visitor & visitor_a) const { visitor_a.node_id_handshake (*this); @@ -1407,7 +1421,7 @@ std::size_t nano::node_id_handshake::size (nano::message_header const & header) } if (is_response (header)) { - result += response_payload::size; + result += response_payload::size (header); } return result; } @@ -1447,14 +1461,81 @@ void nano::node_id_handshake::query_payload::deserialize (nano::stream & stream) void nano::node_id_handshake::response_payload::serialize (nano::stream & stream) const { - nano::write (stream, node_id); - nano::write (stream, signature); + if (v2) + { + nano::write (stream, node_id); + nano::write (stream, v2->salt); + nano::write (stream, v2->genesis); + nano::write (stream, signature); + } + // TODO: Remove legacy handshake + else + { + nano::write (stream, node_id); + nano::write (stream, signature); + } } -void nano::node_id_handshake::response_payload::deserialize (nano::stream & stream) +void nano::node_id_handshake::response_payload::deserialize (nano::stream & stream, nano::message_header const & header) { - nano::read (stream, node_id); - nano::read (stream, signature); + if (is_v2 (header)) + { + nano::read (stream, node_id); + v2_payload pld{}; + nano::read (stream, pld.salt); + nano::read (stream, pld.genesis); + v2 = pld; + nano::read (stream, signature); + } + else + { + nano::read (stream, node_id); + nano::read (stream, signature); + } +} + +std::size_t nano::node_id_handshake::response_payload::size (const nano::message_header & header) +{ + return is_v2 (header) ? size_v2 : size_v1; +} + +std::vector nano::node_id_handshake::response_payload::data_to_sign (const nano::uint256_union & cookie) const +{ + std::vector bytes; + { + nano::vectorstream stream{ bytes }; + + if (v2) + { + nano::write (stream, cookie); + nano::write (stream, v2->salt); + nano::write (stream, v2->genesis); + } + // TODO: Remove legacy handshake + else + { + nano::write (stream, cookie); + } + } + return bytes; +} + +void nano::node_id_handshake::response_payload::sign (const nano::uint256_union & cookie, nano::keypair const & key) +{ + debug_assert (key.pub == node_id); + auto data = data_to_sign (cookie); + signature = nano::sign_message (key.prv, key.pub, data.data (), data.size ()); + debug_assert (validate (cookie)); +} + +bool nano::node_id_handshake::response_payload::validate (const nano::uint256_union & cookie) const +{ + auto data = data_to_sign (cookie); + if (nano::validate_message (node_id, data.data (), data.size (), signature)) // true => error + { + return false; // Fail + } + return true; // OK } /* @@ -1776,4 +1857,4 @@ void nano::asc_pull_ack::account_info_payload::deserialize (nano::stream & strea nano::read_big_endian (stream, account_block_count); nano::read (stream, account_conf_frontier); nano::read_big_endian (stream, account_conf_height); -} \ No newline at end of file +} diff --git a/nano/node/messages.hpp b/nano/node/messages.hpp index 8ff4313c..976ba2b5 100644 --- a/nano/node/messages.hpp +++ b/nano/node/messages.hpp @@ -79,7 +79,7 @@ public: std::bitset<16> extensions; static std::size_t constexpr size = sizeof (nano::networks) + sizeof (version_max) + sizeof (version_using) + sizeof (version_min) + sizeof (type) + sizeof (/* extensions */ uint16_t); - void flag_set (uint8_t); + void flag_set (uint8_t, bool enable = true); static uint8_t constexpr bulk_pull_count_present_flag = 0; static uint8_t constexpr bulk_pull_ascending_flag = 1; bool bulk_pull_is_count_present () const; @@ -326,13 +326,30 @@ public: // Payload definitions { public: void serialize (nano::stream &) const; - void deserialize (nano::stream &); + void deserialize (nano::stream &, nano::message_header const &); - static std::size_t constexpr size = sizeof (nano::account) + sizeof (nano::signature); + void sign (nano::uint256_union const & cookie, nano::keypair const &); + bool validate (nano::uint256_union const & cookie) const; + + private: + std::vector data_to_sign (nano::uint256_union const & cookie) const; + + public: + struct v2_payload + { + nano::uint256_union salt; + nano::block_hash genesis; + }; public: nano::account node_id; nano::signature signature; + std::optional v2; + + public: + static std::size_t constexpr size_v1 = sizeof (nano::account) + sizeof (nano::signature); + static std::size_t constexpr size_v2 = sizeof (nano::account) + sizeof (nano::signature) + sizeof (v2_payload); + static std::size_t size (nano::message_header const &); }; public: @@ -350,9 +367,12 @@ public: public: // Header static uint8_t constexpr query_flag = 0; static uint8_t constexpr response_flag = 1; + static uint8_t constexpr v2_flag = 2; static bool is_query (nano::message_header const &); static bool is_response (nano::message_header const &); + static bool is_v2 (nano::message_header const &); + bool is_v2 () const; public: // Payload std::optional query; diff --git a/nano/node/network.cpp b/nano/node/network.cpp index f67ed7f5..0440d68a 100644 --- a/nano/node/network.cpp +++ b/nano/node/network.cpp @@ -759,6 +759,68 @@ void nano::network::exclude (std::shared_ptr const & c erase (*channel); } +bool nano::network::verify_handshake_response (const nano::node_id_handshake::response_payload & response, const nano::endpoint & remote_endpoint) +{ + // Prevent connection with ourselves + if (response.node_id == node.node_id.pub) + { + node.stats.inc (nano::stat::type::handshake, nano::stat::detail::invalid_node_id); + return false; // Fail + } + + // Prevent mismatched genesis + if (response.v2 && response.v2->genesis != node.network_params.ledger.genesis->hash ()) + { + node.stats.inc (nano::stat::type::handshake, nano::stat::detail::invalid_genesis); + return false; // Fail + } + + auto cookie = syn_cookies.cookie (remote_endpoint); + if (!cookie) + { + node.stats.inc (nano::stat::type::handshake, nano::stat::detail::missing_cookie); + return false; // Fail + } + + if (!response.validate (*cookie)) + { + node.stats.inc (nano::stat::type::handshake, nano::stat::detail::invalid_signature); + return false; // Fail + } + + node.stats.inc (nano::stat::type::handshake, nano::stat::detail::ok); + return true; // OK +} + +std::optional nano::network::prepare_handshake_query (const nano::endpoint & remote_endpoint) +{ + if (auto cookie = syn_cookies.assign (remote_endpoint); cookie) + { + nano::node_id_handshake::query_payload query{ *cookie }; + return query; + } + return std::nullopt; +} + +nano::node_id_handshake::response_payload nano::network::prepare_handshake_response (const nano::node_id_handshake::query_payload & query, bool v2) const +{ + nano::node_id_handshake::response_payload response{}; + response.node_id = node.node_id.pub; + if (v2) + { + nano::node_id_handshake::response_payload::v2_payload response_v2{}; + response_v2.salt = nano::random_pool::generate (); + response_v2.genesis = node.network_params.ledger.genesis->hash (); + response.v2 = response_v2; + } + response.sign (query.cookie, node.node_id); + return response; +} + +/* + * tcp_message_manager + */ + nano::tcp_message_manager::tcp_message_manager (unsigned incoming_connections_max_a) : max_entries (incoming_connections_max_a * nano::tcp_message_manager::max_entries_per_connection + 1) { @@ -810,6 +872,10 @@ void nano::tcp_message_manager::stop () producer_condition.notify_all (); } +/* + * syn_cookies + */ + nano::syn_cookies::syn_cookies (std::size_t max_cookies_per_ip_a) : max_cookies_per_ip (max_cookies_per_ip_a) { @@ -888,6 +954,30 @@ void nano::syn_cookies::purge (std::chrono::steady_clock::time_point const & cut } } +std::optional nano::syn_cookies::cookie (const nano::endpoint & endpoint_a) +{ + auto ip_addr (endpoint_a.address ()); + debug_assert (ip_addr.is_v6 ()); + nano::lock_guard lock{ syn_cookie_mutex }; + auto cookie_it (cookies.find (endpoint_a)); + if (cookie_it != cookies.end ()) + { + auto cookie = cookie_it->second.cookie; + cookies.erase (cookie_it); + unsigned & ip_cookies = cookies_per_ip[ip_addr]; + if (ip_cookies > 0) + { + --ip_cookies; + } + else + { + debug_assert (false && "More SYN cookies deleted than created for IP"); + } + return cookie; + } + return std::nullopt; +} + std::size_t nano::syn_cookies::cookies_size () { nano::lock_guard lock{ syn_cookie_mutex }; diff --git a/nano/node/network.hpp b/nano/node/network.hpp index cb103593..35940208 100644 --- a/nano/node/network.hpp +++ b/nano/node/network.hpp @@ -14,6 +14,7 @@ namespace nano { class node; + class tcp_message_manager final { public: @@ -34,13 +35,15 @@ private: friend class network_tcp_message_manager_Test; }; + /** * Node ID cookies for node ID handshakes */ class syn_cookies final { public: - syn_cookies (std::size_t); + explicit syn_cookies (std::size_t); + void purge (std::chrono::steady_clock::time_point const &); // Returns boost::none if the IP is rate capped on syn cookie requests, // or if the endpoint already has a syn cookie query @@ -48,6 +51,9 @@ public: // Returns false if valid, true if invalid (true on error convention) // Also removes the syn cookie from the store if valid bool validate (nano::endpoint const &, nano::account const &, nano::signature const &); + /** Get cookie associated with endpoint and erases that cookie from this container */ + std::optional cookie (nano::endpoint const &); + std::unique_ptr collect_container_info (std::string const &); std::size_t cookies_size (); @@ -122,6 +128,11 @@ public: /** Disconnects and adds peer to exclusion list */ void exclude (std::shared_ptr const & channel); + /** Verifies that handshake response matches our query. @returns true if OK */ + bool verify_handshake_response (nano::node_id_handshake::response_payload const & response, nano::endpoint const & remote_endpoint); + std::optional prepare_handshake_query (nano::endpoint const & remote_endpoint); + nano::node_id_handshake::response_payload prepare_handshake_response (nano::node_id_handshake::query_payload const & query, bool v2) const; + static std::string to_string (nano::networks); private: diff --git a/nano/node/transport/socket.hpp b/nano/node/transport/socket.hpp index bbdcb4fe..2a5520d6 100644 --- a/nano/node/transport/socket.hpp +++ b/nano/node/transport/socket.hpp @@ -40,6 +40,7 @@ class socket : public std::enable_shared_from_this { friend class server_socket; friend class tcp_server; + friend class tcp_channels; public: enum class type_t diff --git a/nano/node/transport/tcp.cpp b/nano/node/transport/tcp.cpp index 2adc5fa7..ae4d6f38 100644 --- a/nano/node/transport/tcp.cpp +++ b/nano/node/transport/tcp.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include @@ -544,14 +545,7 @@ void nano::transport::tcp_channels::start_tcp (nano::endpoint const & endpoint_a if (!ec && channel) { // TCP node ID handshake - - std::optional query; - if (auto cookie = node_l->network.syn_cookies.assign (endpoint_a); cookie) - { - nano::node_id_handshake::query_payload pld{ *cookie }; - query = pld; - } - + auto query = node_l->network.prepare_handshake_query (endpoint_a); nano::node_id_handshake message{ node_l->network_params.network, query }; if (node_l->config.logging.network_node_id_handshake_logging ()) @@ -619,7 +613,12 @@ void nano::transport::tcp_channels::start_tcp_receive_node_id (std::shared_ptrasync_read (receive_buffer_a, 8 + sizeof (nano::account) + sizeof (nano::account) + sizeof (nano::signature), [node_w, channel_a, endpoint_a, receive_buffer_a, cleanup_node_id_handshake_socket] (boost::system::error_code const & ec, std::size_t size_a) { + auto message_deserializer = std::make_shared (node.network_params.network, node.network.publish_filter, node.block_uniquer, node.vote_uniquer, + [socket_l] (std::shared_ptr> const & data_a, size_t size_a, std::function callback_a) { + debug_assert (socket_l != nullptr); + socket_l->read_impl (data_a, size_a, callback_a); + }); + message_deserializer->read ([node_w, socket_l, channel_a, endpoint_a, cleanup_node_id_handshake_socket] (boost::system::error_code ec, std::unique_ptr message) { auto node_l = node_w.lock (); if (!node_l) { @@ -636,10 +635,9 @@ void nano::transport::tcp_channels::start_tcp_receive_node_id (std::shared_ptrstats.inc (nano::stat::type::message, nano::stat::detail::node_id_handshake, nano::stat::dir::in); auto error (false); - nano::bufferstream stream (receive_buffer_a->data (), size_a); - nano::message_header header (error, stream); + // the header type should in principle be checked after checking the network bytes and the version numbers, I will not change it here since the benefits do not outweight the difficulties - if (error || header.type != nano::message_type::node_id_handshake) + if (error || message->type () != nano::message_type::node_id_handshake) { if (node_l->config.logging.network_node_id_handshake_logging ()) { @@ -648,10 +646,12 @@ void nano::transport::tcp_channels::start_tcp_receive_node_id (std::shared_ptrnetwork_params.network.current_network || header.version_using < node_l->network_params.network.protocol_version_min) + auto & handshake = static_cast (*message); + + if (message->header.network != node_l->network_params.network.current_network || message->header.version_using < node_l->network_params.network.protocol_version_min) { // error handling, either the networks bytes or the version is wrong - if (header.network == node_l->network_params.network.current_network) + if (message->header.network == node_l->network_params.network.current_network) { node_l->stats.inc (nano::stat::type::message, nano::stat::detail::invalid_network); } @@ -668,8 +668,8 @@ void nano::transport::tcp_channels::start_tcp_receive_node_id (std::shared_ptrconfig.logging.network_node_id_handshake_logging ()) { @@ -678,15 +678,16 @@ void nano::transport::tcp_channels::start_tcp_receive_node_id (std::shared_ptrset_network_version (header.version_using); + channel_a->set_network_version (handshake.header.version_using); - debug_assert (message.query); - debug_assert (message.response); + debug_assert (handshake.query); + debug_assert (handshake.response); - auto node_id = message.response->node_id; - bool process = (!node_l->network.syn_cookies.validate (endpoint_a, node_id, message.response->signature) && node_id != node_l->node_id.pub); - if (!process) + auto const node_id = handshake.response->node_id; + + if (!node_l->network.verify_handshake_response (*handshake.response, endpoint_a)) { + cleanup_node_id_handshake_socket (endpoint_a); return; } @@ -695,18 +696,20 @@ void nano::transport::tcp_channels::start_tcp_receive_node_id (std::shared_ptrnetwork.tcp_channels.find_node_id (node_id)); if (existing_channel && !existing_channel->temporary) { + cleanup_node_id_handshake_socket (endpoint_a); return; } channel_a->set_node_id (node_id); channel_a->set_last_packet_received (std::chrono::steady_clock::now ()); - nano::node_id_handshake::response_payload response{ node_l->node_id.pub, nano::sign_message (node_l->node_id.prv, node_l->node_id.pub, message.query->cookie) }; + debug_assert (handshake.query); + auto response = node_l->network.prepare_handshake_response (*handshake.query, handshake.is_v2 ()); nano::node_id_handshake handshake_response (node_l->network_params.network, std::nullopt, response); if (node_l->config.logging.network_node_id_handshake_logging ()) { - node_l->logger.try_log (boost::str (boost::format ("Node ID handshake response sent with node ID %1% to %2%: query %3%") % node_l->node_id.pub.to_node_id () % endpoint_a % message.query->cookie.to_string ())); + node_l->logger.try_log (boost::str (boost::format ("Node ID handshake response sent with node ID %1% to %2%: query %3%") % node_l->node_id.pub.to_node_id () % endpoint_a % handshake.query->cookie.to_string ())); } channel_a->send (handshake_response, [node_w, channel_a, endpoint_a, cleanup_node_id_handshake_socket] (boost::system::error_code const & ec, std::size_t size_a) { diff --git a/nano/node/transport/tcp_server.cpp b/nano/node/transport/tcp_server.cpp index 89fe25a3..9c5ec31a 100644 --- a/nano/node/transport/tcp_server.cpp +++ b/nano/node/transport/tcp_server.cpp @@ -10,6 +10,10 @@ #include +/* + * tcp_listener + */ + nano::transport::tcp_listener::tcp_listener (uint16_t port_a, nano::node & node_a) : node (node_a), port (port_a) @@ -123,6 +127,10 @@ std::unique_ptr nano::transport::collect_contain return composite; } +/* + * tcp_server + */ + nano::transport::tcp_server::tcp_server (std::shared_ptr socket_a, std::shared_ptr node_a, bool allow_bootstrap_a) : socket{ std::move (socket_a) }, node{ std::move (node_a) }, @@ -315,6 +323,7 @@ void nano::transport::tcp_server::handshake_message_visitor::node_id_handshake ( { server->node->logger.try_log (boost::str (boost::format ("Disabled realtime TCP for handshake %1%") % server->remote_endpoint)); } + // Stop invalid handshake server->stop (); return; } @@ -325,6 +334,7 @@ void nano::transport::tcp_server::handshake_message_visitor::node_id_handshake ( { server->node->logger.try_log (boost::str (boost::format ("Detected multiple node_id_handshake query from %1%") % server->remote_endpoint)); } + // Stop invalid handshake server->stop (); return; } @@ -338,37 +348,29 @@ void nano::transport::tcp_server::handshake_message_visitor::node_id_handshake ( if (message.query) { - server->send_handshake_response (*message.query); + server->send_handshake_response (*message.query, message.is_v2 ()); } if (message.response) { - nano::account const & node_id = message.response->node_id; - if (!server->node->network.syn_cookies.validate (nano::transport::map_tcp_to_endpoint (server->remote_endpoint), node_id, message.response->signature) && node_id != server->node->node_id.pub) + if (server->node->network.verify_handshake_response (*message.response, nano::transport::map_tcp_to_endpoint (server->remote_endpoint))) { - server->to_realtime_connection (node_id); + server->to_realtime_connection (message.response->node_id); } else { // Stop invalid handshake server->stop (); + return; } } process = true; } -void nano::transport::tcp_server::send_handshake_response (nano::node_id_handshake::query_payload const & query) +void nano::transport::tcp_server::send_handshake_response (nano::node_id_handshake::query_payload const & query, bool v2) { - nano::node_id_handshake::response_payload response{ node->node_id.pub, nano::sign_message (node->node_id.prv, node->node_id.pub, query.cookie) }; - debug_assert (!nano::validate_message (response.node_id, query.cookie, response.signature)); - - std::optional own_query; - if (auto own_cookie = node->network.syn_cookies.assign (nano::transport::map_tcp_to_endpoint (remote_endpoint)); own_cookie) - { - nano::node_id_handshake::query_payload pld{ *own_cookie }; - own_query = pld; - } - + auto response = node->network.prepare_handshake_response (query, v2); + auto own_query = node->network.prepare_handshake_query (nano::transport::map_tcp_to_endpoint (remote_endpoint)); nano::node_id_handshake handshake_response{ node->network_params.network, own_query, response }; // TODO: Use channel diff --git a/nano/node/transport/tcp_server.hpp b/nano/node/transport/tcp_server.hpp index f7cebd7e..674e50e3 100644 --- a/nano/node/transport/tcp_server.hpp +++ b/nano/node/transport/tcp_server.hpp @@ -63,7 +63,7 @@ public: std::chrono::steady_clock::time_point last_telemetry_req{}; private: - void send_handshake_response (nano::node_id_handshake::query_payload const & query); + void send_handshake_response (nano::node_id_handshake::query_payload const & query, bool v2); void receive_message (); void received_message (std::unique_ptr message);