diff --git a/beacon/include/marlin/beacon/DiscoveryServer.hpp b/beacon/include/marlin/beacon/DiscoveryServer.hpp index f98add49..d1695f8c 100644 --- a/beacon/include/marlin/beacon/DiscoveryServer.hpp +++ b/beacon/include/marlin/beacon/DiscoveryServer.hpp @@ -15,6 +15,10 @@ #include #include +#include +#include +#include + #include #include "Messages.hpp" @@ -23,6 +27,12 @@ namespace marlin { namespace beacon { +struct PeerInfo { + uint64_t last_seen; + std::array key; + std::array address; +}; + //! Class implementing the server side node discovery functionality /*! Features: @@ -57,7 +67,7 @@ class DiscoveryServer { void send_LISTPEER(BaseTransport &transport); void did_recv_HEARTBEAT(BaseTransport &transport, HEARTBEAT &&bytes); - void did_recv_REG(BaseTransport &transport); + void did_recv_REG(BaseTransport &transport, BaseMessageType&& packet); void heartbeat_timer_cb(); asyncio::Timer heartbeat_timer; @@ -65,9 +75,13 @@ class DiscoveryServer { void did_recv_DISCCLUSTER(BaseTransport &transport); void send_LISTCLUSTER(BaseTransport &transport); - std::unordered_map>> peers; + std::unordered_map peers; BaseTransport* rt = nullptr; + + secp256k1_context* ctx_signer = nullptr; + secp256k1_context* ctx_verifier = nullptr; + uint8_t key[32]; public: // Listen delegate bool should_accept(core::SocketAddress const &addr); @@ -78,7 +92,12 @@ class DiscoveryServer { void did_recv(BaseTransport &transport, BaseMessageType &&packet); void did_send(BaseTransport &transport, core::Buffer &&packet); - DiscoveryServer(core::SocketAddress const& baddr, core::SocketAddress const& haddr, std::optional raddr = std::nullopt); + DiscoveryServer( + core::SocketAddress const& baddr, + core::SocketAddress const& haddr, + std::optional raddr = std::nullopt, + std::string key = "" + ); DiscoveryServerDelegate *delegate; }; @@ -137,7 +156,7 @@ void DiscoveryServer::send_LISTPEER( auto f_end = boost::make_filter_iterator(filter, peers.end(), peers.end()); // Extract data into pair format - auto transformation = [](auto x) { return std::make_pair(x.first, x.second.second); }; + auto transformation = [](auto x) { return std::make_pair(x.first, x.second.key); }; auto t_begin = boost::make_transform_iterator(f_begin, transformation); auto t_end = boost::make_transform_iterator(f_end, transformation); @@ -170,13 +189,14 @@ void DiscoveryServer::did_recv_HEARTBEAT( return; } - peers[transport.dst_addr].first = asyncio::EventLoop::now(); - peers[transport.dst_addr].second = bytes.key_array(); + peers[transport.dst_addr].last_seen = asyncio::EventLoop::now(); + peers[transport.dst_addr].key = bytes.key_array(); } template void DiscoveryServer::did_recv_REG( - BaseTransport &transport + BaseTransport &transport, + BaseMessageType &&packet ) { SPDLOG_INFO( "REG <<< {}", @@ -193,7 +213,65 @@ void DiscoveryServer::did_recv_REG( transport.dst_addr.to_string() ); - peers[transport.dst_addr].first = asyncio::EventLoop::now(); + auto payload = packet.payload_buffer(); + if(payload.size() > 73) { + auto cur_time = (uint64_t)std::time(nullptr); + auto p_time = payload.read_uint64_le_unsafe(1); + + if(cur_time - p_time > 60 && p_time - cur_time > 60) { + // Too old or too in future + return; + } + + uint8_t hash[32]; + CryptoPP::Keccak_256 hasher; + // Hash message + hasher.CalculateTruncatedDigest(hash, 32, payload.data()+1, 8); + + secp256k1_ecdsa_recoverable_signature sig; + // Parse signature + secp256k1_ecdsa_recoverable_signature_parse_compact( + ctx_verifier, + &sig, + payload.data()+9, + payload.data()[73] + ); + + // Verify signature + secp256k1_pubkey pubkey; + { + auto res = secp256k1_ecdsa_recover( + ctx_verifier, + &pubkey, + &sig, + hash + ); + + if(res == 0) { + // Recovery failed + return; + } + } + + uint8_t pubkeyser[65]; + size_t len = 65; + secp256k1_ec_pubkey_serialize( + ctx_verifier, + pubkeyser, + &len, + &pubkey, + SECP256K1_EC_UNCOMPRESSED + ); + + // Get address + hasher.CalculateTruncatedDigest(hash, 32, pubkeyser+1, 64); + // address is in hash[12..31] + + SPDLOG_INFO("Address: {:spn}", spdlog::to_hex(hash+12, hash+32)); + std::memcpy(peers[transport.dst_addr].address.data(), hash+12, 20); + } + + peers[transport.dst_addr].last_seen = asyncio::EventLoop::now(); } @@ -207,7 +285,7 @@ void DiscoveryServer::heartbeat_timer_cb() { auto iter = peers.begin(); while(iter != peers.end()) { // Remove stale peers if inactive for a minute - if(now - iter->second.first > 60000) { + if(now - iter->second.last_seen > 60000) { iter = peers.erase(iter); } else { iter++; @@ -215,10 +293,46 @@ void DiscoveryServer::heartbeat_timer_cb() { } if(rt != nullptr) { + auto time = (uint64_t)std::time(nullptr); SPDLOG_INFO("REG >>> {}", rt->dst_addr.to_string()); - BaseMessageType m(1); + BaseMessageType m(74); auto reg = m.payload_buffer(); reg.data()[0] = 7; + reg.write_uint64_le_unsafe(1, time); + + uint8_t hash[32]; + CryptoPP::Keccak_256 hasher; + // Hash message + hasher.CalculateTruncatedDigest(hash, 32, reg.data()+1, 8); + + // Sign + secp256k1_ecdsa_recoverable_signature sig; + auto res = secp256k1_ecdsa_sign_recoverable( + ctx_signer, + &sig, + hash, + key, + nullptr, + nullptr + ); + + if(res == 0) { + // Sign failed + SPDLOG_ERROR("Beacon: Failed to send REG: signature failure"); + return; + } + + // Output + int recid; + secp256k1_ecdsa_recoverable_signature_serialize_compact( + ctx_signer, + reg.data()+9, + &recid, + &sig + ); + + reg.data()[73] = (uint8_t)recid; + rt->send(std::move(m)); } } @@ -326,7 +440,7 @@ void DiscoveryServer::did_recv( case 4: did_recv_HEARTBEAT(transport, std::move(packet)); break; // HEARTBEAT - case 7: did_recv_REG(transport); + case 7: did_recv_REG(transport, std::move(packet)); break; // DISCCLUSTER case 9: did_recv_DISCCLUSTER(transport); @@ -384,7 +498,8 @@ template DiscoveryServer::DiscoveryServer( core::SocketAddress const& baddr, core::SocketAddress const& haddr, - std::optional raddr + std::optional raddr, + std::string key ) : heartbeat_timer(this) { f.bind(baddr); f.listen(*this); @@ -394,6 +509,18 @@ DiscoveryServer::DiscoveryServer( heartbeat_timer.template start(0, 10000); + ctx_signer = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); + ctx_verifier = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY); + if(key.size() == 32) { + if(secp256k1_ec_seckey_verify(ctx_verifier, (uint8_t*)key.c_str()) != 1) { + SPDLOG_ERROR("Beacon: failed to verify key", key.size()); + } + std::memcpy(this->key, key.c_str(), 32); + SPDLOG_INFO("Key: {}", spdlog::to_hex(this->key, this->key+32)); + } else { + SPDLOG_ERROR("Beacon: failed to load key: {}", key.size()); + } + if(raddr.has_value()) { f.dial(*raddr, *this); }