From c825ef8bef4ee9ec734dd1d106c0181d6bee2a43 Mon Sep 17 00:00:00 2001 From: study-faraphel Date: Fri, 3 Jan 2025 21:57:08 +0100 Subject: [PATCH] recreated a base for a python simplified version --- .gitmodules | 3 - CMakeLists.txt | 90 ---------- Dockerfile | 24 --- docker-compose.yml | 37 ---- external/argparse | 1 - requirements.txt | 4 + source/Context.cpp | 22 --- source/Context.hpp | 36 ---- source/Peer.cpp | 78 -------- source/Peer.hpp | 47 ----- .../{utils/serialize/basics.cpp => Peer.py} | 0 source/RemotePeer.cpp | 15 -- source/RemotePeer.hpp | 27 --- source/__init__.py | 3 + source/__main__.py | 15 ++ source/behavior/events/README.md | 1 - source/behavior/events/audio/AudioEvent.cpp | 145 --------------- source/behavior/events/audio/AudioEvent.hpp | 44 ----- .../events/audio/AudioPacketsComparator.cpp | 12 -- .../events/audio/AudioPacketsComparator.hpp | 14 -- source/behavior/events/base/BaseEvent.hpp | 22 --- source/behavior/events/info/InfoEvent.cpp | 52 ------ source/behavior/events/info/InfoEvent.hpp | 21 --- source/behavior/events/pong/PongEvent.cpp | 19 -- source/behavior/events/pong/PongEvent.hpp | 20 --- source/behavior/events/search/SearchEvent.cpp | 59 ------- source/behavior/events/search/SearchEvent.hpp | 20 --- source/behavior/events/types.hpp | 25 --- source/behavior/tasks/README.md | 2 - source/behavior/tasks/base/BaseTask.hpp | 26 --- source/behavior/tasks/client/ClientTask.cpp | 41 ----- source/behavior/tasks/client/ClientTask.hpp | 22 --- source/behavior/tasks/server/ServerTask.cpp | 142 --------------- source/behavior/tasks/server/ServerTask.hpp | 37 ---- source/behavior/tasks/types.hpp | 14 -- .../tasks/undefined/UndefinedTask.cpp | 119 ------------- .../tasks/undefined/UndefinedTask.hpp | 18 -- source/behaviors/__init__.py | 0 source/behaviors/events/DiscoveryEvent.py | 7 + source/behaviors/events/__init__.py | 0 source/behaviors/events/base/BaseEvent.py | 5 + source/behaviors/events/base/__init__.py | 1 + source/behaviors/roles/UndefinedRole.py | 9 + source/behaviors/roles/__init__.py | 1 + source/behaviors/roles/base/BaseRole.py | 11 ++ source/behaviors/roles/base/__init__.py | 1 + source/main.cpp | 41 ----- source/managers/CommunicationManager.py | 118 +++++++++++++ source/managers/Manager.cpp | 112 ------------ source/managers/Manager.hpp | 32 ---- source/managers/Manager.py | 30 ++++ source/managers/ReceiveManager.cpp | 101 ----------- source/managers/ReceiveManager.hpp | 29 --- source/managers/SendManager.cpp | 52 ------ source/managers/SendManager.hpp | 30 ---- source/managers/__init__.py | 2 + source/packets/AudioPacket.py | 30 ++++ source/packets/DiscoveryPacket.py | 22 +++ source/packets/__init__.py | 4 + source/packets/audio/AudioPacketData.cpp | 43 ----- source/packets/audio/AudioPacketData.hpp | 32 ---- source/packets/base/BasePacket.py | 19 ++ source/packets/base/Packet.cpp | 99 ----------- source/packets/base/Packet.hpp | 39 ---- source/packets/base/PacketContent.cpp | 47 ----- source/packets/base/PacketContent.hpp | 35 ---- source/packets/base/SecurityMode.hpp | 14 -- source/packets/base/__init__.py | 1 + source/packets/info/InfoPacketData.cpp | 23 --- source/packets/info/InfoPacketData.hpp | 22 --- source/packets/search/SearchPacketData.cpp | 25 --- source/packets/search/SearchPacketData.hpp | 23 --- source/test.cpp | 146 --------------- source/utils/__init__.py | 0 source/utils/audio/audio.cpp | 47 ----- source/utils/audio/audio.hpp | 19 -- source/utils/crypto/__init__.py | 0 source/utils/crypto/aes.py | 47 +++++ source/utils/crypto/aes/AesKey.cpp | 1 - source/utils/crypto/aes/AesKey.hpp | 166 ------------------ source/utils/crypto/rsa.py | 51 ++++++ source/utils/crypto/rsa/RsaKeyPair.cpp | 98 ----------- source/utils/crypto/rsa/RsaKeyPair.hpp | 44 ----- source/utils/crypto/rsa/RsaPrivateKey.cpp | 121 ------------- source/utils/crypto/rsa/RsaPrivateKey.hpp | 32 ---- source/utils/crypto/rsa/RsaPublicKey.cpp | 121 ------------- source/utils/crypto/rsa/RsaPublicKey.hpp | 37 ---- source/utils/crypto/type.py | 7 + source/utils/crypto/universal.py | 57 ++++++ source/utils/network/network.cpp | 60 ------- source/utils/network/network.hpp | 19 -- source/utils/serialize/basics.hpp | 96 ---------- source/utils/time/Chrony.cpp | 1 - source/utils/time/Chrony.hpp | 12 -- 94 files changed, 445 insertions(+), 3074 deletions(-) delete mode 100644 .gitmodules delete mode 100644 CMakeLists.txt delete mode 100644 Dockerfile delete mode 100644 docker-compose.yml delete mode 160000 external/argparse create mode 100644 requirements.txt delete mode 100644 source/Context.cpp delete mode 100644 source/Context.hpp delete mode 100644 source/Peer.cpp delete mode 100644 source/Peer.hpp rename source/{utils/serialize/basics.cpp => Peer.py} (100%) delete mode 100644 source/RemotePeer.cpp delete mode 100644 source/RemotePeer.hpp create mode 100644 source/__init__.py create mode 100644 source/__main__.py delete mode 100644 source/behavior/events/README.md delete mode 100644 source/behavior/events/audio/AudioEvent.cpp delete mode 100644 source/behavior/events/audio/AudioEvent.hpp delete mode 100644 source/behavior/events/audio/AudioPacketsComparator.cpp delete mode 100644 source/behavior/events/audio/AudioPacketsComparator.hpp delete mode 100644 source/behavior/events/base/BaseEvent.hpp delete mode 100644 source/behavior/events/info/InfoEvent.cpp delete mode 100644 source/behavior/events/info/InfoEvent.hpp delete mode 100644 source/behavior/events/pong/PongEvent.cpp delete mode 100644 source/behavior/events/pong/PongEvent.hpp delete mode 100644 source/behavior/events/search/SearchEvent.cpp delete mode 100644 source/behavior/events/search/SearchEvent.hpp delete mode 100644 source/behavior/events/types.hpp delete mode 100644 source/behavior/tasks/README.md delete mode 100644 source/behavior/tasks/base/BaseTask.hpp delete mode 100644 source/behavior/tasks/client/ClientTask.cpp delete mode 100644 source/behavior/tasks/client/ClientTask.hpp delete mode 100644 source/behavior/tasks/server/ServerTask.cpp delete mode 100644 source/behavior/tasks/server/ServerTask.hpp delete mode 100644 source/behavior/tasks/types.hpp delete mode 100644 source/behavior/tasks/undefined/UndefinedTask.cpp delete mode 100644 source/behavior/tasks/undefined/UndefinedTask.hpp create mode 100644 source/behaviors/__init__.py create mode 100644 source/behaviors/events/DiscoveryEvent.py create mode 100644 source/behaviors/events/__init__.py create mode 100644 source/behaviors/events/base/BaseEvent.py create mode 100644 source/behaviors/events/base/__init__.py create mode 100644 source/behaviors/roles/UndefinedRole.py create mode 100644 source/behaviors/roles/__init__.py create mode 100644 source/behaviors/roles/base/BaseRole.py create mode 100644 source/behaviors/roles/base/__init__.py delete mode 100644 source/main.cpp create mode 100644 source/managers/CommunicationManager.py delete mode 100644 source/managers/Manager.cpp delete mode 100644 source/managers/Manager.hpp create mode 100644 source/managers/Manager.py delete mode 100644 source/managers/ReceiveManager.cpp delete mode 100644 source/managers/ReceiveManager.hpp delete mode 100644 source/managers/SendManager.cpp delete mode 100644 source/managers/SendManager.hpp create mode 100644 source/managers/__init__.py create mode 100644 source/packets/AudioPacket.py create mode 100644 source/packets/DiscoveryPacket.py create mode 100644 source/packets/__init__.py delete mode 100644 source/packets/audio/AudioPacketData.cpp delete mode 100644 source/packets/audio/AudioPacketData.hpp create mode 100644 source/packets/base/BasePacket.py delete mode 100644 source/packets/base/Packet.cpp delete mode 100644 source/packets/base/Packet.hpp delete mode 100644 source/packets/base/PacketContent.cpp delete mode 100644 source/packets/base/PacketContent.hpp delete mode 100644 source/packets/base/SecurityMode.hpp create mode 100644 source/packets/base/__init__.py delete mode 100644 source/packets/info/InfoPacketData.cpp delete mode 100644 source/packets/info/InfoPacketData.hpp delete mode 100644 source/packets/search/SearchPacketData.cpp delete mode 100644 source/packets/search/SearchPacketData.hpp delete mode 100644 source/test.cpp create mode 100644 source/utils/__init__.py delete mode 100644 source/utils/audio/audio.cpp delete mode 100644 source/utils/audio/audio.hpp create mode 100644 source/utils/crypto/__init__.py create mode 100644 source/utils/crypto/aes.py delete mode 100644 source/utils/crypto/aes/AesKey.cpp delete mode 100644 source/utils/crypto/aes/AesKey.hpp create mode 100644 source/utils/crypto/rsa.py delete mode 100644 source/utils/crypto/rsa/RsaKeyPair.cpp delete mode 100644 source/utils/crypto/rsa/RsaKeyPair.hpp delete mode 100644 source/utils/crypto/rsa/RsaPrivateKey.cpp delete mode 100644 source/utils/crypto/rsa/RsaPrivateKey.hpp delete mode 100644 source/utils/crypto/rsa/RsaPublicKey.cpp delete mode 100644 source/utils/crypto/rsa/RsaPublicKey.hpp create mode 100644 source/utils/crypto/type.py create mode 100644 source/utils/crypto/universal.py delete mode 100644 source/utils/network/network.cpp delete mode 100644 source/utils/network/network.hpp delete mode 100644 source/utils/serialize/basics.hpp delete mode 100644 source/utils/time/Chrony.cpp delete mode 100644 source/utils/time/Chrony.hpp diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index f5f9804..0000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "external/argparse"] - path = external/argparse - url = https://github.com/p-ranav/argparse diff --git a/CMakeLists.txt b/CMakeLists.txt deleted file mode 100644 index 3837d74..0000000 --- a/CMakeLists.txt +++ /dev/null @@ -1,90 +0,0 @@ -cmake_minimum_required(VERSION 3.25) -project(M2-PT-DRP LANGUAGES CXX) - - -set(CMAKE_CXX_STANDARD 20) -set(CMAKE_CXX_STANDARD_REQUIRED ON) - - -add_executable(M2-PT-DRP - source/main.cpp - source/packets/audio/AudioPacketData.hpp - source/utils/audio/audio.cpp - source/utils/audio/audio.hpp - source/managers/Manager.cpp - source/managers/Manager.hpp - source/packets/base/Packet.hpp - source/behavior/events/types.hpp - source/packets/base/Packet.cpp - source/RemotePeer.hpp - source/behavior/events/base/BaseEvent.hpp - source/behavior/events/base/BaseEvent.hpp - source/behavior/events/audio/AudioPacketsComparator.cpp - source/behavior/events/audio/AudioPacketsComparator.hpp - source/behavior/events/audio/AudioEvent.hpp - source/behavior/events/audio/AudioEvent.cpp - source/behavior/events/pong/PongEvent.cpp - source/behavior/events/pong/PongEvent.hpp - source/behavior/events/search/SearchEvent.cpp - source/behavior/events/search/SearchEvent.hpp - source/behavior/events/info/InfoEvent.cpp - source/behavior/events/info/InfoEvent.hpp - source/behavior/tasks/types.hpp - source/behavior/tasks/base/BaseTask.hpp - source/behavior/tasks/server/ServerTask.cpp - source/behavior/tasks/server/ServerTask.hpp - source/behavior/tasks/undefined/UndefinedTask.cpp - source/behavior/tasks/undefined/UndefinedTask.hpp - source/behavior/tasks/client/ClientTask.cpp - source/behavior/tasks/client/ClientTask.hpp - source/Context.hpp - source/packets/search/SearchPacketData.hpp - source/packets/base/PacketContent.cpp - source/packets/base/PacketContent.hpp - source/packets/base/SecurityMode.hpp - source/packets/info/InfoPacketData.hpp - source/utils/time/Chrony.cpp - source/utils/time/Chrony.hpp - source/Peer.hpp - source/utils/network/network.cpp - source/utils/network/network.hpp - source/Peer.cpp - source/RemotePeer.cpp - source/Context.cpp - source/test.cpp - source/utils/crypto/aes/AesKey.cpp - source/utils/crypto/aes/AesKey.hpp - source/utils/crypto/rsa/RsaPublicKey.cpp - source/utils/crypto/rsa/RsaPublicKey.hpp - source/utils/crypto/rsa/RsaPrivateKey.cpp - source/utils/crypto/rsa/RsaPrivateKey.hpp - source/utils/crypto/rsa/RsaPrivateKey.hpp - source/utils/crypto/rsa/RsaKeyPair.cpp - source/utils/crypto/rsa/RsaKeyPair.hpp - source/utils/serialize/basics.cpp - source/utils/serialize/basics.hpp - source/packets/audio/AudioPacketData.cpp - source/packets/info/InfoPacketData.cpp - source/packets/search/SearchPacketData.cpp - source/managers/SendManager.cpp - source/managers/ReceiveManager.cpp - source/managers/ReceiveManager.hpp - source/managers/SendManager.hpp -) -target_include_directories(M2-PT-DRP PRIVATE - source - ${MPG123_INCLUDE_DIRS} - ${PORTAUDIO_INCLUDE_DIRS} - ${OPENSSL_INCLUDE_DIRS} -) -target_link_libraries(M2-PT-DRP PRIVATE - argparse - mpg123 - portaudio - ssl - crypto - argparse::argparse -) - - -add_subdirectory(external/argparse) \ No newline at end of file diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index c96906f..0000000 --- a/Dockerfile +++ /dev/null @@ -1,24 +0,0 @@ -FROM debian:latest - -# update repositories -RUN apt-get update -y -RUN apt-get upgrade -y - -# install build dependencies -RUN apt-get install -y cmake gcc g++ ninja-build -# install application dependencies -RUN apt-get install -y libmpg123-dev libssl-dev portaudio19-dev - -# enable sound -# RUN echo "pcm.!default pulse\nctl.!default pulse" > /root/.asoundrc - -# copy the application -WORKDIR /app -COPY . /app - -# build the application -RUN cmake -S . -B build -G Ninja -RUN cmake --build build - -# run the application -CMD ["build/M2-PT-DRP", "--host", "::1", "--port", "15650", "--ipv6"] diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index 044ea56..0000000 --- a/docker-compose.yml +++ /dev/null @@ -1,37 +0,0 @@ -services: - machine-1: - build: - context: ./ - dockerfile: ./Dockerfile - network: host - networks: - - machines - - machine-2: - build: - context: ./ - dockerfile: ./Dockerfile - network: host - networks: - - machines - - machine-3: - build: - context: ./ - dockerfile: ./Dockerfile - network: host - networks: - - machines - - machine-4: - build: - context: ./ - dockerfile: ./Dockerfile - network: host - networks: - - machines - -networks: - machines: - enable_ipv6: true - attachable: true diff --git a/external/argparse b/external/argparse deleted file mode 160000 index cbd9fd8..0000000 --- a/external/argparse +++ /dev/null @@ -1 +0,0 @@ -Subproject commit cbd9fd8ed675ed6a2ac1bd7142d318c6ad5d3462 diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..5b5d990 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +sounddevice +msgpack +cryptography +bidict diff --git a/source/Context.cpp b/source/Context.cpp deleted file mode 100644 index 6b9ea93..0000000 --- a/source/Context.cpp +++ /dev/null @@ -1,22 +0,0 @@ -#include "Context.hpp" - -#include "utils/crypto/rsa/RsaKeyPair.hpp" - - -Context::Context(const int socket, addrinfo* broadcastAddressInfo) { - const auto keyPair = drp::util::crypto::RsaKeyPair(2048); - - // communication - this->socket = socket; - this->broadcastAddressInfo = broadcastAddressInfo; - this->server = nullptr; - - // ourselves - this->me = Peer(keyPair.getPublicKey()); - - // others - this->latestPeerDiscovery = std::chrono::high_resolution_clock::now(); - - // crytography - this->cryptoRsaPrivateKey = keyPair.getPrivateKey(); -} diff --git a/source/Context.hpp b/source/Context.hpp deleted file mode 100644 index d1ed2db..0000000 --- a/source/Context.hpp +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -#include -#include -#include - -#include "RemotePeer.hpp" -#include "utils/crypto/aes/AesKey.hpp" -#include "utils/crypto/rsa/RsaPrivateKey.hpp" - - -/** - * 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(int socket, addrinfo* broadcastAddressInfo); - - // communication - int socket; /// current socket file descriptor, used to communicate - addrinfo* broadcastAddressInfo; /// address used to broadcast messages - std::shared_ptr server; /// peer currently used as the server - - // ourselves - Peer me; /// information about our own machine - - // others - std::list> remotePeers {}; /// information about other machines - std::chrono::high_resolution_clock::time_point latestPeerDiscovery; /// time of the latest discovered machine - - // cryptography - drp::util::crypto::RsaPrivateKey cryptoRsaPrivateKey {}; /// the RSA private key - drp::util::crypto::AesKey256 cryptoAesKey = {}; /// the AES secret key -}; diff --git a/source/Peer.cpp b/source/Peer.cpp deleted file mode 100644 index 9efba66..0000000 --- a/source/Peer.cpp +++ /dev/null @@ -1,78 +0,0 @@ -#include "Peer.hpp" - -#include "behavior/tasks/undefined/UndefinedTask.hpp" -#include "utils/serialize/basics.hpp" - - -Peer::Peer() = default; - - -Peer::Peer(const drp::util::crypto::RsaPublicKey& 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; -} - -Peer::Peer( - std::uint32_t id, - bool serverEnabled, - drp::task::TaskType status, - std::uint8_t channel, - const std::chrono::high_resolution_clock::duration& latencyAverage, - const drp::util::crypto::RsaPublicKey& cryptoRsaPublicKey -) { - this->id = id; - this->serverEnabled = serverEnabled; - this->status = status; - this->channel = channel; - this->latencyAverage = latencyAverage; - this->cryptoRsaPublicKey = cryptoRsaPublicKey; -} - - -std::vector Peer::serialize() const { - std::vector data; - - // serialized the members - const auto serializedId = drp::util::serialize::serializeObject(this->id); - const auto serializedServerEnabled = drp::util::serialize::serializeObject(this->serverEnabled); - const auto serializedStatus = drp::util::serialize::serializeObject(static_cast(this->status)); - const auto serializedChannel = drp::util::serialize::serializeObject(this->channel); - const auto serializedLatencyAverage = drp::util::serialize::serializeObject(this->latencyAverage); - const auto serializedPublicKey = this->cryptoRsaPublicKey.serialize(); - - // store them in the data - data.insert(data.end(), serializedId.begin(), serializedId.end()); - data.insert(data.end(), serializedServerEnabled.begin(), serializedServerEnabled.end()); - data.insert(data.end(), serializedStatus.begin(), serializedStatus.end()); - data.insert(data.end(), serializedChannel.begin(), serializedChannel.end()); - data.insert(data.end(), serializedLatencyAverage.begin(), serializedLatencyAverage.end()); - data.insert(data.end(), serializedPublicKey.begin(), serializedPublicKey.end()); - - return data; -} - -Peer Peer::deserialize(std::vector& data) { - // deserialize the members - const auto id = drp::util::serialize::deserializeObject(data); - const auto serverEnabled = drp::util::serialize::deserializeObject(data); - const auto status = static_cast(drp::util::serialize::deserializeObject(data)); - const auto channel = drp::util::serialize::deserializeObject(data); - const auto latencyAverage = drp::util::serialize::deserializeObject(data); - const auto publicKey = drp::util::crypto::RsaPublicKey::deserialize(data); - - return Peer(id, serverEnabled, status, channel, latencyAverage, publicKey); -} - - -std::mt19937 Peer::randomGenerator = std::mt19937(std::random_device{}()); - -std::uniform_int_distribution Peer::randomDistribution = std::uniform_int_distribution( - std::numeric_limits::min(), - std::numeric_limits::max() -); \ No newline at end of file diff --git a/source/Peer.hpp b/source/Peer.hpp deleted file mode 100644 index ddee339..0000000 --- a/source/Peer.hpp +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once - -#include -#include -#include - -#include "behavior/tasks/types.hpp" -#include "utils/crypto/rsa/RsaPublicKey.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: - Peer(); - explicit Peer(const drp::util::crypto::RsaPublicKey& cryptoRsaPublicKey); - explicit Peer( - std::uint32_t id, - bool serverEnabled, - drp::task::TaskType status, - std::uint8_t channel, - const std::chrono::high_resolution_clock::duration& latencyAverage, - const drp::util::crypto::RsaPublicKey& cryptoRsaPublicKey - ); - - [[nodiscard]] std::vector serialize() const; - static Peer deserialize(std::vector &data); - - // identification - std::uint32_t id {}; // TODO(Faraphel): shall be removed in the future - - // network - bool serverEnabled {}; - drp::task::TaskType status {}; - std::uint8_t channel {}; - std::chrono::high_resolution_clock::duration latencyAverage {}; - - // cryptography - drp::util::crypto::RsaPublicKey cryptoRsaPublicKey {}; - -private: - // random - static std::mt19937 randomGenerator; - static std::uniform_int_distribution randomDistribution; -}; diff --git a/source/utils/serialize/basics.cpp b/source/Peer.py similarity index 100% rename from source/utils/serialize/basics.cpp rename to source/Peer.py diff --git a/source/RemotePeer.cpp b/source/RemotePeer.cpp deleted file mode 100644 index 9bc32f2..0000000 --- a/source/RemotePeer.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include "RemotePeer.hpp" - - -RemotePeer::RemotePeer(const sockaddr_storage& address, const socklen_t addressLength, const Peer& peer) { - this->address = address; - this->addressLength = addressLength; - this->information = peer; - - this->latestConnection = std::chrono::system_clock::now(); - this->latency = std::chrono::high_resolution_clock::duration::max(); -} - -void RemotePeer::update(const Peer& peer) { - this->information = peer; -} diff --git a/source/RemotePeer.hpp b/source/RemotePeer.hpp deleted file mode 100644 index 8ca9252..0000000 --- a/source/RemotePeer.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include -#include - -#include "Peer.hpp" - - -/** - * Contains information about a distant peer. - */ -class RemotePeer { -public: - explicit RemotePeer(const sockaddr_storage& address, socklen_t addressLength, const Peer& peer); - - void update(const Peer& peer); - - // communication - sockaddr_storage address {}; - socklen_t addressLength; - - std::chrono::high_resolution_clock::time_point latestConnection; - std::chrono::high_resolution_clock::duration latency{}; - - // information - Peer information; -}; diff --git a/source/__init__.py b/source/__init__.py new file mode 100644 index 0000000..2af6c6a --- /dev/null +++ b/source/__init__.py @@ -0,0 +1,3 @@ +from . import managers +from . import behaviors +from . import packets diff --git a/source/__main__.py b/source/__main__.py new file mode 100644 index 0000000..434618d --- /dev/null +++ b/source/__main__.py @@ -0,0 +1,15 @@ +import threading + +from source.managers import Manager + + +manager = Manager("wlp1s0") + +thread_receive = threading.Thread(target=manager.receiveLoop) +thread_send = threading.Thread(target=manager.sendLoop) + +thread_receive.start() +thread_send.start() + +thread_receive.join() +thread_send.join() diff --git a/source/behavior/events/README.md b/source/behavior/events/README.md deleted file mode 100644 index 7e13f8a..0000000 --- a/source/behavior/events/README.md +++ /dev/null @@ -1 +0,0 @@ -This directory contains the code describing how to react to a specific event. diff --git a/source/behavior/events/audio/AudioEvent.cpp b/source/behavior/events/audio/AudioEvent.cpp deleted file mode 100644 index a38ceb9..0000000 --- a/source/behavior/events/audio/AudioEvent.cpp +++ /dev/null @@ -1,145 +0,0 @@ -#include "AudioEvent.hpp" - -#include -#include - -#include "utils/audio/audio.hpp" - - -namespace drp::event { - - -AudioEvent::AudioEvent() { - this->stream = nullptr; - this->audioLock = std::unique_lock(this->audioMutex); - - this->streamChannels = 0; - this->streamSampleFormat = 0; - this->streamRate = 0; - - // start a thread for the player - this->playerThread = std::thread(&AudioEvent::loopPlay, this); -} - -AudioEvent::~AudioEvent() { - // stop any currently playing audio - Pa_StopStream(this->stream); - // close the audio stream - if (const PaError error = Pa_CloseStream(this->stream)) - std::cerr << "[Event - Audio] Could not close the stream: " << std::string(Pa_GetErrorText(error)) << std::endl; -} - - -void AudioEvent::handle( - Context& context, - std::vector& data, - const sockaddr_storage& fromAddress, - const socklen_t fromAddressLength -) { - // get the audio data in the content - const auto audioData = packet::audio::AudioPacketData::deserialize(data); - - // save it in the audio queue - this->audioQueue.push(audioData); - // notify that a new audio chunk is available - this->audioCondition.notify_one(); -} - - -void AudioEvent::updateAudioStream(const int channels, const std::uint32_t sampleFormat, const double sampleRate) { - // check if any information changed. If no, ignore this - if ( - this->streamChannels == channels && - this->streamSampleFormat == sampleFormat && - this->streamRate == sampleRate - ) - return; - - // close the current stream - // ignore errors that could happen if no audio is currently playing - Pa_CloseStream(&this->stream); - - // open a new stream with the new settings - if (const PaError error = Pa_OpenDefaultStream( - &this->stream, - 0, - channels, - sampleFormat, - sampleRate, - paFramesPerBufferUnspecified, - nullptr, - nullptr - ) != paNoError) - throw std::runtime_error("[Event - Audio] Could not open the stream: " + std::string(Pa_GetErrorText(error))); - - // update the new audio values - this->streamChannels = channels; - this->streamSampleFormat = sampleFormat; - this->streamRate = sampleRate; -} - - -void AudioEvent::loopPlay() { - while (true) { - // wait for a new element in the audio queue - this->audioCondition.wait( - this->audioLock, - [this] { return !this->audioQueue.empty(); } - ); - // get the most recent audio chunk - const auto audioPacket = this->audioQueue.top(); - - // if the packet should have started playing before, skip it - if (audioPacket.timePlay < std::chrono::high_resolution_clock::now()) { - this->audioQueue.pop(); - continue; - } - - // update the stream with the new audio settings - this->updateAudioStream( - audioPacket.channels, - audioPacket.sampleFormat, - audioPacket.sampleRate - ); - - // wait until it must be played - std::this_thread::sleep_until(audioPacket.timePlay); - - auto cTimePlay = std::chrono::high_resolution_clock::to_time_t(audioPacket.timePlay); - std::cout << "[Event - Audio] Playing: " << std::ctime(&cTimePlay) << std::endl; - - // immediately stop playing music - // this avoids an offset created if this client's clock is too ahead of the others - // don't handle errors since audio might not be playing before - Pa_AbortStream(this->stream); - - // play the new audio data - if (const int error = Pa_StartStream(this->stream) != paNoError) - throw std::runtime_error("[Event - Audio] Could not start the PortAudio stream: " + std::string(Pa_GetErrorText(error))); - - // write the new audio data into the audio buffer - const int error = Pa_WriteStream( - this->stream, - audioPacket.content.data(), - audioPacket.content.size() / Pa_GetSampleSize(this->streamSampleFormat) / this->streamChannels - ); - switch (error) { - // success - case paNoError: - break; - // the output might be very slightly underflowed, - // causing a very small period where no noise will be played. - case paOutputUnderflowed: - break; - - default: - std::cerr << "[Event - Audio] Could not write to the audio stream: " << Pa_GetErrorText(error) << std::endl; - } - - // remove the audio chunk - this->audioQueue.pop(); - } -} - - -} diff --git a/source/behavior/events/audio/AudioEvent.hpp b/source/behavior/events/audio/AudioEvent.hpp deleted file mode 100644 index 4bc855f..0000000 --- a/source/behavior/events/audio/AudioEvent.hpp +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -#include -#include -#include - -#include "AudioPacketsComparator.hpp" -#include "../base/BaseEvent.hpp" - - -namespace drp::event { - - -class AudioEvent : public BaseEvent { -public: - AudioEvent(); - ~AudioEvent() override; - - void updateAudioStream(int channels, std::uint32_t sampleFormat, double sampleRate); - void loopPlay(); - - void handle( - Context& context, - std::vector& data, - const sockaddr_storage& fromAddress, - socklen_t fromAddressLength - ) override; - -private: - std::thread playerThread; - - PaStream* stream; - int streamChannels; - std::uint32_t streamSampleFormat; - double streamRate; - std::priority_queue, AudioPacketsComparator> audioQueue; - - std::mutex audioMutex; - std::unique_lock audioLock; - std::condition_variable audioCondition; -}; - - -} diff --git a/source/behavior/events/audio/AudioPacketsComparator.cpp b/source/behavior/events/audio/AudioPacketsComparator.cpp deleted file mode 100644 index 2d738a3..0000000 --- a/source/behavior/events/audio/AudioPacketsComparator.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include "AudioPacketsComparator.hpp" - - -namespace drp::event { - - -bool AudioPacketsComparator::operator()(const packet::audio::AudioPacketData& a, const packet::audio::AudioPacketData& b) const { - return a.timePlay > b.timePlay; -} - - -} \ No newline at end of file diff --git a/source/behavior/events/audio/AudioPacketsComparator.hpp b/source/behavior/events/audio/AudioPacketsComparator.hpp deleted file mode 100644 index cee4346..0000000 --- a/source/behavior/events/audio/AudioPacketsComparator.hpp +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "packets/audio/AudioPacketData.hpp" - - -namespace drp::event { - - -struct AudioPacketsComparator { - bool operator() (const packet::audio::AudioPacketData& a, const packet::audio::AudioPacketData& b) const; -}; - - -} \ No newline at end of file diff --git a/source/behavior/events/base/BaseEvent.hpp b/source/behavior/events/base/BaseEvent.hpp deleted file mode 100644 index 42a7422..0000000 --- a/source/behavior/events/base/BaseEvent.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include "packets/base/Packet.hpp" -#include "Context.hpp" - - -namespace drp::event { - - -class BaseEvent { -public: - virtual ~BaseEvent() = default; - virtual void handle( - Context& context, - std::vector& data, - const sockaddr_storage& fromAddress, - socklen_t fromAddressLength - ) = 0; -}; - - -} diff --git a/source/behavior/events/info/InfoEvent.cpp b/source/behavior/events/info/InfoEvent.cpp deleted file mode 100644 index 370209f..0000000 --- a/source/behavior/events/info/InfoEvent.cpp +++ /dev/null @@ -1,52 +0,0 @@ -#include "InfoEvent.hpp" - -#include -#include -#include - -#include "packets/info/InfoPacketData.hpp" - - -namespace drp::event { - - -void InfoEvent::handle( - Context& context, - std::vector& data, - const sockaddr_storage& fromAddress, - const socklen_t fromAddressLength -) { - std::cout << "[Event - Info] Received peer information." << std::endl; - - // get the peer information - const auto packetData = packet::info::InfoPacketData::deserialize(data); - const Peer packetPeer = packetData.peer; - - // check if the peer address is already in the map - const auto itRemotePeer = std::ranges::find_if( - context.remotePeers, - [&](const auto& remotePeer) { - return remotePeer->information.id == packetPeer.id; - } - ); - - // 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); - // register it in the peer list - context.remotePeers.push_back(remotePeer); - // update the latest discovery time - context.latestPeerDiscovery = std::chrono::high_resolution_clock::now(); - } else { - // get the peer - const auto& remotePeer = *itRemotePeer; - // update the peer information - remotePeer->update(packetPeer); - } - - // TODO(Faraphel): interpret the timestamp and calculate average ping -} - - -} diff --git a/source/behavior/events/info/InfoEvent.hpp b/source/behavior/events/info/InfoEvent.hpp deleted file mode 100644 index 85b159a..0000000 --- a/source/behavior/events/info/InfoEvent.hpp +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include "../base/BaseEvent.hpp" -#include - - -namespace drp::event { - - -class InfoEvent : public BaseEvent { -public: - void handle( - Context& context, - std::vector& data, - const sockaddr_storage& fromAddress, - socklen_t fromAddressLength - ) override; -}; - - -} \ No newline at end of file diff --git a/source/behavior/events/pong/PongEvent.cpp b/source/behavior/events/pong/PongEvent.cpp deleted file mode 100644 index 0d6efa2..0000000 --- a/source/behavior/events/pong/PongEvent.cpp +++ /dev/null @@ -1,19 +0,0 @@ -#include "PongEvent.hpp" - -#include - - -namespace drp::event { - - -void PongEvent::handle( - Context& context, - std::vector& data, - const sockaddr_storage& fromAddress, - const socklen_t fromAddressLength -) { - std::cout << "[Event - Pong] Pong." << std::endl; -} - - -} diff --git a/source/behavior/events/pong/PongEvent.hpp b/source/behavior/events/pong/PongEvent.hpp deleted file mode 100644 index b496fbb..0000000 --- a/source/behavior/events/pong/PongEvent.hpp +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include "../base/BaseEvent.hpp" - - -namespace drp::event { - - -class PongEvent : public BaseEvent { -public: - void handle( - Context& context, - std::vector& data, - const sockaddr_storage& fromAddress, - socklen_t fromAddressLength - ) override; -}; - - -} \ No newline at end of file diff --git a/source/behavior/events/search/SearchEvent.cpp b/source/behavior/events/search/SearchEvent.cpp deleted file mode 100644 index be99fef..0000000 --- a/source/behavior/events/search/SearchEvent.cpp +++ /dev/null @@ -1,59 +0,0 @@ -#include "SearchEvent.hpp" - -#include -#include -#include -#include -#include - -#include "packets/base/SecurityMode.hpp" -#include "packets/info/InfoPacketData.hpp" - - -namespace drp { - - -void event::SearchEvent::handle( - Context& context, - std::vector& data, - const sockaddr_storage& fromAddress, - const socklen_t fromAddressLength -) { - packet::base::Packet packet {}; - packet::base::PacketContent packetContent {}; - - // create the packet header (available to read for everyone) - packet.channel = 0; - - // create the packet data containing our information - packet::info::InfoPacketData packetData {}; - packetData.peer = context.me; - - packetContent.eventType = EventType::INFO; - packetContent.data = packetData.serialize(); - - packet.setContent(context, packet::base::SecurityMode::PLAIN, packetContent); - - // TODO(Faraphel): send back the timestamp too - - const auto serializedPacket = packet.serialize(); - - // send back our information - if (sendto( - context.socket, - serializedPacket.data(), - serializedPacket.size(), - 0, - reinterpret_cast(&fromAddress), - fromAddressLength - ) == -1) { - std::cerr << "[Event - Search] Could not send information: " << strerror(errno) << std::endl; - return; - } - - std::cout << "[Event - Search] Sent back information." << std::endl; -} - - - -} diff --git a/source/behavior/events/search/SearchEvent.hpp b/source/behavior/events/search/SearchEvent.hpp deleted file mode 100644 index 37b9934..0000000 --- a/source/behavior/events/search/SearchEvent.hpp +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include "../base/BaseEvent.hpp" - - -namespace drp::event { - - -class SearchEvent : public BaseEvent { -public: - void handle( - Context& context, - std::vector& data, - const sockaddr_storage& fromAddress, - socklen_t fromAddressLength - ) override; -}; - - -} diff --git a/source/behavior/events/types.hpp b/source/behavior/events/types.hpp deleted file mode 100644 index 52416b9..0000000 --- a/source/behavior/events/types.hpp +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - - -namespace drp::event { - - -enum class EventType { - // debug - PING = 0x00, // simple ping - PONG = 0x01, // ping response - - // protocol - SEARCH = 0x10, // search for devices - INFO = 0x011, // information about ourselves - - // security - RSA_PUBLIC_KEY = 0x20, // sharing asymmetric public key - AES_SECRET_KEY = 0x21, // sharing symmetric private key - - // functionality - AUDIO = 0x30, // play a sound at a given time -}; - - -} \ No newline at end of file diff --git a/source/behavior/tasks/README.md b/source/behavior/tasks/README.md deleted file mode 100644 index cdf1c27..0000000 --- a/source/behavior/tasks/README.md +++ /dev/null @@ -1,2 +0,0 @@ -This directory contains the code describing how should the machine send event to its peers. -TODO(Faraphel): rename "roles" ? diff --git a/source/behavior/tasks/base/BaseTask.hpp b/source/behavior/tasks/base/BaseTask.hpp deleted file mode 100644 index 3f74da7..0000000 --- a/source/behavior/tasks/base/BaseTask.hpp +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include "Context.hpp" - - -namespace drp::task { - - -/** - * The base to define a task. - * A task is a state for the machine, defining how it shall behave. - */ -class BaseTask { -public: - virtual ~BaseTask() = default; - - /** - * The handle of the task. - * Contain the behavior of that specific task. - * @param context the context to use. - */ - virtual void handle(Context& context) = 0; -}; - - -} \ No newline at end of file diff --git a/source/behavior/tasks/client/ClientTask.cpp b/source/behavior/tasks/client/ClientTask.cpp deleted file mode 100644 index fd6f6cc..0000000 --- a/source/behavior/tasks/client/ClientTask.cpp +++ /dev/null @@ -1,41 +0,0 @@ -#include "ClientTask.hpp" - -#include -#include - - -namespace drp::task { - - -void ClientTask::use(Context& context, const std::shared_ptr& server) { - context.me.status = TaskType::CLIENT; - context.server = server; -} - - - -void ClientTask::handle(Context& context) { - // get the server hostname - char host[NI_MAXHOST]; - char port[NI_MAXSERV]; - getnameinfo( - reinterpret_cast(&context.server->address), context.server->addressLength, - host, sizeof(host), - port, sizeof(port), - NI_NUMERICHOST | NI_NUMERICSERV - ); - - // connect to the chrony server - // TODO(Faraphel): only once ? - FILE* chronyProcess = popen(("chronyc add server " + std::string(host) + " iburst 2>&1").c_str(), "r"); - if (pclose(chronyProcess) == -1) - std::cerr << "[Task - Client] Failed to connect to chrony server !" << std::endl; - - // TODO(Faraphel): check if the server is still reachable. - // if connection lost, go back to undefined mode. - - std::this_thread::sleep_for(std::chrono::seconds(1)); -} - - -} diff --git a/source/behavior/tasks/client/ClientTask.hpp b/source/behavior/tasks/client/ClientTask.hpp deleted file mode 100644 index c6a531a..0000000 --- a/source/behavior/tasks/client/ClientTask.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include "../base/BaseTask.hpp" - - -namespace drp::task { - - -class ClientTask final : public BaseTask { -public: - void handle(Context& context) override; - - /** - * Set this task as the current one. - * @param context the context to apply the state on. - * @param server the server to use. - */ - static void use(Context& context, const std::shared_ptr& server); -}; - - -} diff --git a/source/behavior/tasks/server/ServerTask.cpp b/source/behavior/tasks/server/ServerTask.cpp deleted file mode 100644 index 5461118..0000000 --- a/source/behavior/tasks/server/ServerTask.cpp +++ /dev/null @@ -1,142 +0,0 @@ -#include "ServerTask.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "packets/audio/AudioPacketData.hpp" -#include "packets/base/Packet.hpp" -#include "packets/base/SecurityMode.hpp" -#include "utils/audio/audio.hpp" - - -namespace drp::task { - - -ServerTask::ServerTask() { - this->channels = 0; - this->encoding = 0; - this->sampleRate = 0; - - // create a new mpg123 handle - int error; - this->mpgHandle = mpg123_new(nullptr, &error); - if (this->mpgHandle == nullptr) - throw std::runtime_error("[Task - Server] Could not create a mpg123 handle."); - - // open the mp3 file - // TODO(Faraphel): mp3 file as argument - if (mpg123_open( - this->mpgHandle, - "./assets/Queen - Another One Bites the Dust.mp3" - ) != MPG123_OK) - throw std::runtime_error("[Task - Server] Could not open file."); - - // get the format of the file - if (mpg123_getformat( - this->mpgHandle, - &this->sampleRate, - &this->channels, - &this->encoding - ) != MPG123_OK) - throw std::runtime_error("[Task - Server] Could not get the format of the file."); -} - - -void ServerTask::use(Context& context, const std::shared_ptr& server) { - context.me.status = TaskType::SERVER; - context.server = server; -} - - -ServerTask::~ServerTask() { - // delete the mpg123 handle - mpg123_close(this->mpgHandle); - mpg123_delete(this->mpgHandle); -} - -void ServerTask::handle(Context& context) { - // TODO(Faraphel): create a chrony server - - // get the time of the start of the processing - const auto startProcessingTime = std::chrono::high_resolution_clock::now(); - - // prepare the packet structure - packet::base::Packet packet {}; - packet::base::PacketContent packetContent {}; - std::size_t done; - - // create a packet - packet::audio::AudioPacketData audioPacket; - packet.channel = 0; - - // set the audio settings - audioPacket.channels = this->channels; - audioPacket.sampleFormat = util::encoding_mpg123_to_PulseAudio(this->encoding); - audioPacket.sampleRate = this->sampleRate; - - std::vector content(64992); - - // read the file - if (mpg123_read( - this->mpgHandle, - content.data(), - content.size(), - &done - ) != MPG123_OK) { - std::cerr << "[Task - Server] Could not read audio data from file." << std::endl; - std::this_thread::sleep_for(std::chrono::seconds(1)); - return; - } - - // resize the content to fit - content.resize(done); - audioPacket.content = content; - - // set the target time - // TODO(Faraphel): dynamically change this delay to be the lowest possible - audioPacket.timePlay = - std::chrono::high_resolution_clock::now() + - std::chrono::milliseconds(5000); - - packetContent.eventType = event::EventType::AUDIO; - packetContent.data = audioPacket.serialize(); - - packet.setContent(context, packet::base::SecurityMode::AES, packetContent); - - const auto serializedPacket = packet.serialize(); - - // broadcast the audio data - if (sendto( - context.socket, - serializedPacket.data(), - serializedPacket.size(), - 0, - context.broadcastAddressInfo->ai_addr, - context.broadcastAddressInfo->ai_addrlen - ) == -1) { - std::cerr << "[Task - Server] Could not send audio packet: " << strerror(errno) << std::endl; - std::this_thread::sleep_for(std::chrono::seconds(1)); - return; - } - - std::cout << "[Task - Server] Sent: " << done << " bytes" << std::endl; - - // wait for the duration of the audio chunk, since the start of the processing - std::this_thread::sleep_until(startProcessingTime + util::get_audio_chunk_duration( - this->sampleRate, - this->channels, - mpg123_encsize(this->encoding), - done - )); -} - - -} diff --git a/source/behavior/tasks/server/ServerTask.hpp b/source/behavior/tasks/server/ServerTask.hpp deleted file mode 100644 index a510a68..0000000 --- a/source/behavior/tasks/server/ServerTask.hpp +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once -#include -#include - -#include "../base/BaseTask.hpp" - - -namespace drp::task { - - -/** - * the audio Server. - * Read and broadcast audio data. - */ -class ServerTask : public BaseTask { -public: - explicit ServerTask(); - ~ServerTask() override; - - /** - * Set this task as the current one. - * @param context the context to apply the state on. - * @param server the server to use. - */ - static void use(Context& context, const std::shared_ptr& server); - - void handle(Context& context) override; - -private: - mpg123_handle* mpgHandle; - long sampleRate; - int channels; - int encoding; -}; - - -} diff --git a/source/behavior/tasks/types.hpp b/source/behavior/tasks/types.hpp deleted file mode 100644 index 02c6e95..0000000 --- a/source/behavior/tasks/types.hpp +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - - -namespace drp::task { - - -enum class TaskType { - UNDEFINED = 0x00, - CLIENT = 0x01, - SERVER = 0x02, -}; - - -} \ No newline at end of file diff --git a/source/behavior/tasks/undefined/UndefinedTask.cpp b/source/behavior/tasks/undefined/UndefinedTask.cpp deleted file mode 100644 index e07b17f..0000000 --- a/source/behavior/tasks/undefined/UndefinedTask.cpp +++ /dev/null @@ -1,119 +0,0 @@ -#include "UndefinedTask.hpp" -#include "../types.hpp" - -#include -#include -#include -#include -#include -#include -#include - -#include "Context.hpp" -#include "behavior/tasks/client/ClientTask.hpp" -#include "behavior/tasks/server/ServerTask.hpp" -#include "packets/base/Packet.hpp" -#include "packets/base/SecurityMode.hpp" -#include "packets/search/SearchPacketData.hpp" -#include "utils/network/network.hpp" - - -namespace drp::task { - - -void UndefinedTask::use(Context &context) { - context.me.status = TaskType::UNDEFINED; - context.server = nullptr; - context.remotePeers.clear(); -} - - -void UndefinedTask::handle(Context& context) { - std::cout << "[Task - Undefined] List of peers: " << std::endl; - for (const auto& remotePeer : context.remotePeers) - std::cout << - "\tPeer(id=" << remotePeer->information.id << ", " << - "status=" << std::to_string(static_cast(remotePeer->information.status)) << ")" << - std::endl; - - // search if a server is available among the peer. - const auto& server = std::ranges::find_if( - context.remotePeers, - [&](const auto& peer) { return peer->information.status == TaskType::SERVER; } - ); - // if a server have been found, use it - if (server != context.remotePeers.end()) { - // go into client mode - ClientTask::use(context, *server); - return; - } - - // wait that no more new peers are being discovered - if ( - std::chrono::high_resolution_clock::now() - context.latestPeerDiscovery > - std::chrono::milliseconds(5000) - ) { - std::cout << "No more peers discovered." << std::endl; - - // otherwise, become the server if we have the highest ID. - // TODO(Faraphel): should use the machine with the lowest average ping - if (context.me.serverEnabled) { - // find the remote peer with the highest id that can be a server - const std::shared_ptr serverCandidate = *std::ranges::max_element( - context.remotePeers, - [&](auto& remotePeer1, auto& remotePeer2) { - return ( - (remotePeer1->information.serverEnabled ? remotePeer1->information.id : 0) < - (remotePeer2->information.serverEnabled ? remotePeer2->information.id : 0) - ); - } - ); - - // check if we are this peer - if (util::network::is_localhost(serverCandidate->address, serverCandidate->addressLength)) { - std::cout << "[Task - Undefined] Becoming server..." << std::endl; - // go into server mode - ServerTask::use(context, serverCandidate); - return; - } - } - - std::this_thread::sleep_for(std::chrono::seconds(1)); - } - - // prepare a search message - packet::base::Packet packet {}; - packet::base::PacketContent packetContent {}; - packet::search::SearchPacketData packetData {}; - - // broadcast message - packet.channel = 0; - - // search message with the time of the message being sent - packetData.timestamp = std::chrono::high_resolution_clock::now(); - - packetContent.eventType = event::EventType::SEARCH; - packetContent.data = packetData.serialize(); - - packet.setContent(context, packet::base::SecurityMode::PLAIN, packetContent); - - std::cout << "[Task - Undefined] Looking for new peers." << std::endl; - - const auto serializedPacket = packet.serialize(); - - // send the search message - if (sendto( - context.socket, - serializedPacket.data(), - serializedPacket.size(), - 0, - context.broadcastAddressInfo->ai_addr, - context.broadcastAddressInfo->ai_addrlen - ) == -1) - std::cerr << "[Task - Undefined] Could not send search event: " << strerror(errno) << std::endl; - - std::this_thread::sleep_for(std::chrono::seconds(1)); -} - - -} diff --git a/source/behavior/tasks/undefined/UndefinedTask.hpp b/source/behavior/tasks/undefined/UndefinedTask.hpp deleted file mode 100644 index af50d08..0000000 --- a/source/behavior/tasks/undefined/UndefinedTask.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once -#include - -#include "../base/BaseTask.hpp" - - -namespace drp::task { - - -class UndefinedTask final : public BaseTask { -public: - static void use(Context& context); - - void handle(Context& context) override; -}; - - -} diff --git a/source/behaviors/__init__.py b/source/behaviors/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/source/behaviors/events/DiscoveryEvent.py b/source/behaviors/events/DiscoveryEvent.py new file mode 100644 index 0000000..2fc8da7 --- /dev/null +++ b/source/behaviors/events/DiscoveryEvent.py @@ -0,0 +1,7 @@ +from . import base +from source import packets + + +class DiscoveryEvent(base.BaseEvent): + def handle(self, packet: packets.DiscoveryPacket): + pass diff --git a/source/behaviors/events/__init__.py b/source/behaviors/events/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/source/behaviors/events/base/BaseEvent.py b/source/behaviors/events/base/BaseEvent.py new file mode 100644 index 0000000..7d32e30 --- /dev/null +++ b/source/behaviors/events/base/BaseEvent.py @@ -0,0 +1,5 @@ +import abc + + +class BaseEvent(abc.ABC): + pass diff --git a/source/behaviors/events/base/__init__.py b/source/behaviors/events/base/__init__.py new file mode 100644 index 0000000..1525289 --- /dev/null +++ b/source/behaviors/events/base/__init__.py @@ -0,0 +1 @@ +from .BaseEvent import BaseEvent diff --git a/source/behaviors/roles/UndefinedRole.py b/source/behaviors/roles/UndefinedRole.py new file mode 100644 index 0000000..26d5193 --- /dev/null +++ b/source/behaviors/roles/UndefinedRole.py @@ -0,0 +1,9 @@ +from . import base + +from source import managers, packets + + +class UndefinedRole(base.BaseRole): + def run(self, manager: "managers.Manager"): + packet = packets.DiscoveryPacket() + manager.communication.broadcast(packet) diff --git a/source/behaviors/roles/__init__.py b/source/behaviors/roles/__init__.py new file mode 100644 index 0000000..ed6f133 --- /dev/null +++ b/source/behaviors/roles/__init__.py @@ -0,0 +1 @@ +from .UndefinedRole import UndefinedRole diff --git a/source/behaviors/roles/base/BaseRole.py b/source/behaviors/roles/base/BaseRole.py new file mode 100644 index 0000000..378fbe6 --- /dev/null +++ b/source/behaviors/roles/base/BaseRole.py @@ -0,0 +1,11 @@ +import abc + +from source import managers + + +class BaseRole(abc.ABC): + @abc.abstractmethod + def run(self, manager: "managers.Manager") -> None: + """ + Behavior of the role + """ diff --git a/source/behaviors/roles/base/__init__.py b/source/behaviors/roles/base/__init__.py new file mode 100644 index 0000000..357978e --- /dev/null +++ b/source/behaviors/roles/base/__init__.py @@ -0,0 +1 @@ +from .BaseRole import BaseRole diff --git a/source/main.cpp b/source/main.cpp deleted file mode 100644 index 4df621b..0000000 --- a/source/main.cpp +++ /dev/null @@ -1,41 +0,0 @@ -#include -#include -#include -#include "argparse/argparse.hpp" -#include "managers/Manager.hpp" - - -int main(const int argc, char* argv[]) { - // initialize the mpg123 library - if (mpg123_init() != MPG123_OK) - throw std::runtime_error("Error while initializing mpg123."); - - // initialize the PortAudio library - if (Pa_Initialize() != paNoError) - throw std::runtime_error("Could not initialize PortAudio."); - - argparse::ArgumentParser parser("Program"); - parser.add_argument("-h", "--host").help("Host address").default_value(std::string("127.0.0.1")); - parser.add_argument("-p", "--port").help("Port").default_value(std::string("15650")); - parser.add_argument("-6", "--ipv6").help("Use IPv6").flag(); - - try { - parser.parse_args(argc, argv); - } catch (const std::exception& exception) { - std::cerr << exception.what() << std::endl; - return EXIT_FAILURE; - } - - auto manager = drp::managers::Manager( - parser.get("--host"), - parser.get("--port"), - parser.get("-6") - ); - manager.loop(); - - // close the libraries - Pa_Terminate(); - mpg123_exit(); - - return EXIT_SUCCESS; -} diff --git a/source/managers/CommunicationManager.py b/source/managers/CommunicationManager.py new file mode 100644 index 0000000..eaa2393 --- /dev/null +++ b/source/managers/CommunicationManager.py @@ -0,0 +1,118 @@ +import socket +import typing +import zlib + +import bidict + +from source import packets +from source.utils.crypto.type import CipherType + + +class CommunicationManager: + """ + Manage everything about communication + """ + + def __init__(self, interface: str, broadcast_address: str = "ff02::1", port: int = 5555): + self.broadcast_address = broadcast_address + self.port = port + + # create an IPv6 UDP socket + self.socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) + # enable broadcast messages + self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, True) + # use multicast on the selected interface + self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_IF, socket.if_nametoindex(interface)) + # bind to listen for any message on this port + self.socket.bind(("::", self.port)) + + # create a dictionary to hold the types of packets and their headers. + self.packet_types: bidict.bidict[bytes, typing.Type[packets.base.BasePacket]] = bidict.bidict() + + # the secret key used for AES communication + self.secret_key: bytes = b"secret key!" + + def __del__(self): + # close the socket + self.socket.close() + + def register_packet_type(self, header: bytes, packet_type: typing.Type[packets.base.BasePacket]) -> None: + """ + Register a new kind of packet that can be sent or received. + :param header: the binary header identifying the packet + :param packet_type: the class of the packet + """ + + if len(header) != 4: + raise Exception("The header should be exactly 4 bytes long.") + + self.packet_types[header] = packet_type + + def packet_encode(self, packet: packets.base.BasePacket, cipher_type: CipherType) -> bytes: + """ + Encode a packet for diffusion + :param packet: a packet to encode to be sent + :param cipher_type: the type of cipher + :return: an encoded packet + """ + + # get the header identifier of the type of this packet + header: typing.Optional[bytes] = self.packet_types.inverse.get(type(packet)) + if header is None: + raise Exception(f"Unrecognised packet type: {type(packet)}. Has it been registered ?") + + # get the encoded packet data + data = packet.pack() + + # calculate its checksum using CRC32 + checksum = zlib.crc32(data).to_bytes(4, byteorder='big') + + return checksum + header + data + + def packet_decode(self, payload: bytes) -> packets.base.BasePacket: + """ + Decode a payload into a packet + :param payload: the data of the packet + :return: the deserialized packet + """ + + # split the header and data from the raw payload + checksum: int = int.from_bytes(payload[:4], "big") + header: bytes = payload[4:8] + data: bytes = payload[8:] + + # verify the checksum for corruption + if zlib.crc32(data) != checksum: + raise ValueError("The checksum is invalid.") + + # get the type of the packet from its header + packet_type: typing.Optional[typing.Type[packets.base.BasePacket]] = self.packet_types.get(header) + if header is None: + raise Exception(f"Unrecognised packet header: {header}. Has it been registered ?") + + # unpack the packet + return packet_type.unpack(data) + + def broadcast(self, packet: packets.base.BasePacket, cipher_type: CipherType = None): + """ + Broadcast a message in the network + :param cipher_type: the type of cipher + :param packet: the message to broadcast + """ + + # TODO(Faraphel): should encrypt the data if required, prepend encryption mode + # TODO(Faraphel): use a channel system (OR ESTABLISH ANOTHER PORT ???) + self.socket.sendto(self.packet_encode(packet, cipher_type), (self.broadcast_address, self.port)) + + def receive(self) -> tuple[packets.base.BasePacket, tuple]: + """ + Receive a packet + :return: the packet content alongside the address of the sender + """ + + # receive a message + payload, address = self.socket.recvfrom(65536) + # decode the payload + return self.packet_decode(payload), address + + # TODO(Faraphel): should decrypt the data diff --git a/source/managers/Manager.cpp b/source/managers/Manager.cpp deleted file mode 100644 index 5db4bd0..0000000 --- a/source/managers/Manager.cpp +++ /dev/null @@ -1,112 +0,0 @@ -#include "Manager.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "behavior/events/audio/AudioEvent.hpp" -#include "behavior/tasks/server/ServerTask.hpp" -#include "utils/crypto/aes/AesKey.hpp" -#include "utils/crypto/rsa/RsaKeyPair.hpp" - - -namespace drp::managers { - - -Manager::Manager(const std::string& address, const std::string& port, const bool useIpv6) { - std::cout << "Broadcast address: " << address << ":" << port << " (" << (useIpv6 ? "IPv6" : "IPv4") << ")" << std::endl; - - // hints for the communication - addrinfo broadcastAddressHints {}; - broadcastAddressHints.ai_family = useIpv6 ? AF_INET6 : AF_INET; - broadcastAddressHints.ai_socktype = SOCK_DGRAM; - broadcastAddressHints.ai_protocol = IPPROTO_UDP; - - // create the client socket - const int sock = socket( - broadcastAddressHints.ai_family, - broadcastAddressHints.ai_socktype, - broadcastAddressHints.ai_protocol - ); - if (sock < 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. - constexpr int socketLoopback = 1; - if (setsockopt( - sock, - IPPROTO_IPV6, - IPV6_MULTICAST_LOOP, - &socketLoopback, - sizeof(socketLoopback) - ) < 0) { - std::cerr << "Failed to set IPV6_MULTICAST_LOOP: " << strerror(errno) << std::endl; - } - - // get the information for the broadcast local-link address - addrinfo* broadcastAddressInfo = nullptr; - if (const int error = getaddrinfo( - address.c_str(), - port.c_str(), - &broadcastAddressHints, - &broadcastAddressInfo - ) != 0) - throw std::runtime_error("Could not get the broadcast address: " + std::string(gai_strerror(error))); - - // hints for the bind address - addrinfo anyAddressHints {}; - anyAddressHints.ai_family = useIpv6 ? AF_INET6 : AF_INET; - anyAddressHints.ai_flags = AI_PASSIVE; - anyAddressHints.ai_socktype = SOCK_DGRAM; - anyAddressHints.ai_protocol = IPPROTO_UDP; - - // get the information for the broadcast local-link address - addrinfo *anyAddressInfo; - if (const int error = getaddrinfo( - nullptr, - port.c_str(), - &anyAddressHints, - &anyAddressInfo - ) != 0) - throw std::runtime_error("Could not get the any address: " + std::string(gai_strerror(error))); - - // bind the socket to the address - if (bind( - sock, - anyAddressInfo->ai_addr, - anyAddressInfo->ai_addrlen - ) < 0) - throw std::runtime_error("Could not bind to the address: " + std::string(strerror(errno))); - - // create the context - this->context = std::make_shared(sock, broadcastAddressInfo); - // TODO(Faraphel): should only be enabled if it can really emit sound. - this->context->me.serverEnabled = true; - - // create the sub-managers. - this->sendManager = std::make_unique(context); - this->receiveManager = std::make_unique(context); -} - -Manager::~Manager() { - freeaddrinfo(this->context->broadcastAddressInfo); -} - - -void Manager::loop() { - // run an event receiver and sender - std::thread senderThread(&SendManager::loop, this->sendManager.get()); - std::thread receiverThread(&ReceiveManager::loop, this->receiveManager.get()); - - senderThread.join(); - receiverThread.join(); -} - - -} \ No newline at end of file diff --git a/source/managers/Manager.hpp b/source/managers/Manager.hpp deleted file mode 100644 index 08c5ed4..0000000 --- a/source/managers/Manager.hpp +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include - -#include "Context.hpp" -#include "managers/ReceiveManager.hpp" -#include "managers/SendManager.hpp" - - -namespace drp::managers { - - -/** - * The Manager. - * serve as the mainloop of the program. - */ -class Manager { -public: - Manager(const std::string& address, const std::string& port, bool useIpv6 = false); - ~Manager(); - - void loop(); - -private: - std::shared_ptr context; /// context used between the events types - - std::unique_ptr sendManager; - std::unique_ptr receiveManager; -}; - - -} \ No newline at end of file diff --git a/source/managers/Manager.py b/source/managers/Manager.py new file mode 100644 index 0000000..e0a2942 --- /dev/null +++ b/source/managers/Manager.py @@ -0,0 +1,30 @@ +from . import CommunicationManager +from source import packets +from source.behaviors import roles + + +class Manager: + def __init__(self, interface: str): + self.communication = CommunicationManager(interface) + self.communication.register_packet_type(b"DISC", packets.DiscoveryPacket) + + # define the role of our machine + # TODO(Faraphel): give the manager to the role directly ? register ? + self.role = roles.UndefinedRole() + + + def sendLoop(self): + while True: + self.role.run(self) + + def receiveLoop(self): + try: + while True: + packet, address = self.communication.receive() + print(f"Received message from {address}: {packet}") + + # get corresponding event + # handle it + + except KeyboardInterrupt: + print("Stopping listener.") \ No newline at end of file diff --git a/source/managers/ReceiveManager.cpp b/source/managers/ReceiveManager.cpp deleted file mode 100644 index ed81f9e..0000000 --- a/source/managers/ReceiveManager.cpp +++ /dev/null @@ -1,101 +0,0 @@ -#include "ReceiveManager.hpp" - -#include -#include - -#include "behavior/events/audio/AudioEvent.hpp" -#include "behavior/events/info/InfoEvent.hpp" -#include "behavior/events/pong/PongEvent.hpp" -#include "behavior/events/search/SearchEvent.hpp" - - -namespace drp::managers { - - -ReceiveManager::ReceiveManager(const std::shared_ptr& context) { - this->context = context; - - // register the different events type - this->registry = { - {event::EventType::PONG, std::make_shared()}, - {event::EventType::SEARCH, std::make_shared()}, - {event::EventType::INFO, std::make_shared()}, - {event::EventType::AUDIO, std::make_shared()}, - }; -} - - -void ReceiveManager::run() const { - // prepare space for the sender address - sockaddr_storage fromAddress {}; - socklen_t fromAddressLength = sizeof(fromAddress); - - std::array buffer {}; - - // receive new data - const ssize_t size = recvfrom( - this->context->socket, - buffer.data(), - buffer.size(), - 0, - reinterpret_cast(&fromAddress), - &fromAddressLength - ); - if (size == -1) - throw std::runtime_error("[Receiver] Could not receive the packet: " + std::string(strerror(errno))); - - // deserialize the packet - std::vector data(buffer.begin(), buffer.end()); - const auto packet = drp::packet::base::Packet::deserialize(data); - - // if the packet channel is neither 0 (all) nor the current one, ignore it - if (packet.channel != 0 && packet.channel != this->context->me.channel) - return; - - // decrypt the packet - // TODO(Faraphel): handle exception ? - drp::packet::base::PacketContent packetContent = packet.getContent(*this->context); - - // look for a saved peer with the same address - auto remotePeer = std::ranges::find_if( - 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()) - (*remotePeer)->latestConnection = std::chrono::high_resolution_clock::now(); - - // get the corresponding event class - std::shared_ptr event; - try { - event = this->registry.at(packetContent.eventType); - } catch (const std::out_of_range& exception) { - std::cerr << "[Receiver] Unsupported event type: " << std::to_string(static_cast(packetContent.eventType)) << std::endl; - return; - } - - std::cout << "[Receiver] handling event: " << std::to_string(static_cast(packetContent.eventType)) << std::endl; - - // ask the event class to handle the event - try { - event->handle( - *this->context, - packetContent.data, - fromAddress, - fromAddressLength - ); - } catch (const std::exception& exception) { - std::cerr << "[Receiver] Unhandled exception: " << exception.what() << std::endl; - } -} - -void ReceiveManager::loop() const { - while (true) - this->run(); -} - - -} diff --git a/source/managers/ReceiveManager.hpp b/source/managers/ReceiveManager.hpp deleted file mode 100644 index 68308c4..0000000 --- a/source/managers/ReceiveManager.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once -#include -#include - -#include "behavior/events/types.hpp" -#include "behavior/events/base/BaseEvent.hpp" - - -namespace drp::managers { - - -/** - * The ReceiveManager class. - * Handle everything related to receiving messages. - */ -class ReceiveManager { -public: - explicit ReceiveManager(const std::shared_ptr& context); - - void run() const; - [[noreturn]] void loop() const; - -private: - std::shared_ptr context; - std::map> registry; -}; - - -} diff --git a/source/managers/SendManager.cpp b/source/managers/SendManager.cpp deleted file mode 100644 index d3356b5..0000000 --- a/source/managers/SendManager.cpp +++ /dev/null @@ -1,52 +0,0 @@ -#include "SendManager.hpp" - -#include - -#include "behavior/tasks/client/ClientTask.hpp" -#include "behavior/tasks/server/ServerTask.hpp" -#include "behavior/tasks/undefined/UndefinedTask.hpp" - - -namespace drp::managers { - - -SendManager::SendManager(const std::shared_ptr& context) { - this->context = context; - - // register the different tasks type - this->registry = { - {task::TaskType::UNDEFINED, std::make_shared()}, - {task::TaskType::CLIENT, std::make_shared()}, - {task::TaskType::SERVER, std::make_shared()}, - }; -} - - -void SendManager::run() const { - 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->registry.at(this->context->me.status); - } catch (const std::out_of_range& exception) { - std::cerr << "[Sender] Unsupported status: " << std::to_string(static_cast(this->context->me.status)) << std::endl; - return; - } - - // ask the task class to handle the task - try { - task->handle(*this->context); - } catch (const std::exception& exception) { - std::cerr << "[Sender] Unhandled exception: " << exception.what() << std::endl; - } -} - - -void SendManager::loop() const { - while (true) - this->run(); -} - - -} diff --git a/source/managers/SendManager.hpp b/source/managers/SendManager.hpp deleted file mode 100644 index 5ef3e5c..0000000 --- a/source/managers/SendManager.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#include -#include -#include - -#include "behavior/tasks/types.hpp" -#include "behavior/tasks/base/BaseTask.hpp" - - -namespace drp::managers { - -/** - * The SendManager class. - * Handle everything related to sending messages. - */ -class SendManager { -public: - explicit SendManager(const std::shared_ptr& context); - - void run() const; - [[noreturn]] void loop() const; - -private: - std::shared_ptr context; - std::map> registry; -}; - - -} \ No newline at end of file diff --git a/source/managers/__init__.py b/source/managers/__init__.py new file mode 100644 index 0000000..723dbce --- /dev/null +++ b/source/managers/__init__.py @@ -0,0 +1,2 @@ +from .CommunicationManager import CommunicationManager +from .Manager import Manager diff --git a/source/packets/AudioPacket.py b/source/packets/AudioPacket.py new file mode 100644 index 0000000..b12f94e --- /dev/null +++ b/source/packets/AudioPacket.py @@ -0,0 +1,30 @@ +import msgpack + +from source.packets import base + + +class AudioPacket(base.BasePacket): + """ + Represent a packet of audio data + """ + + def __init__(self, data: bytes, rate: int, channels: int, encoding: int): + super().__init__() + + self.data = data + + self.rate = rate + self.channels = channels + self.encoding = encoding + + def pack(self) -> bytes: + return msgpack.packb(( + self.data, + self.rate, + self.channels, + self.encoding + )) + + @classmethod + def unpack(cls, data: bytes): + return cls(*msgpack.unpackb(data)) diff --git a/source/packets/DiscoveryPacket.py b/source/packets/DiscoveryPacket.py new file mode 100644 index 0000000..16bf3f9 --- /dev/null +++ b/source/packets/DiscoveryPacket.py @@ -0,0 +1,22 @@ +import msgpack + +from source.packets import base + + +class DiscoveryPacket(base.BasePacket): + """ + Represent a packet used to discover new devices in the network. + """ + + def __init__(self): + super().__init__() + + def __repr__(self) -> str: + return f"<{self.__class__.__name__}>" + + def pack(self) -> bytes: + return msgpack.packb(()) + + @classmethod + def unpack(cls, data: bytes): + return cls() diff --git a/source/packets/__init__.py b/source/packets/__init__.py new file mode 100644 index 0000000..2ce4785 --- /dev/null +++ b/source/packets/__init__.py @@ -0,0 +1,4 @@ +from . import base + +from .AudioPacket import AudioPacket +from .DiscoveryPacket import DiscoveryPacket diff --git a/source/packets/audio/AudioPacketData.cpp b/source/packets/audio/AudioPacketData.cpp deleted file mode 100644 index 8552c2e..0000000 --- a/source/packets/audio/AudioPacketData.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#include "AudioPacketData.hpp" - -#include "utils/serialize/basics.hpp" - - -namespace drp::packet::audio { - - -std::vector AudioPacketData::serialize() const { - // serialize the members - const auto serializedTimePlay = util::serialize::serializeObject(this->timePlay); - const auto serializedChannels = util::serialize::serializeObject(this->channels); - const auto serializedSampleFormat = util::serialize::serializeObject(this->sampleFormat); - const auto serializedSampleRate = util::serialize::serializeObject(this->sampleRate); - const auto serializedContent = util::serialize::serializeVector(this->content); - - // create a buffer to store our members - std::vector data; - - // store our members - data.insert(data.end(), serializedTimePlay.begin(), serializedTimePlay.end()); - data.insert(data.end(), serializedChannels.begin(), serializedChannels.end()); - data.insert(data.end(), serializedSampleFormat.begin(), serializedSampleFormat.end()); - data.insert(data.end(), serializedSampleRate.begin(), serializedSampleRate.end()); - data.insert(data.end(), serializedContent.begin(), serializedContent.end()); - - return data; -} - - -AudioPacketData AudioPacketData::deserialize(std::vector& data) { - // deserialize the members - const auto packetTimePlay = util::serialize::deserializeObject>(data); - const auto packetChannels = util::serialize::deserializeObject(data); - const auto packetSampleFormat = util::serialize::deserializeObject(data); - const auto packetSampleRate = util::serialize::deserializeObject(data); - const auto packetContent = util::serialize::deserializeVector(data); - - return {packetTimePlay, packetChannels, packetSampleFormat, packetSampleRate, packetContent}; -} - - -} diff --git a/source/packets/audio/AudioPacketData.hpp b/source/packets/audio/AudioPacketData.hpp deleted file mode 100644 index 7a9d559..0000000 --- a/source/packets/audio/AudioPacketData.hpp +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include -#include -#include - - -namespace drp::packet::audio { - - -/** - * Represent the content of an audio packet. - * Contains a chunk of audio and its metadata to play it. - */ -class AudioPacketData { -public: - [[nodiscard]] std::vector serialize() const; - static AudioPacketData deserialize(std::vector& data); - - // scheduling - // TODO(Faraphel): use a more "fixed" size format ? - std::chrono::time_point timePlay; - // audio settings - std::uint8_t channels {}; - std::uint32_t sampleFormat {}; - std::uint32_t sampleRate {}; - // content - std::vector content {}; -}; - - -} diff --git a/source/packets/base/BasePacket.py b/source/packets/base/BasePacket.py new file mode 100644 index 0000000..997687b --- /dev/null +++ b/source/packets/base/BasePacket.py @@ -0,0 +1,19 @@ +import abc + + +class BasePacket(abc.ABC): + @abc.abstractmethod + def pack(self) -> bytes: + """ + Serialize the object to bytes. + :return: bytes representing the object + """ + + @classmethod + @abc.abstractmethod + def unpack(cls, data: bytes) -> "BasePacket": + """ + Deserialize the object from bytes. + :param data: the data to deserialize + :return: the deserialized object + """ diff --git a/source/packets/base/Packet.cpp b/source/packets/base/Packet.cpp deleted file mode 100644 index fc883af..0000000 --- a/source/packets/base/Packet.cpp +++ /dev/null @@ -1,99 +0,0 @@ -#include "Packet.hpp" - -#include - -#include "SecurityMode.hpp" -#include "utils/serialize/basics.hpp" - - -namespace drp::packet::base { - - -Packet::Packet() = default; - -Packet::Packet(const std::uint8_t channel, const SecurityMode securityMode, const std::vector& content) { - this->channel = channel; - this->securityMode = securityMode; - this->content = content; -} - - -PacketContent Packet::getContent(const Context& context) const { - std::vector content; - - switch (this->securityMode) { - case SecurityMode::PLAIN: - // copy the content - content = this->content; - break; - - case SecurityMode::AES: - // decrypt the content - content = context.cryptoAesKey.decrypt(this->content); - break; - - case SecurityMode::RSA: - throw std::runtime_error("Not implemented."); - - default: - throw std::runtime_error("Unsupported security mode."); - } - - // deserialize the content - return PacketContent::deserialize(content); -} - - -void Packet::setContent(const Context& context, const SecurityMode securityMode, const PacketContent& packetContent) { - this->securityMode = securityMode; - - const std::vector content = packetContent.serialize(); - - switch (this->securityMode) { - case SecurityMode::PLAIN: - // directly save the serialized content - this->content = content; - break; - - case SecurityMode::AES: - // encrypt it with the defined AES key. - this->content = context.cryptoAesKey.encrypt(content); - break; - - case SecurityMode::RSA: - throw std::runtime_error("Not implemented."); - - default: - throw std::runtime_error("Unsupported security mode."); - } -} - - -std::vector Packet::serialize() const { - // serialize the members - const auto serializedChannel = util::serialize::serializeObject(this->channel); - const auto serializedSecurityMode = util::serialize::serializeObject(static_cast(this->securityMode)); - const auto serializedContent = util::serialize::serializeVector(this->content); - - // create a buffer to store our members - std::vector data; - - // store our members - data.insert(data.end(), serializedChannel.begin(), serializedChannel.end()); - data.insert(data.end(), serializedSecurityMode.begin(), serializedSecurityMode.end()); - data.insert(data.end(), serializedContent.begin(), serializedContent.end()); - - return data; -} - -Packet Packet::deserialize(std::vector& data) { - // deserialize the members - const auto packetChannel = util::serialize::deserializeObject(data); - const auto packetSecurityMode = static_cast(util::serialize::deserializeObject(data)); - const auto packetContent = util::serialize::deserializeVector(data); - - return Packet(packetChannel, packetSecurityMode, packetContent); -} - - -} diff --git a/source/packets/base/Packet.hpp b/source/packets/base/Packet.hpp deleted file mode 100644 index 63132e2..0000000 --- a/source/packets/base/Packet.hpp +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -#include - -#include "Context.hpp" -#include "PacketContent.hpp" -#include "SecurityMode.hpp" - - -namespace drp::packet::base { - - -/** - * A generic packet that can be transmitted through the network. - * @param channel the channel of the packet. Two system can be created inside a same network by using different - * channels value. "0" is used for "broadcast" message across networks. - * @param securityMode the type of security used in the packet. - * @param _content the content of the packet. It is encrypted accordingly to the securityMode. - */ -class Packet { -public: - Packet(); - explicit Packet(std::uint8_t channel, SecurityMode securityMode, const std::vector& content); - - [[nodiscard]] PacketContent getContent(const Context& context) const; - void setContent(const Context& context, SecurityMode securityMode, const PacketContent& packetContent); - - [[nodiscard]] std::vector serialize() const; - static Packet deserialize(std::vector& data); - - std::uint8_t channel {}; - -private: - SecurityMode securityMode {}; - std::vector content; -}; - - -} diff --git a/source/packets/base/PacketContent.cpp b/source/packets/base/PacketContent.cpp deleted file mode 100644 index b424d16..0000000 --- a/source/packets/base/PacketContent.cpp +++ /dev/null @@ -1,47 +0,0 @@ -#include "PacketContent.hpp" - -#include -#include - -#include "Packet.hpp" -#include "SecurityMode.hpp" -#include "behavior/events/types.hpp" -#include "utils/serialize/basics.hpp" - - -namespace drp::packet::base { - - -PacketContent::PacketContent() = default; - -PacketContent::PacketContent(const event::EventType eventType, const std::vector& data) { - this->eventType = eventType; - this->data = data; -} - - -std::vector PacketContent::serialize() const { - // serialize the members - const auto serializedEventType = util::serialize::serializeObject(static_cast(this->eventType)); - const auto serializedData = util::serialize::serializeVector(this->data); - - // create a buffer to store our members - std::vector data; - - // store our members - data.insert(data.end(), serializedEventType.begin(), serializedEventType.end()); - data.insert(data.end(), serializedData.begin(), serializedData.end()); - - return data; -} - -PacketContent PacketContent::deserialize(std::vector& data) { - // deserialize the members - const auto contentEventType = static_cast(util::serialize::deserializeObject(data)); - const auto contentData = util::serialize::deserializeVector(data); - - return {contentEventType, contentData}; -} - - -} diff --git a/source/packets/base/PacketContent.hpp b/source/packets/base/PacketContent.hpp deleted file mode 100644 index ac15b75..0000000 --- a/source/packets/base/PacketContent.hpp +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include -#include -#include - -#include "behavior/events/types.hpp" - - -namespace drp::packet::base { - - -// The maximum length of a packet. Cannot be larger than 65565 (uint16 max). -constexpr std::uint16_t maxPacketLength = std::numeric_limits::max(); - - -/** - * The content of a generic packet. - * @param eventType the type of event that the packet want to trigger. - * @param data the data of the event. - */ -class PacketContent { -public: - PacketContent(); - PacketContent(event::EventType eventType, const std::vector& data); - - [[nodiscard]] std::vector serialize() const; - static PacketContent deserialize(std::vector& data); - - event::EventType eventType {}; - std::vector data; -}; - - -} diff --git a/source/packets/base/SecurityMode.hpp b/source/packets/base/SecurityMode.hpp deleted file mode 100644 index 3cd3be1..0000000 --- a/source/packets/base/SecurityMode.hpp +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - - -namespace drp::packet::base { - - -enum class SecurityMode { - PLAIN = 0x00, - AES = 0x01, - RSA = 0x02, -}; - - -} \ No newline at end of file diff --git a/source/packets/base/__init__.py b/source/packets/base/__init__.py new file mode 100644 index 0000000..e91f0c1 --- /dev/null +++ b/source/packets/base/__init__.py @@ -0,0 +1 @@ +from .BasePacket import BasePacket diff --git a/source/packets/info/InfoPacketData.cpp b/source/packets/info/InfoPacketData.cpp deleted file mode 100644 index 6514363..0000000 --- a/source/packets/info/InfoPacketData.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include "InfoPacketData.hpp" - - -namespace drp::packet::info { - - -std::vector InfoPacketData::serialize() const { - // serialize the members - const auto serializedPeer = this->peer.serialize(); - - return serializedPeer; -} - - -InfoPacketData InfoPacketData::deserialize(std::vector& data) { - // deserialize the members - const auto packetPeer = Peer::deserialize(data); - - return {packetPeer}; -} - - -} \ No newline at end of file diff --git a/source/packets/info/InfoPacketData.hpp b/source/packets/info/InfoPacketData.hpp deleted file mode 100644 index 72c534d..0000000 --- a/source/packets/info/InfoPacketData.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include "RemotePeer.hpp" - - -namespace drp::packet::info { - - -/** - * Represent the content of an info packet. - * Contains information about the peer sending it. - */ -class InfoPacketData { -public: - [[nodiscard]] std::vector serialize() const; - static InfoPacketData deserialize(std::vector& data); - - Peer peer; -}; - - -} diff --git a/source/packets/search/SearchPacketData.cpp b/source/packets/search/SearchPacketData.cpp deleted file mode 100644 index 9865bdf..0000000 --- a/source/packets/search/SearchPacketData.cpp +++ /dev/null @@ -1,25 +0,0 @@ -#include "SearchPacketData.hpp" - -#include "utils/serialize/basics.hpp" - - -namespace drp::packet::search { - - -std::vector SearchPacketData::serialize() const { - // serialize the members - const auto serializedTimestamp = util::serialize::serializeObject(this->timestamp); - - return serializedTimestamp; -} - - -SearchPacketData SearchPacketData::deserialize(std::vector& data) { - // deserialize the members - const auto packetTimestamp = util::serialize::deserializeObject>(data); - - return SearchPacketData(packetTimestamp); -} - - -} diff --git a/source/packets/search/SearchPacketData.hpp b/source/packets/search/SearchPacketData.hpp deleted file mode 100644 index 3239fea..0000000 --- a/source/packets/search/SearchPacketData.hpp +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include -#include - - -namespace drp::packet::search { - - -/** - * Represent a discovery request. - * Sent by someone to get information about other available machine in the network. - */ -class SearchPacketData { -public: - [[nodiscard]] std::vector serialize() const; - static SearchPacketData deserialize(std::vector& data); - - std::chrono::time_point timestamp; /// timestamp when the search request was sent -}; - - -} \ No newline at end of file diff --git a/source/test.cpp b/source/test.cpp deleted file mode 100644 index 55c16a8..0000000 --- a/source/test.cpp +++ /dev/null @@ -1,146 +0,0 @@ -#include -#include - -#include "Peer.hpp" -#include "utils/crypto/aes/AesKey.hpp" -#include "utils/crypto/rsa/RsaKeyPair.hpp" -#include "utils/serialize/basics.hpp" - - -int mainAes() { - const auto aesKey = drp::util::crypto::AesKey256(); - - // plain - std::string text = "hello world!"; - const std::vector plainData(text.begin(), text.end()); - std::cout << "plain: "; - for (const auto& byte : plainData) - std::cout << std::to_string(byte) << "-"; - std::cout << std::endl; - - // encrypted - const auto encryptedData = aesKey.encrypt(plainData); - std::cout << "encrypted: "; - for (const auto& byte : encryptedData) - std::cout << std::to_string(byte) << "-"; - std::cout << std::endl; - - // decrypted - const auto decryptedData = aesKey.decrypt(encryptedData); - std::cout << "decrypted: "; - for (const auto& byte : decryptedData) - std::cout << std::to_string(byte) << "-"; - std::cout << std::endl; - - return 0; -} - - -int mainRsa() { - const auto rsaKey = drp::util::crypto::RsaKeyPair(2048); - const auto rsaPrivateKey = rsaKey.getPrivateKey(); - const auto rsaPublicKey = rsaKey.getPublicKey(); - - // plain - std::string text = "hello world!"; - const std::vector plainData(text.begin(), text.end()); - std::cout << "plain: "; - for (const auto& byte : plainData) - std::cout << std::to_string(byte) << "-"; - std::cout << std::endl; - - // encrypted - const auto encryptedData = rsaPublicKey.encrypt(plainData); - std::cout << "encrypted: "; - for (const auto& byte : encryptedData) - std::cout << std::to_string(byte) << "-"; - std::cout << std::endl; - - // decrypted - const auto decryptedData = rsaPrivateKey.decrypt(encryptedData); - std::cout << "decrypted: "; - for (const auto& byte : decryptedData) - std::cout << std::to_string(byte) << "-"; - std::cout << std::endl; - - return 0; -} - - -int mainSerialize() { - std::string text = "hello world!"; - const std::vector plainData(text.begin(), text.end()); - - std::cout << "plain: "; - for (const auto& byte : plainData) - std::cout << std::to_string(byte) << "-"; - std::cout << std::endl; - - std::vector serializedData = drp::util::serialize::serializeVector(plainData); - - std::cout << "serialized: "; - for (const auto& byte : serializedData) - std::cout << std::to_string(byte) << "-"; - std::cout << std::endl; - - std::vector deserializedData = drp::util::serialize::deserializeVector(serializedData); - - std::cout << "deserialized: "; - for (const auto& byte : deserializedData) - std::cout << std::to_string(byte) << "-"; - std::cout << std::endl; - - return 0; -} - - -int mainRsaSerialize() { - // create a key pair - drp::util::crypto::RsaKeyPair keyPair(2048); - - // create an object - Peer peer; - peer.channel = 7; - peer.id = 253; - - // serialize it - auto serializedPeer = peer.serialize(); - - std::cout << "serialized: "; - for (const auto& byte : serializedPeer) - std::cout << std::to_string(byte) << "-"; - std::cout << std::endl; - - // encrypt it - const std::vector encryptedSerializedPeer = keyPair.getPublicKey().encrypt(serializedPeer); - - std::cout << "encrypted: "; - for (const auto& byte : encryptedSerializedPeer) - std::cout << std::to_string(byte) << "-"; - std::cout << std::endl; - - // decrypt it - std::vector decryptedSerializedPeer = keyPair.getPrivateKey().decrypt(encryptedSerializedPeer); - - std::cout << "decrypted: "; - for (const auto& byte : decryptedSerializedPeer) - std::cout << std::to_string(byte) << "-"; - std::cout << std::endl; - - const auto deserializedPeer = Peer::deserialize(decryptedSerializedPeer); - - std::cout << "peer id: " << std::to_string(deserializedPeer.id) << std::endl; - std::cout << "peer channel: " << std::to_string(deserializedPeer.channel) << std::endl; - - return 0; -} - - -int main_test() { - // mainAes(); - // mainRsa(); - // mainSerialize(); - mainRsaSerialize(); - - return 0; -} \ No newline at end of file diff --git a/source/utils/__init__.py b/source/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/source/utils/audio/audio.cpp b/source/utils/audio/audio.cpp deleted file mode 100644 index a811108..0000000 --- a/source/utils/audio/audio.cpp +++ /dev/null @@ -1,47 +0,0 @@ -#include "audio.hpp" - -#include -#include - -#include -#include - - -namespace drp::util { - - -std::uint32_t encoding_mpg123_to_PulseAudio(const int encoding_mpg123) { - switch (encoding_mpg123) { - case MPG123_ENC_UNSIGNED_8: - return paUInt8; - case MPG123_ENC_SIGNED_8: - return paInt8; - case MPG123_ENC_SIGNED_16: - return paInt16; - case MPG123_ENC_SIGNED_24: - return paInt24; - case MPG123_ENC_SIGNED_32: - return paInt32; - case MPG123_ENC_FLOAT: - case MPG123_ENC_FLOAT_32: - return paFloat32; - default: - throw std::runtime_error("Invalid encoding value."); - } -} - - -std::chrono::milliseconds get_audio_chunk_duration( - const long sampleRate, - const int channels, - const int encodingSize, - const std::size_t length -) { - return std::chrono::milliseconds(static_cast( - (1 / static_cast(sampleRate * channels * encodingSize)) * - 1000 * static_cast(length) - )); -} - - -} \ No newline at end of file diff --git a/source/utils/audio/audio.hpp b/source/utils/audio/audio.hpp deleted file mode 100644 index d4ae37e..0000000 --- a/source/utils/audio/audio.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include -#include - - -namespace drp::util { - - -std::uint32_t encoding_mpg123_to_PulseAudio(int encoding_mpg123); - -std::chrono::milliseconds get_audio_chunk_duration( - long sampleRate, - int channels, - int encodingSize, - std::size_t length -); - -} \ No newline at end of file diff --git a/source/utils/crypto/__init__.py b/source/utils/crypto/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/source/utils/crypto/aes.py b/source/utils/crypto/aes.py new file mode 100644 index 0000000..49dd523 --- /dev/null +++ b/source/utils/crypto/aes.py @@ -0,0 +1,47 @@ +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import padding +from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes + + +def aes_ecb_encrypt(data: bytes, key: bytes) -> bytes: + """ + Encrypt the message using AES in ECB mode. + :param data: the data to cipher + :param key: the key to use for the cipher + :return: the encrypted data + """ + + # pad the data with PKCS7 for AES to work properly + padder = padding.PKCS7(128).padder() + padded_data = padder.update(data) + padder.finalize() + + # create the AES cipher in ECB mode + cipher = Cipher(algorithms.AES(key), modes.ECB(), backend=default_backend()) + encryptor = cipher.encryptor() + + # encrypt the padded data + encrypted_data = encryptor.update(padded_data) + encryptor.finalize() + + return encrypted_data + + +def aes_ecb_decrypt(encrypted_data: bytes, key: bytes) -> bytes: + """ + Decrypt data encrypted with AES in CBC mode. + :param encrypted_data: the encrypted data + :param key: the key used to encrypt it + :return: the decrypted data + """ + + # create the AES cipher in ECB mode + cipher = Cipher(algorithms.AES(key), modes.ECB(), backend=default_backend()) + decryptor = cipher.decryptor() + + # decrypt the encrypted data + decrypted_data = decryptor.update(encrypted_data) + decryptor.finalize() + + # unpad the data + unpadder = padding.PKCS7(128).unpadder() + data = unpadder.update(decrypted_data) + unpadder.finalize() + + return data diff --git a/source/utils/crypto/aes/AesKey.cpp b/source/utils/crypto/aes/AesKey.cpp deleted file mode 100644 index d330336..0000000 --- a/source/utils/crypto/aes/AesKey.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "AesKey.hpp" diff --git a/source/utils/crypto/aes/AesKey.hpp b/source/utils/crypto/aes/AesKey.hpp deleted file mode 100644 index d4e53cc..0000000 --- a/source/utils/crypto/aes/AesKey.hpp +++ /dev/null @@ -1,166 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - - -namespace drp::util::crypto { - - -/** - * Represent an AES key. - * Allow for encrypting and decrypting data. - * @tparam keySize the size of the key (in bytes) - * @tparam ivSize the size of the initialisation vector (in bytes) - */ -template -class AesKey { -public: - /** - * Create a random AES key. - */ - AesKey() { - // generate a random key - for (auto& byte : this->_data) - byte = randomDistribution(randomGenerator); - } - - explicit AesKey(const std::array& data) { - this->_data = data; - } - - /** - * Encrypt data with this key. - * @param plainData the data to encrypt - * @return the encrypted data. It will always be longer than the original data. - */ - [[nodiscard]] std::vector encrypt(const std::vector& plainData) const { - // create an initialization vector - std::array iv {}; - for (auto& byte : iv) - byte = randomDistribution(randomGenerator); - - // create the cipher context - const auto context = std::unique_ptr( - EVP_CIPHER_CTX_new(), - EVP_CIPHER_CTX_free - ); - - if (context == nullptr) - throw std::runtime_error("Error creating EVP_CIPHER_CTX"); - - // initialize the encryptor - if (EVP_EncryptInit_ex( - context.get(), - cipherFunction(), - nullptr, - this->_data.data(), - iv.data() - ) != 1) - throw std::runtime_error("Error initializing encryption"); - - std::vector encryptedData(ivSize + plainData.size() + EVP_CIPHER_block_size(EVP_aes_256_cbc())); - std::copy(iv.begin(), iv.end(), encryptedData.begin()); - int length; - - // encrypt the data - if (EVP_EncryptUpdate( - context.get(), - encryptedData.data() + ivSize, - &length, - plainData.data(), - static_cast(plainData.size()) - ) != 1) - throw std::runtime_error("Error encrypting data"); - int encryptedDataLength = length; - - // finalize the encryption - if (EVP_EncryptFinal_ex( - context.get(), - encryptedData.data() + ivSize + encryptedDataLength, - &length - ) != 1) - throw std::runtime_error("Error finalizing encryption"); - encryptedDataLength += length; - encryptedData.resize(ivSize + encryptedDataLength); - - return encryptedData; - } - - /** - * Decrypt data with this key. - * @param rawEncryptedData the encrypted data to decrypt. - * @return the decrypted data. - */ - [[nodiscard]] std::vector decrypt(const std::vector& rawEncryptedData) const { - // create a cipher context - const auto context = std::unique_ptr( - EVP_CIPHER_CTX_new(), - EVP_CIPHER_CTX_free - ); - - std::array iv; - std::copy(rawEncryptedData.begin(), rawEncryptedData.begin() + ivSize, iv.data()); - const std::vector encryptedData(rawEncryptedData.begin() + ivSize, rawEncryptedData.end()); - - // initialize the decryptor - if (EVP_DecryptInit_ex( - context.get(), - cipherFunction(), - nullptr, - this->_data.data(), - iv.data() - ) != 1) - throw std::runtime_error("Error initializing decryptor"); - - std::vector plainData(encryptedData.size()); - int length; - - // decrypt the data - if (EVP_DecryptUpdate( - context.get(), - plainData.data(), - &length, - encryptedData.data(), - static_cast(encryptedData.size()) - ) != 1) - throw std::runtime_error("Error decrypting data"); - - // finalize the decryption - if (EVP_DecryptFinal_ex( - context.get(), - plainData.data(), - &length - ) != 1) - throw std::runtime_error("Error finalizing decryptor"); - plainData.resize(length); - - return plainData; - } - -private: - static std::mt19937 randomGenerator; - static std::uniform_int_distribution randomDistribution; - - std::array _data; -}; - - -template -std::mt19937 AesKey::randomGenerator = std::mt19937(std::random_device{}()); - -template -std::uniform_int_distribution AesKey::randomDistribution = std::uniform_int_distribution( - std::numeric_limits::min(), - std::numeric_limits::max() -); - - -using AesKey256 = AesKey<256/8, 16, EVP_aes_256_cbc>; - - -} \ No newline at end of file diff --git a/source/utils/crypto/rsa.py b/source/utils/crypto/rsa.py new file mode 100644 index 0000000..77a4cd5 --- /dev/null +++ b/source/utils/crypto/rsa.py @@ -0,0 +1,51 @@ +from cryptography.hazmat.primitives import serialization, hashes +from cryptography.hazmat.primitives.asymmetric import rsa, padding + + +def rsa_encrypt(data: bytes, public_key_data: bytes) -> bytes: + """ + Encrypt data with RSA using a public key + :param data: the data to encrypt + :param public_key_data: the public key to encrypt with + :return: the encrypted data + """ + + # load the public key + public_key = serialization.load_der_public_key(public_key_data) + # verify if the key is loaded + if not isinstance(public_key, rsa.RSAPublicKey): + raise ValueError("Could not load the public key.") + + # encrypt the data with the key + return public_key.encrypt( + data, + padding.OAEP( + mgf=padding.MGF1(algorithm=hashes.SHA256()), + algorithm=hashes.SHA256(), + label=None + ) + ) + +def rsa_decrypt(encrypted_data: bytes, private_key_data: bytes) -> bytes: + """ + Decrypt the data with the RSA private key + :param encrypted_data: the data to decrypt + :param private_key_data: the private key data + :return: the decrypted data + """ + + # load the private key + private_key = serialization.load_der_private_key(private_key_data, None) + # verify if the key is loaded + if not isinstance(private_key, rsa.RSAPrivateKey): + raise ValueError("Could not load the private key.") + + # decrypt the data + return private_key.decrypt( + encrypted_data, + padding.OAEP( + mgf=padding.MGF1(algorithm=hashes.SHA256()), + algorithm=hashes.SHA256(), + label=None + ) + ) diff --git a/source/utils/crypto/rsa/RsaKeyPair.cpp b/source/utils/crypto/rsa/RsaKeyPair.cpp deleted file mode 100644 index 7694996..0000000 --- a/source/utils/crypto/rsa/RsaKeyPair.cpp +++ /dev/null @@ -1,98 +0,0 @@ -#include "RsaKeyPair.hpp" - -#include - - -namespace drp::util::crypto { - - -RsaKeyPair::RsaKeyPair(const std::size_t size, const int padMode) { - // create the context - const std::unique_ptr context( - 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(), static_cast(size)) <= 0) - throw std::runtime_error("Error setting RSA key size."); - - // create the key pair - EVP_PKEY* rawKeyPair = nullptr; - if (EVP_PKEY_keygen(context.get(), &rawKeyPair) <= 0) - throw std::runtime_error("Could not generate RSA key pair."); - if (rawKeyPair == nullptr) - throw std::runtime_error("Could not generate RSA key pair."); - - std::unique_ptr keyPair( - rawKeyPair, - EVP_PKEY_free - ); - - // extract the private key - const std::unique_ptr privateBio( - BIO_new(BIO_s_mem()), - BIO_free - ); - if (privateBio == nullptr) - throw std::runtime_error("Could not create RSA private key."); - - if (!i2d_PrivateKey_bio( - privateBio.get(), - keyPair.get() - )) - throw std::runtime_error("Could not generate RSA private key."); - - std::vector privateKeyData(BIO_pending(privateBio.get())); - if (BIO_read( - privateBio.get(), - privateKeyData.data(), - static_cast(privateKeyData.size()) - ) <= 0) - throw std::runtime_error("Could not read RSA private key."); - - this->privateKey = RsaPrivateKey(privateKeyData, padMode); - - // extract the public key - const std::unique_ptr publicBio( - BIO_new(BIO_s_mem()), - BIO_free - ); - if (publicBio == nullptr) - throw std::runtime_error("Could not create RSA public key."); - - if (!i2d_PUBKEY_bio( - publicBio.get(), - keyPair.get() - )) - throw std::runtime_error("Could not generate RSA public key."); - - std::vector publicKeyData(BIO_pending(publicBio.get())); - if (BIO_read( - publicBio.get(), - publicKeyData.data(), - static_cast(publicKeyData.size()) - ) <= 0) - throw std::runtime_error("Could not read RSA public key."); - - this->publicKey = RsaPublicKey(publicKeyData, padMode); -} - - -RsaPublicKey RsaKeyPair::getPublicKey() const { - return this->publicKey; -} - - -RsaPrivateKey RsaKeyPair::getPrivateKey() const { - return this->privateKey; -} - - -} diff --git a/source/utils/crypto/rsa/RsaKeyPair.hpp b/source/utils/crypto/rsa/RsaKeyPair.hpp deleted file mode 100644 index 36dbbb5..0000000 --- a/source/utils/crypto/rsa/RsaKeyPair.hpp +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -#include "RsaPrivateKey.hpp" -#include "RsaPublicKey.hpp" - - -namespace drp::util::crypto { - - -/** - * Represent a pair of RSA key. - */ -class RsaKeyPair { -public: - /** - * Generate a pair of public and private RSA keys. - */ - explicit RsaKeyPair(std::size_t size, int padMode = RSA_PKCS1_OAEP_PADDING); - - /** - * Get the public key. - * @return the public key. - */ - [[nodiscard]] RsaPublicKey getPublicKey() const; - - /** - * Get the private key. - * @return the private key. - */ - [[nodiscard]] RsaPrivateKey getPrivateKey() const; - -private: - RsaPrivateKey privateKey; - RsaPublicKey publicKey; -}; - - -} \ No newline at end of file diff --git a/source/utils/crypto/rsa/RsaPrivateKey.cpp b/source/utils/crypto/rsa/RsaPrivateKey.cpp deleted file mode 100644 index fb71230..0000000 --- a/source/utils/crypto/rsa/RsaPrivateKey.cpp +++ /dev/null @@ -1,121 +0,0 @@ -#include "RsaPrivateKey.hpp" - -#include -#include -#include - -#include "utils/serialize/basics.hpp" - - -namespace drp::util::crypto { - - -RsaPrivateKey::RsaPrivateKey() = default; - -RsaPrivateKey::RsaPrivateKey(const std::vector& data, const int padMode) { - this->_data = data; - this->padMode = padMode; -} - - -std::vector RsaPrivateKey::decrypt(const std::vector& encryptedData) const { - const auto key = this->getOpenSslKey(); - - // initialize the encryption context - const std::unique_ptr context( - EVP_PKEY_CTX_new(key.get(), nullptr), - EVP_PKEY_CTX_free - ); - - if (context == nullptr) - throw std::runtime_error("Could not create EVP_PKEY_CTX."); - - // initialize the encryption operation - if (EVP_PKEY_decrypt_init(context.get()) <= 0) - throw std::runtime_error("Could not initialize encryption."); - - // set the padding - if (EVP_PKEY_CTX_set_rsa_padding(context.get(), RSA_PKCS1_OAEP_PADDING) <= 0) - throw std::runtime_error("Could not set RSA padding."); - - // get the size of the output buffer - std::size_t decryptedDataLength; - if (EVP_PKEY_decrypt( - context.get(), - nullptr, - &decryptedDataLength, - encryptedData.data(), - encryptedData.size() - ) <= 0) - throw std::runtime_error("Could not determine output length."); - - std::vector decryptedData(decryptedDataLength); - - // encrypt the data - if (EVP_PKEY_decrypt( - context.get(), - decryptedData.data(), - &decryptedDataLength, - encryptedData.data(), - encryptedData.size() - ) <= 0) - throw std::runtime_error("Could not decrypt data."); - - decryptedData.resize(decryptedDataLength); - - return decryptedData; -} - - -std::vector RsaPrivateKey::serialize() const { - // serialize the members - const auto serializedData = serialize::serializeVector(this->_data); - const auto serializedPadMode = serialize::serializeObject(static_cast(this->padMode)); - - // create a buffer to store our members - std::vector data; - - // store our members - data.insert(data.end(), serializedData.begin(), serializedData.end()); - data.insert(data.end(), serializedPadMode.begin(), serializedPadMode.end()); - - return data; -} - -RsaPrivateKey RsaPrivateKey::deserialize(std::vector& data) { - // deserialize the members - const auto keyData = serialize::deserializeVector(data); - const auto keyPadding = static_cast(serialize::deserializeObject(data)); - - return RsaPrivateKey(keyData, keyPadding); -} - - -[[nodiscard]] std::unique_ptr RsaPrivateKey::getOpenSslKey() const { - // get the bio from the private key data - const std::unique_ptr bio( - BIO_new_mem_buf( - this->_data.data(), - static_cast(this->_data.size()) - ), - BIO_free - ); - if (bio == nullptr) - throw std::runtime_error("Could not create BIO for private key."); - - // get the key from the bio - EVP_PKEY* rawKey = nullptr; - if (!d2i_PrivateKey_bio( - bio.get(), - &rawKey - )) - throw std::runtime_error("Could not deserialize RSA private key."); - - return { - rawKey, - EVP_PKEY_free - }; -} - - -} diff --git a/source/utils/crypto/rsa/RsaPrivateKey.hpp b/source/utils/crypto/rsa/RsaPrivateKey.hpp deleted file mode 100644 index 6476fde..0000000 --- a/source/utils/crypto/rsa/RsaPrivateKey.hpp +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include -#include -#include - - -namespace drp::util::crypto { - - -/** - * Represent an RSA private key. - */ -class RsaPrivateKey { -public: - RsaPrivateKey(); - explicit RsaPrivateKey(const std::vector& data, int padMode); - - [[nodiscard]] std::vector decrypt(const std::vector& encryptedData) const; - - [[nodiscard]] std::vector serialize() const; - static RsaPrivateKey deserialize(std::vector& data); - -private: - [[nodiscard]] std::unique_ptr getOpenSslKey() const; - - int padMode {}; - std::vector _data; -}; - - -} diff --git a/source/utils/crypto/rsa/RsaPublicKey.cpp b/source/utils/crypto/rsa/RsaPublicKey.cpp deleted file mode 100644 index ef36603..0000000 --- a/source/utils/crypto/rsa/RsaPublicKey.cpp +++ /dev/null @@ -1,121 +0,0 @@ -#include "RsaPublicKey.hpp" - -#include -#include -#include - -#include "utils/serialize/basics.hpp" - - -namespace drp::util::crypto { - - -RsaPublicKey::RsaPublicKey() = default; - -RsaPublicKey::RsaPublicKey(const std::vector& data, const int padMode) { - this->_data = data; - this->padMode = padMode; -} - - -[[nodiscard]] std::vector RsaPublicKey::encrypt(const std::vector& plainData) const { - const auto key = this->getOpenSslKey(); - - // initialize the encryption context - const std::unique_ptr context( - EVP_PKEY_CTX_new(key.get(), nullptr), - EVP_PKEY_CTX_free - ); - - if (context == nullptr) - throw std::runtime_error("Could not create EVP_PKEY_CTX."); - - // initialize the encryption operation - if (EVP_PKEY_encrypt_init(context.get()) <= 0) - throw std::runtime_error("Could not initialize encryption."); - - // set the padding - if (EVP_PKEY_CTX_set_rsa_padding(context.get(), this->padMode) <= 0) - throw std::runtime_error("Could not set RSA padding."); - - // get the size of the output buffer - std::size_t encryptedDataLength; - if (EVP_PKEY_encrypt( - context.get(), - nullptr, - &encryptedDataLength, - plainData.data(), - plainData.size() - ) <= 0) - throw std::runtime_error("Could not determine output length."); - - std::vector encryptedData(encryptedDataLength); - - // encrypt the data - if (EVP_PKEY_encrypt( - context.get(), - encryptedData.data(), - &encryptedDataLength, - plainData.data(), - plainData.size() - ) <= 0) - throw std::runtime_error("Could not encrypt data."); - - encryptedData.resize(encryptedDataLength); - - return encryptedData; -} - - -std::vector RsaPublicKey::serialize() const { - // serialize the members - const auto serializedData = serialize::serializeVector(this->_data); - const auto serializedPadMode = serialize::serializeObject(static_cast(this->padMode)); - - // create a buffer to store our members - std::vector data; - - // store our members - data.insert(data.end(), serializedData.begin(), serializedData.end()); - data.insert(data.end(), serializedPadMode.begin(), serializedPadMode.end()); - - return data; -} - -RsaPublicKey RsaPublicKey::deserialize(std::vector& data) { - // deserialize the members - const auto keyData = serialize::deserializeVector(data); - const auto keyPadding = static_cast(serialize::deserializeObject(data)); - - return RsaPublicKey(keyData, keyPadding); -} - - -[[nodiscard]] std::unique_ptr RsaPublicKey::getOpenSslKey() const { - // get the bio from the public key data - const std::unique_ptr bio( - BIO_new_mem_buf( - this->_data.data(), - static_cast(this->_data.size()) - ), - BIO_free - ); - if (bio == nullptr) - throw std::runtime_error("Could not create BIO for public key."); - - // get the key from the bio - EVP_PKEY* rawKey = nullptr; - if (!d2i_PUBKEY_bio( - bio.get(), - &rawKey - )) - throw std::runtime_error("Could not deserialize RSA public key."); - - return { - rawKey, - EVP_PKEY_free - }; -} - - -} diff --git a/source/utils/crypto/rsa/RsaPublicKey.hpp b/source/utils/crypto/rsa/RsaPublicKey.hpp deleted file mode 100644 index b97eba3..0000000 --- a/source/utils/crypto/rsa/RsaPublicKey.hpp +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -#include - -#include "RsaPrivateKey.hpp" - - -namespace drp::util::crypto { - - -/** - * Represent an RSA public key. - */ -class RsaPublicKey { -public: - RsaPublicKey(); - explicit RsaPublicKey(const std::vector& data, int padMode); - - /** - * Encrypt data with the public key. Can only be decrypted with the corresponding private key. - * @param plainData the plain data. - * @return the encrypted data. - */ - [[nodiscard]] std::vector encrypt(const std::vector& plainData) const; - - [[nodiscard]] std::vector serialize() const; - static RsaPublicKey deserialize(std::vector& data); - -private: - [[nodiscard]] std::unique_ptr getOpenSslKey() const; - - int padMode {}; - std::vector _data; -}; - - -} diff --git a/source/utils/crypto/type.py b/source/utils/crypto/type.py new file mode 100644 index 0000000..1e8fbd7 --- /dev/null +++ b/source/utils/crypto/type.py @@ -0,0 +1,7 @@ +import enum + + +class CipherType(enum.Enum): + NONE = 0x00 + AES_ECB = 0x01 + RSA = 0x02 diff --git a/source/utils/crypto/universal.py b/source/utils/crypto/universal.py new file mode 100644 index 0000000..fb6b7b1 --- /dev/null +++ b/source/utils/crypto/universal.py @@ -0,0 +1,57 @@ +import typing + +from source.utils.crypto import aes, rsa +from source.utils.crypto.type import CipherType + + +def encrypt(data: bytes, key: typing.Optional[bytes] = None, cipher_type: CipherType = CipherType.NONE) -> bytes: + """ + Encrypt data on various cipher type. + :param data: the data to cipher + :param key: the key to cipher the data + :param cipher_type: the type of cipher to use + :return: + """ + + match cipher_type: + case CipherType.NONE: + return data + + case CipherType.AES_ECB: + if key is None: + raise ValueError("The key cannot be None.") + return aes.aes_ecb_encrypt(data, key) + + case CipherType.RSA: + if key is None: + raise ValueError("The key cannot be None.") + return rsa.rsa_encrypt(data, key) + + case _: + raise KeyError("Unknown cipher mode.") + +def decrypt(data: bytes, key: typing.Optional[bytes] = None, cipher_type: CipherType = CipherType.NONE) -> bytes: + """ + Encrypt data on various cipher type. + :param data: the data to decipher + :param key: the key to cipher the data + :param cipher_type: the type of cipher to use + :return: + """ + + match cipher_type: + case CipherType.NONE: + return data + + case CipherType.AES_ECB: + if key is None: + raise ValueError("The key cannot be None.") + return aes.aes_ecb_decrypt(data, key) + + case CipherType.RSA: + if key is None: + raise ValueError("The key cannot be None.") + return rsa.rsa_decrypt(data, key) + + case _: + raise KeyError("Unknown cipher mode.") diff --git a/source/utils/network/network.cpp b/source/utils/network/network.cpp deleted file mode 100644 index 1b7a3fb..0000000 --- a/source/utils/network/network.cpp +++ /dev/null @@ -1,60 +0,0 @@ -#include "network.hpp" - -#include -#include -#include -#include - - -namespace drp::util::network { - - -bool is_localhost(const sockaddr_storage& address, const socklen_t addressLength) { - // iterate through all the interfaces - ifaddrs* interfaces; - if (getifaddrs(&interfaces) == -1) - throw std::runtime_error("Could not get network interfaces."); - - bool result = false; - for (const ifaddrs *interface = interfaces; interface; interface = interface->ifa_next) { - // if the interface family does not correspond to the server candidate, ignore it - if (interface->ifa_addr->sa_family != address.ss_family) - continue; - - switch (address.ss_family) { - case AF_INET: { - const auto interfaceIpv4 = reinterpret_cast(interface->ifa_addr); - const auto addressIpv4 = reinterpret_cast(&address); - - // the family have already been checked. - if (std::memcmp(&interfaceIpv4->sin_addr, &addressIpv4->sin_addr, sizeof(in_addr)) == 0) - result = true; - - break; - } - - case AF_INET6: { - const auto interfaceIpv6 = reinterpret_cast(interface->ifa_addr); - const auto addressIpv6 = reinterpret_cast(&address); - - // the interface and the family have already been checked. - if (std::memcmp(&interfaceIpv6->sin6_addr, &addressIpv6->sin6_addr, sizeof(in6_addr)) == 0) - result = true; - - break; - } - - default: - throw std::runtime_error("Unknown address family."); - } - - if (result == true) - break; - } - - freeifaddrs(interfaces); - return result; -} - - -} diff --git a/source/utils/network/network.hpp b/source/utils/network/network.hpp deleted file mode 100644 index 3ca213f..0000000 --- a/source/utils/network/network.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include -#include - - -namespace drp::util::network { - - -/** - * Indicate if an address refer to the local machine. - * @param address the address to check. - * @param addressLength the length of the address. - * @return True if the address refer to ourselves, False otherwise. - */ -bool is_localhost(const sockaddr_storage& address, socklen_t addressLength); - - -} diff --git a/source/utils/serialize/basics.hpp b/source/utils/serialize/basics.hpp deleted file mode 100644 index 6d7d7a3..0000000 --- a/source/utils/serialize/basics.hpp +++ /dev/null @@ -1,96 +0,0 @@ -#pragma once - -#include -#include -#include - - -namespace drp::util::serialize { - - -/** - * Serialize a basic object to a vector of bytes - * @tparam Type the type of the object. - * @param object the object to serialize. - * @return the object as a vector of bytes. - */ -template -std::vector serializeObject(const Type& object) { - // create a vector with enough space for the object - std::vector buffer(sizeof(Type)); - // copy the object data to the buffer - std::memcpy(buffer.data(), &object, sizeof(Type)); - - return buffer; -} - - -/** - * Deserialize a vector of bytes into an object. - * @warning the data used in parameter will be stripped of the data used to deserialize the object. - * @tparam Type the type of the object - * @param data the data of the object - * @return the object - */ -template -Type deserializeObject(std::vector& data) { - // create an object based on the data - Type object; - std::memcpy(&object, data.data(), sizeof(Type)); - - // remove the space used by the object in the data - data.erase(data.begin(), data.begin() + sizeof(Type)); - - return object; -} - - -/** - * Serialize a vector of anything to a vector of bytes. - * @tparam Type the type of the data contained in the vector. - * @tparam SizeType the range of the size of the vector. - * @param object the vector to serialize. - * @return the vector data as a vector of bytes. - */ -template -std::vector serializeVector(const std::vector& object) { - // create a vector with enough size for the size and the data of the vector - std::vector buffer(sizeof(SizeType) + object.size() * sizeof(Type)); - - // save the size of the vector - auto size = static_cast(object.size()); - std::memcpy(buffer.data(), &size, sizeof(SizeType)); - // save the content of the vector - std::memcpy(buffer.data() + sizeof(SizeType), object.data(), object.size() * sizeof(Type)); - - return buffer; -} - - -/** - * Deserialize a vector of bytes into a vector of object. - * @warning the data used in parameter will be stripped of the data used to deserialize the object. - * @tparam Type the type of the object - * @tparam SizeType the type of the size of the vector - * @param data the data in the vector - * @return the vector of object - */ -template -std::vector deserializeVector(std::vector& data) { - // get the size of the vector - SizeType size; - std::memcpy(&size, data.data(), sizeof(SizeType)); - // create a vector with enough size of the data - std::vector object(size); - - // restore the data into the object - std::memcpy(object.data(), data.data() + sizeof(SizeType), size * sizeof(Type)); - - // remove the data used for the deserialization from the source - data.erase(data.begin(), data.begin() + sizeof(SizeType) + size * sizeof(Type)); - - return object; -} - - -} diff --git a/source/utils/time/Chrony.cpp b/source/utils/time/Chrony.cpp deleted file mode 100644 index 9ec6aae..0000000 --- a/source/utils/time/Chrony.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "Chrony.hpp" diff --git a/source/utils/time/Chrony.hpp b/source/utils/time/Chrony.hpp deleted file mode 100644 index 40fe00f..0000000 --- a/source/utils/time/Chrony.hpp +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - - -namespace drp::util::time { - - -class Chrony { - -}; - - -} \ No newline at end of file