From 53154c0cf25c9f488e1cf44852f4fe4d43f28b4b Mon Sep 17 00:00:00 2001 From: Dimitrios Siganos Date: Thu, 12 May 2022 22:01:32 +0100 Subject: [PATCH] Node ID persistence (#3816) * Node ID persistence After this change, node IDs will persist on the disk file system and reused. The node ID will not be changing after every restart. This will help to more reliably track PRs. The node can be forced to generate a new ID by deleting the file: node_id_private.key Note that this change does not prevent the man in the middle attack. However, it makes it possible to detect telemetry spoofing. Sites like nanolooker and nanoticker can maintain associations between node ID and PR and only accept a telemetry packet, if the telemetry message is signed by the known Node ID. Co-authored-by: clemahieu --- nano/core_test/node.cpp | 28 ++++++++++++++++++++++++++++ nano/node/node.cpp | 30 +++++++++++++++++++++++++++++- nano/node/node.hpp | 1 + 3 files changed, 58 insertions(+), 1 deletion(-) diff --git a/nano/core_test/node.cpp b/nano/core_test/node.cpp index 11721ee7..71592d5b 100644 --- a/nano/core_test/node.cpp +++ b/nano/core_test/node.cpp @@ -11,6 +11,7 @@ #include #include +#include #include using namespace std::chrono_literals; @@ -4472,3 +4473,30 @@ TEST (node, pruning_depth) ASSERT_TRUE (node1.ledger.block_or_pruned_exists (send1->hash ())); ASSERT_TRUE (node1.ledger.block_or_pruned_exists (send2->hash ())); } + +TEST (node_config, node_id_private_key_persistence) +{ + nano::logger_mt logger; + + // create the directory and the file + auto path = nano::unique_path (); + ASSERT_TRUE (boost::filesystem::create_directories (path)); + auto priv_key_filename = path / "node_id_private.key"; + + // check that the key generated is random when the key does not exist + nano::keypair kp1 = nano::load_or_create_node_id (path, logger); + boost::filesystem::remove (priv_key_filename); + nano::keypair kp2 = nano::load_or_create_node_id (path, logger); + ASSERT_NE (kp1.prv, kp2.prv); + + // check that the key persists + nano::keypair kp3 = nano::load_or_create_node_id (path, logger); + ASSERT_EQ (kp2.prv, kp3.prv); + + // write the key file manually and check that right key is loaded + std::ofstream ofs (priv_key_filename.string (), std::ofstream::out | std::ofstream::trunc); + ofs << "3F28D035B8AA75EA53DF753BFD065CF6138E742971B2C99B84FD8FE328FED2D9" << std::flush; + ofs.close (); + nano::keypair kp4 = nano::load_or_create_node_id (path, logger); + ASSERT_EQ (kp4.prv, nano::keypair ("3F28D035B8AA75EA53DF753BFD065CF6138E742971B2C99B84FD8FE328FED2D9").prv); +} diff --git a/nano/node/node.cpp b/nano/node/node.cpp index edefe8e5..7a11e2fd 100644 --- a/nano/node/node.cpp +++ b/nano/node/node.cpp @@ -16,6 +16,7 @@ #include #include +#include #include #include @@ -74,6 +75,33 @@ std::unique_ptr nano::collect_container_info (re return composite; } +nano::keypair nano::load_or_create_node_id (boost::filesystem::path const & application_path, nano::logger_mt & logger) +{ + auto node_private_key_path = application_path / "node_id_private.key"; + std::ifstream ifs (node_private_key_path.c_str ()); + if (ifs.good ()) + { + logger.always_log (boost::str (boost::format ("%1% exists, reading node id from it") % node_private_key_path.string ())); + std::string node_private_key; + ifs >> node_private_key; + release_assert (node_private_key.size () == 64); + nano::keypair kp = nano::keypair (node_private_key); + return kp; + } + else + { + // no node_id found, generate new one + logger.always_log (boost::str (boost::format ("%1% does not exist, creating a new node_id") % node_private_key_path.string ())); + nano::keypair kp; + std::ofstream ofs (node_private_key_path.c_str (), std::ofstream::out | std::ofstream::trunc); + ofs << kp.prv.to_string () << std::endl + << std::flush; + ofs.close (); + release_assert (!ofs.fail ()); + return kp; + } +} + nano::node::node (boost::asio::io_context & io_ctx_a, uint16_t peering_port_a, boost::filesystem::path const & application_path_a, nano::logging const & logging_a, nano::work_pool & work_a, nano::node_flags flags_a, unsigned seq) : node (io_ctx_a, application_path_a, nano::node_config (peering_port_a, logging_a), work_a, flags_a, seq) { @@ -395,7 +423,7 @@ nano::node::node (boost::asio::io_context & io_ctx_a, boost::filesystem::path co logger.always_log (stream.str ()); } - node_id = nano::keypair (); + node_id = nano::load_or_create_node_id (application_path, logger); logger.always_log ("Node ID: ", node_id.pub.to_node_id ()); if ((network_params.network.is_live_network () || network_params.network.is_beta_network ()) && !flags.inactive_node) diff --git a/nano/node/node.hpp b/nano/node/node.hpp index bf1c46dc..7e7dd66d 100644 --- a/nano/node/node.hpp +++ b/nano/node/node.hpp @@ -219,6 +219,7 @@ private: nano::locked> epoch_upgrading; }; +nano::keypair load_or_create_node_id (boost::filesystem::path const & application_path, nano::logger_mt & logger); std::unique_ptr collect_container_info (node & node, std::string const & name); nano::node_flags const & inactive_node_flag_defaults ();