From 523c86237c375317b078b9d9b655808a50335f66 Mon Sep 17 00:00:00 2001 From: study-faraphel Date: Sun, 17 Nov 2024 22:34:52 +0100 Subject: [PATCH] peer now calculate an asymetric private and public key, for future encrypted communication. --- CMakeLists.txt | 11 ++-- source/Context.cpp | 12 ++++ source/Context.hpp | 11 ++-- source/Manager.cpp | 34 ++++++----- source/Manager.hpp | 2 +- source/Peer.cpp | 7 ++- source/Peer.hpp | 7 ++- source/behavior/events/info/InfoEvent.cpp | 2 +- source/packets/info/InfoPacketData.hpp | 2 +- source/utils/crypto.cpp | 1 + source/utils/crypto.hpp | 70 +++++++++++++++++++++++ 11 files changed, 128 insertions(+), 31 deletions(-) create mode 100644 source/Context.cpp create mode 100644 source/utils/crypto.cpp create mode 100644 source/utils/crypto.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index afb3f96..16d777a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,7 +17,6 @@ pkg_check_modules(PORTAUDIO REQUIRED portaudio-2.0) pkg_check_modules(OpenSSL REQUIRED openssl) - add_executable(M2-PT-DRP source/main.cpp source/packets/audio/AudioPacketData.hpp @@ -64,6 +63,9 @@ add_executable(M2-PT-DRP source/utils/network.hpp source/Peer.cpp source/RemotePeer.cpp + source/Context.cpp + source/utils/crypto.cpp + source/utils/crypto.hpp ) target_include_directories(M2-PT-DRP PRIVATE source @@ -73,7 +75,8 @@ target_include_directories(M2-PT-DRP PRIVATE ) target_link_libraries(M2-PT-DRP PRIVATE argparse - ${MPG123_LIBRARIES} - ${PORTAUDIO_LIBRARIES} - ${OPENSSL_LIBRARIES} + mpg123 + portaudio + ssl + crypto ) diff --git a/source/Context.cpp b/source/Context.cpp new file mode 100644 index 0000000..4f33a23 --- /dev/null +++ b/source/Context.cpp @@ -0,0 +1,12 @@ +#include "Context.hpp" + + +Context::Context(const std::array &privateKey, const std::array &publicKey) : me(publicKey) { + this->cryptoRsaPrivateKey = privateKey; + + this->socket = -1; + this->broadcastAddressInfo = nullptr; + this->server = nullptr; + + this->latestPeerDiscovery = std::chrono::high_resolution_clock::now(); +} diff --git a/source/Context.hpp b/source/Context.hpp index b44cf94..5c55adb 100644 --- a/source/Context.hpp +++ b/source/Context.hpp @@ -10,16 +10,11 @@ /** * Information about the current state of our machine. * Used everywhere to determinate how to behave. + * Information contained here may be private, contrary to the "me" attribute holding public data that can be shared. */ class Context { public: - explicit Context() { - this->socket = -1; - this->broadcastAddressInfo = nullptr; - this->server = nullptr; - - this->latestPeerDiscovery = std::chrono::high_resolution_clock::now(); - } + explicit Context(const std::array& privateKey, const std::array& publicKey); int socket; /// current socket file descriptor, used to communicate addrinfo* broadcastAddressInfo; /// address used to broadcast messages @@ -28,4 +23,6 @@ public: Peer me; /// information about our own machine std::list> remotePeers {}; /// information about other machines std::chrono::high_resolution_clock::time_point latestPeerDiscovery; /// time of the latest discovered machine + + std::array cryptoRsaPrivateKey {}; /// the RSA private key }; diff --git a/source/Manager.cpp b/source/Manager.cpp index 1f52ef8..b112d18 100644 --- a/source/Manager.cpp +++ b/source/Manager.cpp @@ -20,11 +20,15 @@ #include "behavior/tasks/client/ClientTask.hpp" #include "behavior/tasks/server/ServerTask.hpp" #include "behavior/tasks/undefined/UndefinedTask.hpp" +#include "utils/crypto.hpp" Manager::Manager(const std::string& address, const std::string& port, const bool useIpv6) { std::cout << "Broadcast address: " << address << ":" << port << " (" << (useIpv6 ? "IPv6" : "IPv4") << ")" << std::endl; + auto [privateKey, publicKey] = newRsaKeys<2048>(); + this->context = std::make_shared(privateKey, publicKey); + // register the different events type this->eventRegistry = { {drp::event::EventType::PONG, std::make_shared()}, @@ -47,19 +51,19 @@ Manager::Manager(const std::string& address, const std::string& port, const bool broadcastAddressHints.ai_protocol = IPPROTO_UDP; // create the client socket - this->context.socket = socket( + this->context->socket = socket( broadcastAddressHints.ai_family, broadcastAddressHints.ai_socktype, broadcastAddressHints.ai_protocol ); - if (this->context.socket < 0) + if (this->context->socket < 0) throw std::runtime_error("Could not create the socket: " + std::string(strerror(errno))); // allow IPv6 multicast loopback so that we can receive our own messages. const int socketLoopback = 1; if (setsockopt( - context.socket, + context->socket, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &socketLoopback, @@ -73,7 +77,7 @@ Manager::Manager(const std::string& address, const std::string& port, const bool address.c_str(), port.c_str(), &broadcastAddressHints, - &context.broadcastAddressInfo + &context->broadcastAddressInfo ) != 0) throw std::runtime_error("Could not get the broadcast address: " + std::string(gai_strerror(error))); @@ -96,14 +100,14 @@ Manager::Manager(const std::string& address, const std::string& port, const bool // bind the socket to the address if (bind( - this->context.socket, + this->context->socket, anyAddressInfo->ai_addr, anyAddressInfo->ai_addrlen ) < 0) throw std::runtime_error("Could not bind to the address: " + std::string(strerror(errno))); // TODO(Faraphel): should only be enabled in specific case. - this->context.me.serverEnabled = true; + this->context->me.serverEnabled = true; } @@ -115,25 +119,25 @@ void Manager::loop() { this->senderThread.join(); this->receiverThread.join(); - freeaddrinfo(this->context.broadcastAddressInfo); + freeaddrinfo(this->context->broadcastAddressInfo); } void Manager::loopSender() { while (true) { - std::cout << "[Sender] Handling status: " + std::to_string(static_cast(this->context.me.status)) << std::endl; + std::cout << "[Sender] Handling status: " + std::to_string(static_cast(this->context->me.status)) << std::endl; // get the corresponding task class std::shared_ptr task; try { - task = this->taskRegistry.at(this->context.me.status); + task = this->taskRegistry.at(this->context->me.status); } catch (const std::out_of_range& exception) { std::cerr << "[Sender] Unsupported status." << std::endl; continue; } // ask the task class to handle the task - task->handle(this->context); + task->handle(*this->context); } } @@ -149,7 +153,7 @@ void Manager::loopReceiver() { while (true) { // receive new data const ssize_t size = recvfrom( - this->context.socket, + this->context->socket, &packet, sizeof(packet), 0, @@ -160,7 +164,7 @@ void Manager::loopReceiver() { throw std::runtime_error("[Receiver] Could not receive the packet: " + std::string(strerror(errno))); // if the packet channel is neither 0 (all) nor the current one, ignore it - if (packet.channel != 0 && packet.channel != this->context.me.channel) + if (packet.channel != 0 && packet.channel != this->context->me.channel) continue; // decrypt the packet @@ -169,14 +173,14 @@ void Manager::loopReceiver() { // look for a saved peer with the same address auto remotePeer = std::ranges::find_if( - this->context.remotePeers, + this->context->remotePeers, [&](const std::shared_ptr& remotePeer) { return remotePeer->addressLength == fromAddressLength and std::memcmp(&fromAddress, &remotePeer->address, fromAddressLength) == 0; } ); // if found, update the latest connection date - if (remotePeer != this->context.remotePeers.end()) { + if (remotePeer != this->context->remotePeers.end()) { (*remotePeer)->latestConnection = std::chrono::high_resolution_clock::now(); } @@ -193,7 +197,7 @@ void Manager::loopReceiver() { // ask the event class to handle the event event->handle( - this->context, + *this->context, packetContent, fromAddress, fromAddressLength diff --git a/source/Manager.hpp b/source/Manager.hpp index 909839e..cc1fb1f 100644 --- a/source/Manager.hpp +++ b/source/Manager.hpp @@ -31,5 +31,5 @@ private: std::map> eventRegistry; /// hold the event to call depending on the event type std::map> taskRegistry; /// hold the task to call depending on the server status - Context context; /// context used between the events types + std::shared_ptr context; /// context used between the events types }; diff --git a/source/Peer.cpp b/source/Peer.cpp index 88aef33..b1f088f 100644 --- a/source/Peer.cpp +++ b/source/Peer.cpp @@ -1,13 +1,18 @@ #include "Peer.hpp" -Peer::Peer() { +Peer::Peer() : Peer(std::array()) {} + + +Peer::Peer(const std::array& cryptoRsaPublicKey) { this->id = randomDistribution(randomGenerator); this->channel = 0; this->serverEnabled = false; this->status = drp::task::TaskType::UNDEFINED; this->latencyAverage = std::chrono::high_resolution_clock::duration::max(); + + this->cryptoRsaPublicKey = cryptoRsaPublicKey; } diff --git a/source/Peer.hpp b/source/Peer.hpp index 615e84a..a9ca63b 100644 --- a/source/Peer.hpp +++ b/source/Peer.hpp @@ -3,16 +3,19 @@ #include #include #include +#include #include "behavior/tasks/types.hpp" /** * Contains common information about a certain peer. + * All the information contained here are "public": they can be communicated to anybody else safely. */ class Peer { public: - explicit Peer(); + Peer(); + explicit Peer(const std::array& cryptoRsaPublicKey); std::uint32_t id; @@ -22,6 +25,8 @@ public: std::chrono::high_resolution_clock::duration latencyAverage {}; + std::array cryptoRsaPublicKey {}; + private: // random static std::random_device randomDevice; diff --git a/source/behavior/events/info/InfoEvent.cpp b/source/behavior/events/info/InfoEvent.cpp index 05d1264..ca3382e 100644 --- a/source/behavior/events/info/InfoEvent.cpp +++ b/source/behavior/events/info/InfoEvent.cpp @@ -29,7 +29,7 @@ void InfoEvent::handle( } ); - // if not found, create a new remote peer. + // if not already stored, create a new remote peer. if (itRemotePeer == context.remotePeers.end()) { // if not found, create a new peer const auto remotePeer = std::make_shared(fromAddress, fromAddressLength, packetPeer); diff --git a/source/packets/info/InfoPacketData.hpp b/source/packets/info/InfoPacketData.hpp index cda5a7a..7a137fd 100644 --- a/source/packets/info/InfoPacketData.hpp +++ b/source/packets/info/InfoPacketData.hpp @@ -14,7 +14,7 @@ namespace drp::packet::info { */ class InfoPacketData : public base::PacketData { public: - Peer peer {}; + Peer peer; }; diff --git a/source/utils/crypto.cpp b/source/utils/crypto.cpp new file mode 100644 index 0000000..05f8386 --- /dev/null +++ b/source/utils/crypto.cpp @@ -0,0 +1 @@ +#include "crypto.hpp" diff --git a/source/utils/crypto.hpp b/source/utils/crypto.hpp new file mode 100644 index 0000000..3df5127 --- /dev/null +++ b/source/utils/crypto.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + + +template +std::pair, std::array> newRsaKeys() { + // create the context + const auto context = std::unique_ptr( + EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, nullptr), + EVP_PKEY_CTX_free + ); + + if (context == nullptr) + throw std::runtime_error("Could not create an EVP context."); + + if (EVP_PKEY_keygen_init(context.get()) <= 0) + throw std::runtime_error("Could not initialize the EVP context."); + + // configure the context + if (EVP_PKEY_CTX_set_rsa_keygen_bits(context.get(), size) <= 0) + throw std::runtime_error("Error setting RSA key size."); + + // create the private key + EVP_PKEY* rawKeyPair = nullptr; + if (EVP_PKEY_keygen(context.get(), &rawKeyPair) <= 0) + throw std::runtime_error("Could not generate RSA private key."); + + const std::unique_ptr keyPair( + rawKeyPair, + EVP_PKEY_free + ); + + // extract the private and public key + const std::shared_ptr privateBio( + BIO_new(BIO_s_mem()), + BIO_free + ); + + if (!PEM_write_bio_PrivateKey( + privateBio.get(), + keyPair.get(), + nullptr, + nullptr, + 0, + nullptr, + nullptr + )) + throw std::runtime_error("Could not generate RSA private key."); + + std::array privateKey; + BIO_read(privateBio.get(), privateKey.data(), BIO_pending(privateBio.get())); + + const std::shared_ptr publicBio( + BIO_new(BIO_s_mem()), + BIO_free + ); + if (!PEM_write_bio_PUBKEY(publicBio.get(), keyPair.get())) + throw std::runtime_error("Could not generate RSA public key."); + + std::array publicKey; + BIO_read(publicBio.get(), publicKey.data(), BIO_pending(publicBio.get())); + + return {privateKey, publicKey}; +}