Remove telemetry signature partitions (#3014)

* Remove telemetry signature partitions

* Doesn't like [[maybe_unused]] used here for some reason.

* Add test for max size with unknown_data

* Telemetry mask off by 1 in debug_assert check
This commit is contained in:
Wesley Shillingford 2021-01-07 17:49:47 +00:00 committed by GitHub
commit 053c4ffda7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 85 additions and 22 deletions

View file

@ -193,7 +193,7 @@ TEST (telemetry, signatures)
data.maker = 1;
data.timestamp = std::chrono::system_clock::time_point (100ms);
data.sign (node_id);
ASSERT_FALSE (data.validate_signature (nano::telemetry_data::size));
ASSERT_FALSE (data.validate_signature ());
auto signature = data.signature;
// Check that the signature is different if changing a piece of data
data.maker = 2;
@ -201,6 +201,22 @@ TEST (telemetry, signatures)
ASSERT_NE (data.signature, signature);
}
TEST (telemetry, unknown_data)
{
nano::keypair node_id;
nano::telemetry_data data;
data.node_id = node_id.pub;
data.major_version = 20;
data.minor_version = 1;
data.patch_version = 5;
data.pre_release_version = 2;
data.maker = 1;
data.timestamp = std::chrono::system_clock::time_point (100ms);
data.unknown_data.push_back (1);
data.sign (node_id);
ASSERT_FALSE (data.validate_signature ());
}
TEST (telemetry, no_peers)
{
nano::system system (1);
@ -504,6 +520,29 @@ TEST (telemetry, disable_metrics)
ASSERT_TIMELY (10s, done);
}
TEST (telemetry, max_possible_size)
{
nano::system system;
nano::node_flags node_flags;
node_flags.disable_initial_telemetry_requests = true;
node_flags.disable_ongoing_telemetry_requests = true;
auto node_client = system.add_node (node_flags);
auto node_server = system.add_node (node_flags);
nano::telemetry_data data;
data.unknown_data.resize (nano::message_header::telemetry_size_mask.to_ulong () - nano::telemetry_data::latest_size);
nano::telemetry_ack message (data);
wait_peer_connections (system);
auto channel = node_client->network.tcp_channels.find_channel (nano::transport::map_endpoint_to_tcp (node_server->network.endpoint ()));
channel->send (message, [](boost::system::error_code const & ec, size_t size_a) {
ASSERT_FALSE (ec);
});
ASSERT_TIMELY (10s, 1 == node_server->stats.count (nano::stat::type::message, nano::stat::detail::telemetry_ack, nano::stat::dir::in));
}
namespace nano
{
TEST (telemetry, remove_peer_different_genesis)
@ -634,4 +673,4 @@ TEST (telemetry, maker_pruning)
}
ASSERT_EQ (nano::telemetry_maker::nf_pruned_node, static_cast<nano::telemetry_maker> (telemetry_data.maker));
}
}

View file

@ -10,11 +10,11 @@ namespace nano
using stream = std::basic_streambuf<uint8_t>;
// Read a raw byte stream the size of `T' and fill value. Returns true if there was an error, false otherwise
template <typename T>
bool try_read (nano::stream & stream_a, T & value)
bool try_read (nano::stream & stream_a, T & value_a)
{
static_assert (std::is_standard_layout<T>::value, "Can't stream read non-standard layout types");
auto amount_read (stream_a.sgetn (reinterpret_cast<uint8_t *> (&value), sizeof (value)));
return amount_read != sizeof (value);
auto amount_read (stream_a.sgetn (reinterpret_cast<uint8_t *> (&value_a), sizeof (value_a)));
return amount_read != sizeof (value_a);
}
// A wrapper of try_read which throws if there is an error
template <typename T>
@ -27,12 +27,28 @@ void read (nano::stream & stream_a, T & value)
}
}
inline void read (nano::stream & stream_a, std::vector<uint8_t> & value_a, size_t size_a)
{
value_a.resize (size_a);
if (stream_a.sgetn (value_a.data (), size_a) != size_a)
{
throw std::runtime_error ("Failed to read this number of bytes");
}
}
template <typename T>
void write (nano::stream & stream_a, T const & value)
void write (nano::stream & stream_a, T const & value_a)
{
static_assert (std::is_standard_layout<T>::value, "Can't stream write non-standard layout types");
auto amount_written (stream_a.sputn (reinterpret_cast<uint8_t const *> (&value), sizeof (value)));
auto amount_written (stream_a.sputn (reinterpret_cast<uint8_t const *> (&value_a), sizeof (value_a)));
(void)amount_written;
debug_assert (amount_written == sizeof (value));
debug_assert (amount_written == sizeof (value_a));
}
inline void write (nano::stream & stream_a, std::vector<uint8_t> const & value_a)
{
auto amount_written (stream_a.sputn (value_a.data (), value_a.size ()));
(void)amount_written;
debug_assert (amount_written == value_a.size ());
}
}

View file

@ -1108,9 +1108,9 @@ nano::telemetry_ack::telemetry_ack (nano::telemetry_data const & telemetry_data_
message (nano::message_type::telemetry_ack),
data (telemetry_data_a)
{
debug_assert (telemetry_data::size < 2048); // Maximum size the mask allows
debug_assert (telemetry_data::size + telemetry_data_a.unknown_data.size () <= message_header::telemetry_size_mask.to_ulong ()); // Maximum size the mask allows
header.extensions &= ~message_header::telemetry_size_mask;
header.extensions |= std::bitset<16> (static_cast<unsigned long long> (telemetry_data::size));
header.extensions |= std::bitset<16> (static_cast<unsigned long long> (telemetry_data::size) + telemetry_data_a.unknown_data.size ());
}
void nano::telemetry_ack::serialize (nano::stream & stream_a, bool use_epoch_2_min_version_a) const
@ -1193,9 +1193,13 @@ void nano::telemetry_data::deserialize (nano::stream & stream_a, uint16_t payloa
timestamp = std::chrono::system_clock::time_point (std::chrono::milliseconds (timestamp_l));
read (stream_a, active_difficulty);
boost::endian::big_to_native_inplace (active_difficulty);
if (payload_length_a > latest_size)
{
read (stream_a, unknown_data, payload_length_a - latest_size);
}
}
void nano::telemetry_data::serialize_without_signature (nano::stream & stream_a, uint16_t /* size_a */) const
void nano::telemetry_data::serialize_without_signature (nano::stream & stream_a) const
{
// All values should be serialized in big endian
write (stream_a, node_id);
@ -1215,12 +1219,13 @@ void nano::telemetry_data::serialize_without_signature (nano::stream & stream_a,
write (stream_a, maker);
write (stream_a, boost::endian::native_to_big (std::chrono::duration_cast<std::chrono::milliseconds> (timestamp.time_since_epoch ()).count ()));
write (stream_a, boost::endian::native_to_big (active_difficulty));
write (stream_a, unknown_data);
}
void nano::telemetry_data::serialize (nano::stream & stream_a) const
{
write (stream_a, signature);
serialize_without_signature (stream_a, size);
serialize_without_signature (stream_a);
}
nano::error nano::telemetry_data::serialize_json (nano::jsonconfig & json, bool ignore_identification_metrics_a) const
@ -1307,7 +1312,7 @@ nano::error nano::telemetry_data::deserialize_json (nano::jsonconfig & json, boo
bool nano::telemetry_data::operator== (nano::telemetry_data const & data_a) const
{
return (signature == data_a.signature && node_id == data_a.node_id && block_count == data_a.block_count && cemented_count == data_a.cemented_count && unchecked_count == data_a.unchecked_count && account_count == data_a.account_count && bandwidth_cap == data_a.bandwidth_cap && uptime == data_a.uptime && peer_count == data_a.peer_count && protocol_version == data_a.protocol_version && genesis_block == data_a.genesis_block && major_version == data_a.major_version && minor_version == data_a.minor_version && patch_version == data_a.patch_version && pre_release_version == data_a.pre_release_version && maker == data_a.maker && timestamp == data_a.timestamp && active_difficulty == data_a.active_difficulty);
return (signature == data_a.signature && node_id == data_a.node_id && block_count == data_a.block_count && cemented_count == data_a.cemented_count && unchecked_count == data_a.unchecked_count && account_count == data_a.account_count && bandwidth_cap == data_a.bandwidth_cap && uptime == data_a.uptime && peer_count == data_a.peer_count && protocol_version == data_a.protocol_version && genesis_block == data_a.genesis_block && major_version == data_a.major_version && minor_version == data_a.minor_version && patch_version == data_a.patch_version && pre_release_version == data_a.pre_release_version && maker == data_a.maker && timestamp == data_a.timestamp && active_difficulty == data_a.active_difficulty && unknown_data == data_a.unknown_data);
}
bool nano::telemetry_data::operator!= (nano::telemetry_data const & data_a) const
@ -1321,18 +1326,18 @@ void nano::telemetry_data::sign (nano::keypair const & node_id_a)
std::vector<uint8_t> bytes;
{
nano::vectorstream stream (bytes);
serialize_without_signature (stream, size);
serialize_without_signature (stream);
}
signature = nano::sign_message (node_id_a.prv, node_id_a.pub, bytes.data (), bytes.size ());
}
bool nano::telemetry_data::validate_signature (uint16_t size_a) const
bool nano::telemetry_data::validate_signature () const
{
std::vector<uint8_t> bytes;
{
nano::vectorstream stream (bytes);
serialize_without_signature (stream, size_a);
serialize_without_signature (stream);
}
return nano::validate_message (node_id, bytes.data (), bytes.size (), signature);

View file

@ -220,7 +220,7 @@ public:
static std::bitset<16> constexpr block_type_mask{ 0x0f00 };
static std::bitset<16> constexpr count_mask{ 0xf000 };
static std::bitset<16> constexpr telemetry_size_mask{ 0x07ff };
static std::bitset<16> constexpr telemetry_size_mask{ 0x3ff };
};
class message
{
@ -368,20 +368,22 @@ public:
uint8_t maker{ static_cast<std::underlying_type_t<telemetry_maker>> (telemetry_maker::nf_node) }; // Where this telemetry information originated
std::chrono::system_clock::time_point timestamp;
uint64_t active_difficulty{ 0 };
std::vector<uint8_t> unknown_data;
void serialize (nano::stream &) const;
void deserialize (nano::stream &, uint16_t);
nano::error serialize_json (nano::jsonconfig &, bool) const;
nano::error deserialize_json (nano::jsonconfig &, bool);
void sign (nano::keypair const &);
bool validate_signature (uint16_t) const;
bool validate_signature () const;
bool operator== (nano::telemetry_data const &) const;
bool operator!= (nano::telemetry_data const &) const;
// Size does not include unknown_data
static auto constexpr size = sizeof (signature) + sizeof (node_id) + sizeof (block_count) + sizeof (cemented_count) + sizeof (unchecked_count) + sizeof (account_count) + sizeof (bandwidth_cap) + sizeof (peer_count) + sizeof (protocol_version) + sizeof (uptime) + sizeof (genesis_block) + sizeof (major_version) + sizeof (minor_version) + sizeof (patch_version) + sizeof (pre_release_version) + sizeof (maker) + sizeof (uint64_t) + sizeof (active_difficulty);
static auto constexpr latest_size = size; // This needs to be updated for each new telemetry version
private:
void serialize_without_signature (nano::stream &, uint16_t) const;
void serialize_without_signature (nano::stream &) const;
};
class telemetry_req final : public message
{

View file

@ -90,7 +90,7 @@ bool nano::telemetry::verify_message (nano::telemetry_ack const & message_a, nan
if (!node_id_mismatch)
{
// The data could be correctly signed but for a different node id
remove_channel = message_a.data.validate_signature (message_a.size ());
remove_channel = message_a.data.validate_signature ();
if (!remove_channel)
{
// Check for different genesis blocks

View file

@ -21,11 +21,12 @@ void nano::compare_default_telemetry_response_data_excluding_signature (nano::te
ASSERT_EQ (telemetry_data_a.maker, static_cast<std::underlying_type_t<nano::telemetry_maker>> (nano::telemetry_maker::nf_node));
ASSERT_GT (telemetry_data_a.timestamp, std::chrono::system_clock::now () - std::chrono::seconds (100));
ASSERT_EQ (telemetry_data_a.active_difficulty, active_difficulty_a);
ASSERT_EQ (telemetry_data_a.unknown_data, std::vector<uint8_t>{});
}
void nano::compare_default_telemetry_response_data (nano::telemetry_data const & telemetry_data_a, nano::network_params const & network_params_a, uint64_t bandwidth_limit_a, uint64_t active_difficulty_a, nano::keypair const & node_id_a)
{
ASSERT_FALSE (telemetry_data_a.validate_signature (nano::telemetry_data::size));
ASSERT_FALSE (telemetry_data_a.validate_signature ());
nano::telemetry_data telemetry_data_l = telemetry_data_a;
telemetry_data_l.signature.clear ();
telemetry_data_l.sign (node_id_a);