peer now calculate an asymetric private and public key, for future encrypted communication.

This commit is contained in:
study-faraphel 2024-11-17 22:34:52 +01:00
parent 7871a4f4dd
commit 523c86237c
11 changed files with 128 additions and 31 deletions

View file

@ -17,7 +17,6 @@ pkg_check_modules(PORTAUDIO REQUIRED portaudio-2.0)
pkg_check_modules(OpenSSL REQUIRED openssl)
add_executable(M2-PT-DRP
source/main.cpp
source/packets/audio/AudioPacketData.hpp
@ -64,6 +63,9 @@ add_executable(M2-PT-DRP
source/utils/network.hpp
source/Peer.cpp
source/RemotePeer.cpp
source/Context.cpp
source/utils/crypto.cpp
source/utils/crypto.hpp
)
target_include_directories(M2-PT-DRP PRIVATE
source
@ -73,7 +75,8 @@ target_include_directories(M2-PT-DRP PRIVATE
)
target_link_libraries(M2-PT-DRP PRIVATE
argparse
${MPG123_LIBRARIES}
${PORTAUDIO_LIBRARIES}
${OPENSSL_LIBRARIES}
mpg123
portaudio
ssl
crypto
)

12
source/Context.cpp Normal file
View file

@ -0,0 +1,12 @@
#include "Context.hpp"
Context::Context(const std::array<std::uint8_t, 2048> &privateKey, const std::array<std::uint8_t, 2048> &publicKey) : me(publicKey) {
this->cryptoRsaPrivateKey = privateKey;
this->socket = -1;
this->broadcastAddressInfo = nullptr;
this->server = nullptr;
this->latestPeerDiscovery = std::chrono::high_resolution_clock::now();
}

View file

@ -10,16 +10,11 @@
/**
* Information about the current state of our machine.
* Used everywhere to determinate how to behave.
* Information contained here may be private, contrary to the "me" attribute holding public data that can be shared.
*/
class Context {
public:
explicit Context() {
this->socket = -1;
this->broadcastAddressInfo = nullptr;
this->server = nullptr;
this->latestPeerDiscovery = std::chrono::high_resolution_clock::now();
}
explicit Context(const std::array<std::uint8_t, 2048>& privateKey, const std::array<std::uint8_t, 2048>& publicKey);
int socket; /// current socket file descriptor, used to communicate
addrinfo* broadcastAddressInfo; /// address used to broadcast messages
@ -28,4 +23,6 @@ public:
Peer me; /// information about our own machine
std::list<std::shared_ptr<RemotePeer>> remotePeers {}; /// information about other machines
std::chrono::high_resolution_clock::time_point latestPeerDiscovery; /// time of the latest discovered machine
std::array<std::uint8_t, 2048> cryptoRsaPrivateKey {}; /// the RSA private key
};

View file

@ -20,11 +20,15 @@
#include "behavior/tasks/client/ClientTask.hpp"
#include "behavior/tasks/server/ServerTask.hpp"
#include "behavior/tasks/undefined/UndefinedTask.hpp"
#include "utils/crypto.hpp"
Manager::Manager(const std::string& address, const std::string& port, const bool useIpv6) {
std::cout << "Broadcast address: " << address << ":" << port << " (" << (useIpv6 ? "IPv6" : "IPv4") << ")" << std::endl;
auto [privateKey, publicKey] = newRsaKeys<2048>();
this->context = std::make_shared<Context>(privateKey, publicKey);
// register the different events type
this->eventRegistry = {
{drp::event::EventType::PONG, std::make_shared<drp::event::PongEvent>()},
@ -47,19 +51,19 @@ Manager::Manager(const std::string& address, const std::string& port, const bool
broadcastAddressHints.ai_protocol = IPPROTO_UDP;
// create the client socket
this->context.socket = socket(
this->context->socket = socket(
broadcastAddressHints.ai_family,
broadcastAddressHints.ai_socktype,
broadcastAddressHints.ai_protocol
);
if (this->context.socket < 0)
if (this->context->socket < 0)
throw std::runtime_error("Could not create the socket: " + std::string(strerror(errno)));
// allow IPv6 multicast loopback so that we can receive our own messages.
const int socketLoopback = 1;
if (setsockopt(
context.socket,
context->socket,
IPPROTO_IPV6,
IPV6_MULTICAST_LOOP,
&socketLoopback,
@ -73,7 +77,7 @@ Manager::Manager(const std::string& address, const std::string& port, const bool
address.c_str(),
port.c_str(),
&broadcastAddressHints,
&context.broadcastAddressInfo
&context->broadcastAddressInfo
) != 0)
throw std::runtime_error("Could not get the broadcast address: " + std::string(gai_strerror(error)));
@ -96,14 +100,14 @@ Manager::Manager(const std::string& address, const std::string& port, const bool
// bind the socket to the address
if (bind(
this->context.socket,
this->context->socket,
anyAddressInfo->ai_addr,
anyAddressInfo->ai_addrlen
) < 0)
throw std::runtime_error("Could not bind to the address: " + std::string(strerror(errno)));
// TODO(Faraphel): should only be enabled in specific case.
this->context.me.serverEnabled = true;
this->context->me.serverEnabled = true;
}
@ -115,25 +119,25 @@ void Manager::loop() {
this->senderThread.join();
this->receiverThread.join();
freeaddrinfo(this->context.broadcastAddressInfo);
freeaddrinfo(this->context->broadcastAddressInfo);
}
void Manager::loopSender() {
while (true) {
std::cout << "[Sender] Handling status: " + std::to_string(static_cast<int>(this->context.me.status)) << std::endl;
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<drp::task::BaseTask> task;
try {
task = this->taskRegistry.at(this->context.me.status);
task = this->taskRegistry.at(this->context->me.status);
} catch (const std::out_of_range& exception) {
std::cerr << "[Sender] Unsupported status." << std::endl;
continue;
}
// ask the task class to handle the task
task->handle(this->context);
task->handle(*this->context);
}
}
@ -149,7 +153,7 @@ void Manager::loopReceiver() {
while (true) {
// receive new data
const ssize_t size = recvfrom(
this->context.socket,
this->context->socket,
&packet,
sizeof(packet),
0,
@ -160,7 +164,7 @@ void Manager::loopReceiver() {
throw std::runtime_error("[Receiver] Could not receive the packet: " + std::string(strerror(errno)));
// if the packet channel is neither 0 (all) nor the current one, ignore it
if (packet.channel != 0 && packet.channel != this->context.me.channel)
if (packet.channel != 0 && packet.channel != this->context->me.channel)
continue;
// decrypt the packet
@ -169,14 +173,14 @@ void Manager::loopReceiver() {
// look for a saved peer with the same address
auto remotePeer = std::ranges::find_if(
this->context.remotePeers,
this->context->remotePeers,
[&](const std::shared_ptr<RemotePeer>& remotePeer) { return
remotePeer->addressLength == fromAddressLength and
std::memcmp(&fromAddress, &remotePeer->address, fromAddressLength) == 0;
}
);
// if found, update the latest connection date
if (remotePeer != this->context.remotePeers.end()) {
if (remotePeer != this->context->remotePeers.end()) {
(*remotePeer)->latestConnection = std::chrono::high_resolution_clock::now();
}
@ -193,7 +197,7 @@ void Manager::loopReceiver() {
// ask the event class to handle the event
event->handle(
this->context,
*this->context,
packetContent,
fromAddress,
fromAddressLength

View file

@ -31,5 +31,5 @@ private:
std::map<drp::event::EventType, std::shared_ptr<drp::event::BaseEvent>> eventRegistry; /// hold the event to call depending on the event type
std::map<drp::task::TaskType, std::shared_ptr<drp::task::BaseTask>> taskRegistry; /// hold the task to call depending on the server status
Context context; /// context used between the events types
std::shared_ptr<Context> context; /// context used between the events types
};

View file

@ -1,13 +1,18 @@
#include "Peer.hpp"
Peer::Peer() {
Peer::Peer() : Peer(std::array<std::uint8_t, 2048>()) {}
Peer::Peer(const std::array<std::uint8_t, 2048>& 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;
}

View file

@ -3,16 +3,19 @@
#include <chrono>
#include <cstdint>
#include <random>
#include <openssl/types.h>
#include "behavior/tasks/types.hpp"
/**
* Contains common information about a certain peer.
* All the information contained here are "public": they can be communicated to anybody else safely.
*/
class Peer {
public:
explicit Peer();
Peer();
explicit Peer(const std::array<std::uint8_t, 2048>& cryptoRsaPublicKey);
std::uint32_t id;
@ -22,6 +25,8 @@ public:
std::chrono::high_resolution_clock::duration latencyAverage {};
std::array<std::uint8_t, 2048> cryptoRsaPublicKey {};
private:
// random
static std::random_device randomDevice;

View file

@ -29,7 +29,7 @@ void InfoEvent::handle(
}
);
// if not found, create a new remote peer.
// if not already stored, create a new remote peer.
if (itRemotePeer == context.remotePeers.end()) {
// if not found, create a new peer
const auto remotePeer = std::make_shared<RemotePeer>(fromAddress, fromAddressLength, packetPeer);

View file

@ -14,7 +14,7 @@ namespace drp::packet::info {
*/
class InfoPacketData : public base::PacketData<event::EventType::INFO, InfoPacketData> {
public:
Peer peer {};
Peer peer;
};

1
source/utils/crypto.cpp Normal file
View file

@ -0,0 +1 @@
#include "crypto.hpp"

70
source/utils/crypto.hpp Normal file
View file

@ -0,0 +1,70 @@
#pragma once
#include <vector>
#include <cstdint>
#include <memory>
#include <stdexcept>
#include <openssl/evp.h>
#include <openssl/pem.h>
template<std::size_t size>
std::pair<std::array<std::uint8_t, size>, std::array<std::uint8_t, size>> newRsaKeys() {
// create the context
const auto context = std::unique_ptr<EVP_PKEY_CTX, decltype(&EVP_PKEY_CTX_free)>(
EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, nullptr),
EVP_PKEY_CTX_free
);
if (context == nullptr)
throw std::runtime_error("Could not create an EVP context.");
if (EVP_PKEY_keygen_init(context.get()) <= 0)
throw std::runtime_error("Could not initialize the EVP context.");
// configure the context
if (EVP_PKEY_CTX_set_rsa_keygen_bits(context.get(), size) <= 0)
throw std::runtime_error("Error setting RSA key size.");
// create the private key
EVP_PKEY* rawKeyPair = nullptr;
if (EVP_PKEY_keygen(context.get(), &rawKeyPair) <= 0)
throw std::runtime_error("Could not generate RSA private key.");
const std::unique_ptr<EVP_PKEY, decltype(&EVP_PKEY_free)> keyPair(
rawKeyPair,
EVP_PKEY_free
);
// extract the private and public key
const std::shared_ptr<BIO> privateBio(
BIO_new(BIO_s_mem()),
BIO_free
);
if (!PEM_write_bio_PrivateKey(
privateBio.get(),
keyPair.get(),
nullptr,
nullptr,
0,
nullptr,
nullptr
))
throw std::runtime_error("Could not generate RSA private key.");
std::array<std::uint8_t, size> privateKey;
BIO_read(privateBio.get(), privateKey.data(), BIO_pending(privateBio.get()));
const std::shared_ptr<BIO> publicBio(
BIO_new(BIO_s_mem()),
BIO_free
);
if (!PEM_write_bio_PUBKEY(publicBio.get(), keyPair.get()))
throw std::runtime_error("Could not generate RSA public key.");
std::array<std::uint8_t, size> publicKey;
BIO_read(publicBio.get(), publicKey.data(), BIO_pending(publicBio.get()));
return {privateKey, publicKey};
}