diff --git a/src/avalanche/peermanager.h b/src/avalanche/peermanager.h --- a/src/avalanche/peermanager.h +++ b/src/avalanche/peermanager.h @@ -79,6 +79,18 @@ struct next_request_time {}; +struct AvaHelloSigData { + uint256 sigHash; + SchnorrSig signature; + + AvaHelloSigData(uint256 hash, SchnorrSig sig) + : sigHash(hash), signature(sig) {} + + bool verify(const CPubKey pubkey) const { + return pubkey.VerifySchnorr(sigHash, signature); + } +}; + class PeerManager { std::vector slots; uint64_t slotCount = 0; @@ -128,7 +140,8 @@ * Node API. */ bool addNode(NodeId nodeid, const Proof &proof, - const Delegation &delegation); + const Delegation &delegation, + std::optional sigData = std::nullopt); bool removeNode(NodeId nodeid); bool forNode(NodeId nodeid, std::function func) const; diff --git a/src/avalanche/peermanager.cpp b/src/avalanche/peermanager.cpp --- a/src/avalanche/peermanager.cpp +++ b/src/avalanche/peermanager.cpp @@ -14,7 +14,8 @@ namespace avalanche { bool PeerManager::addNode(NodeId nodeid, const Proof &proof, - const Delegation &delegation) { + const Delegation &delegation, + std::optional sigData) { auto it = fetchOrCreatePeer(proof); if (it == peers.end()) { return false; @@ -28,6 +29,11 @@ return false; } + // Check AVAHELLO signature if applicable. + if (sigData && !sigData->verify(pubkey)) { + return false; + } + auto nit = nodes.find(nodeid); if (nit == nodes.end()) { if (!nodes.emplace(nodeid, peerid, std::move(pubkey)).second) { diff --git a/src/avalanche/processor.h b/src/avalanche/processor.h --- a/src/avalanche/processor.h +++ b/src/avalanche/processor.h @@ -289,8 +289,12 @@ bool registerVotes(NodeId nodeid, const Response &response, std::vector &updates); + // Add node with RPC bool addNode(NodeId nodeid, const Proof &proof, const Delegation &delegation); + // Add node with peer discovery + bool addNode(CNode *pfrom, const Proof &proof, const Delegation &delegation, + const SchnorrSig avaHelloSig); bool forNode(NodeId nodeid, std::function func) const; CPubKey getSessionPubKey() const; diff --git a/src/avalanche/processor.cpp b/src/avalanche/processor.cpp --- a/src/avalanche/processor.cpp +++ b/src/avalanche/processor.cpp @@ -448,6 +448,15 @@ return peerManager->addNode(nodeid, proof, delegation); } +bool Processor::addNode(CNode *pfrom, const Proof &proof, + const Delegation &delegation, + const SchnorrSig avaHelloSig) { + LOCK(cs_peerManager); + auto sigData = std::make_optional( + buildRemoteSighash(pfrom), avaHelloSig); + return peerManager->addNode(pfrom->GetId(), proof, delegation, sigData); +} + bool Processor::forNode(NodeId nodeid, std::function func) const { LOCK(cs_peerManager); diff --git a/src/avalanche/test/peermanager_tests.cpp b/src/avalanche/test/peermanager_tests.cpp --- a/src/avalanche/test/peermanager_tests.cpp +++ b/src/avalanche/test/peermanager_tests.cpp @@ -438,4 +438,37 @@ NO_PEER); } +BOOST_AUTO_TEST_CASE(addnode_with_signature) { + CKey proofMaster; + proofMaster.MakeNewKey(true); + + // Create one peer. + Proof proof = buildRandomProof(1000, proofMaster.GetPubKey()); + Delegation dg = DelegationBuilder(proof).build(); + + CHashWriter ss(SER_GETHASH, 0); + ss << "very message"; + const uint256 sighash = ss.GetHash(); + SchnorrSig signature; + proofMaster.SignSchnorr(sighash, signature); + + { + // Add a node using correct sighash & signature + auto sigData = std::make_optional(sighash, signature); + avalanche::PeerManager pm; + BOOST_CHECK(pm.addNode(0, proof, dg, sigData)); + } + + { + // Fail to add node using mismatching sighash & signature + CHashWriter ss2(SER_GETHASH, 0); + ss2 << "much message"; + const uint256 wrongSighash = ss.GetHash(); + auto sigData = + std::make_optional(wrongSighash, signature); + avalanche::PeerManager pm; + BOOST_CHECK(!pm.addNode(0, proof, dg, sigData)); + } +} + BOOST_AUTO_TEST_SUITE_END()