recreated a base for a python simplified version
This commit is contained in:
parent
dfb96a7853
commit
c825ef8bef
94 changed files with 445 additions and 3074 deletions
3
.gitmodules
vendored
3
.gitmodules
vendored
|
@ -1,3 +0,0 @@
|
||||||
[submodule "external/argparse"]
|
|
||||||
path = external/argparse
|
|
||||||
url = https://github.com/p-ranav/argparse
|
|
|
@ -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)
|
|
24
Dockerfile
24
Dockerfile
|
@ -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"]
|
|
|
@ -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
|
|
1
external/argparse
vendored
1
external/argparse
vendored
|
@ -1 +0,0 @@
|
||||||
Subproject commit cbd9fd8ed675ed6a2ac1bd7142d318c6ad5d3462
|
|
4
requirements.txt
Normal file
4
requirements.txt
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
sounddevice
|
||||||
|
msgpack
|
||||||
|
cryptography
|
||||||
|
bidict
|
|
@ -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();
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <list>
|
|
||||||
#include <memory>
|
|
||||||
#include <netdb.h>
|
|
||||||
|
|
||||||
#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<RemotePeer> server; /// peer currently used as the server
|
|
||||||
|
|
||||||
// ourselves
|
|
||||||
Peer me; /// information about our own machine
|
|
||||||
|
|
||||||
// others
|
|
||||||
std::list<std::shared_ptr<RemotePeer>> 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
|
|
||||||
};
|
|
|
@ -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<std::uint8_t> Peer::serialize() const {
|
|
||||||
std::vector<std::uint8_t> data;
|
|
||||||
|
|
||||||
// serialized the members
|
|
||||||
const auto serializedId = drp::util::serialize::serializeObject<std::uint32_t>(this->id);
|
|
||||||
const auto serializedServerEnabled = drp::util::serialize::serializeObject<std::uint8_t>(this->serverEnabled);
|
|
||||||
const auto serializedStatus = drp::util::serialize::serializeObject<std::uint8_t>(static_cast<std::uint8_t>(this->status));
|
|
||||||
const auto serializedChannel = drp::util::serialize::serializeObject<std::uint8_t>(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<std::uint8_t>& data) {
|
|
||||||
// deserialize the members
|
|
||||||
const auto id = drp::util::serialize::deserializeObject<std::uint32_t>(data);
|
|
||||||
const auto serverEnabled = drp::util::serialize::deserializeObject<std::uint8_t>(data);
|
|
||||||
const auto status = static_cast<drp::task::TaskType>(drp::util::serialize::deserializeObject<std::uint8_t>(data));
|
|
||||||
const auto channel = drp::util::serialize::deserializeObject<std::uint8_t>(data);
|
|
||||||
const auto latencyAverage = drp::util::serialize::deserializeObject<std::chrono::high_resolution_clock::duration>(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<std::uint32_t> Peer::randomDistribution = std::uniform_int_distribution(
|
|
||||||
std::numeric_limits<std::uint32_t>::min(),
|
|
||||||
std::numeric_limits<std::uint32_t>::max()
|
|
||||||
);
|
|
|
@ -1,47 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <chrono>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <random>
|
|
||||||
|
|
||||||
#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<std::uint8_t> serialize() const;
|
|
||||||
static Peer deserialize(std::vector<std::uint8_t> &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<std::uint32_t> randomDistribution;
|
|
||||||
};
|
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -1,27 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <chrono>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
|
|
||||||
#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;
|
|
||||||
};
|
|
3
source/__init__.py
Normal file
3
source/__init__.py
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
from . import managers
|
||||||
|
from . import behaviors
|
||||||
|
from . import packets
|
15
source/__main__.py
Normal file
15
source/__main__.py
Normal file
|
@ -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()
|
|
@ -1 +0,0 @@
|
||||||
This directory contains the code describing how to react to a specific event.
|
|
|
@ -1,145 +0,0 @@
|
||||||
#include "AudioEvent.hpp"
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <bits/unique_lock.h>
|
|
||||||
|
|
||||||
#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<std::uint8_t>& 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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,44 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <condition_variable>
|
|
||||||
#include <portaudio.h>
|
|
||||||
#include <queue>
|
|
||||||
|
|
||||||
#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<std::uint8_t>& 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<packet::audio::AudioPacketData, std::vector<packet::audio::AudioPacketData>, AudioPacketsComparator> audioQueue;
|
|
||||||
|
|
||||||
std::mutex audioMutex;
|
|
||||||
std::unique_lock<std::mutex> audioLock;
|
|
||||||
std::condition_variable audioCondition;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -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<std::uint8_t>& data,
|
|
||||||
const sockaddr_storage& fromAddress,
|
|
||||||
socklen_t fromAddressLength
|
|
||||||
) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,52 +0,0 @@
|
||||||
#include "InfoEvent.hpp"
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <ranges>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
|
|
||||||
#include "packets/info/InfoPacketData.hpp"
|
|
||||||
|
|
||||||
|
|
||||||
namespace drp::event {
|
|
||||||
|
|
||||||
|
|
||||||
void InfoEvent::handle(
|
|
||||||
Context& context,
|
|
||||||
std::vector<std::uint8_t>& 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<RemotePeer>(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
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "../base/BaseEvent.hpp"
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
|
|
||||||
namespace drp::event {
|
|
||||||
|
|
||||||
|
|
||||||
class InfoEvent : public BaseEvent {
|
|
||||||
public:
|
|
||||||
void handle(
|
|
||||||
Context& context,
|
|
||||||
std::vector<std::uint8_t>& data,
|
|
||||||
const sockaddr_storage& fromAddress,
|
|
||||||
socklen_t fromAddressLength
|
|
||||||
) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
#include "PongEvent.hpp"
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
|
|
||||||
namespace drp::event {
|
|
||||||
|
|
||||||
|
|
||||||
void PongEvent::handle(
|
|
||||||
Context& context,
|
|
||||||
std::vector<std::uint8_t>& data,
|
|
||||||
const sockaddr_storage& fromAddress,
|
|
||||||
const socklen_t fromAddressLength
|
|
||||||
) {
|
|
||||||
std::cout << "[Event - Pong] Pong." << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "../base/BaseEvent.hpp"
|
|
||||||
|
|
||||||
|
|
||||||
namespace drp::event {
|
|
||||||
|
|
||||||
|
|
||||||
class PongEvent : public BaseEvent {
|
|
||||||
public:
|
|
||||||
void handle(
|
|
||||||
Context& context,
|
|
||||||
std::vector<std::uint8_t>& data,
|
|
||||||
const sockaddr_storage& fromAddress,
|
|
||||||
socklen_t fromAddressLength
|
|
||||||
) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,59 +0,0 @@
|
||||||
#include "SearchEvent.hpp"
|
|
||||||
|
|
||||||
#include <cerrno>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <cstring>
|
|
||||||
#include <iostream>
|
|
||||||
#include <ostream>
|
|
||||||
|
|
||||||
#include "packets/base/SecurityMode.hpp"
|
|
||||||
#include "packets/info/InfoPacketData.hpp"
|
|
||||||
|
|
||||||
|
|
||||||
namespace drp {
|
|
||||||
|
|
||||||
|
|
||||||
void event::SearchEvent::handle(
|
|
||||||
Context& context,
|
|
||||||
std::vector<std::uint8_t>& 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<const sockaddr*>(&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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "../base/BaseEvent.hpp"
|
|
||||||
|
|
||||||
|
|
||||||
namespace drp::event {
|
|
||||||
|
|
||||||
|
|
||||||
class SearchEvent : public BaseEvent {
|
|
||||||
public:
|
|
||||||
void handle(
|
|
||||||
Context& context,
|
|
||||||
std::vector<std::uint8_t>& data,
|
|
||||||
const sockaddr_storage& fromAddress,
|
|
||||||
socklen_t fromAddressLength
|
|
||||||
) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,2 +0,0 @@
|
||||||
This directory contains the code describing how should the machine send event to its peers.
|
|
||||||
TODO(Faraphel): rename "roles" ?
|
|
|
@ -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;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,41 +0,0 @@
|
||||||
#include "ClientTask.hpp"
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <thread>
|
|
||||||
|
|
||||||
|
|
||||||
namespace drp::task {
|
|
||||||
|
|
||||||
|
|
||||||
void ClientTask::use(Context& context, const std::shared_ptr<RemotePeer>& 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<sockaddr*>(&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));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -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<RemotePeer>& server);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,142 +0,0 @@
|
||||||
#include "ServerTask.hpp"
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <chrono>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <cstring>
|
|
||||||
#include <mpg123.h>
|
|
||||||
#include <netdb.h>
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <thread>
|
|
||||||
#include <utility>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
|
|
||||||
#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<RemotePeer>& 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<std::uint8_t> 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
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,37 +0,0 @@
|
||||||
#pragma once
|
|
||||||
#include <mpg123.h>
|
|
||||||
#include <netdb.h>
|
|
||||||
|
|
||||||
#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<RemotePeer>& server);
|
|
||||||
|
|
||||||
void handle(Context& context) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
mpg123_handle* mpgHandle;
|
|
||||||
long sampleRate;
|
|
||||||
int channels;
|
|
||||||
int encoding;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
|
|
||||||
namespace drp::task {
|
|
||||||
|
|
||||||
|
|
||||||
enum class TaskType {
|
|
||||||
UNDEFINED = 0x00,
|
|
||||||
CLIENT = 0x01,
|
|
||||||
SERVER = 0x02,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,119 +0,0 @@
|
||||||
#include "UndefinedTask.hpp"
|
|
||||||
#include "../types.hpp"
|
|
||||||
|
|
||||||
#include <chrono>
|
|
||||||
#include <cstring>
|
|
||||||
#include <iostream>
|
|
||||||
#include <thread>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <ifaddrs.h>
|
|
||||||
|
|
||||||
#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<std::uint8_t>(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<RemotePeer> 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));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
#pragma once
|
|
||||||
#include <map>
|
|
||||||
|
|
||||||
#include "../base/BaseTask.hpp"
|
|
||||||
|
|
||||||
|
|
||||||
namespace drp::task {
|
|
||||||
|
|
||||||
|
|
||||||
class UndefinedTask final : public BaseTask {
|
|
||||||
public:
|
|
||||||
static void use(Context& context);
|
|
||||||
|
|
||||||
void handle(Context& context) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
0
source/behaviors/__init__.py
Normal file
0
source/behaviors/__init__.py
Normal file
7
source/behaviors/events/DiscoveryEvent.py
Normal file
7
source/behaviors/events/DiscoveryEvent.py
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
from . import base
|
||||||
|
from source import packets
|
||||||
|
|
||||||
|
|
||||||
|
class DiscoveryEvent(base.BaseEvent):
|
||||||
|
def handle(self, packet: packets.DiscoveryPacket):
|
||||||
|
pass
|
0
source/behaviors/events/__init__.py
Normal file
0
source/behaviors/events/__init__.py
Normal file
5
source/behaviors/events/base/BaseEvent.py
Normal file
5
source/behaviors/events/base/BaseEvent.py
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
import abc
|
||||||
|
|
||||||
|
|
||||||
|
class BaseEvent(abc.ABC):
|
||||||
|
pass
|
1
source/behaviors/events/base/__init__.py
Normal file
1
source/behaviors/events/base/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
from .BaseEvent import BaseEvent
|
9
source/behaviors/roles/UndefinedRole.py
Normal file
9
source/behaviors/roles/UndefinedRole.py
Normal file
|
@ -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)
|
1
source/behaviors/roles/__init__.py
Normal file
1
source/behaviors/roles/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
from .UndefinedRole import UndefinedRole
|
11
source/behaviors/roles/base/BaseRole.py
Normal file
11
source/behaviors/roles/base/BaseRole.py
Normal file
|
@ -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
|
||||||
|
"""
|
1
source/behaviors/roles/base/__init__.py
Normal file
1
source/behaviors/roles/base/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
from .BaseRole import BaseRole
|
|
@ -1,41 +0,0 @@
|
||||||
#include <mpg123.h>
|
|
||||||
#include <portaudio.h>
|
|
||||||
#include <stdexcept>
|
|
||||||
#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<std::string>("--host"),
|
|
||||||
parser.get<std::string>("--port"),
|
|
||||||
parser.get<bool>("-6")
|
|
||||||
);
|
|
||||||
manager.loop();
|
|
||||||
|
|
||||||
// close the libraries
|
|
||||||
Pa_Terminate();
|
|
||||||
mpg123_exit();
|
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
|
118
source/managers/CommunicationManager.py
Normal file
118
source/managers/CommunicationManager.py
Normal file
|
@ -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
|
|
@ -1,112 +0,0 @@
|
||||||
#include "Manager.hpp"
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <cstring>
|
|
||||||
#include <iostream>
|
|
||||||
#include <netdb.h>
|
|
||||||
#include <ostream>
|
|
||||||
#include <thread>
|
|
||||||
#include <random>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
|
|
||||||
#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<Context>(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<SendManager>(context);
|
|
||||||
this->receiveManager = std::make_unique<ReceiveManager>(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();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#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; /// context used between the events types
|
|
||||||
|
|
||||||
std::unique_ptr<SendManager> sendManager;
|
|
||||||
std::unique_ptr<ReceiveManager> receiveManager;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
30
source/managers/Manager.py
Normal file
30
source/managers/Manager.py
Normal file
|
@ -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.")
|
|
@ -1,101 +0,0 @@
|
||||||
#include "ReceiveManager.hpp"
|
|
||||||
|
|
||||||
#include <cstring>
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
#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>& context) {
|
|
||||||
this->context = context;
|
|
||||||
|
|
||||||
// register the different events type
|
|
||||||
this->registry = {
|
|
||||||
{event::EventType::PONG, std::make_shared<event::PongEvent>()},
|
|
||||||
{event::EventType::SEARCH, std::make_shared<event::SearchEvent>()},
|
|
||||||
{event::EventType::INFO, std::make_shared<event::InfoEvent>()},
|
|
||||||
{event::EventType::AUDIO, std::make_shared<event::AudioEvent>()},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void ReceiveManager::run() const {
|
|
||||||
// prepare space for the sender address
|
|
||||||
sockaddr_storage fromAddress {};
|
|
||||||
socklen_t fromAddressLength = sizeof(fromAddress);
|
|
||||||
|
|
||||||
std::array<std::uint8_t, packet::base::maxPacketLength> buffer {};
|
|
||||||
|
|
||||||
// receive new data
|
|
||||||
const ssize_t size = recvfrom(
|
|
||||||
this->context->socket,
|
|
||||||
buffer.data(),
|
|
||||||
buffer.size(),
|
|
||||||
0,
|
|
||||||
reinterpret_cast<sockaddr*>(&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>& 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::BaseEvent> 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<int>(packetContent.eventType)) << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << "[Receiver] handling event: " << std::to_string(static_cast<int>(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();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,29 +0,0 @@
|
||||||
#pragma once
|
|
||||||
#include <map>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#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>& context);
|
|
||||||
|
|
||||||
void run() const;
|
|
||||||
[[noreturn]] void loop() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::shared_ptr<Context> context;
|
|
||||||
std::map<event::EventType, std::shared_ptr<event::BaseEvent>> registry;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,52 +0,0 @@
|
||||||
#include "SendManager.hpp"
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
#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>& context) {
|
|
||||||
this->context = context;
|
|
||||||
|
|
||||||
// register the different tasks type
|
|
||||||
this->registry = {
|
|
||||||
{task::TaskType::UNDEFINED, std::make_shared<task::UndefinedTask>()},
|
|
||||||
{task::TaskType::CLIENT, std::make_shared<task::ClientTask>()},
|
|
||||||
{task::TaskType::SERVER, std::make_shared<task::ServerTask>()},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void SendManager::run() const {
|
|
||||||
std::cout << "[Sender] Handling status: " + std::to_string(static_cast<int>(this->context->me.status)) << std::endl;
|
|
||||||
|
|
||||||
// get the corresponding task class
|
|
||||||
std::shared_ptr<task::BaseTask> 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<int>(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();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <map>
|
|
||||||
#include <memory>
|
|
||||||
#include <thread>
|
|
||||||
|
|
||||||
#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>& context);
|
|
||||||
|
|
||||||
void run() const;
|
|
||||||
[[noreturn]] void loop() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::shared_ptr<Context> context;
|
|
||||||
std::map<task::TaskType, std::shared_ptr<task::BaseTask>> registry;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
2
source/managers/__init__.py
Normal file
2
source/managers/__init__.py
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
from .CommunicationManager import CommunicationManager
|
||||||
|
from .Manager import Manager
|
30
source/packets/AudioPacket.py
Normal file
30
source/packets/AudioPacket.py
Normal file
|
@ -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))
|
22
source/packets/DiscoveryPacket.py
Normal file
22
source/packets/DiscoveryPacket.py
Normal file
|
@ -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()
|
4
source/packets/__init__.py
Normal file
4
source/packets/__init__.py
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
from . import base
|
||||||
|
|
||||||
|
from .AudioPacket import AudioPacket
|
||||||
|
from .DiscoveryPacket import DiscoveryPacket
|
|
@ -1,43 +0,0 @@
|
||||||
#include "AudioPacketData.hpp"
|
|
||||||
|
|
||||||
#include "utils/serialize/basics.hpp"
|
|
||||||
|
|
||||||
|
|
||||||
namespace drp::packet::audio {
|
|
||||||
|
|
||||||
|
|
||||||
std::vector<std::uint8_t> 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<std::uint8_t> 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<std::uint8_t>& data) {
|
|
||||||
// deserialize the members
|
|
||||||
const auto packetTimePlay = util::serialize::deserializeObject<std::chrono::time_point<std::chrono::high_resolution_clock>>(data);
|
|
||||||
const auto packetChannels = util::serialize::deserializeObject<std::uint8_t>(data);
|
|
||||||
const auto packetSampleFormat = util::serialize::deserializeObject<std::uint32_t>(data);
|
|
||||||
const auto packetSampleRate = util::serialize::deserializeObject<std::uint32_t>(data);
|
|
||||||
const auto packetContent = util::serialize::deserializeVector<std::uint8_t>(data);
|
|
||||||
|
|
||||||
return {packetTimePlay, packetChannels, packetSampleFormat, packetSampleRate, packetContent};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <chrono>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
|
|
||||||
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<std::uint8_t> serialize() const;
|
|
||||||
static AudioPacketData deserialize(std::vector<std::uint8_t>& data);
|
|
||||||
|
|
||||||
// scheduling
|
|
||||||
// TODO(Faraphel): use a more "fixed" size format ?
|
|
||||||
std::chrono::time_point<std::chrono::high_resolution_clock> timePlay;
|
|
||||||
// audio settings
|
|
||||||
std::uint8_t channels {};
|
|
||||||
std::uint32_t sampleFormat {};
|
|
||||||
std::uint32_t sampleRate {};
|
|
||||||
// content
|
|
||||||
std::vector<std::uint8_t> content {};
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
19
source/packets/base/BasePacket.py
Normal file
19
source/packets/base/BasePacket.py
Normal file
|
@ -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
|
||||||
|
"""
|
|
@ -1,99 +0,0 @@
|
||||||
#include "Packet.hpp"
|
|
||||||
|
|
||||||
#include <stdexcept>
|
|
||||||
|
|
||||||
#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<uint8_t>& content) {
|
|
||||||
this->channel = channel;
|
|
||||||
this->securityMode = securityMode;
|
|
||||||
this->content = content;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
PacketContent Packet::getContent(const Context& context) const {
|
|
||||||
std::vector<std::uint8_t> 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<std::uint8_t> 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<std::uint8_t> Packet::serialize() const {
|
|
||||||
// serialize the members
|
|
||||||
const auto serializedChannel = util::serialize::serializeObject(this->channel);
|
|
||||||
const auto serializedSecurityMode = util::serialize::serializeObject(static_cast<std::uint8_t>(this->securityMode));
|
|
||||||
const auto serializedContent = util::serialize::serializeVector(this->content);
|
|
||||||
|
|
||||||
// create a buffer to store our members
|
|
||||||
std::vector<std::uint8_t> 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<std::uint8_t>& data) {
|
|
||||||
// deserialize the members
|
|
||||||
const auto packetChannel = util::serialize::deserializeObject<std::uint8_t>(data);
|
|
||||||
const auto packetSecurityMode = static_cast<SecurityMode>(util::serialize::deserializeObject<std::uint8_t>(data));
|
|
||||||
const auto packetContent = util::serialize::deserializeVector<std::uint8_t>(data);
|
|
||||||
|
|
||||||
return Packet(packetChannel, packetSecurityMode, packetContent);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,39 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
#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<uint8_t>& content);
|
|
||||||
|
|
||||||
[[nodiscard]] PacketContent getContent(const Context& context) const;
|
|
||||||
void setContent(const Context& context, SecurityMode securityMode, const PacketContent& packetContent);
|
|
||||||
|
|
||||||
[[nodiscard]] std::vector<std::uint8_t> serialize() const;
|
|
||||||
static Packet deserialize(std::vector<std::uint8_t>& data);
|
|
||||||
|
|
||||||
std::uint8_t channel {};
|
|
||||||
|
|
||||||
private:
|
|
||||||
SecurityMode securityMode {};
|
|
||||||
std::vector<std::uint8_t> content;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,47 +0,0 @@
|
||||||
#include "PacketContent.hpp"
|
|
||||||
|
|
||||||
#include <cstring>
|
|
||||||
#include <stdexcept>
|
|
||||||
|
|
||||||
#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<std::uint8_t>& data) {
|
|
||||||
this->eventType = eventType;
|
|
||||||
this->data = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
std::vector<std::uint8_t> PacketContent::serialize() const {
|
|
||||||
// serialize the members
|
|
||||||
const auto serializedEventType = util::serialize::serializeObject(static_cast<std::uint8_t>(this->eventType));
|
|
||||||
const auto serializedData = util::serialize::serializeVector(this->data);
|
|
||||||
|
|
||||||
// create a buffer to store our members
|
|
||||||
std::vector<std::uint8_t> 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<std::uint8_t>& data) {
|
|
||||||
// deserialize the members
|
|
||||||
const auto contentEventType = static_cast<event::EventType>(util::serialize::deserializeObject<std::uint8_t>(data));
|
|
||||||
const auto contentData = util::serialize::deserializeVector<std::uint8_t>(data);
|
|
||||||
|
|
||||||
return {contentEventType, contentData};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,35 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <limits>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#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<std::uint16_t>::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<std::uint8_t>& data);
|
|
||||||
|
|
||||||
[[nodiscard]] std::vector<std::uint8_t> serialize() const;
|
|
||||||
static PacketContent deserialize(std::vector<std::uint8_t>& data);
|
|
||||||
|
|
||||||
event::EventType eventType {};
|
|
||||||
std::vector<std::uint8_t> data;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
|
|
||||||
namespace drp::packet::base {
|
|
||||||
|
|
||||||
|
|
||||||
enum class SecurityMode {
|
|
||||||
PLAIN = 0x00,
|
|
||||||
AES = 0x01,
|
|
||||||
RSA = 0x02,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
1
source/packets/base/__init__.py
Normal file
1
source/packets/base/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
from .BasePacket import BasePacket
|
|
@ -1,23 +0,0 @@
|
||||||
#include "InfoPacketData.hpp"
|
|
||||||
|
|
||||||
|
|
||||||
namespace drp::packet::info {
|
|
||||||
|
|
||||||
|
|
||||||
std::vector<std::uint8_t> InfoPacketData::serialize() const {
|
|
||||||
// serialize the members
|
|
||||||
const auto serializedPeer = this->peer.serialize();
|
|
||||||
|
|
||||||
return serializedPeer;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
InfoPacketData InfoPacketData::deserialize(std::vector<std::uint8_t>& data) {
|
|
||||||
// deserialize the members
|
|
||||||
const auto packetPeer = Peer::deserialize(data);
|
|
||||||
|
|
||||||
return {packetPeer};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -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<std::uint8_t> serialize() const;
|
|
||||||
static InfoPacketData deserialize(std::vector<std::uint8_t>& data);
|
|
||||||
|
|
||||||
Peer peer;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,25 +0,0 @@
|
||||||
#include "SearchPacketData.hpp"
|
|
||||||
|
|
||||||
#include "utils/serialize/basics.hpp"
|
|
||||||
|
|
||||||
|
|
||||||
namespace drp::packet::search {
|
|
||||||
|
|
||||||
|
|
||||||
std::vector<std::uint8_t> SearchPacketData::serialize() const {
|
|
||||||
// serialize the members
|
|
||||||
const auto serializedTimestamp = util::serialize::serializeObject(this->timestamp);
|
|
||||||
|
|
||||||
return serializedTimestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
SearchPacketData SearchPacketData::deserialize(std::vector<std::uint8_t>& data) {
|
|
||||||
// deserialize the members
|
|
||||||
const auto packetTimestamp = util::serialize::deserializeObject<std::chrono::time_point<std::chrono::high_resolution_clock>>(data);
|
|
||||||
|
|
||||||
return SearchPacketData(packetTimestamp);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <chrono>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
|
|
||||||
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<std::uint8_t> serialize() const;
|
|
||||||
static SearchPacketData deserialize(std::vector<std::uint8_t>& data);
|
|
||||||
|
|
||||||
std::chrono::time_point<std::chrono::system_clock> timestamp; /// timestamp when the search request was sent
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
146
source/test.cpp
146
source/test.cpp
|
@ -1,146 +0,0 @@
|
||||||
#include <iostream>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#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<std::uint8_t> 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<std::uint8_t> 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<char> 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<std::uint8_t> 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<char> deserializedData = drp::util::serialize::deserializeVector<char>(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<std::uint8_t> 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<std::uint8_t> 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;
|
|
||||||
}
|
|
0
source/utils/__init__.py
Normal file
0
source/utils/__init__.py
Normal file
|
@ -1,47 +0,0 @@
|
||||||
#include "audio.hpp"
|
|
||||||
|
|
||||||
#include <chrono>
|
|
||||||
#include <stdexcept>
|
|
||||||
|
|
||||||
#include <fmt123.h>
|
|
||||||
#include <portaudio.h>
|
|
||||||
|
|
||||||
|
|
||||||
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<std::uint64_t>(
|
|
||||||
(1 / static_cast<double>(sampleRate * channels * encodingSize)) *
|
|
||||||
1000 * static_cast<double>(length)
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <chrono>
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
|
|
||||||
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
|
|
||||||
);
|
|
||||||
|
|
||||||
}
|
|
0
source/utils/crypto/__init__.py
Normal file
0
source/utils/crypto/__init__.py
Normal file
47
source/utils/crypto/aes.py
Normal file
47
source/utils/crypto/aes.py
Normal file
|
@ -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
|
|
@ -1 +0,0 @@
|
||||||
#include "AesKey.hpp"
|
|
|
@ -1,166 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <functional>
|
|
||||||
#include <memory>
|
|
||||||
#include <random>
|
|
||||||
#include <openssl/evp.h>
|
|
||||||
|
|
||||||
|
|
||||||
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<std::size_t keySize, std::size_t ivSize, const EVP_CIPHER*(cipherFunction)()>
|
|
||||||
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<std::uint8_t, keySize>& 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<std::uint8_t> encrypt(const std::vector<std::uint8_t>& plainData) const {
|
|
||||||
// create an initialization vector
|
|
||||||
std::array<std::uint8_t, ivSize> iv {};
|
|
||||||
for (auto& byte : iv)
|
|
||||||
byte = randomDistribution(randomGenerator);
|
|
||||||
|
|
||||||
// create the cipher context
|
|
||||||
const auto context = std::unique_ptr<EVP_CIPHER_CTX, decltype(&EVP_CIPHER_CTX_free)>(
|
|
||||||
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<std::uint8_t> 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<int>(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<std::uint8_t> decrypt(const std::vector<std::uint8_t>& rawEncryptedData) const {
|
|
||||||
// create a cipher context
|
|
||||||
const auto context = std::unique_ptr<EVP_CIPHER_CTX, decltype(&EVP_CIPHER_CTX_free)>(
|
|
||||||
EVP_CIPHER_CTX_new(),
|
|
||||||
EVP_CIPHER_CTX_free
|
|
||||||
);
|
|
||||||
|
|
||||||
std::array<std::uint8_t, ivSize> 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<std::uint8_t> plainData(encryptedData.size());
|
|
||||||
int length;
|
|
||||||
|
|
||||||
// decrypt the data
|
|
||||||
if (EVP_DecryptUpdate(
|
|
||||||
context.get(),
|
|
||||||
plainData.data(),
|
|
||||||
&length,
|
|
||||||
encryptedData.data(),
|
|
||||||
static_cast<int>(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<std::uint8_t> randomDistribution;
|
|
||||||
|
|
||||||
std::array<std::uint8_t, keySize> _data;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
template<std::size_t keySize, std::size_t ivSize, const EVP_CIPHER*(cipherFunction)()>
|
|
||||||
std::mt19937 AesKey<keySize, ivSize, cipherFunction>::randomGenerator = std::mt19937(std::random_device{}());
|
|
||||||
|
|
||||||
template<std::size_t keySize, std::size_t ivSize, const EVP_CIPHER*(cipherFunction)()>
|
|
||||||
std::uniform_int_distribution<std::uint8_t> AesKey<keySize, ivSize, cipherFunction>::randomDistribution = std::uniform_int_distribution(
|
|
||||||
std::numeric_limits<std::uint8_t>::min(),
|
|
||||||
std::numeric_limits<std::uint8_t>::max()
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
using AesKey256 = AesKey<256/8, 16, EVP_aes_256_cbc>;
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
51
source/utils/crypto/rsa.py
Normal file
51
source/utils/crypto/rsa.py
Normal file
|
@ -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
|
||||||
|
)
|
||||||
|
)
|
|
@ -1,98 +0,0 @@
|
||||||
#include "RsaKeyPair.hpp"
|
|
||||||
|
|
||||||
#include <openssl/x509.h>
|
|
||||||
|
|
||||||
|
|
||||||
namespace drp::util::crypto {
|
|
||||||
|
|
||||||
|
|
||||||
RsaKeyPair::RsaKeyPair(const std::size_t size, const int padMode) {
|
|
||||||
// create the context
|
|
||||||
const std::unique_ptr<EVP_PKEY_CTX, decltype(&EVP_PKEY_CTX_free)> 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<int>(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<EVP_PKEY, decltype(&EVP_PKEY_free)> keyPair(
|
|
||||||
rawKeyPair,
|
|
||||||
EVP_PKEY_free
|
|
||||||
);
|
|
||||||
|
|
||||||
// extract the private key
|
|
||||||
const std::unique_ptr<BIO, decltype(&BIO_free)> 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<std::uint8_t> privateKeyData(BIO_pending(privateBio.get()));
|
|
||||||
if (BIO_read(
|
|
||||||
privateBio.get(),
|
|
||||||
privateKeyData.data(),
|
|
||||||
static_cast<int>(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<BIO, decltype(&BIO_free)> 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<std::uint8_t> publicKeyData(BIO_pending(publicBio.get()));
|
|
||||||
if (BIO_read(
|
|
||||||
publicBio.get(),
|
|
||||||
publicKeyData.data(),
|
|
||||||
static_cast<int>(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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,44 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <memory>
|
|
||||||
#include <openssl/evp.h>
|
|
||||||
#include <openssl/rsa.h>
|
|
||||||
#include <openssl/types.h>
|
|
||||||
|
|
||||||
#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;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,121 +0,0 @@
|
||||||
#include "RsaPrivateKey.hpp"
|
|
||||||
|
|
||||||
#include <openssl/rsa.h>
|
|
||||||
#include <openssl/evp.h>
|
|
||||||
#include <openssl/x509.h>
|
|
||||||
|
|
||||||
#include "utils/serialize/basics.hpp"
|
|
||||||
|
|
||||||
|
|
||||||
namespace drp::util::crypto {
|
|
||||||
|
|
||||||
|
|
||||||
RsaPrivateKey::RsaPrivateKey() = default;
|
|
||||||
|
|
||||||
RsaPrivateKey::RsaPrivateKey(const std::vector<std::uint8_t>& data, const int padMode) {
|
|
||||||
this->_data = data;
|
|
||||||
this->padMode = padMode;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
std::vector<std::uint8_t> RsaPrivateKey::decrypt(const std::vector<std::uint8_t>& encryptedData) const {
|
|
||||||
const auto key = this->getOpenSslKey();
|
|
||||||
|
|
||||||
// initialize the encryption context
|
|
||||||
const std::unique_ptr<EVP_PKEY_CTX, decltype(&EVP_PKEY_CTX_free)> 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<std::uint8_t> 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<std::uint8_t> RsaPrivateKey::serialize() const {
|
|
||||||
// serialize the members
|
|
||||||
const auto serializedData = serialize::serializeVector(this->_data);
|
|
||||||
const auto serializedPadMode = serialize::serializeObject(static_cast<std::uint8_t>(this->padMode));
|
|
||||||
|
|
||||||
// create a buffer to store our members
|
|
||||||
std::vector<std::uint8_t> 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<std::uint8_t>& data) {
|
|
||||||
// deserialize the members
|
|
||||||
const auto keyData = serialize::deserializeVector<std::uint8_t>(data);
|
|
||||||
const auto keyPadding = static_cast<int>(serialize::deserializeObject<std::uint8_t>(data));
|
|
||||||
|
|
||||||
return RsaPrivateKey(keyData, keyPadding);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
[[nodiscard]] std::unique_ptr<EVP_PKEY, decltype(&EVP_PKEY_free)> RsaPrivateKey::getOpenSslKey() const {
|
|
||||||
// get the bio from the private key data
|
|
||||||
const std::unique_ptr<BIO, decltype(&BIO_free)> bio(
|
|
||||||
BIO_new_mem_buf(
|
|
||||||
this->_data.data(),
|
|
||||||
static_cast<int>(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
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <vector>
|
|
||||||
#include <openssl/evp.h>
|
|
||||||
|
|
||||||
|
|
||||||
namespace drp::util::crypto {
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represent an RSA private key.
|
|
||||||
*/
|
|
||||||
class RsaPrivateKey {
|
|
||||||
public:
|
|
||||||
RsaPrivateKey();
|
|
||||||
explicit RsaPrivateKey(const std::vector<std::uint8_t>& data, int padMode);
|
|
||||||
|
|
||||||
[[nodiscard]] std::vector<std::uint8_t> decrypt(const std::vector<std::uint8_t>& encryptedData) const;
|
|
||||||
|
|
||||||
[[nodiscard]] std::vector<std::uint8_t> serialize() const;
|
|
||||||
static RsaPrivateKey deserialize(std::vector<std::uint8_t>& data);
|
|
||||||
|
|
||||||
private:
|
|
||||||
[[nodiscard]] std::unique_ptr<EVP_PKEY, decltype(&EVP_PKEY_free)> getOpenSslKey() const;
|
|
||||||
|
|
||||||
int padMode {};
|
|
||||||
std::vector<std::uint8_t> _data;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,121 +0,0 @@
|
||||||
#include "RsaPublicKey.hpp"
|
|
||||||
|
|
||||||
#include <ranges>
|
|
||||||
#include <openssl/rsa.h>
|
|
||||||
#include <openssl/x509.h>
|
|
||||||
|
|
||||||
#include "utils/serialize/basics.hpp"
|
|
||||||
|
|
||||||
|
|
||||||
namespace drp::util::crypto {
|
|
||||||
|
|
||||||
|
|
||||||
RsaPublicKey::RsaPublicKey() = default;
|
|
||||||
|
|
||||||
RsaPublicKey::RsaPublicKey(const std::vector<uint8_t>& data, const int padMode) {
|
|
||||||
this->_data = data;
|
|
||||||
this->padMode = padMode;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
[[nodiscard]] std::vector<uint8_t> RsaPublicKey::encrypt(const std::vector<std::uint8_t>& plainData) const {
|
|
||||||
const auto key = this->getOpenSslKey();
|
|
||||||
|
|
||||||
// initialize the encryption context
|
|
||||||
const std::unique_ptr<EVP_PKEY_CTX, decltype(&EVP_PKEY_CTX_free)> 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<std::uint8_t> 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<std::uint8_t> RsaPublicKey::serialize() const {
|
|
||||||
// serialize the members
|
|
||||||
const auto serializedData = serialize::serializeVector(this->_data);
|
|
||||||
const auto serializedPadMode = serialize::serializeObject<std::uint8_t>(static_cast<std::uint8_t>(this->padMode));
|
|
||||||
|
|
||||||
// create a buffer to store our members
|
|
||||||
std::vector<std::uint8_t> 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<std::uint8_t>& data) {
|
|
||||||
// deserialize the members
|
|
||||||
const auto keyData = serialize::deserializeVector<std::uint8_t>(data);
|
|
||||||
const auto keyPadding = static_cast<int>(serialize::deserializeObject<std::uint8_t>(data));
|
|
||||||
|
|
||||||
return RsaPublicKey(keyData, keyPadding);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
[[nodiscard]] std::unique_ptr<EVP_PKEY, decltype(&EVP_PKEY_free)> RsaPublicKey::getOpenSslKey() const {
|
|
||||||
// get the bio from the public key data
|
|
||||||
const std::unique_ptr<BIO, decltype(&BIO_free)> bio(
|
|
||||||
BIO_new_mem_buf(
|
|
||||||
this->_data.data(),
|
|
||||||
static_cast<int>(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
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,37 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "RsaPrivateKey.hpp"
|
|
||||||
|
|
||||||
|
|
||||||
namespace drp::util::crypto {
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represent an RSA public key.
|
|
||||||
*/
|
|
||||||
class RsaPublicKey {
|
|
||||||
public:
|
|
||||||
RsaPublicKey();
|
|
||||||
explicit RsaPublicKey(const std::vector<uint8_t>& 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<uint8_t> encrypt(const std::vector<std::uint8_t>& plainData) const;
|
|
||||||
|
|
||||||
[[nodiscard]] std::vector<std::uint8_t> serialize() const;
|
|
||||||
static RsaPublicKey deserialize(std::vector<std::uint8_t>& data);
|
|
||||||
|
|
||||||
private:
|
|
||||||
[[nodiscard]] std::unique_ptr<EVP_PKEY, decltype(&EVP_PKEY_free)> getOpenSslKey() const;
|
|
||||||
|
|
||||||
int padMode {};
|
|
||||||
std::vector<uint8_t> _data;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
7
source/utils/crypto/type.py
Normal file
7
source/utils/crypto/type.py
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import enum
|
||||||
|
|
||||||
|
|
||||||
|
class CipherType(enum.Enum):
|
||||||
|
NONE = 0x00
|
||||||
|
AES_ECB = 0x01
|
||||||
|
RSA = 0x02
|
57
source/utils/crypto/universal.py
Normal file
57
source/utils/crypto/universal.py
Normal file
|
@ -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.")
|
|
@ -1,60 +0,0 @@
|
||||||
#include "network.hpp"
|
|
||||||
|
|
||||||
#include <cstring>
|
|
||||||
#include <ifaddrs.h>
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <netinet/in.h>
|
|
||||||
|
|
||||||
|
|
||||||
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<const sockaddr_in*>(interface->ifa_addr);
|
|
||||||
const auto addressIpv4 = reinterpret_cast<const sockaddr_in*>(&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<const sockaddr_in6*>(interface->ifa_addr);
|
|
||||||
const auto addressIpv6 = reinterpret_cast<const sockaddr_in6*>(&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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,96 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstring>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
|
|
||||||
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<typename Type>
|
|
||||||
std::vector<std::uint8_t> serializeObject(const Type& object) {
|
|
||||||
// create a vector with enough space for the object
|
|
||||||
std::vector<std::uint8_t> 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<typename Type>
|
|
||||||
Type deserializeObject(std::vector<std::uint8_t>& 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<typename Type, typename SizeType = std::uint16_t>
|
|
||||||
std::vector<std::uint8_t> serializeVector(const std::vector<Type>& object) {
|
|
||||||
// create a vector with enough size for the size and the data of the vector
|
|
||||||
std::vector<std::uint8_t> buffer(sizeof(SizeType) + object.size() * sizeof(Type));
|
|
||||||
|
|
||||||
// save the size of the vector
|
|
||||||
auto size = static_cast<SizeType>(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<typename Type, typename SizeType = std::uint16_t>
|
|
||||||
std::vector<Type> deserializeVector(std::vector<std::uint8_t>& 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<Type> 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
#include "Chrony.hpp"
|
|
|
@ -1,12 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
|
|
||||||
namespace drp::util::time {
|
|
||||||
|
|
||||||
|
|
||||||
class Chrony {
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
Loading…
Reference in a new issue