2016-01-28 10:19:43 -05:00
|
|
|
#include "STDInclude.hpp"
|
|
|
|
|
2016-02-12 16:23:41 -05:00
|
|
|
using namespace std::literals;
|
|
|
|
|
2016-01-28 10:19:43 -05:00
|
|
|
namespace Components
|
|
|
|
{
|
2016-02-08 12:43:31 -05:00
|
|
|
Utils::Cryptography::ECDSA::Key Node::SignatureKey;
|
2016-01-28 10:19:43 -05:00
|
|
|
std::vector<Node::NodeEntry> Node::Nodes;
|
2016-02-09 16:13:49 -05:00
|
|
|
std::vector<Node::ClientSession> Node::Sessions;
|
2016-01-28 10:19:43 -05:00
|
|
|
|
|
|
|
void Node::LoadNodes()
|
|
|
|
{
|
2016-02-12 09:06:06 -05:00
|
|
|
Proto::Node::List list;
|
2016-02-12 10:29:48 -05:00
|
|
|
std::string nodes = Utils::ReadFile("players/nodes.dat");
|
|
|
|
if (nodes.empty() || !list.ParseFromString(nodes)) return;
|
2016-01-28 15:37:48 -05:00
|
|
|
|
2016-02-12 09:06:06 -05:00
|
|
|
for (int i = 0; i < list.address_size(); ++i)
|
2016-01-28 10:19:43 -05:00
|
|
|
{
|
2016-02-12 10:29:48 -05:00
|
|
|
Node::AddNode(list.address(i));
|
2016-01-28 10:19:43 -05:00
|
|
|
}
|
|
|
|
}
|
2016-02-06 18:48:39 -05:00
|
|
|
void Node::StoreNodes(bool force)
|
2016-01-28 10:19:43 -05:00
|
|
|
{
|
2016-02-06 18:48:39 -05:00
|
|
|
static int lastStorage = 0;
|
|
|
|
|
|
|
|
// Don't store nodes if the delta is too small and were not forcing it
|
|
|
|
if ((Game::Com_Milliseconds() - lastStorage) < NODE_STORE_INTERVAL && !force) return;
|
|
|
|
lastStorage = Game::Com_Milliseconds();
|
|
|
|
|
2016-02-12 09:06:06 -05:00
|
|
|
Proto::Node::List list;
|
2016-01-28 10:19:43 -05:00
|
|
|
|
2016-02-12 10:29:48 -05:00
|
|
|
// This is obsolete when storing to file.
|
|
|
|
// However, defining another proto message due to this would be redundant.
|
|
|
|
//list.set_is_dedi(Dedicated::IsDedicated());
|
|
|
|
|
2016-02-09 16:13:49 -05:00
|
|
|
for (auto node : Node::Nodes)
|
2016-01-28 10:19:43 -05:00
|
|
|
{
|
2016-02-09 16:13:49 -05:00
|
|
|
if (node.state == Node::STATE_VALID && node.registered)
|
2016-01-28 17:36:57 -05:00
|
|
|
{
|
2016-02-12 09:06:06 -05:00
|
|
|
node.address.Serialize(list.add_address());
|
2016-01-28 17:36:57 -05:00
|
|
|
}
|
2016-01-28 15:37:48 -05:00
|
|
|
}
|
2016-01-28 10:19:43 -05:00
|
|
|
|
2016-01-28 15:37:48 -05:00
|
|
|
CreateDirectoryW(L"players", NULL);
|
2016-02-12 09:06:06 -05:00
|
|
|
Utils::WriteFile("players/nodes.dat", list.SerializeAsString());
|
2016-01-28 10:19:43 -05:00
|
|
|
}
|
|
|
|
|
2016-02-09 16:13:49 -05:00
|
|
|
Node::NodeEntry* Node::FindNode(Network::Address address)
|
2016-01-28 10:19:43 -05:00
|
|
|
{
|
2016-02-09 19:22:00 -05:00
|
|
|
for (auto i = Node::Nodes.begin(); i != Node::Nodes.end(); ++i)
|
2016-01-28 10:19:43 -05:00
|
|
|
{
|
2016-02-09 16:13:49 -05:00
|
|
|
if (i->address == address)
|
2016-01-28 10:19:43 -05:00
|
|
|
{
|
2016-02-09 16:13:49 -05:00
|
|
|
// I don't know if that's safe, but we'll see that later...
|
|
|
|
return &*i;
|
2016-01-28 10:19:43 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-09 16:13:49 -05:00
|
|
|
return nullptr;
|
2016-01-28 10:19:43 -05:00
|
|
|
}
|
2016-02-09 16:13:49 -05:00
|
|
|
Node::ClientSession* Node::FindSession(Network::Address address)
|
2016-01-28 10:19:43 -05:00
|
|
|
{
|
2016-02-09 19:22:00 -05:00
|
|
|
for (auto i = Node::Sessions.begin(); i != Node::Sessions.end(); ++i)
|
2016-01-28 10:19:43 -05:00
|
|
|
{
|
2016-02-09 16:13:49 -05:00
|
|
|
if (i->address == address)
|
2016-01-28 10:19:43 -05:00
|
|
|
{
|
2016-02-09 16:13:49 -05:00
|
|
|
// I don't know if that's safe, but we'll see that later...
|
|
|
|
return &*i;
|
2016-01-28 10:19:43 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-09 16:13:49 -05:00
|
|
|
return nullptr;
|
2016-01-28 10:19:43 -05:00
|
|
|
}
|
|
|
|
|
2016-02-09 16:13:49 -05:00
|
|
|
void Node::AddNode(Network::Address address)
|
2016-01-28 10:19:43 -05:00
|
|
|
{
|
2016-02-09 16:13:49 -05:00
|
|
|
#ifdef DEBUG
|
|
|
|
if (address.IsSelf()) return;
|
|
|
|
#else
|
|
|
|
if (address.IsLocal() || address.IsSelf()) return;
|
|
|
|
#endif
|
2016-01-28 10:19:43 -05:00
|
|
|
|
2016-02-09 16:13:49 -05:00
|
|
|
Node::NodeEntry* existingEntry = Node::FindNode(address);
|
|
|
|
if (existingEntry)
|
2016-01-28 10:19:43 -05:00
|
|
|
{
|
2016-02-09 16:13:49 -05:00
|
|
|
existingEntry->lastHeard = Game::Com_Milliseconds();
|
2016-01-28 10:19:43 -05:00
|
|
|
}
|
2016-02-09 16:13:49 -05:00
|
|
|
else
|
|
|
|
{
|
|
|
|
Node::NodeEntry entry;
|
2016-01-28 10:19:43 -05:00
|
|
|
|
2016-02-09 16:13:49 -05:00
|
|
|
entry.lastHeard = Game::Com_Milliseconds();
|
|
|
|
entry.lastTime = 0;
|
|
|
|
entry.lastListQuery = 0;
|
|
|
|
entry.registered = false;
|
|
|
|
entry.state = Node::STATE_UNKNOWN;
|
|
|
|
entry.address = address;
|
2016-01-28 10:19:43 -05:00
|
|
|
|
2016-02-09 16:13:49 -05:00
|
|
|
Node::Nodes.push_back(entry);
|
|
|
|
}
|
2016-01-28 10:19:43 -05:00
|
|
|
}
|
|
|
|
|
2016-02-09 16:13:49 -05:00
|
|
|
void Node::SendNodeList(Network::Address address)
|
2016-01-28 10:19:43 -05:00
|
|
|
{
|
2016-02-09 19:22:00 -05:00
|
|
|
if (address.IsSelf()) return;
|
2016-01-28 10:19:43 -05:00
|
|
|
|
2016-02-12 09:06:06 -05:00
|
|
|
Proto::Node::List list;
|
|
|
|
list.set_is_dedi(Dedicated::IsDedicated());
|
2016-01-28 10:19:43 -05:00
|
|
|
|
2016-02-12 09:06:06 -05:00
|
|
|
for (auto node : Node::Nodes)
|
2016-01-28 10:19:43 -05:00
|
|
|
{
|
2016-02-12 09:06:06 -05:00
|
|
|
if (node.state == Node::STATE_VALID && node.registered)
|
2016-01-28 10:19:43 -05:00
|
|
|
{
|
2016-02-12 09:06:06 -05:00
|
|
|
node.address.Serialize(list.add_address());
|
2016-01-28 10:19:43 -05:00
|
|
|
}
|
2016-02-01 15:59:45 -05:00
|
|
|
|
2016-02-12 09:06:06 -05:00
|
|
|
if (list.address_size() >= NODE_PACKET_LIMIT)
|
2016-02-01 15:59:45 -05:00
|
|
|
{
|
2016-02-12 09:06:06 -05:00
|
|
|
Network::SendCommand(address, "nodeListResponse", list.SerializeAsString());
|
|
|
|
list.clear_address();
|
2016-02-01 15:59:45 -05:00
|
|
|
}
|
2016-01-28 10:19:43 -05:00
|
|
|
}
|
|
|
|
|
2016-02-12 09:06:06 -05:00
|
|
|
if (list.address_size() > 0)
|
|
|
|
{
|
|
|
|
Network::SendCommand(address, "nodeListResponse", list.SerializeAsString());
|
|
|
|
}
|
2016-01-28 10:19:43 -05:00
|
|
|
}
|
|
|
|
|
2016-02-09 16:13:49 -05:00
|
|
|
void Node::DeleteInvalidSessions()
|
2016-01-28 15:37:48 -05:00
|
|
|
{
|
2016-02-09 16:13:49 -05:00
|
|
|
std::vector<Node::ClientSession> cleanSessions;
|
|
|
|
|
|
|
|
for (auto session : Node::Sessions)
|
2016-01-28 15:37:48 -05:00
|
|
|
{
|
2016-02-09 19:22:00 -05:00
|
|
|
if (session.lastTime > 0 && (Game::Com_Milliseconds() - session.lastTime) <= SESSION_TIMEOUT)
|
|
|
|
{
|
|
|
|
cleanSessions.push_back(session);
|
|
|
|
}
|
2016-01-28 15:37:48 -05:00
|
|
|
}
|
|
|
|
|
2016-02-09 16:13:49 -05:00
|
|
|
if (cleanSessions.size() != Node::Sessions.size())
|
2016-02-03 14:10:54 -05:00
|
|
|
{
|
2016-02-09 16:13:49 -05:00
|
|
|
//Node::Sessions.clear();
|
|
|
|
//Utils::Merge(&Node::Sessions, cleanSessions);
|
|
|
|
Node::Sessions = cleanSessions;
|
2016-02-03 14:10:54 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-28 17:36:57 -05:00
|
|
|
void Node::DeleteInvalidNodes()
|
|
|
|
{
|
|
|
|
std::vector<Node::NodeEntry> cleanNodes;
|
|
|
|
|
|
|
|
for (auto node : Node::Nodes)
|
|
|
|
{
|
2016-02-01 15:59:45 -05:00
|
|
|
if (node.state == Node::STATE_INVALID && (Game::Com_Milliseconds() - node.lastHeard) > NODE_INVALID_DELETE)
|
2016-01-28 17:36:57 -05:00
|
|
|
{
|
2016-02-01 15:59:45 -05:00
|
|
|
Logger::Print("Removing invalid node %s\n", node.address.GetString());
|
2016-01-28 17:36:57 -05:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-02-01 15:59:45 -05:00
|
|
|
cleanNodes.push_back(node);
|
2016-01-28 17:36:57 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cleanNodes.size() != Node::Nodes.size())
|
|
|
|
{
|
2016-02-09 16:13:49 -05:00
|
|
|
//Node::Nodes.clear();
|
|
|
|
//Utils::Merge(&Node::Nodes, cleanNodes);
|
|
|
|
Node::Nodes = cleanNodes;
|
2016-01-28 17:36:57 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-12 21:18:49 -05:00
|
|
|
void Node::SyncNodeList()
|
2016-02-03 14:10:54 -05:00
|
|
|
{
|
2016-02-12 21:18:49 -05:00
|
|
|
for (auto& node : Node::Nodes)
|
2016-02-03 14:10:54 -05:00
|
|
|
{
|
2016-02-12 21:18:49 -05:00
|
|
|
if (node.state == Node::STATE_VALID && node.registered)
|
2016-02-03 14:10:54 -05:00
|
|
|
{
|
2016-02-12 21:18:49 -05:00
|
|
|
node.state = Node::STATE_UNKNOWN;
|
|
|
|
node.registered = false;
|
2016-02-09 16:13:49 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-02-03 14:10:54 -05:00
|
|
|
|
2016-02-09 16:13:49 -05:00
|
|
|
void Node::FrameHandler()
|
|
|
|
{
|
|
|
|
int registerCount = 0;
|
|
|
|
int listQueryCount = 0;
|
2016-02-03 14:10:54 -05:00
|
|
|
|
2016-02-09 16:13:49 -05:00
|
|
|
for (auto &node : Node::Nodes)
|
|
|
|
{
|
|
|
|
// TODO: Decide how to handle nodes that were already registered, but timed out re-registering.
|
|
|
|
if (node.state == STATE_NEGOTIATING && (Game::Com_Milliseconds() - node.lastTime) > (NODE_QUERY_TIMEOUT))
|
2016-02-03 14:10:54 -05:00
|
|
|
{
|
2016-02-09 16:13:49 -05:00
|
|
|
node.registered = false; // Definitely unregister here!
|
2016-02-03 14:10:54 -05:00
|
|
|
node.state = Node::STATE_INVALID;
|
2016-02-09 16:13:49 -05:00
|
|
|
node.lastHeard = Game::Com_Milliseconds();
|
2016-02-03 14:10:54 -05:00
|
|
|
node.lastTime = Game::Com_Milliseconds();
|
|
|
|
|
2016-02-09 16:13:49 -05:00
|
|
|
Logger::Print("Node negotiation timed out. Invalidating %s\n", node.address.GetString());
|
2016-02-03 14:10:54 -05:00
|
|
|
}
|
|
|
|
|
2016-02-09 16:13:49 -05:00
|
|
|
if (registerCount < NODE_FRAME_QUERY_LIMIT)
|
2016-02-03 14:10:54 -05:00
|
|
|
{
|
2016-02-09 16:13:49 -05:00
|
|
|
// Register when unregistered and in UNKNOWN state (I doubt it's possible to be unregistered and in VALID state)
|
|
|
|
if (!node.registered && (node.state != Node::STATE_NEGOTIATING && node.state != Node::STATE_INVALID))
|
|
|
|
{
|
|
|
|
registerCount++;
|
|
|
|
node.state = Node::STATE_NEGOTIATING;
|
|
|
|
node.lastTime = Game::Com_Milliseconds();
|
2016-02-03 14:10:54 -05:00
|
|
|
|
2016-02-09 16:13:49 -05:00
|
|
|
if (Dedicated::IsDedicated())
|
|
|
|
{
|
|
|
|
node.challenge = Utils::VA("%X", Utils::Cryptography::Rand::GenerateInt());
|
2016-02-03 14:10:54 -05:00
|
|
|
|
2016-02-12 09:06:06 -05:00
|
|
|
Proto::Node::Packet packet;
|
2016-02-10 08:54:50 -05:00
|
|
|
packet.set_challenge(node.challenge);
|
2016-02-03 14:10:54 -05:00
|
|
|
|
2016-02-09 16:13:49 -05:00
|
|
|
Logger::Print("Sending registration request to %s\n", node.address.GetString());
|
2016-02-12 09:06:06 -05:00
|
|
|
Network::SendCommand(node.address, "nodeRegisterRequest", packet.SerializeAsString());
|
2016-02-09 19:22:00 -05:00
|
|
|
}
|
2016-02-09 16:13:49 -05:00
|
|
|
else
|
|
|
|
{
|
|
|
|
Logger::Print("Sending session request to %s\n", node.address.GetString());
|
2016-02-10 11:18:45 -05:00
|
|
|
Network::SendCommand(node.address, "sessionRequest");
|
2016-02-09 16:13:49 -05:00
|
|
|
}
|
|
|
|
}
|
2016-02-03 14:10:54 -05:00
|
|
|
}
|
|
|
|
|
2016-02-09 16:13:49 -05:00
|
|
|
if (listQueryCount < NODE_FRAME_QUERY_LIMIT)
|
2016-02-03 14:10:54 -05:00
|
|
|
{
|
2016-02-09 16:13:49 -05:00
|
|
|
if (node.registered && node.state == Node::STATE_VALID && (!node.lastListQuery || (Game::Com_Milliseconds() - node.lastListQuery) > NODE_QUERY_INTERVAL))
|
|
|
|
{
|
|
|
|
listQueryCount++;
|
|
|
|
node.state = Node::STATE_NEGOTIATING;
|
|
|
|
node.lastTime = Game::Com_Milliseconds();
|
2016-02-03 14:10:54 -05:00
|
|
|
|
2016-02-09 16:13:49 -05:00
|
|
|
if (Dedicated::IsDedicated())
|
|
|
|
{
|
2016-02-10 11:18:45 -05:00
|
|
|
Network::SendCommand(node.address, "nodeListRequest");
|
2016-02-09 16:13:49 -05:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-02-10 11:18:45 -05:00
|
|
|
Network::SendCommand(node.address, "sessionRequest");
|
2016-02-09 16:13:49 -05:00
|
|
|
}
|
|
|
|
}
|
2016-02-03 14:10:54 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-09 16:13:49 -05:00
|
|
|
Node::DeleteInvalidSessions();
|
2016-02-03 14:10:54 -05:00
|
|
|
Node::DeleteInvalidNodes();
|
2016-02-06 18:48:39 -05:00
|
|
|
Node::StoreNodes(false);
|
2016-02-03 14:10:54 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
const char* Node::GetStateName(EntryState state)
|
|
|
|
{
|
|
|
|
switch (state)
|
|
|
|
{
|
|
|
|
case Node::STATE_UNKNOWN:
|
|
|
|
return "Unknown";
|
|
|
|
|
2016-02-09 16:13:49 -05:00
|
|
|
case Node::STATE_NEGOTIATING:
|
|
|
|
return "Negotiating";
|
2016-02-03 14:10:54 -05:00
|
|
|
|
|
|
|
case Node::STATE_INVALID:
|
|
|
|
return "Invalid";
|
|
|
|
|
|
|
|
case Node::STATE_VALID:
|
|
|
|
return "Valid";
|
|
|
|
}
|
|
|
|
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
2016-01-28 10:19:43 -05:00
|
|
|
Node::Node()
|
|
|
|
{
|
2016-02-03 14:10:54 -05:00
|
|
|
// ZoneBuilder doesn't require node stuff
|
|
|
|
if (ZoneBuilder::IsEnabled()) return;
|
|
|
|
|
2016-02-08 12:43:31 -05:00
|
|
|
// Generate our ECDSA key
|
|
|
|
Node::SignatureKey = Utils::Cryptography::ECDSA::GenerateKey(512);
|
|
|
|
|
2016-02-09 16:13:49 -05:00
|
|
|
// Load stored nodes
|
2016-01-28 10:19:43 -05:00
|
|
|
Dvar::OnInit([] ()
|
|
|
|
{
|
2016-02-12 16:23:41 -05:00
|
|
|
//Node::Nodes.clear();
|
2016-01-28 10:19:43 -05:00
|
|
|
Node::LoadNodes();
|
|
|
|
});
|
|
|
|
|
2016-02-09 16:13:49 -05:00
|
|
|
// Send deadline when shutting down
|
2016-01-28 15:37:48 -05:00
|
|
|
if (Dedicated::IsDedicated())
|
|
|
|
{
|
|
|
|
QuickPatch::OnShutdown([] ()
|
|
|
|
{
|
2016-02-12 09:06:06 -05:00
|
|
|
std::string challenge = Utils::VA("%X", Utils::Cryptography::Rand::GenerateInt());
|
2016-02-10 08:54:50 -05:00
|
|
|
|
2016-02-12 09:06:06 -05:00
|
|
|
Proto::Node::Packet packet;
|
2016-02-10 08:54:50 -05:00
|
|
|
packet.set_challenge(challenge);
|
|
|
|
packet.set_signature(Utils::Cryptography::ECDSA::SignMessage(Node::SignatureKey, challenge));
|
2016-02-09 16:13:49 -05:00
|
|
|
|
2016-01-28 15:37:48 -05:00
|
|
|
for (auto node : Node::Nodes)
|
|
|
|
{
|
2016-02-12 09:06:06 -05:00
|
|
|
Network::SendCommand(node.address, "nodeDeregister", packet.SerializeAsString());
|
2016-01-28 15:37:48 -05:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2016-02-09 16:13:49 -05:00
|
|
|
// This is the handler that accepts registration requests from other nodes
|
|
|
|
// If you want to get accepted as node, you have to send a request to this handler
|
|
|
|
Network::Handle("nodeRegisterRequest", [] (Network::Address address, std::string data)
|
2016-02-03 14:10:54 -05:00
|
|
|
{
|
2016-02-09 16:13:49 -05:00
|
|
|
Node::NodeEntry* entry = Node::FindNode(address);
|
|
|
|
|
|
|
|
// Create a new entry, if we don't already know it
|
|
|
|
if (!entry)
|
|
|
|
{
|
|
|
|
Node::AddNode(address);
|
|
|
|
entry = Node::FindNode(address);
|
|
|
|
if (!entry) return;
|
|
|
|
}
|
|
|
|
|
|
|
|
Logger::Print("Received registration request from %s\n", address.GetString());
|
2016-01-28 10:19:43 -05:00
|
|
|
|
2016-02-12 09:06:06 -05:00
|
|
|
Proto::Node::Packet packet;
|
2016-02-10 08:54:50 -05:00
|
|
|
if (!packet.ParseFromString(data)) return;
|
2016-02-12 09:06:06 -05:00
|
|
|
if (packet.challenge().empty()) return;
|
2016-02-05 07:10:22 -05:00
|
|
|
|
2016-02-10 08:54:50 -05:00
|
|
|
std::string signature = Utils::Cryptography::ECDSA::SignMessage(Node::SignatureKey, packet.challenge());
|
|
|
|
std::string challenge = Utils::VA("%X", Utils::Cryptography::Rand::GenerateInt());
|
2016-02-09 16:13:49 -05:00
|
|
|
|
2016-02-10 08:54:50 -05:00
|
|
|
packet.Clear();
|
|
|
|
packet.set_challenge(challenge);
|
|
|
|
packet.set_signature(signature);
|
|
|
|
packet.set_publickey(Node::SignatureKey.GetPublicKey());
|
2016-02-09 16:13:49 -05:00
|
|
|
|
|
|
|
entry->lastTime = Game::Com_Milliseconds();
|
|
|
|
entry->challenge = challenge;
|
|
|
|
entry->state = Node::STATE_NEGOTIATING;
|
|
|
|
|
2016-02-12 09:06:06 -05:00
|
|
|
Network::SendCommand(address, "nodeRegisterSynchronize", packet.SerializeAsString());
|
2016-02-03 14:10:54 -05:00
|
|
|
});
|
2016-01-28 10:19:43 -05:00
|
|
|
|
2016-02-09 16:13:49 -05:00
|
|
|
Network::Handle("nodeRegisterSynchronize", [] (Network::Address address, std::string data)
|
2016-01-28 10:19:43 -05:00
|
|
|
{
|
2016-02-09 16:13:49 -05:00
|
|
|
Node::NodeEntry* entry = Node::FindNode(address);
|
|
|
|
if (!entry || entry->state != Node::STATE_NEGOTIATING) return;
|
2016-01-28 10:19:43 -05:00
|
|
|
|
2016-02-09 16:13:49 -05:00
|
|
|
Logger::Print("Received synchronization data for registration from %s!\n", address.GetString());
|
2016-01-28 10:19:43 -05:00
|
|
|
|
2016-02-12 09:06:06 -05:00
|
|
|
Proto::Node::Packet packet;
|
2016-02-10 08:54:50 -05:00
|
|
|
if (!packet.ParseFromString(data)) return;
|
2016-02-12 09:06:06 -05:00
|
|
|
if (packet.challenge().empty()) return;
|
|
|
|
if (packet.publickey().empty()) return;
|
|
|
|
if (packet.signature().empty()) return;
|
2016-02-10 08:54:50 -05:00
|
|
|
|
|
|
|
std::string challenge = packet.challenge();
|
|
|
|
std::string publicKey = packet.publickey();
|
|
|
|
std::string signature = packet.signature();
|
2016-01-28 10:19:43 -05:00
|
|
|
|
2016-02-09 16:13:49 -05:00
|
|
|
// Verify signature
|
|
|
|
entry->publicKey.Set(publicKey);
|
|
|
|
if (!Utils::Cryptography::ECDSA::VerifyMessage(entry->publicKey, entry->challenge, signature))
|
|
|
|
{
|
|
|
|
Logger::Print("Signature from %s for challenge '%s' is invalid!\n", address.GetString(), entry->challenge.data());
|
|
|
|
return;
|
|
|
|
}
|
2016-01-28 10:19:43 -05:00
|
|
|
|
2016-02-09 16:13:49 -05:00
|
|
|
Logger::Print("Signature from %s for challenge '%s' is valid!\n", address.GetString(), entry->challenge.data());
|
2016-01-28 10:19:43 -05:00
|
|
|
|
2016-02-09 16:13:49 -05:00
|
|
|
// Mark as registered
|
|
|
|
entry->lastTime = Game::Com_Milliseconds();
|
|
|
|
entry->state = Node::STATE_VALID;
|
|
|
|
entry->registered = true;
|
2016-01-28 10:19:43 -05:00
|
|
|
|
2016-02-09 16:13:49 -05:00
|
|
|
Logger::Print("Node %s registered\n", address.GetString());
|
2016-01-28 10:19:43 -05:00
|
|
|
|
2016-02-09 16:13:49 -05:00
|
|
|
// Build response
|
|
|
|
publicKey = Node::SignatureKey.GetPublicKey();
|
|
|
|
signature = Utils::Cryptography::ECDSA::SignMessage(Node::SignatureKey, challenge);
|
2016-01-28 10:19:43 -05:00
|
|
|
|
2016-02-10 08:54:50 -05:00
|
|
|
packet.Clear();
|
|
|
|
packet.set_signature(signature);
|
|
|
|
packet.set_publickey(publicKey);
|
2016-01-28 10:19:43 -05:00
|
|
|
|
2016-02-12 09:06:06 -05:00
|
|
|
Network::SendCommand(address, "nodeRegisterAcknowledge", packet.SerializeAsString());
|
2016-02-09 16:13:49 -05:00
|
|
|
});
|
|
|
|
|
|
|
|
Network::Handle("nodeRegisterAcknowledge", [] (Network::Address address, std::string data)
|
2016-01-28 10:19:43 -05:00
|
|
|
{
|
2016-02-09 16:13:49 -05:00
|
|
|
// Ignore requests from nodes we don't know
|
|
|
|
Node::NodeEntry* entry = Node::FindNode(address);
|
|
|
|
if (!entry || entry->state != Node::STATE_NEGOTIATING) return;
|
2016-01-28 10:19:43 -05:00
|
|
|
|
2016-02-09 16:13:49 -05:00
|
|
|
Logger::Print("Received acknowledgment from %s\n", address.GetString());
|
2016-01-28 15:37:48 -05:00
|
|
|
|
2016-02-12 09:06:06 -05:00
|
|
|
Proto::Node::Packet packet;
|
2016-02-10 08:54:50 -05:00
|
|
|
if (!packet.ParseFromString(data)) return;
|
2016-02-12 09:06:06 -05:00
|
|
|
if (packet.signature().empty()) return;
|
|
|
|
if (packet.publickey().empty()) return;
|
2016-02-10 08:54:50 -05:00
|
|
|
|
|
|
|
std::string publicKey = packet.publickey();
|
|
|
|
std::string signature = packet.signature();
|
2016-01-28 17:36:57 -05:00
|
|
|
|
2016-02-09 16:13:49 -05:00
|
|
|
entry->publicKey.Set(publicKey);
|
|
|
|
|
|
|
|
if (Utils::Cryptography::ECDSA::VerifyMessage(entry->publicKey, entry->challenge, signature))
|
2016-01-28 15:37:48 -05:00
|
|
|
{
|
2016-02-09 16:13:49 -05:00
|
|
|
entry->lastTime = Game::Com_Milliseconds();
|
|
|
|
entry->state = Node::STATE_VALID;
|
|
|
|
entry->registered = true;
|
|
|
|
|
|
|
|
Logger::Print("Signature from %s for challenge '%s' is valid!\n", address.GetString(), entry->challenge.data());
|
|
|
|
Logger::Print("Node %s registered\n", address.GetString());
|
2016-01-28 15:37:48 -05:00
|
|
|
}
|
2016-02-09 16:13:49 -05:00
|
|
|
else
|
|
|
|
{
|
|
|
|
Logger::Print("Signature from %s for challenge '%s' is invalid!\n", address.GetString(), entry->challenge.data());
|
|
|
|
}
|
|
|
|
});
|
2016-01-28 17:36:57 -05:00
|
|
|
|
2016-02-09 16:13:49 -05:00
|
|
|
Network::Handle("nodeListRequest", [] (Network::Address address, std::string data)
|
2016-01-28 17:36:57 -05:00
|
|
|
{
|
2016-02-12 21:18:49 -05:00
|
|
|
// Check if this is a registered node
|
2016-02-09 16:13:49 -05:00
|
|
|
bool allowed = false;
|
|
|
|
Node::NodeEntry* entry = Node::FindNode(address);
|
|
|
|
if (entry && entry->registered)
|
2016-01-28 17:36:57 -05:00
|
|
|
{
|
2016-02-09 16:13:49 -05:00
|
|
|
entry->lastTime = Game::Com_Milliseconds();
|
|
|
|
allowed = true;
|
2016-01-28 17:36:57 -05:00
|
|
|
}
|
2016-01-28 15:37:48 -05:00
|
|
|
|
2016-02-09 16:13:49 -05:00
|
|
|
// Check if there is any open session
|
|
|
|
if (!allowed)
|
|
|
|
{
|
|
|
|
Node::ClientSession* session = Node::FindSession(address);
|
|
|
|
if (session)
|
|
|
|
{
|
2016-02-09 19:22:00 -05:00
|
|
|
session->lastTime = Game::Com_Milliseconds();
|
|
|
|
allowed = session->valid;
|
2016-02-09 16:13:49 -05:00
|
|
|
}
|
|
|
|
}
|
2016-01-28 10:19:43 -05:00
|
|
|
|
2016-02-09 16:13:49 -05:00
|
|
|
if (allowed)
|
|
|
|
{
|
|
|
|
Node::SendNodeList(address);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Unallowed connection
|
2016-02-09 19:22:00 -05:00
|
|
|
Logger::Print("Node list requested by %s, but no valid session was present!\n", address.GetString());
|
2016-02-10 11:18:45 -05:00
|
|
|
Network::SendCommand(address, "nodeListError");
|
2016-02-09 16:13:49 -05:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2016-02-09 19:22:00 -05:00
|
|
|
Network::Handle("nodeDeregister", [] (Network::Address address, std::string data)
|
|
|
|
{
|
|
|
|
Node::NodeEntry* entry = Node::FindNode(address);
|
|
|
|
if (!entry || !entry->registered) return;
|
|
|
|
|
2016-02-12 09:06:06 -05:00
|
|
|
Proto::Node::Packet packet;
|
2016-02-10 08:54:50 -05:00
|
|
|
if (!packet.ParseFromString(data)) return;
|
2016-02-12 09:06:06 -05:00
|
|
|
if (packet.challenge().empty()) return;
|
|
|
|
if (packet.signature().empty()) return;
|
2016-02-10 08:54:50 -05:00
|
|
|
|
|
|
|
std::string challenge = packet.challenge();
|
|
|
|
std::string signature = packet.signature();
|
2016-02-09 19:22:00 -05:00
|
|
|
|
|
|
|
if (Utils::Cryptography::ECDSA::VerifyMessage(entry->publicKey, challenge, signature))
|
|
|
|
{
|
|
|
|
entry->lastHeard = Game::Com_Milliseconds();
|
|
|
|
entry->lastTime = Game::Com_Milliseconds();
|
|
|
|
entry->registered = false;
|
|
|
|
entry->state = Node::STATE_INVALID;
|
|
|
|
|
|
|
|
Logger::Print("Node %s unregistered\n", address.GetString());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Logger::Print("Node %s tried to unregister using an invalid signature!\n", address.GetString());
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2016-02-09 16:13:49 -05:00
|
|
|
Network::Handle("sessionRequest", [] (Network::Address address, std::string data)
|
2016-01-28 10:19:43 -05:00
|
|
|
{
|
2016-02-12 21:18:49 -05:00
|
|
|
// Search an active session, if we haven't found one, register a template
|
|
|
|
if (!Node::FindSession(address))
|
|
|
|
{
|
|
|
|
Node::ClientSession templateSession;
|
|
|
|
templateSession.address = address;
|
|
|
|
Node::Sessions.push_back(templateSession);
|
|
|
|
}
|
2016-02-09 16:13:49 -05:00
|
|
|
|
2016-02-12 21:18:49 -05:00
|
|
|
// Search our target session (this should not fail!)
|
|
|
|
Node::ClientSession* session = Node::FindSession(address);
|
|
|
|
if (!session) return; // Registering template session failed, odd...
|
2016-02-09 16:13:49 -05:00
|
|
|
|
2016-02-12 21:18:49 -05:00
|
|
|
Logger::Print("Client %s is requesting a new session\n", address.GetString());
|
2016-02-09 16:13:49 -05:00
|
|
|
|
2016-02-12 21:18:49 -05:00
|
|
|
// Initialize session data
|
|
|
|
session->challenge = Utils::VA("%X", Utils::Cryptography::Rand::GenerateInt());
|
|
|
|
session->lastTime = Game::Com_Milliseconds();
|
|
|
|
session->valid = false;
|
2016-02-09 16:13:49 -05:00
|
|
|
|
2016-02-12 21:18:49 -05:00
|
|
|
Network::SendCommand(address, "sessionInitialize", session->challenge);
|
2016-02-09 16:13:49 -05:00
|
|
|
});
|
|
|
|
|
|
|
|
Network::Handle("sessionSynchronize", [] (Network::Address address, std::string data)
|
|
|
|
{
|
|
|
|
// Return if we don't have a session for this address
|
|
|
|
Node::ClientSession* session = Node::FindSession(address);
|
2016-02-09 19:22:00 -05:00
|
|
|
if (!session || session->valid) return;
|
2016-01-28 10:19:43 -05:00
|
|
|
|
2016-02-09 16:13:49 -05:00
|
|
|
if (session->challenge == data)
|
|
|
|
{
|
|
|
|
Logger::Print("Session for %s validated.\n", address.GetString());
|
|
|
|
session->valid = true;
|
2016-02-10 11:18:45 -05:00
|
|
|
Network::SendCommand(address, "sessionAcknowledge");
|
2016-02-09 16:13:49 -05:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-02-09 19:22:00 -05:00
|
|
|
session->lastTime = -1;
|
2016-02-09 16:13:49 -05:00
|
|
|
Logger::Print("Challenge mismatch. Validating session for %s failed.\n", address.GetString());
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
else
|
2016-01-28 15:37:48 -05:00
|
|
|
{
|
2016-02-09 16:13:49 -05:00
|
|
|
Network::Handle("sessionInitialize", [] (Network::Address address, std::string data)
|
|
|
|
{
|
|
|
|
Node::NodeEntry* entry = Node::FindNode(address);
|
|
|
|
if (!entry) return;
|
|
|
|
|
|
|
|
Logger::Print("Session initialization received. Synchronizing...\n", address.GetString());
|
|
|
|
|
|
|
|
entry->lastTime = Game::Com_Milliseconds();
|
2016-02-10 11:18:45 -05:00
|
|
|
Network::SendCommand(address, "sessionSynchronize", data);
|
2016-02-09 16:13:49 -05:00
|
|
|
});
|
2016-01-28 15:37:48 -05:00
|
|
|
|
2016-02-09 16:13:49 -05:00
|
|
|
Network::Handle("sessionAcknowledge", [] (Network::Address address, std::string data)
|
2016-01-28 15:37:48 -05:00
|
|
|
{
|
2016-02-09 16:13:49 -05:00
|
|
|
Node::NodeEntry* entry = Node::FindNode(address);
|
|
|
|
if (!entry) return;
|
2016-01-28 15:37:48 -05:00
|
|
|
|
2016-02-09 16:13:49 -05:00
|
|
|
entry->state = Node::STATE_VALID;
|
|
|
|
entry->registered = true;
|
|
|
|
entry->lastTime = Game::Com_Milliseconds();
|
2016-02-09 19:22:00 -05:00
|
|
|
|
|
|
|
Logger::Print("Session acknowledged, synchronizing node list...\n", address.GetString());
|
2016-02-10 11:18:45 -05:00
|
|
|
Network::SendCommand(address, "nodeListRequest");
|
2016-02-09 19:22:00 -05:00
|
|
|
Node::SendNodeList(address);
|
2016-02-09 16:13:49 -05:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
Network::Handle("nodeListResponse", [] (Network::Address address, std::string data)
|
2016-01-28 10:19:43 -05:00
|
|
|
{
|
2016-02-12 09:06:06 -05:00
|
|
|
Proto::Node::List list;
|
|
|
|
|
|
|
|
if (data.empty() || !list.ParseFromString(data))
|
2016-02-09 16:13:49 -05:00
|
|
|
{
|
|
|
|
Logger::Print("Received invalid node list from %s!\n", address.GetString());
|
|
|
|
return;
|
|
|
|
}
|
2016-01-28 10:19:43 -05:00
|
|
|
|
2016-02-09 16:13:49 -05:00
|
|
|
Node::NodeEntry* entry = Node::FindNode(address);
|
|
|
|
if (entry)
|
2016-01-28 21:49:51 -05:00
|
|
|
{
|
2016-02-09 16:13:49 -05:00
|
|
|
if (entry->registered)
|
2016-01-28 21:49:51 -05:00
|
|
|
{
|
2016-02-12 09:06:06 -05:00
|
|
|
Logger::Print("Received valid node list with %i entries from %s\n", list.address_size(), address.GetString());
|
2016-02-09 16:13:49 -05:00
|
|
|
|
2016-02-12 09:06:06 -05:00
|
|
|
entry->isDedi = list.is_dedi();
|
2016-02-09 16:13:49 -05:00
|
|
|
entry->state = Node::STATE_VALID;
|
|
|
|
entry->lastTime = Game::Com_Milliseconds();
|
|
|
|
entry->lastListQuery = Game::Com_Milliseconds();
|
|
|
|
|
2016-02-12 21:18:49 -05:00
|
|
|
if (!Dedicated::IsDedicated() && entry->isDedi && ServerList::IsOnlineList())
|
|
|
|
{
|
|
|
|
ServerList::InsertRequest(entry->address, true);
|
|
|
|
}
|
|
|
|
|
2016-02-12 09:06:06 -05:00
|
|
|
for (int i = 0; i < list.address_size(); ++i)
|
2016-02-09 16:13:49 -05:00
|
|
|
{
|
2016-02-12 10:29:48 -05:00
|
|
|
Node::AddNode(list.address(i));
|
2016-02-09 16:13:49 -05:00
|
|
|
}
|
2016-01-28 21:49:51 -05:00
|
|
|
}
|
|
|
|
}
|
2016-02-09 16:13:49 -05:00
|
|
|
else
|
|
|
|
{
|
2016-02-09 19:22:00 -05:00
|
|
|
//Node::AddNode(address);
|
|
|
|
|
|
|
|
Node::ClientSession* session = Node::FindSession(address);
|
|
|
|
if (session && session->valid)
|
|
|
|
{
|
|
|
|
session->lastTime = Game::Com_Milliseconds();
|
|
|
|
|
2016-02-12 09:06:06 -05:00
|
|
|
for (int i = 0; i < list.address_size(); ++i)
|
2016-02-09 19:22:00 -05:00
|
|
|
{
|
2016-02-12 10:29:48 -05:00
|
|
|
Node::AddNode(list.address(i));
|
2016-02-09 19:22:00 -05:00
|
|
|
}
|
|
|
|
}
|
2016-02-09 16:13:49 -05:00
|
|
|
}
|
2016-01-28 10:19:43 -05:00
|
|
|
});
|
2016-02-01 15:59:45 -05:00
|
|
|
|
2016-02-09 16:13:49 -05:00
|
|
|
// If we receive that response, our request was not permitted
|
|
|
|
// So we either have to register as node, or register a remote session
|
|
|
|
Network::Handle("nodeListError", [] (Network::Address address, std::string data)
|
2016-02-01 15:59:45 -05:00
|
|
|
{
|
2016-02-09 16:13:49 -05:00
|
|
|
if (Dedicated::IsDedicated())
|
2016-02-01 15:59:45 -05:00
|
|
|
{
|
2016-02-09 16:13:49 -05:00
|
|
|
Node::NodeEntry* entry = Node::FindNode(address);
|
|
|
|
if (entry)
|
2016-02-01 15:59:45 -05:00
|
|
|
{
|
2016-02-09 16:13:49 -05:00
|
|
|
// Set to unregistered to perform registration later on
|
|
|
|
entry->lastTime = Game::Com_Milliseconds();
|
|
|
|
entry->registered = false;
|
|
|
|
entry->state = Node::STATE_UNKNOWN;
|
2016-02-01 15:59:45 -05:00
|
|
|
}
|
2016-02-09 16:13:49 -05:00
|
|
|
else
|
|
|
|
{
|
|
|
|
// Add as new entry to perform registration
|
|
|
|
Node::AddNode(address);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// TODO: Implement client handshake stuff
|
|
|
|
// Nvm, clients can simply ignore that i guess
|
2016-02-01 15:59:45 -05:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2016-02-09 16:13:49 -05:00
|
|
|
Command::Add("listnodes", [] (Command::Params params)
|
|
|
|
{
|
|
|
|
Logger::Print("Nodes: %d\n", Node::Nodes.size());
|
|
|
|
|
|
|
|
for (auto node : Node::Nodes)
|
|
|
|
{
|
|
|
|
Logger::Print("%s\t(%s)\n", node.address.GetString(), Node::GetStateName(node.state));
|
2016-02-01 15:59:45 -05:00
|
|
|
}
|
|
|
|
});
|
2016-02-03 14:10:54 -05:00
|
|
|
|
2016-02-09 16:13:49 -05:00
|
|
|
Command::Add("addnode", [] (Command::Params params)
|
|
|
|
{
|
|
|
|
if (params.Length() < 2) return;
|
|
|
|
|
|
|
|
Network::Address address(params[1]);
|
|
|
|
Node::AddNode(address);
|
2016-02-09 19:22:00 -05:00
|
|
|
|
|
|
|
Node::NodeEntry* entry = Node::FindNode(address);
|
|
|
|
if (entry)
|
|
|
|
{
|
|
|
|
entry->state = Node::STATE_UNKNOWN;
|
|
|
|
entry->registered = false;
|
|
|
|
}
|
2016-02-09 16:13:49 -05:00
|
|
|
});
|
|
|
|
|
2016-02-03 14:10:54 -05:00
|
|
|
// Install frame handlers
|
|
|
|
Dedicated::OnFrame(Node::FrameHandler);
|
|
|
|
Renderer::OnFrame(Node::FrameHandler);
|
2016-02-12 16:23:41 -05:00
|
|
|
|
|
|
|
Network::OnStart([] ()
|
|
|
|
{
|
|
|
|
std::async([] ()
|
|
|
|
{
|
|
|
|
std::this_thread::sleep_for(100ms);
|
|
|
|
|
|
|
|
auto nodes = Utils::WebIO("IW4x", "http://hastebin.com/raw/qodibixora").Get();
|
|
|
|
auto nodeArray = Utils::Explode(nodes, '\n');
|
|
|
|
|
|
|
|
for (auto nodeEntry : nodeArray)
|
|
|
|
{
|
|
|
|
if (!nodeEntry.empty())
|
|
|
|
{
|
|
|
|
Node::AddNode(nodeEntry);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
2016-01-28 10:19:43 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
Node::~Node()
|
|
|
|
{
|
2016-02-08 12:43:31 -05:00
|
|
|
Node::SignatureKey.Free();
|
|
|
|
|
2016-02-06 18:48:39 -05:00
|
|
|
Node::StoreNodes(true);
|
2016-01-28 10:19:43 -05:00
|
|
|
Node::Nodes.clear();
|
2016-02-09 16:13:49 -05:00
|
|
|
Node::Sessions.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Node::UnitTest()
|
|
|
|
{
|
|
|
|
printf("Testing ECDSA key...");
|
|
|
|
|
|
|
|
if (!Node::SignatureKey.IsValid())
|
|
|
|
{
|
|
|
|
printf("Error\n");
|
|
|
|
printf("ECDSA key seems invalid!\n");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
printf("Success\n");
|
|
|
|
printf("Testing 10 valid signatures...");
|
|
|
|
|
2016-02-09 19:22:00 -05:00
|
|
|
for (int i = 0; i < 10; ++i)
|
2016-02-09 16:13:49 -05:00
|
|
|
{
|
2016-02-10 11:18:45 -05:00
|
|
|
std::string message = Utils::VA("%X", Utils::Cryptography::Rand::GenerateInt());
|
2016-02-09 16:13:49 -05:00
|
|
|
std::string signature = Utils::Cryptography::ECDSA::SignMessage(Node::SignatureKey, message);
|
|
|
|
|
|
|
|
if (!Utils::Cryptography::ECDSA::VerifyMessage(Node::SignatureKey, message, signature))
|
|
|
|
{
|
|
|
|
printf("Error\n");
|
|
|
|
printf("Signature for '%s' (%d) was invalid!\n", message.data(), i);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
printf("Success\n");
|
|
|
|
printf("Testing 10 invalid signatures...");
|
|
|
|
|
2016-02-09 19:22:00 -05:00
|
|
|
for (int i = 0; i < 10; ++i)
|
2016-02-09 16:13:49 -05:00
|
|
|
{
|
2016-02-10 11:18:45 -05:00
|
|
|
std::string message = Utils::VA("%X", Utils::Cryptography::Rand::GenerateInt());
|
2016-02-09 16:13:49 -05:00
|
|
|
std::string signature = Utils::Cryptography::ECDSA::SignMessage(Node::SignatureKey, message);
|
|
|
|
|
|
|
|
// Invalidate the message...
|
|
|
|
message[Utils::Cryptography::Rand::GenerateInt() % message.size()]++;
|
|
|
|
|
|
|
|
if (Utils::Cryptography::ECDSA::VerifyMessage(Node::SignatureKey, message, signature))
|
|
|
|
{
|
|
|
|
printf("Error\n");
|
|
|
|
printf("Signature for '%s' (%d) was valid? What the fuck? That is absolutely impossible...\n", message.data(), i);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
printf("Success\n");
|
|
|
|
printf("Testing ECDSA key import...");
|
|
|
|
|
|
|
|
std::string pubKey = Node::SignatureKey.GetPublicKey();
|
2016-02-10 11:18:45 -05:00
|
|
|
std::string message = Utils::VA("%X", Utils::Cryptography::Rand::GenerateInt());
|
2016-02-09 16:13:49 -05:00
|
|
|
std::string signature = Utils::Cryptography::ECDSA::SignMessage(Node::SignatureKey, message);
|
|
|
|
|
|
|
|
Utils::Cryptography::ECDSA::Key testKey;
|
|
|
|
testKey.Set(pubKey);
|
|
|
|
|
|
|
|
if (!Utils::Cryptography::ECDSA::VerifyMessage(Node::SignatureKey, message, signature))
|
|
|
|
{
|
|
|
|
printf("Error\n");
|
|
|
|
printf("Verifying signature for message '%s' using imported keys failed!\n", message.data());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
printf("Success\n");
|
|
|
|
return true;
|
2016-01-28 10:19:43 -05:00
|
|
|
}
|
|
|
|
}
|