peer now calculate an asymetric private and public key, for future encrypted communication.
This commit is contained in:
parent
7871a4f4dd
commit
523c86237c
11 changed files with 128 additions and 31 deletions
|
@ -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
12
source/Context.cpp
Normal 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();
|
||||
}
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
1
source/utils/crypto.cpp
Normal file
|
@ -0,0 +1 @@
|
|||
#include "crypto.hpp"
|
70
source/utils/crypto.hpp
Normal file
70
source/utils/crypto.hpp
Normal 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};
|
||||
}
|
Loading…
Reference in a new issue