diff --git a/Doxyfile_internal b/Doxyfile_internal index 3a9457c..1621192 100644 --- a/Doxyfile_internal +++ b/Doxyfile_internal @@ -2095,7 +2095,7 @@ HIDE_UNDOC_RELATIONS = YES # set to NO # The default value is: NO. -HAVE_DOT = NO +HAVE_DOT = YES # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed # to run in parallel. When set to 0 doxygen will base this on the number of diff --git a/src/common/Config.h b/src/common/Config.h index ca79ea3..9e3d0ac 100644 --- a/src/common/Config.h +++ b/src/common/Config.h @@ -70,6 +70,7 @@ class ConfigManager : public Loggable, public Singleton { // Overlay config.put("overlay.connection.processed_queue_size", 100); config.put("overlay.connection.key_rotation_spam_limit", 5); + config.put("overlay.connection.timeout", 120); config.put("overlay.key_provider.history_size", 10); config.put("overlay.key_provider.renew_interval", 600); @@ -103,18 +104,12 @@ class ConfigManager : public Loggable, public Singleton { std::string getDefaultFile(); void setFile(std::string filename); - void configChanged(); - void resetToDefaults(); void loadFromFile(); public: ConfigManager(); virtual ~ConfigManager(); - std::set config_clients; - void registerClient(ConfigClient* client); - void unregisterClient(ConfigClient* client); - std::string getDirectory(); std::string getFile(); diff --git a/src/common/crypto/Hash.h b/src/common/crypto/Hash.h index c2dbe00..ab1e97a 100644 --- a/src/common/crypto/Hash.h +++ b/src/common/crypto/Hash.h @@ -29,7 +29,7 @@ class PublicKeyDSA; * Base class for hashing operations. * Now it represents Keccak algorithm, which was selected as SHA-3. */ -class Hash : public MathString< Hash > { +class Hash : public MathString { friend class MathString; private: binary_vector_t hash; diff --git a/src/daemon/AsioIOService.h b/src/daemon/AsioIOService.h index 96a2584..f77d63f 100644 --- a/src/daemon/AsioIOService.h +++ b/src/daemon/AsioIOService.h @@ -12,8 +12,7 @@ * along with this program. If not, see . */ -#ifndef ASIOIOSERVICE_H_ -#define ASIOIOSERVICE_H_ +#pragma once #include @@ -28,4 +27,3 @@ class AsioIOService { }; } /* namespace p2pnet */ -#endif /* ASIOIOSERVICE_H_ */ diff --git a/src/daemon/discovery/DiscoveryService.cpp b/src/daemon/discovery/DiscoveryService.cpp index 6876e84..9013687 100644 --- a/src/daemon/discovery/DiscoveryService.cpp +++ b/src/daemon/discovery/DiscoveryService.cpp @@ -13,22 +13,22 @@ */ #include "DiscoveryService.h" -#include "Protocol.pb.h" -#include "../overlay/OverlayKeyProvider.h" +#include "../overlay/KeyProvider.h" #include "../transport/Connection.h" #include "../transport/Socket.h" namespace p2pnet { namespace discovery { -DiscoveryService::DiscoveryService(std::shared_ptr socket) : socket(socket) {} +DiscoveryService::DiscoveryService(std::shared_ptr transport_socket, std::shared_ptr overlay_socket) : + transport_socket(transport_socket), overlay_socket(overlay_socket) {} DiscoveryService::~DiscoveryService() {} -protocol::ConnectionRequestMessage DiscoveryService::generateConnectionRequest() { - auto pks = overlay::OverlaySocket::getInstance()->getKeyProvider(); +protocol::OverlayMessage DiscoveryService::generateConnectionRequest() { + auto pks = overlay_socket->getKeyProvider(); - protocol::ConnectionRequestMessage message; - message.set_src_th(pks->getKeyInfo()->th.toBinaryString()); + protocol::OverlayMessage message; + message.mutable_header()->set_src_th(pks->getKeyInfo()->th.toBinaryString()); return message; } diff --git a/src/daemon/discovery/DiscoveryService.h b/src/daemon/discovery/DiscoveryService.h index e49356a..ca8a591 100644 --- a/src/daemon/discovery/DiscoveryService.h +++ b/src/daemon/discovery/DiscoveryService.h @@ -18,17 +18,18 @@ #include "../../common/Config.h" #include "../../common/Loggable.h" #include "../transport/SocketEndpoint.h" -#include "Protocol.pb.h" +#include "OverlayProtocol.pb.h" namespace p2pnet { namespace discovery { class DiscoveryService : public ConfigClient, public Loggable { - protocol::ConnectionRequestMessage generateConnectionRequest(); + protocol::OverlayMessage generateConnectionRequest(); - std::shared_ptr socket; + std::shared_ptr transport_socket; + std::shared_ptr overlay_socket; public: - DiscoveryService(std::shared_ptr socket); + DiscoveryService(std::shared_ptr transport_socket, std::shared_ptr overlay_socket); virtual ~DiscoveryService(); void handshake(transport::SocketEndpoint endpoint); diff --git a/src/daemon/discovery/UDPLPD.h b/src/daemon/discovery/UDPLPD.h index 1c678c3..1ef2b48 100755 --- a/src/daemon/discovery/UDPLPD.h +++ b/src/daemon/discovery/UDPLPD.h @@ -14,7 +14,7 @@ #pragma once #include "DiscoveryService.h" -#include "Protocol.pb.h" +#include "UDPLPD.pb.h" #include "../transport/udp/UDPInterface.h" #include #include diff --git a/src/daemon/overlay/Connection.cpp b/src/daemon/overlay/Connection.cpp index 13145d9..e80fda4 100644 --- a/src/daemon/overlay/Connection.cpp +++ b/src/daemon/overlay/Connection.cpp @@ -11,59 +11,41 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#include "OverlayConnection.h" -#include "OverlaySocket.h" -#include "../transport/TransportSocket.h" +#include "Connection.h" +#include "Socket.h" +#include "../errors/Errors.h" +#include "../transport/Socket.h" #include "../AsioIOService.h" -#include -#include #include #include +#include +#include + namespace p2pnet { namespace overlay { -Connection::Connection(Socket* parent_socket, std::shared_ptr node) : - key_rotation_spam_timer(AsioIOService::getIOService()), - key_rotation_spam_limit(getValue("overlay.connection.key_rotation_spam_limit")), socket_ptr(parent_socket) { - node_ptr = node; - log() << "New Overlay Connection initiated with TH:" << remoteTH().toBase58() << std::endl; +Connection::Connection(std::weak_ptr parent_socket, TH th) : parent_socket(parent_socket) { + node_info.th = th; + log() << "New overlay::Connection initiated with TH:" << th.toBase58() << std::endl; } Connection::~Connection() { disconnect(); } -void Connection::performRemoteKeyRotation(std::pair previous_keys){ - if(state == State::ESTABLISHED){ - OverlayMessage message = genMessageSkel(previous_keys.second, remoteTH(), Priority::RELIABLE); - Handshake handshake; - - handshake.set_stage(handshake.PUBKEY_ROTATION); - handshake.mutable_pubkey()->CopyFrom(genPubkeyStage(handshake.PUBKEY_ROTATION, previous_keys.first)); - - sendMessage(message); - } -} - void Connection::setState(const State& state_to_set){ state = state_to_set; if(state_to_set == State::ESTABLISHED){ - socket_ptr->getDHT()->registerInKBucket(node_ptr.get()); + onConnect(); }else if(state_to_set == State::CLOSED){ - socket_ptr->getDHT()->removeFromKBucket(node_ptr.get()); - } - - if(state_to_set == State::ESTABLISHED && !suspended_payloads.empty()){ - for(auto& payload : suspended_payloads){ - send(payload.first, payload.second); - } + onDisconnect(); } } -void Connection::sendMessage(OverlayMessage send_message) { +//void Connection::sendMessage(OverlayMessage send_message) { /* @@ -118,167 +100,78 @@ void Connection::sendMessage(OverlayMessage send_message) { // Then we will try to find additional nodes OverlaySocket::getInstance()->getDHT()->findNodeIterative(remoteTH()); }*/ -} +//} -OverlayMessage Connection::genMessageSkel(const TH& src, const TH& dest, Priority prio){ +OverlayMessage Connection::genMessageSkel(const TH& from, const TH& to){ OverlayMessage new_message; - new_message.mutable_header()->set_src_th(src.toBinaryString()); - new_message.mutable_header()->set_dest_th(dest.toBinaryString()); - new_message.mutable_header()->set_prio((OverlayMessage::Header::MessagePriority)prio); + new_message.mutable_header()->set_src_th(from.toBinaryString()); + new_message.mutable_header()->set_dest_th(to.toBinaryString()); return new_message; } -OverlayMessage Connection::genMessageSkel(const OverlayMessage_Header& reply_to, Priority prio){ - return genMessageSkel(TH::fromBinaryString(reply_to.dest_th()), TH::fromBinaryString(reply_to.src_th()), prio); +OverlayMessage Connection::genMessageSkel(){ + return genMessageSkel(localKeyInfo().th, remoteKeyInfo().th); } -Handshake_PubkeyStage Connection::genPubkeyStage(Handshake::Stage for_stage, boost::optional our_hist_key){ - Handshake_PubkeyStage stage; - - std::string new_key_s = localPublicKey().toBinaryString(); - stage.mutable_signed_content()->set_new_ecdsa_key(new_key_s); - - stage.mutable_signed_content()->set_expiration_time(system_clock::to_time_t(getLocalKeyInfo().expiration_time)); - stage.mutable_signed_content()->set_lose_time(system_clock::to_time_t(getLocalKeyInfo().lose_time)); - - if(for_stage == Handshake::PUBKEY_ROTATION){ - std::string old_key_s = our_hist_key->derivePublicKey().toBinaryString(); - stage.mutable_signed_content()->set_old_ecdsa_key(old_key_s); - - auto signed_content = stage.signed_content().SerializeAsString(); - - stage.set_old_signature(our_hist_key->sign(signed_content)); - stage.set_new_signature(localPrivateKey().sign(signed_content)); - } - - return stage; +void Connection::send(const OverlayMessage& message, Socket::SendCallback callback) { + parent_socket.lock()->send(message, callback, shared_from_this()); } -Handshake_ECDHStage Connection::genECDHStage(){ - Handshake_ECDHStage stage; - - if(!session_ecdh_key.isPresent()) - session_ecdh_key = crypto::ECDH::generateNewKey(); - - auto public_ecdh_component = session_ecdh_key.derivePublicKey(); - - stage.set_src_ecdh_pubkey(public_ecdh_component); - stage.set_signature(localPrivateKey().sign(public_ecdh_component)); - - return stage; -} - -void Connection::processHandshake(const OverlayMessage_Header& header, std::string serialized_payload) { - Handshake handshake; - handshake.ParseFromString(serialized_payload); - - switch(handshake.stage()){ - case Handshake_Stage_PUBKEY: - case Handshake_Stage_PUBKEY_ACK: - case Handshake_Stage_PUBKEY_ROTATION: - processPubkeyStage(header, handshake); - break; - - case Handshake_Stage_ECDH: - case Handshake_Stage_ECDH_ACK: - processECDHStage(header, handshake); - break; - } -} - -void Connection::processPubkeyStage(const OverlayMessage_Header& header, Handshake handshake_payload){ - auto new_ecdsa_key = crypto::PublicKeyDSA::fromBinaryString(handshake_payload.pubkey().signed_content().new_ecdsa_key()); - - if(crypto::Hash(new_ecdsa_key) == remoteTH()){ - node_ptr->setPublicKey(new_ecdsa_key); - }else if(handshake_payload.stage() == handshake_payload.PUBKEY_ROTATION && - handshake_payload.pubkey().signed_content().has_old_ecdsa_key()){ - auto signed_content = handshake_payload.pubkey().signed_content().SerializeAsString(); - auto old_ecdsa_key = crypto::PublicKeyDSA::fromBinaryString(handshake_payload.pubkey().signed_content().old_ecdsa_key()); - - if(old_ecdsa_key.verify(signed_content, handshake_payload.pubkey().old_signature()) && - new_ecdsa_key.validate() && - new_ecdsa_key.verify(signed_content, handshake_payload.pubkey().new_signature())){ - // So, the new key is good - node_ptr->setPublicKey(new_ecdsa_key); - } +void Connection::send(const OverlayMessage_Payload& payload, Socket::SendCallback callback, bool encrypted, bool force_non_connected) { + OverlayMessage message = genMessageSkel(); // Construct message + OverlayMessage_MultiPayload multipayload; + multipayload.add_payload()->CopyFrom(payload); + + if(encrypted && connected()){ + message.mutable_body()->set_encrypted_multipayload(session_aes_key.encrypt(multipayload.SerializeAsString())); + send(message, callback); + }else if(!encrypted && (connected() || force_non_connected)){ + message.mutable_body()->mutable_open_multipayload()->CopyFrom(multipayload); + send(message, callback); }else{ - return; - } - - if(handshake_payload.stage() != handshake_payload.PUBKEY_ROTATION){ - auto reply = genMessageSkel(header); - Handshake new_handshake_payload; - - switch(handshake_payload.stage()){ - case Handshake_Stage_PUBKEY: - new_handshake_payload.mutable_pubkey()->CopyFrom(genPubkeyStage(Handshake_Stage_PUBKEY_ACK)); - break; - case Handshake_Stage_PUBKEY_ACK: - new_handshake_payload.mutable_pubkey()->CopyFrom(genECDHStage()); - break; - default: - ; - } - addPayload(new_handshake_payload.SerializeAsString(), PayloadType::HANDSHAKE, reply); - sendMessage(reply); + AsioIOService::getIOService().post(std::bind(callback, not_connected, shared_from_this(), "", 0)); } } -void Connection::processECDHStage(const OverlayMessage_Header& header, Handshake handshake_payload){ - if(!session_ecdh_key.isPresent()) - session_ecdh_key = crypto::ECDH::generateNewKey(); - - auto salt_v = (remoteTH() ^ localTH()).toBinaryString(); - auto derived_aes_string = session_ecdh_key.deriveSymmetricKey(crypto::AES::vectorSize(), - handshake_payload.ecdh().src_ecdh_pubkey(), salt_v); - session_aes_key = crypto::AES::fromBinaryString(derived_aes_string); +void Connection::process(std::shared_ptr origin, const OverlayMessage_Header& header, const OverlayMessage_Body& body) { + updateTransport(origin); - log() << "Received ECDH public key from: TH:" << remoteTH().toBase58() << std::endl; + TH src_th = TH::fromBinaryString(header.src_th()); + TH dest_th = header.has_dest_th() ? TH::fromBinaryString(header.dest_th()) : TH(); - auto reply = genMessageSkel(header); - Handshake new_handshake_payload; - - if(handshake_payload.stage() == handshake_payload.ECDH && state == State::PUBKEY_RECEIVED){ - /* We need to send back ECDH_ACK */ - new_handshake_payload.mutable_ecdh()->CopyFrom(genECDHStage()); + log() << "<- OverlayMessage: TH:" << src_th.toBase58() << std::endl; - addPayload(new_handshake_payload.SerializeAsString(), PayloadType::HANDSHAKE, reply); - sendMessage(reply); - } + if(dest_th){ + std::list> payloads; - setState(State::ESTABLISHED); - log() << "AES encrypted connection with TH:" << remoteTH().toBase58() << " established" << std::endl; -} - -bool Connection::connected() const { - return state == State::ESTABLISHED; -} + for(auto payload : body.open_multipayload().payload()){ + payloads.push_back(std::make_pair(payload, false)); + } -void Connection::send(const OverlayMessage_Payload& send_payload, Priority prio) { - if(state == State::ESTABLISHED){ - OverlayMessage message = genMessageSkel(localTH(), remoteTH(), prio); + if(connected()){ + OverlayMessage_MultiPayload encrypted_multipayload; + encrypted_multipayload.ParseFromString(session_aes_key.decrypt(body.encrypted_multipayload())); -// message.mutable_header()->set_src_th(localTH().toBinaryString()); -// message.mutable_header()->set_dest_th(remoteTH().toBinaryString()); -// message.mutable_header()->set_prio((OverlayMessage_Header_MessagePriority)prio); -// message.set_encrypted_payload(encryptPayload(send_payload)); - sendMessage(message); - }else{ - if(prio == Priority::RELIABLE){ - suspended_payloads.push_back(std::make_pair(send_payload, prio)); - }else if(prio == Priority::REALTIME){ - // TODO: We need a new queue for use with realtime messages. + for(auto payload : encrypted_multipayload){ + payloads.push_back(std::make_pair(payload, true)); + } } - if(state == State::CLOSED){ - connect(); + + // Firing processors + for(auto payload_pair : payloads){ + for(auto processor : parent_socket.lock()->getProcessors((PayloadType)payload_pair.first.type())){ + if(!(processor->isEncryptionMandatory() && !payload_pair.second)){ + processor->process(shared_from_this(), header, payload_pair.first); + } + } } } } -void Connection::process(const OverlayMessage& recv_message, const transport::SocketEndpoint& from) { +/*void Connection::process(std::shared_ptr origin, const OverlayMessage_Header& header, const OverlayMessage_Body& body) { + node_ptr->updateEndpoint(from); std::shared_ptr our_historic_ecdsa_privkey; @@ -290,16 +183,16 @@ void Connection::process(const OverlayMessage& recv_message, const transport::So if(getKeyProvider()->getPrivateKey(dest_th) != boost::none){ // So, this message is for us. - /* Key Rotation stage */ + // Key Rotation stage if(recv_message.payload().has_key_rotation_part()) if(performLocalKeyRotation(recv_message)) node_ptr->updateEndpoint(from, true); - /* Encryption stage */ + // Encryption stage if(recv_message.has_encrypted_payload() && session_aes_key.isPresent()){ OverlayMessage_Payload decrypted_payload; auto decrypted_payload_s = session_aes_key.decrypt(recv_message.encrypted_payload()); if(decrypted_payload.ParseFromString(decrypted_payload_s)){ - /* Encrypted processing */ + // Encrypted processing node_ptr->updateEndpoint(from, true); processHandshake(recv_message, decrypted_payload); // DHT @@ -308,7 +201,7 @@ void Connection::process(const OverlayMessage& recv_message, const transport::So // TODO: Reconnect } }else{ - /* Open (unencrypted) processing */ + // Open (unencrypted) processing processHandshake(recv_message); } if(recv_message.header().prio() == recv_message.header().RELIABLE){ @@ -324,9 +217,9 @@ void Connection::process(const OverlayMessage& recv_message, const transport::So }else{ // This message is completely stale, or it is intended to be retransmitted. } -} +}*/ -void Connection::connect() { +void Connection::connect(Socket::ConnectCallback callback) { OverlayMessage message; message.mutable_header()->set_src_th(localTH().toBinaryString()); message.mutable_header()->set_dest_th(remoteTH().toBinaryString()); @@ -339,5 +232,26 @@ void Connection::disconnect() { setState(State::CLOSED); } +void Connection::updateTransport(std::shared_ptr tconn, bool verified){ + auto it = std::find(transport_connections.begin(), transport_connections.end(), tconn); + + if(it != transport_connections.end()) + transport_connections.erase(it); + + if(verified) + transport_connections.push_front(tconn); + else + transport_connections.push_back(tconn); +} + +void Connection::updateExpirationTime(std::chrono::system_clock::time_point expiry_time) { + if(expiry_time > node_info.expiration_time) // This is to prevent spoofing. + node_info.expiration_time = expiry_time; +} + +void Connection::updateLoseTime(std::chrono::system_clock::time_point lost_time) { + node_info.lose_time = lost_time; +} + } /* namespace overlay */ } /* namespace p2pnet */ diff --git a/src/daemon/overlay/Connection.h b/src/daemon/overlay/Connection.h index 51adafe..10b33a6 100644 --- a/src/daemon/overlay/Connection.h +++ b/src/daemon/overlay/Connection.h @@ -11,35 +11,93 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#ifndef OVERLAYCONNECTION_H_ -#define OVERLAYCONNECTION_H_ +#pragma once -using std::chrono::system_clock; +#include "processors/Handshake.h" + +#include "TH.h" +#include "PayloadParams.h" +#include "Socket.h" + +#include "OverlayProtocol.pb.h" + +#include "../transport/Connection.h" +#include "../dht/DHTService.h" + +#include "../../common/crypto/ECDH.h" +#include "../../common/crypto/AES.h" +#include "../../common/crypto/PublicKeyDSA.h" + +#include "../../common/Config.h" + + +#include +#include namespace p2pnet { using namespace protocol; namespace overlay { -class Connection : public std::enable_shared_from_this, protected Loggable { +class Connection : Loggable, std::enable_shared_from_this, public ConfigClient { + friend class Socket; + friend class processors::Handshake; std::weak_ptr parent_socket; -protected: - Connection() = delete; - Connection(std::shared_ptr parent_interface); + // Types + enum class State { + CLOSED = 0, + HANDSHAKE_INIT_SENT = 1, + HANDSHAKE_REPLY_SENT = 2, + ESTABLISHED = 3, + + LOST = 255 + } state = State::CLOSED; + + // Identity information + KeyInfo node_info; + crypto::ECDH session_ecdh_key; + crypto::AES session_aes_key; + + std::chrono::system_clock::time_point last_activity; + + std::list> transport_connections; + + // Private Getters + KeyProvider* getKeyProvider(){return parent_socket.lock()->getKeyProvider();} + std::shared_ptr getHandshakeProcessor(){return parent_socket.lock()->getHandshakeProcessor();} + + // Actions + void setState(const State& state_to_set); + void onConnect(); + void onDisconnect(); + public: - virtual ~Connection(){}; + Connection(std::weak_ptr parent_socket, TH th); + virtual ~Connection(); + + // Senders + void send(const OverlayMessage& message, Socket::SendCallback callback); + void send(const OverlayMessage_Payload& payload, Socket::SendCallback callback, bool encrypted = true, bool force_non_connected = false); - virtual SocketEndpoint getEndpoint() = 0; - std::shared_ptr getParent(){return parent_socket.lock();} + void process(std::shared_ptr origin, const OverlayMessage_Header& header, const OverlayMessage_Body& body); - void connect(Socket::ConnectCallback callback) = 0; - void send(const std::string& data, Socket::SendCallback callback) = 0; - bool connected(); + // Public Actions + void connect(Socket::ConnectCallback callback); + void disconnect(); - virtual void onConnect(); - virtual void onDisconnect(); + // Public Generators + OverlayMessage genMessageSkel(const TH& from, const TH& to); + OverlayMessage genMessageSkel(); + + // Public getters + KeyInfo localKeyInfo(){return *(getKeyProvider()->getKeyInfo());} + KeyInfo remoteKeyInfo(){return node_info;} + + bool connected() const {return state == State::ESTABLISHED;} + + void updateTransport(std::shared_ptr tconn, bool verified = false); + void updateExpirationTime(std::chrono::system_clock::time_point expiry_time); + void updateLoseTime(std::chrono::system_clock::time_point lost_time); }; } /* namespace overlay */ } /* namespace p2pnet */ - -#endif /* OVERLAYCONNECTION_H_ */ diff --git a/src/daemon/overlay/Copy of Connection.h b/src/daemon/overlay/Copy of Connection.h deleted file mode 100644 index 6890c2a..0000000 --- a/src/daemon/overlay/Copy of Connection.h +++ /dev/null @@ -1,157 +0,0 @@ -/* - * You may redistribute this program and/or modify it under the terms of - * the GNU General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#ifndef OVERLAYCONNECTION_H_ -#define OVERLAYCONNECTION_H_ - -#include "TH.h" -#include "PayloadTypes.h" - -#include "../transport/TransportConnection.h" -#include "../../common/crypto/ECDH.h" -#include "../../common/crypto/AES.h" -#include "../../common/crypto/PublicKeyDSA.h" -#include "Protocol.pb.h" -#include "Handshake.pb.h" -#include "../../common/Config.h" -#include "../dht/DHTService.h" - -#include -#include -#include - -using std::chrono::system_clock; - -namespace p2pnet { -using namespace protocol; -namespace overlay { - -class Connection : Loggable, std::enable_shared_from_this, public ConfigClient { - friend class Socket; - - std::shared_ptr node_ptr; - Socket* socket_ptr; - - // Types - enum class State { - CLOSED = 0, - PUBKEY_SENT = 1, - PUBKEY_RECEIVED = 2, - ECDH_SENT = 3, - ESTABLISHED = 4, - - LOST = 255 - } state = State::CLOSED; - enum class Stage { - PUBKEY = Handshake_Stage_PUBKEY, - PUBKEY_ACK = Handshake_Stage_PUBKEY_ACK, - ECDH = Handshake_Stage_ECDH, - ECDH_ACK = Handshake_Stage_ECDH_ACK, - }; - typedef Socket::Priority Priority; - - // Variables - crypto::ECDH session_ecdh_key; - crypto::AES session_aes_key; - - uint32_t seq_counter = 0; - - /** - * This variable holds messages, that were suspended inside sendBinaryData() function - * They can be suspended because all TransportSockets are inactive. - * They will be sent after discovery new route to TH. - */ - std::deque suspended_messages; - /** - * This variable holds messages, that were suspended inside send() function - * They can be suspended because AES encryption is not set up yet. - * They will be sent after handshake. - */ - std::deque> suspended_payloads; - /** - * This variable holds messages, that have been sent lately. Useful for resending messages. - */ - std::unordered_map sent_message_buffer; - /** - * This variable holds messages, that have been processed by us. - * It is intended to avoid double-processing in case of resending already processed messages by remote peer - * if our ACK messages were not delivered well. - */ - std::set processed_messages; - std::set acked_messages; // Temporary storage for ACK message numbers. - - /** - * This is a part of key rotation spam mechanism. If our keys were changed and we receive message, - * that was intended for one of our historic keys, then we add KeyRotationPart to all RELIABLE messages. - * And begin to performRemoteKeyRotation every X seconds. - */ - bool key_rotation_spam_lock = false; - boost::asio::deadline_timer key_rotation_spam_timer; - boost::posix_time::seconds key_rotation_spam_limit; - - // Actions - void performRemoteKeyRotation(std::pair previous_keys); - - void setState(const State& state_to_set); - - /** - * It is a function, that tries to manage TransportConnection directly. - * This function is about connectivity, and "send" is about encryption. - * @param data - */ - void sendMessage(OverlayMessage send_message); - - // Generators - OverlayMessage genMessageSkel(const TH& src, const TH& dest, Priority prio = Priority::RELIABLE); - OverlayMessage genMessageSkel(const OverlayMessage_Header& reply_to, Priority prio = Priority::RELIABLE); - - Handshake_PubkeyStage genPubkeyStage(Handshake::Stage for_stage, boost::optional our_hist_key = boost::none); - Handshake_ECDHStage genECDHStage(); - - OverlayMessage& addPayload(const std::string& serialized_payload, PayloadType type, OverlayMessage& message); - - // Processors - std::map> processors; - - void processHandshake(const OverlayMessage_Header& header, std::string serialized_payload); - - void processPubkeyStage(const OverlayMessage_Header& header, Handshake handshake_payload); - void processECDHStage(const OverlayMessage_Header& header, Handshake handshake_payload); -public: - Connection(Socket* parent_socket, std::shared_ptr node); - virtual ~Connection(); - - bool connected() const; - - void send(const OverlayMessage_Payload& send_payload, Priority prio); - void process(const OverlayMessage& recv_message, const transport::SocketEndpoint& from); - - void connect(); - void disconnect(); - - // Public getters - KeyInfo getLocalKeyInfo(){return *(socket_ptr->getKeyProvider()->getKeyInfo());} - - TH localTH(){return socket_ptr->getKeyProvider()->getKeyInfo()->th;} - TH remoteTH(){return node_ptr->getHash();} - - crypto::PublicKeyDSA localPublicKey(){return socket_ptr->getKeyProvider()->getKeyInfo()->public_key;} - crypto::PrivateKeyDSA localPrivateKey(){return socket_ptr->getKeyProvider()->getKeyInfo()->private_key;} - - crypto::PublicKeyDSA remotePublicKey(){return node_ptr->getKeyInfo().public_key;} -}; - -} /* namespace overlay */ -} /* namespace p2pnet */ - -#endif /* OVERLAYCONNECTION_H_ */ diff --git a/src/daemon/overlay/DHT.cpp b/src/daemon/overlay/DHT.cpp index 6dcf180..6acc7d2 100644 --- a/src/daemon/overlay/DHT.cpp +++ b/src/daemon/overlay/DHT.cpp @@ -11,9 +11,9 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#include "OverlayDHT.h" -#include "OverlaySocket.h" -#include "OverlayConnection.h" +#include "DHT.h" +#include "Socket.h" +#include "Connection.h" #include "../transport/TransportSocketEndpoint.h" namespace p2pnet { @@ -39,12 +39,12 @@ crypto::Hash DHT::getMyHash(){ } /* K-bucket mgmt */ -void DHT::registerInKBucket(Node* node){ - k_buckets->addNode(node, true); +void DHT::registerInKBucket(std::shared_ptr connection){ + k_buckets->addNode(connection, true); } -void DHT::removeFromKBucket(Node* node) { - k_buckets->removeNode(node); +void DHT::removeFromKBucket(std::shared_ptr connection) { + k_buckets->removeNode(connection); } void DHT::rebuild() { diff --git a/src/daemon/overlay/DHT.h b/src/daemon/overlay/DHT.h index 0b8a133..ccacd80 100644 --- a/src/daemon/overlay/DHT.h +++ b/src/daemon/overlay/DHT.h @@ -36,8 +36,8 @@ class DHT : public dht::DHTService, Loggable { crypto::Hash getMyHash(); - void registerInKBucket(Node* node); - void removeFromKBucket(Node* node); + void registerInKBucket(std::shared_ptr connection); + void removeFromKBucket(std::shared_ptr connection); void rebuild(); diff --git a/src/daemon/overlay/KeyProvider.cpp b/src/daemon/overlay/KeyProvider.cpp index 597d072..1397a9c 100644 --- a/src/daemon/overlay/KeyProvider.cpp +++ b/src/daemon/overlay/KeyProvider.cpp @@ -13,7 +13,7 @@ */ #include "KeyProvider.h" -#include "OverlaySocket.h" +#include "Socket.h" #include "../../common/crypto/Hash.h" #include "../AsioIOService.h" @@ -27,6 +27,21 @@ namespace p2pnet { namespace overlay { +void KeyInfo::fromProtobuf(const protocol::Handshake_SignedHandshake_KeyInfo& key_info_s){ + public_key.setAsBinaryString(key_info_s.ecdsa_key()); + th = TH(public_key); + expiration_time = std::chrono::system_clock::from_time_t(key_info_s.expiration_time()); + lose_time = std::chrono::system_clock::from_time_t(key_info_s.lose_time()); +} + +protocol::Handshake_SignedHandshake_KeyInfo KeyInfo::toProtobuf() const{ + protocol::Handshake_SignedHandshake_KeyInfo key_info_protobuf; + key_info_protobuf.set_ecdsa_key(public_key.toBinaryString()); + key_info_protobuf.set_expiration_time((int64_t)std::chrono::system_clock::to_time_t(expiration_time)); + key_info_protobuf.set_lose_time((int64_t)std::chrono::system_clock::to_time_t(lose_time)); + return key_info_protobuf; +} + KeyProvider::KeyProvider(Socket* parent) : max_history_size(getValue("overlay.key_provider.history_size") + 1), //Yes, +1 means newly generated key. timer(AsioIOService::getIOService()), diff --git a/src/daemon/overlay/KeyProvider.h b/src/daemon/overlay/KeyProvider.h index 4e95761..b6be5b0 100644 --- a/src/daemon/overlay/KeyProvider.h +++ b/src/daemon/overlay/KeyProvider.h @@ -20,9 +20,12 @@ #include "../../common/Config.h" #include "../../common/Loggable.h" +#include "Handshake.pb.h" + #include #include #include +#include #include #include @@ -30,7 +33,10 @@ namespace p2pnet { namespace overlay { -struct KeyInfo { +class KeyInfo { + void fromProtobuf(const protocol::Handshake_SignedHandshake_KeyInfo& key_info_s); + protocol::Handshake_SignedHandshake_KeyInfo toProtobuf() const; +public: crypto::PrivateKeyDSA private_key; crypto::PublicKeyDSA public_key; TH th; diff --git a/src/daemon/overlay/Node.cpp b/src/daemon/overlay/Node.cpp index dc7f558..1605ea4 100644 --- a/src/daemon/overlay/Node.cpp +++ b/src/daemon/overlay/Node.cpp @@ -11,7 +11,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#include "OverlayNode.h" +#include "Node.h" #include "OverlayNode.pb.h" #include "../AsioIOService.h" diff --git a/src/daemon/overlay/Node.h b/src/daemon/overlay/Node.h index 65c330f..510b94f 100644 --- a/src/daemon/overlay/Node.h +++ b/src/daemon/overlay/Node.h @@ -46,8 +46,7 @@ class Node : public dht::DHTNode, public std::enable_shared_from_this { KeyInfo getKeyInfo() const; void setPublicKey(const crypto::PublicKeyDSA& public_key); - void updateExpirationTime(system_clock::time_point expiry_time); - void updateLoseTime(system_clock::time_point lost_time); + // Serialization functions virtual std::string getSerializedContact() const; @@ -62,6 +61,8 @@ class Node : public dht::DHTNode, public std::enable_shared_from_this { // TransportEndpoint functions decltype(transport_endpoints) getTransportEndpoints() const {return transport_endpoints;} void updateEndpoint(const transport::SocketEndpoint& endpoint, bool verified = false); + void updateExpirationTime(system_clock::time_point expiry_time); + void updateLoseTime(system_clock::time_point lost_time); }; } /* namespace overlay */ diff --git a/src/daemon/overlay/PayloadParams.h b/src/daemon/overlay/PayloadParams.h index b237f74..67e07fb 100644 --- a/src/daemon/overlay/PayloadParams.h +++ b/src/daemon/overlay/PayloadParams.h @@ -14,7 +14,7 @@ #ifndef PAYLOADTYPES_H_ #define PAYLOADTYPES_H_ -#include "Protocol.pb.h" +#include "OverlayProtocol.pb.h" namespace p2pnet { namespace overlay { @@ -22,9 +22,8 @@ namespace overlay { enum class PayloadType { UNKNOWN = 255, - ENCRYPTED = 0, - MULTIPART = 1, - HANDSHAKE = 2, + HANDSHAKE = 1, + ONION = 2, DHT = 3 }; diff --git a/src/daemon/overlay/Socket.cpp b/src/daemon/overlay/Socket.cpp index 28d66be..37ab254 100644 --- a/src/daemon/overlay/Socket.cpp +++ b/src/daemon/overlay/Socket.cpp @@ -13,41 +13,118 @@ */ #include "Socket.h" #include "Connection.h" -#include "Protocol.pb.h" +#include "OverlayProtocol.pb.h" +#include "PayloadParams.h" +#include "processors/Handshake.h" namespace p2pnet { namespace overlay { -Socket::Socket() : key_provider(this), dht_service(this) {} +Socket::Socket() : key_provider(this), dht_service(this) { + handshake_processor = std::make_shared(shared_from_this()); + registerProcessor(PayloadType::HANDSHAKE, handshake_processor); +} Socket::~Socket() {} -void Socket::send(const TH& dest, - const protocol::OverlayMessage_Payload& message_payload, Priority prio) { - getNodeDB()->getConnection(dest)->send(message_payload, prio); +void Socket::process(int error, std::shared_ptr origin, std::string data) { + protocol::OverlayMessage message; + + /* + * Parsing message into components + */ + if(!message.ParseFromString(data)){return;} + + TH src_th(TH::fromBinaryString(message.header().src_th())); + TH dest_th = message.header().has_dest_th() ? TH::fromBinaryString(message.header().src_th()) : TH(); + + log() << "<- OverlayMessage: TH:" << src_th.toBase58() << std::endl; + + /* + * Processing message + */ + if(dest_th == getKeyProvider()->getKeyInfo()->th){ // Processing message by ourselves as regular message. + processSelf(origin, message.header(), message.body()); + }else if(dest_th){ // Not processing message by ourselves as we just relay this. + processRelay(origin, message.header(), message.body()); + }else{ // Processing message by ourselves as connection request. + processRequest(origin, message.header(), message.body()); + } } -void Socket::process(std::string data, const transport::SocketEndpoint& from) { - protocol::OverlayMessage overlay_message; - protocol::ConnectionRequestMessage request_message; +void Socket::processSelf(std::shared_ptr origin, + const OverlayMessage_Header& header, + const OverlayMessage_Body& body) { + TH src_th(TH::fromBinaryString(header.src_th())); + auto conn_it = connections.find(src_th); + std::shared_ptr conn_ptr; - if(overlay_message.ParseFromString(data)){ - overlay::TH packet_src_th(overlay::TH::fromBinaryString(overlay_message.header().src_th())); + if(conn_it == connections.end()){ + conn_ptr = std::make_shared(shared_from_this(), src_th); + connections.insert(std::make_pair(src_th, conn_ptr)); + }else{ + conn_ptr = conn_it->second; + } + + conn_it->second->process(origin, header, body); +} - log() << "<- OverlayMessage: TH:" << overlay::TH::fromBinaryString(overlay_message.header().src_th()).toBase58() << std::endl; +void Socket::processRelay(std::shared_ptr origin, + const OverlayMessage_Header& header, + const OverlayMessage_Body& body) { +} - getNodeDB()->getConnection(packet_src_th)->process(overlay_message, from); - }else if(!getValue("policies.outgoing_only") && request_message.ParseFromString(data)){ - overlay::TH packet_src_th(overlay::TH::fromBinaryString(request_message.src_th())); +void Socket::processRequest(std::shared_ptr origin, + const OverlayMessage_Header& header, + const OverlayMessage_Body& body) { + if(!getValue("policies.outgoing_only")){ + // Connect + } // else Drop. +} - log() << "<- Connection Request: TH:" << overlay::TH::fromBinaryString(request_message.src_th()).toBase58() << std::endl; +void Socket::registerConnection(std::shared_ptr connection) { // TODO + getDHT()->registerInKBucket(connection); +} - getNodeDB()->getNode(packet_src_th)->updateEndpoint(from); - if(!(getNodeDB()->getNode(packet_src_th)->hasConnection())){ - getNodeDB()->getNode(packet_src_th)->getConnection()->connect(); +void Socket::unregisterConnection(std::shared_ptr connection) { // TODO + getDHT()->removeFromKBucket(connection); +} + +void Socket::connect(const TH& dest, ConnectCallback callback) { + auto it = connections.find(dest); + auto connection_ptr = (it != connections.end()) ? (*it).second : std::make_shared(shared_from_this(), dest); + connection_ptr->connect(callback); +} + +void Socket::send(const OverlayMessage& message, SendCallback callback, std::shared_ptr connection){ + // Searching for at least one alive transport::Connection + for(auto transport_connection : connection->transport_connections){ + if(transport_connection->connected()){ + transport_connection->send( + message.SerializeAsString(), + std::bind([](int error, std::shared_ptr tconn, std::string message, int total, std::shared_ptr oconn, SendCallback ocallback){ + ocallback(error, oconn, message, total); + }, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, connection, callback) + ); } } } +void Socket::registerProcessor(PayloadType payload_type, + std::shared_ptr processor) { + processors.insert(std::make_pair(payload_type, processor)); +} + +std::list> Socket::getProcessors(PayloadType payload_type){ + auto range = processors.equal_range(payload_type); + std::list> processor_list; + + for(auto it = range.first; it != range.second; it++){ + processor_list.push_back(it->second); + } + + return processor_list; +} + } /* namespace overlay */ } /* namespace p2pnet */ diff --git a/src/daemon/overlay/Socket.h b/src/daemon/overlay/Socket.h index 84732ce..4dd0a2c 100644 --- a/src/daemon/overlay/Socket.h +++ b/src/daemon/overlay/Socket.h @@ -11,30 +11,37 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#ifndef OVERLAYSOCKET_H_ -#define OVERLAYSOCKET_H_ +#pragma once #include "TH.h" +#include "PayloadParams.h" + #include "DHT.h" #include "KeyProvider.h" +#include "processors/Processor.h" +#include "OverlayProtocol.pb.h" + +#include "../transport/Connection.h" #include "../transport/SocketEndpoint.h" -#include "../../common/Singleton.h" #include "../../common/Loggable.h" -namespace p2pnet { -namespace overlay { +#include +namespace p2pnet { using namespace protocol; +namespace overlay { +namespace processors { +class Handshake; +} -class Socket : public Loggable, ConfigClient { +class Connection; +class Socket : public Loggable, ConfigClient, public std::enable_shared_from_this { public: - //using SendCallback = std::function, std::string, int)>; // void(int error_code, SocketEndpoint endpoint, std::string message, int bytes_transferred) + using SendCallback = std::function, std::string, int)>; // void(int error_code, SocketEndpoint endpoint, std::string message, int bytes_transferred) //using ReceiveCallback = std::function, std::string)>; using ConnectCallback = std::function)>; //using DisconnectCallback = std::function)>; - - using processor_t = boost::signals2::signal, const OverlayMessage_Header&, const OverlayMessage_Payload&)>; private: KeyProvider key_provider; DHT dht_service; @@ -42,8 +49,12 @@ class Socket : public Loggable, ConfigClient { std::map> connections; std::map timed_destroy_queue; - std::map processors; + std::shared_ptr handshake_processor; + std::multimap> processors; + void processSelf(std::shared_ptr origin, const OverlayMessage_Header& header, const OverlayMessage_Body& body); + void processRelay(std::shared_ptr origin, const OverlayMessage_Header& header, const OverlayMessage_Body& body); + void processRequest(std::shared_ptr origin, const OverlayMessage_Header& header, const OverlayMessage_Body& body); public: Socket(); virtual ~Socket(); @@ -52,16 +63,17 @@ class Socket : public Loggable, ConfigClient { DHT* getDHT(){return &dht_service;} // Well, you see, it is COMPLETELY DIFFERENT from transport::Socket :) - std::shared_ptr getConnection(const TH& th); // Nullable. If nullptr, then use connect(); void registerConnection(std::shared_ptr connection); void unregisterConnection(std::shared_ptr connection); - void connect(const TH& dest, ConnectCallback); - void process(int error, std::shared_ptr origin, std::string data); // For invocation with transport::Socket::ReceiveCallback - void process(std::shared_ptr origin, const OverlayMessage_Header& header, const OverlayMessage_Payload& payload); + void connect(const TH& dest, ConnectCallback callback); + void process(int error, std::shared_ptr origin, std::string data); // For invocation with transport::Socket::ReceiveCallback + void send(const OverlayMessage& message, Socket::SendCallback callback, std::shared_ptr connection); + + void registerProcessor(PayloadType payload_type, std::shared_ptr processor); + std::list> getProcessors(PayloadType payload_type); + std::shared_ptr getHandshakeProcessor(); }; } /* namespace overlay */ } /* namespace p2pnet */ - -#endif /* OVERLAYSOCKET_H_ */ diff --git a/src/daemon/overlay/TH.h b/src/daemon/overlay/TH.h index dce4bb7..bb51bd8 100644 --- a/src/daemon/overlay/TH.h +++ b/src/daemon/overlay/TH.h @@ -11,9 +11,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - -#ifndef TH_H_ -#define TH_H_ +#pragma once #include "../../common/crypto/Hash.h" @@ -24,5 +22,3 @@ using TH = crypto::Hash; } /* namespace overlay */ } /* namespace p2pnet */ - -#endif /* TH_H_ */ diff --git a/src/daemon/overlay/processors/Handshake.cpp b/src/daemon/overlay/processors/Handshake.cpp new file mode 100644 index 0000000..9a189e5 --- /dev/null +++ b/src/daemon/overlay/processors/Handshake.cpp @@ -0,0 +1,162 @@ +/* + * You may redistribute this program and/or modify it under the terms of + * the GNU General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "Handshake.h" + +namespace p2pnet { +namespace overlay { +namespace processors { + +Handshake::Handshake() {} + +Handshake::~Handshake() {} + +void Handshake::process(std::shared_ptr connection, const protocol::OverlayMessage_Header& header, const protocol::OverlayMessage_Payload& payload) { + +} + +void Connection::performRemoteKeyRotation(std::pair previous_keys){ + if(state == State::ESTABLISHED){ + OverlayMessage message = genMessageSkel(previous_keys.second, remoteTH(), Priority::RELIABLE); + Handshake handshake; + + handshake.set_stage(handshake.PUBKEY_ROTATION); + handshake.mutable_pubkey()->CopyFrom(genPubkeyStage(handshake.PUBKEY_ROTATION, previous_keys.first)); + + sendMessage(message); + } +} + + +Handshake_PubkeyStage Connection::genPubkeyStage(Handshake::Stage for_stage, boost::optional our_hist_key){ + Handshake_PubkeyStage stage; + + std::string new_key_s = localPublicKey().toBinaryString(); + stage.mutable_signed_content()->set_new_ecdsa_key(new_key_s); + + stage.mutable_signed_content()->set_expiration_time(system_clock::to_time_t(getLocalKeyInfo().expiration_time)); + stage.mutable_signed_content()->set_lose_time(system_clock::to_time_t(getLocalKeyInfo().lose_time)); + + if(for_stage == Handshake::PUBKEY_ROTATION){ + std::string old_key_s = our_hist_key->derivePublicKey().toBinaryString(); + stage.mutable_signed_content()->set_old_ecdsa_key(old_key_s); + + auto signed_content = stage.signed_content().SerializeAsString(); + + stage.set_old_signature(our_hist_key->sign(signed_content)); + stage.set_new_signature(localPrivateKey().sign(signed_content)); + } + + return stage; +} + +Handshake_ECDHStage Connection::genECDHStage(){ + Handshake_ECDHStage stage; + + if(!session_ecdh_key.isPresent()) + session_ecdh_key = crypto::ECDH::generateNewKey(); + + auto public_ecdh_component = session_ecdh_key.derivePublicKey(); + + stage.set_src_ecdh_pubkey(public_ecdh_component); + stage.set_signature(localPrivateKey().sign(public_ecdh_component)); + + return stage; +} + +void Connection::processHandshake(const OverlayMessage_Header& header, std::string serialized_payload) { + Handshake handshake; + handshake.ParseFromString(serialized_payload); + + switch(handshake.stage()){ + case Handshake_Stage_PUBKEY: + case Handshake_Stage_PUBKEY_ACK: + case Handshake_Stage_PUBKEY_ROTATION: + processPubkeyStage(header, handshake); + break; + + case Handshake_Stage_ECDH: + case Handshake_Stage_ECDH_ACK: + processECDHStage(header, handshake); + break; + } +} + +void Connection::processPubkeyStage(const OverlayMessage_Header& header, Handshake handshake_payload){ + auto new_ecdsa_key = crypto::PublicKeyDSA::fromBinaryString(handshake_payload.pubkey().signed_content().new_ecdsa_key()); + + if(crypto::Hash(new_ecdsa_key) == remoteTH()){ + node_ptr->setPublicKey(new_ecdsa_key); + }else if(handshake_payload.stage() == handshake_payload.PUBKEY_ROTATION && + handshake_payload.pubkey().signed_content().has_old_ecdsa_key()){ + auto signed_content = handshake_payload.pubkey().signed_content().SerializeAsString(); + auto old_ecdsa_key = crypto::PublicKeyDSA::fromBinaryString(handshake_payload.pubkey().signed_content().old_ecdsa_key()); + + if(old_ecdsa_key.verify(signed_content, handshake_payload.pubkey().old_signature()) && + new_ecdsa_key.validate() && + new_ecdsa_key.verify(signed_content, handshake_payload.pubkey().new_signature())){ + // So, the new key is good + node_ptr->setPublicKey(new_ecdsa_key); + } + }else{ + return; + } + + if(handshake_payload.stage() != handshake_payload.PUBKEY_ROTATION){ + auto reply = genMessageSkel(header); + Handshake new_handshake_payload; + + switch(handshake_payload.stage()){ + case Handshake_Stage_PUBKEY: + new_handshake_payload.mutable_pubkey()->CopyFrom(genPubkeyStage(Handshake_Stage_PUBKEY_ACK)); + break; + case Handshake_Stage_PUBKEY_ACK: + new_handshake_payload.mutable_pubkey()->CopyFrom(genECDHStage()); + break; + default: + ; + } + addPayload(new_handshake_payload.SerializeAsString(), PayloadType::HANDSHAKE, reply); + sendMessage(reply); + } +} + +void Connection::processECDHStage(const OverlayMessage_Header& header, Handshake handshake_payload){ + if(!session_ecdh_key.isPresent()) + session_ecdh_key = crypto::ECDH::generateNewKey(); + + auto salt_v = (remoteTH() ^ localTH()).toBinaryString(); + auto derived_aes_string = session_ecdh_key.deriveSymmetricKey(crypto::AES::vectorSize(), + handshake_payload.ecdh().src_ecdh_pubkey(), salt_v); + session_aes_key = crypto::AES::fromBinaryString(derived_aes_string); + + log() << "Received ECDH public key from: TH:" << remoteTH().toBase58() << std::endl; + + auto reply = genMessageSkel(header); + Handshake new_handshake_payload; + + if(handshake_payload.stage() == handshake_payload.ECDH && state == State::PUBKEY_RECEIVED){ + /* We need to send back ECDH_ACK */ + new_handshake_payload.mutable_ecdh()->CopyFrom(genECDHStage()); + + addPayload(new_handshake_payload.SerializeAsString(), PayloadType::HANDSHAKE, reply); + sendMessage(reply); + } + + setState(State::ESTABLISHED); + log() << "AES encrypted connection with TH:" << remoteTH().toBase58() << " established" << std::endl; +} + +} /* namespace processors */ +} /* namespace overlay */ +} /* namespace p2pnet */ diff --git a/src/daemon/overlay/processors/Handshake.h b/src/daemon/overlay/processors/Handshake.h new file mode 100644 index 0000000..f130e33 --- /dev/null +++ b/src/daemon/overlay/processors/Handshake.h @@ -0,0 +1,44 @@ +/* + * You may redistribute this program and/or modify it under the terms of + * the GNU General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include "Processor.h" +#include "Handshake.pb.h" + +namespace p2pnet { +namespace overlay { +namespace processors { + +class Handshake: public Processor { +public: + Handshake(); + virtual ~Handshake(); + + protocol::Handshake genPubkeyStage(Handshake::Stage for_stage, boost::optional our_hist_key = boost::none); + protocol::Handshake genECDHStage(); + + void performRemoteKeyRotation(std::pair previous_keys); + + void processHandshake(const OverlayMessage_Header& header, std::string serialized_payload); + + void processPubkeyStage(const OverlayMessage_Header& header, Handshake handshake_payload); + void processECDHStage(const OverlayMessage_Header& header, Handshake handshake_payload); + + virtual bool isEncryptionMandatory() const {return false;}; // Encryption is mandatory by default + void process(std::shared_ptr connection, const OverlayMessage_Header& header, const OverlayMessage_Payload& payload); +}; + +} /* namespace processors */ +} /* namespace overlay */ +} /* namespace p2pnet */ diff --git a/src/daemon/overlay/processors/Processor.h b/src/daemon/overlay/processors/Processor.h new file mode 100644 index 0000000..a6250b4 --- /dev/null +++ b/src/daemon/overlay/processors/Processor.h @@ -0,0 +1,40 @@ +/* + * You may redistribute this program and/or modify it under the terms of + * the GNU General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include "OverlayProtocol.pb.h" +#include + +namespace p2pnet { +using namespace protocol; +namespace overlay { +namespace processors { + +class Connection; +class Processor { + friend class Socket; + std::weak_ptr parent; +protected: + std::shared_ptr getParent(){return parent.lock();}; +public: + Processor(std::weak_ptr parent){this->parent(parent);} + virtual ~Processor(); + + virtual bool isEncryptionMandatory() const {return true;}; // Encryption is mandatory by default + virtual void process(std::shared_ptr connection, const OverlayMessage_Header& header, const OverlayMessage_Payload& payload) = 0; +}; + +} /* namespace processors */ +} /* namespace overlay */ +} /* namespace p2pnet */ diff --git a/src/daemon/protobuf/DHT.proto b/src/daemon/protobuf/DHT.proto index f87ff61..413f2dd 100644 --- a/src/daemon/protobuf/DHT.proto +++ b/src/daemon/protobuf/DHT.proto @@ -14,7 +14,6 @@ syntax = "proto2"; -import "Protocol.proto"; package p2pnet.protocol; message DHTPart { diff --git a/src/daemon/protobuf/Handshake.proto b/src/daemon/protobuf/Handshake.proto index e755c4c..037abc9 100644 --- a/src/daemon/protobuf/Handshake.proto +++ b/src/daemon/protobuf/Handshake.proto @@ -14,38 +14,29 @@ syntax = "proto2"; -import "Protocol.proto"; package p2pnet.protocol; message Handshake { enum Stage { - PUBKEY = 0; PUBKEY_ACK = 1; - ECDH = 2; ECDH_ACK = 3; + REQUEST = 0; REPLY = 1; PUBKEY_ROTATION = 8; + CONNECTION_REQUEST = 9; } - required Stage stage = 1; - message PubkeyStage { - message SignedContent { - optional bytes old_ecdsa_key = 1; // not used in PUBKEY, PUBKEY_ACK - required bytes new_ecdsa_key = 2; + message SignedHandshake { + message KeyInfo { + required bytes ecdsa_key = 2; required int64 expiration_time = 3; required int64 lose_time = 4; } - - required SignedContent signed_content = 1; - optional bytes old_signature = 2; // not used, either - optional bytes new_signature = 3; // not used, either - } - - message ECDHStage { - optional bytes src_ecdh_pubkey = 5; // This connection ECDH key, signed with our ECDSA key. - optional bytes signature = 6; // OUR_ECDSA_SIG(src_ecdh_pubkey) + required KeyInfo key_info = 1; + optional bytes ecdh_key = 2; // Not used in PUBKEY_ROTATION. } - optional PubkeyStage pubkey = 2; - optional ECDHStage ecdh = 3; + required SignedHandshake signed_handshake = 2; + required bytes new_key_signature = 3; + optional bytes old_key_signature = 4; // For PUBKEY_ROTATION only. } \ No newline at end of file diff --git a/src/daemon/protobuf/Protocol.proto b/src/daemon/protobuf/OverlayProtocol.proto similarity index 57% rename from src/daemon/protobuf/Protocol.proto rename to src/daemon/protobuf/OverlayProtocol.proto index 7f804ea..a3cb3e6 100644 --- a/src/daemon/protobuf/Protocol.proto +++ b/src/daemon/protobuf/OverlayProtocol.proto @@ -18,34 +18,30 @@ package p2pnet.protocol; message OverlayMessage { message Header { required bytes src_th = 1; - required bytes dest_th = 2; + optional bytes dest_th = 2; - enum MessagePriority { - REALTIME = 0; // This is like UDP. - RELIABLE = 1; // This is like SCTP (RFC 4960), but only with delivery control. - } + //enum MessagePriority { + // REALTIME = 0; // This is like UDP. + // RELIABLE = 1; // This is like SCTP (RFC 4960), but only with delivery control. + //} - optional MessagePriority prio = 3 [default = RELIABLE]; - optional uint32 seq_num = 4; - repeated uint32 ack_num = 5 [packed = true]; + //optional MessagePriority prio = 3 [default = RELIABLE]; + //optional uint32 seq_num = 4; + //repeated uint32 ack_num = 5 [packed = true]; } required Header header = 1; message Payload { required uint32 type = 1; - repeated bytes content = 2; + required bytes content = 2; + } + message MultiPayload { + repeated Payload payload = 1; + } + message Body { + optional MultiPayload open_multipayload = 1; + optional bytes encrypted_multipayload = 2; } - required Payload payload = 2; -} - -/* - * This message type is used in Local Peer Discovery through UDP. - */ -message UDPDiscoveryMessage { - required uint32 port = 1; - optional uint32 version = 2; -} -message ConnectionRequestMessage { - required bytes src_th = 1; + required Body body = 2; } \ No newline at end of file diff --git a/src/daemon/transport/Errors.h b/src/daemon/protobuf/UDPLPD.proto similarity index 68% rename from src/daemon/transport/Errors.h rename to src/daemon/protobuf/UDPLPD.proto index caf18a6..f541040 100644 --- a/src/daemon/transport/Errors.h +++ b/src/daemon/protobuf/UDPLPD.proto @@ -11,16 +11,14 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#pragma once -// Error codes. "Any resemblance to real used error codes is purely coincidental", lol. +syntax = "proto2"; +package p2pnet.protocol; -namespace p2pnet { -namespace transport { - -const int timed_out = 110; -const int connection_reset = 104; -const int no_such_interface = 180; // - -} /* namespace transport */ -} /* namespace p2pnet */ +/* + * This message type is used in Local Peer Discovery through UDP. + */ +message UDPDiscoveryMessage { + required uint32 port = 1; + optional uint32 version = 2; +} \ No newline at end of file diff --git a/src/daemon/transport/Socket.h b/src/daemon/transport/Socket.h index 85c7706..6844b8a 100644 --- a/src/daemon/transport/Socket.h +++ b/src/daemon/transport/Socket.h @@ -1 +1 @@ -/* * You may redistribute this program and/or modify it under the terms of * the GNU General Public License as published by the Free Software Foundation, * either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include #include #include #include "../../common/Loggable.h" namespace p2pnet { namespace transport { class SocketEndpoint; class Connection; class Interface; class Socket : public std::enable_shared_from_this, Loggable { protected: std::map> interfaces; std::map> connections; std::set banned_peers; public: using SendCallback = std::function, std::string, int)>; // void(int error_code, SocketEndpoint endpoint, std::string message, int bytes_transferred) using ReceiveCallback = std::function, std::string)>; using ConnectCallback = std::function)>; using DisconnectCallback = std::function)>; Socket(); virtual ~Socket(); std::shared_ptr getInterface(std::string prefix); void registerInterface(std::shared_ptr interface); // Basically, asynchronous I/O void setReceiveCallback(ReceiveCallback callback); void setDisconnectCallback(DisconnectCallback callback); // Something for interfaces std::shared_ptr getConnection(SocketEndpoint endpoint); // Nullable. If nullptr, then use connect(); void registerConnection(std::shared_ptr connection); void unregisterConnection(std::shared_ptr connection); // For everybody void connect(SocketEndpoint endpoint, ConnectCallback callback); // TODO: Exceptions or errno void ban(SocketEndpoint endpoint); // TODO void unban(SocketEndpoint endpoint); // TODO }; } /* namespace net */ } /* namespace p2pnet */ \ No newline at end of file +/* * You may redistribute this program and/or modify it under the terms of * the GNU General Public License as published by the Free Software Foundation, * either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include #include #include #include #include "../../common/Loggable.h" namespace p2pnet { namespace transport { class SocketEndpoint; class Connection; class Interface; class Socket : public std::enable_shared_from_this, Loggable { protected: std::map> interfaces; std::map> connections; std::set banned_peers; //TODO public: using SendCallback = std::function, std::string, int)>; // void(int error_code, SocketEndpoint endpoint, std::string message, int bytes_transferred) using ReceiveCallback = std::function, std::string)>; using ConnectCallback = std::function)>; using DisconnectCallback = std::function)>; Socket(); virtual ~Socket(); std::shared_ptr getInterface(std::string prefix); void registerInterface(std::shared_ptr interface); // Basically, asynchronous I/O void setReceiveCallback(ReceiveCallback callback); void setDisconnectCallback(DisconnectCallback callback); // Something for interfaces std::shared_ptr getConnection(SocketEndpoint endpoint); // Nullable. If nullptr, then use connect(); void registerConnection(std::shared_ptr connection); void unregisterConnection(std::shared_ptr connection); // For everybody void connect(SocketEndpoint endpoint, ConnectCallback callback); // TODO: Exceptions or errno void ban(SocketEndpoint endpoint); // TODO void unban(SocketEndpoint endpoint); // TODO }; } /* namespace net */ } /* namespace p2pnet */ \ No newline at end of file