From c7c3b1c54c20d789337ca784a7fed3818e73c71d Mon Sep 17 00:00:00 2001 From: DcruBro Date: Thu, 6 Nov 2025 22:32:32 +0100 Subject: [PATCH] Almost finished with the TCP Handshake procedure, need to properly handle disconnects (currently pretty forceful) --- CMakeLists.txt | 37 ++-- .../columnlynx/client/net/tcp/tcp_client.hpp | 93 +++++++++- .../columnlynx/common/libsodium_wrapper.hpp | 168 +++++++++++++++++- .../common/net/tcp/tcp_message_type.hpp | 5 +- include/columnlynx/common/utils.hpp | 13 ++ .../server/net/tcp/tcp_connection.hpp | 88 ++++++++- .../columnlynx/server/net/tcp/tcp_server.hpp | 1 + src/client/main.cpp | 4 +- src/common/libsodium_wrapper.cpp | 19 +- src/common/utils.cpp | 14 ++ src/server/main.cpp | 4 +- src/server/server/net/tcp/tcp_server.cpp | 3 +- 12 files changed, 408 insertions(+), 41 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e64b48b..3f4fb42 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,21 +19,12 @@ set(CMAKE_CXX_EXTENSIONS OFF) include(FetchContent) -FetchContent_Declare( - Sodium - GIT_REPOSITORY https://github.com/robinlinden/libsodium-cmake.git - GIT_TAG e5b985ad0dd235d8c4307ea3a385b45e76c74c6a # Last updated at 2025-04-13 -) - -set(SODIUM_DISABLE_TESTS ON CACHE BOOL "" FORCE) -set(SODIUM_STATIC ON CACHE BOOL "" FORCE) - -FetchContent_MakeAvailable(Sodium) - # --------------------------------------------------------- -# macOS: Build universal (ARM + x86_64) +# macOS: Force architecture before fetching dependencies # --------------------------------------------------------- if(APPLE) + # Build universal (arm64 + x86_64), or limit to one arch if you prefer + # e.g., set(CMAKE_OSX_ARCHITECTURES "arm64" CACHE STRING "" FORCE) set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64" CACHE STRING "Build architectures" FORCE) endif() @@ -43,10 +34,30 @@ endif() if(WIN32) add_compile_definitions(_WIN32_WINNT=0x0A00 NOMINMAX WIN32_LEAN_AND_MEAN) elseif(UNIX) - add_compile_options(-Wall -Wextra -Wpedantic -std=c++20) + add_compile_options(-Wall -Wextra -Wpedantic) add_link_options(-pthread) endif() +# --------------------------------------------------------- +# Fetch libsodium (after arch and platform settings) +# --------------------------------------------------------- +FetchContent_Declare( + Sodium + GIT_REPOSITORY https://github.com/robinlinden/libsodium-cmake.git + GIT_TAG e5b985ad0dd235d8c4307ea3a385b45e76c74c6a # Last updated at 2025-04-13 +) + +set(SODIUM_DISABLE_TESTS ON CACHE BOOL "" FORCE) +set(SODIUM_STATIC ON CACHE BOOL "" FORCE) + +# Forward architecture and build type to subproject +set(SODIUM_CMAKE_ARGS + -DCMAKE_OSX_ARCHITECTURES=${CMAKE_OSX_ARCHITECTURES} + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} +) + +FetchContent_MakeAvailable(Sodium) + # --------------------------------------------------------- # Output directories # --------------------------------------------------------- diff --git a/include/columnlynx/client/net/tcp/tcp_client.hpp b/include/columnlynx/client/net/tcp/tcp_client.hpp index 91617a4..712d0e5 100644 --- a/include/columnlynx/client/net/tcp/tcp_client.hpp +++ b/include/columnlynx/client/net/tcp/tcp_client.hpp @@ -9,6 +9,10 @@ #include #include #include +#include +#include +#include +#include using asio::ip::tcp; @@ -17,8 +21,9 @@ namespace ColumnLynx::Net::TCP { public: TCPClient(asio::io_context& ioContext, const std::string& host, - const std::string& port) - : mResolver(ioContext), mSocket(ioContext), mHost(host), mPort(port) {} + const std::string& port, + Utils::LibSodiumWrapper* sodiumWrapper) + : mResolver(ioContext), mSocket(ioContext), mHost(host), mPort(port), mLibSodiumWrapper(sodiumWrapper) {} void start() { auto self = shared_from_this(); @@ -35,10 +40,10 @@ namespace ColumnLynx::Net::TCP { mHandleMessage(static_cast(MessageHandler::toUint8(type)), data); }); mHandler->start(); - + // Init connection handshake Utils::log("Sending handshake init to server."); - mHandler->sendMessage(ClientMessageType::HANDSHAKE_INIT, "Hello, I am " + Utils::getHostname()); + mHandler->sendMessage(ClientMessageType::HANDSHAKE_INIT, Utils::uint8ArrayToString(mLibSodiumWrapper->getXPublicKey(), crypto_box_PUBLICKEYBYTES)); } else { Utils::error("Client connect failed: " + ec.message()); } @@ -88,7 +93,81 @@ namespace ColumnLynx::Net::TCP { switch (type) { case ServerMessageType::HANDSHAKE_IDENTIFY: Utils::log("Received server identity: " + data); - //std::memcpy(mServerPublicKey, data.data(), std::min(data.size(), sizeof(mServerPublicKey))); + std::memcpy(mServerPublicKey, data.data(), std::min(data.size(), sizeof(mServerPublicKey))); + + // Generate and send challenge + { + Utils::log("Sending challenge to server."); + mSubmittedChallenge = Utils::LibSodiumWrapper::generateRandom256Bit(); // Temporarily store the challenge to verify later + mHandler->sendMessage(ClientMessageType::HANDSHAKE_CHALLENGE, Utils::uint8ArrayToString(mSubmittedChallenge)); + } + + break; + case ServerMessageType::HANDSHAKE_CHALLENGE_RESPONSE: + Utils::log("Received challenge response from server."); + { + // Verify the signature + Signature sig{}; + std::memcpy(sig.data(), data.data(), std::min(data.size(), sig.size())); + if (Utils::LibSodiumWrapper::verifyMessage(mSubmittedChallenge.data(), mSubmittedChallenge.size(), sig, mServerPublicKey)) { + Utils::log("Challenge response verified successfully."); + + // Convert the server's public key to Curve25519 for encryption + AsymPublicKey serverXPubKey{}; + crypto_sign_ed25519_pk_to_curve25519(serverXPubKey.data(), mServerPublicKey); + + // Generate AES key and send confirmation + mConnectionAESKey = Utils::LibSodiumWrapper::generateRandom256Bit(); + AsymNonce nonce{}; + randombytes_buf(nonce.data(), nonce.size()); + + // TODO: This is pretty redundant, it should return the required type directly + std::array arrayPrivateKey; + std::copy(mLibSodiumWrapper->getXPrivateKey(), + mLibSodiumWrapper->getXPrivateKey() + 32, + arrayPrivateKey.begin()); + + std::vector encr = Utils::LibSodiumWrapper::encryptAsymmetric( + mConnectionAESKey.data(), mConnectionAESKey.size(), + nonce, + serverXPubKey, + arrayPrivateKey + ); + + std::vector payload; + payload.reserve(nonce.size() + encr.size()); + payload.insert(payload.end(), nonce.begin(), nonce.end()); + payload.insert(payload.end(), encr.begin(), encr.end()); + + mHandler->sendMessage(ClientMessageType::HANDSHAKE_EXCHANGE_KEY, Utils::uint8ArrayToString(payload.data(), payload.size())); + } else { + Utils::error("Challenge response verification failed. Terminating connection."); + disconnect(); + } + } + + break; + case ServerMessageType::HANDSHAKE_EXCHANGE_KEY_CONFIRM: + Utils::log("Received handshake exchange key confirmation from server."); + // Decrypt the session ID using the established AES key + { + Nonce symNonce{}; // All zeros + std::vector ciphertext(data.begin(), data.end()); + std::vector decrypted = Utils::LibSodiumWrapper::decryptMessage( + ciphertext.data(), ciphertext.size(), + mConnectionAESKey, symNonce + ); + + if (decrypted.size() != sizeof(mConnectionSessionID)) { + Utils::error("Decrypted session ID has invalid size. Terminating connection."); + disconnect(); + return; + } + + std::memcpy(&mConnectionSessionID, decrypted.data(), sizeof(mConnectionSessionID)); + Utils::log("Connection established with Session ID: " + std::to_string(mConnectionSessionID)); + } + break; case ServerMessageType::GRACEFUL_DISCONNECT: Utils::log("Server is disconnecting: " + data); @@ -108,5 +187,9 @@ namespace ColumnLynx::Net::TCP { std::shared_ptr mHandler; std::string mHost, mPort; uint8_t mServerPublicKey[32]; // Assuming 256-bit public key + std::array mSubmittedChallenge{}; + Utils::LibSodiumWrapper* mLibSodiumWrapper; + uint64_t mConnectionSessionID; + SymmetricKey mConnectionAESKey; }; } \ No newline at end of file diff --git a/include/columnlynx/common/libsodium_wrapper.hpp b/include/columnlynx/common/libsodium_wrapper.hpp index a75263a..6afe572 100644 --- a/include/columnlynx/common/libsodium_wrapper.hpp +++ b/include/columnlynx/common/libsodium_wrapper.hpp @@ -9,19 +9,179 @@ #include #include #include +#include +#include + +namespace ColumnLynx { + using PublicKey = std::array; // Ed25519 + using PrivateKey = std::array; // Ed25519 + using Signature = std::array; // 64 bytes + using SymmetricKey = std::array; // 32 bytes + using Nonce = std::array; // 12 bytes + + using AsymPublicKey = std::array; // 32 bytes + using AsymSecretKey = std::array; // 32 bytes + using AsymNonce = std::array; // 24 bytes +} namespace ColumnLynx::Utils { - class LibSodiumWrapper { public: LibSodiumWrapper(); uint8_t* getPublicKey(); uint8_t* getPrivateKey(); - uint8_t generateRandomAESKey(); + uint8_t* getXPublicKey() { return mXPublicKey.data(); } + uint8_t* getXPrivateKey() { return mXPrivateKey.data(); } + + // Helper section + + // Generates a random 256-bit (32-byte) array + static std::array generateRandom256Bit(); + + static inline Signature signMessage(const uint8_t* msg, size_t len, const PrivateKey& sk) { + Signature sig{}; + crypto_sign_detached(sig.data(), nullptr, msg, len, sk.data()); + return sig; + } + + static inline bool verifyMessage(const uint8_t* msg, size_t len, + const Signature& sig, const PublicKey& pk) { + return crypto_sign_verify_detached(sig.data(), msg, len, pk.data()) == 0; + } + + // Overloads for std::string / std::array + static inline Signature signMessage(const std::string& msg, const PrivateKey& sk) { + return signMessage(reinterpret_cast(msg.data()), msg.size(), sk); + } + + template + static inline Signature signMessage(const std::array& msg, const PrivateKey& sk) { + return signMessage(msg.data(), msg.size(), sk); + } + + static inline Signature signMessage(const uint8_t* msg, size_t len, const uint8_t* sk_raw) { + Signature sig{}; + crypto_sign_detached(sig.data(), nullptr, msg, len, sk_raw); + return sig; + } + + static inline bool verifyMessage(const std::string& msg, const Signature& sig, const PublicKey& pk) { + return verifyMessage(reinterpret_cast(msg.data()), msg.size(), sig, pk); + } + + template + static inline bool verifyMessage(const std::array& msg, const Signature& sig, const PublicKey& pk) { + return verifyMessage(msg.data(), msg.size(), sig, pk); + } + + static inline bool verifyMessage(const uint8_t* msg, size_t len, + const Signature& sig, const uint8_t* pk_raw) { + return crypto_sign_verify_detached(sig.data(), msg, len, pk_raw) == 0; + } + + // Encrypt with ChaCha20-Poly1305 (returns ciphertext as bytes) + static inline std::vector encryptMessage( + const uint8_t* plaintext, size_t len, + const SymmetricKey& key, const Nonce& nonce, + const std::string& aad = "") + { + std::vector ciphertext(len + crypto_aead_chacha20poly1305_ietf_ABYTES); + unsigned long long clen = 0; + + if (crypto_aead_chacha20poly1305_ietf_encrypt( + ciphertext.data(), &clen, + plaintext, len, + reinterpret_cast(aad.data()), aad.size(), + nullptr, // no additional secret data + nonce.data(), key.data()) != 0) + { + throw std::runtime_error("Encryption failed"); + } + + ciphertext.resize(static_cast(clen)); + return ciphertext; + } + + // Decrypt with ChaCha20-Poly1305 (returns plaintext as bytes) + static inline std::vector decryptMessage( + const uint8_t* ciphertext, size_t len, + const SymmetricKey& key, const Nonce& nonce, + const std::string& aad = "") + { + if (len < crypto_aead_chacha20poly1305_ietf_ABYTES) + throw std::runtime_error("Ciphertext too short"); + + std::vector plaintext(len - crypto_aead_chacha20poly1305_ietf_ABYTES); + unsigned long long plen = 0; + + if (crypto_aead_chacha20poly1305_ietf_decrypt( + plaintext.data(), &plen, + nullptr, + ciphertext, len, + reinterpret_cast(aad.data()), aad.size(), + nonce.data(), key.data()) != 0) + { + throw std::runtime_error("Decryption failed or authentication tag invalid"); + } + + plaintext.resize(static_cast(plen)); + return plaintext; + } + + static inline Nonce generateNonce() { + Nonce n{}; + randombytes_buf(n.data(), n.size()); + return n; + } + + static inline std::vector encryptAsymmetric( + const uint8_t* plaintext, size_t len, + const AsymNonce& nonce, + const AsymPublicKey& recipient_pk, + const AsymSecretKey& sender_sk) + { + std::vector ciphertext(len + crypto_box_MACBYTES); + + if (crypto_box_easy( + ciphertext.data(), + plaintext, len, + nonce.data(), + recipient_pk.data(), sender_sk.data()) != 0) + { + throw std::runtime_error("Asymmetric encryption failed"); + } + + return ciphertext; + } + + static inline std::vector decryptAsymmetric( + const uint8_t* ciphertext, size_t len, + const AsymNonce& nonce, + const AsymPublicKey& sender_pk, + const AsymSecretKey& recipient_sk) + { + if (len < crypto_box_MACBYTES) + throw std::runtime_error("Ciphertext too short"); + + std::vector plaintext(len - crypto_box_MACBYTES); + + if (crypto_box_open_easy( + plaintext.data(), + ciphertext, len, + nonce.data(), + sender_pk.data(), recipient_sk.data()) != 0) + { + throw std::runtime_error("Asymmetric decryption failed"); + } + + return plaintext; + } private: - uint8_t mPublicKey[crypto_kx_PUBLICKEYBYTES]; - uint8_t mPrivateKey[crypto_kx_SECRETKEYBYTES]; + std::array mPublicKey; + std::array mPrivateKey; + std::array mXPublicKey; + std::array mXPrivateKey; }; } \ No newline at end of file diff --git a/include/columnlynx/common/net/tcp/tcp_message_type.hpp b/include/columnlynx/common/net/tcp/tcp_message_type.hpp index fef53c8..5a90ebb 100644 --- a/include/columnlynx/common/net/tcp/tcp_message_type.hpp +++ b/include/columnlynx/common/net/tcp/tcp_message_type.hpp @@ -11,7 +11,7 @@ namespace ColumnLynx::Net::TCP { enum class ServerMessageType : uint8_t { // Server to Client HANDSHAKE_IDENTIFY = 0x02, // Send server identity (public key, server name, etc) HANDSHAKE_CHALLENGE_RESPONSE = 0x04, // Response to client's challenge - HANDSHAKE_EXCHANGE_KEY = 0x06, // If accepted, send encrypted AES key and session ID + HANDSHAKE_EXCHANGE_KEY_CONFIRM = 0x06, // If accepted, send encrypted AES key and session ID GRACEFUL_DISCONNECT = 0xFE, // Notify client of impending disconnection KILL_CONNECTION = 0xFF, // Forecefully terminate the connection (with cleanup if possible), reserved for unrecoverable errors @@ -20,8 +20,7 @@ namespace ColumnLynx::Net::TCP { enum class ClientMessageType : uint8_t { // Client to Server HANDSHAKE_INIT = 0xA1, // Request connection HANDSHAKE_CHALLENGE = 0xA3, // Challenge ownership of private key - HANDSHAKE_CONFIRM = 0xA5, // Accept or reject identity, can kill the connection - HANDSHAKE_EXCHANGE_KEY_CONFIRM = 0xA7, // Confirm receipt of AES key and session ID + HANDSHAKE_EXCHANGE_KEY = 0xA5, // Accept or reject identity, can kill the connection, also sends the AES key GRACEFUL_DISCONNECT = 0xFE, // Notify server of impending disconnection KILL_CONNECTION = 0xFF, // Forecefully terminate the connection (with cleanup if possible), reserved for unrecoverable errors diff --git a/include/columnlynx/common/utils.hpp b/include/columnlynx/common/utils.hpp index 894e77d..2c488d6 100644 --- a/include/columnlynx/common/utils.hpp +++ b/include/columnlynx/common/utils.hpp @@ -22,4 +22,17 @@ namespace ColumnLynx::Utils { std::string getHostname(); std::string getVersion(); unsigned short serverPort(); + + // Raw byte to hex string conversion helper + std::string bytesToHexString(const uint8_t* bytes, size_t length); + + // uint8_t to raw string conversion helper + template + inline std::string uint8ArrayToString(const std::array& arr) { + return std::string(reinterpret_cast(arr.data()), N); + } + + inline std::string uint8ArrayToString(const uint8_t* data, size_t length) { + return std::string(reinterpret_cast(data), length); + } }; \ No newline at end of file diff --git a/include/columnlynx/server/net/tcp/tcp_connection.hpp b/include/columnlynx/server/net/tcp/tcp_connection.hpp index 6d5e763..8f3cf97 100644 --- a/include/columnlynx/server/net/tcp/tcp_connection.hpp +++ b/include/columnlynx/server/net/tcp/tcp_connection.hpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -22,10 +23,10 @@ namespace ColumnLynx::Net::TCP { static pointer create( asio::ip::tcp::socket socket, - Utils::LibSodiumWrapper *libsodium, + Utils::LibSodiumWrapper* sodiumWrapper, std::function onDisconnect) { - auto conn = pointer(new TCPConnection(std::move(socket), libsodium)); + auto conn = pointer(new TCPConnection(std::move(socket), sodiumWrapper)); conn->mOnDisconnect = std::move(onDisconnect); return conn; } @@ -71,16 +72,88 @@ namespace ColumnLynx::Net::TCP { } private: - TCPConnection(asio::ip::tcp::socket socket, Utils::LibSodiumWrapper *libsodium) - : mHandler(std::make_shared(std::move(socket))), mLibSodiumWrapper(libsodium) {} + TCPConnection(asio::ip::tcp::socket socket, Utils::LibSodiumWrapper* sodiumWrapper) + : mHandler(std::make_shared(std::move(socket))), mLibSodiumWrapper(sodiumWrapper) {} void mHandleMessage(ClientMessageType type, const std::string& data) { std::string reqAddr = mHandler->socket().remote_endpoint().address().to_string(); switch (type) { case ClientMessageType::HANDSHAKE_INIT: { - Utils::log("Received HANDSHAKE_INIT from " + reqAddr + ": " + data); - mHandler->sendMessage(ServerMessageType::HANDSHAKE_IDENTIFY, std::string(reinterpret_cast(mLibSodiumWrapper->getPublicKey()))); + Utils::log("Received HANDSHAKE_INIT from " + reqAddr); + std::memcpy(mConnectionPublicKey.data(), data.data(), std::min(data.size(), sizeof(mConnectionPublicKey))); // Store the client's public key (for identification) + mHandler->sendMessage(ServerMessageType::HANDSHAKE_IDENTIFY, Utils::uint8ArrayToString(mLibSodiumWrapper->getPublicKey(), crypto_sign_PUBLICKEYBYTES)); // This public key should always exist + break; + } + case ClientMessageType::HANDSHAKE_CHALLENGE: { + Utils::log("Received HANDSHAKE_CHALLENGE from " + reqAddr); + + // Convert to byte array + uint8_t challengeData[32]; + std::memcpy(challengeData, data.data(), std::min(data.size(), sizeof(challengeData))); + + // Sign the challenge + Signature sig = Utils::LibSodiumWrapper::signMessage( + challengeData, sizeof(challengeData), + mLibSodiumWrapper->getPrivateKey() + ); + + mHandler->sendMessage(ServerMessageType::HANDSHAKE_CHALLENGE_RESPONSE, Utils::uint8ArrayToString(sig.data(), sig.size())); // Placeholder response + break; + } + case ClientMessageType::HANDSHAKE_EXCHANGE_KEY: { + Utils::log("Received HANDSHAKE_EXCHANGE_KEY from " + reqAddr); + + // Extract encrypted AES key and nonce (nonce is the first 24 bytes, rest is the ciphertext) + if (data.size() < 24) { // Minimum size check (nonce) + Utils::warn("HANDSHAKE_EXCHANGE_KEY from " + reqAddr + " is too short."); + disconnect(); + return; + } + + AsymNonce nonce{}; + std::memcpy(nonce.data(), data.data(), nonce.size()); + std::vector ciphertext(data.size() - nonce.size()); + std::memcpy(ciphertext.data(), data.data() + nonce.size(), ciphertext.size()); + try { + std::array arrayPrivateKey; + std::copy(mLibSodiumWrapper->getXPrivateKey(), + mLibSodiumWrapper->getXPrivateKey() + 32, + arrayPrivateKey.begin()); + + // Decrypt the AES key using the client's public key and server's private key + std::vector decrypted = Utils::LibSodiumWrapper::decryptAsymmetric( + ciphertext.data(), ciphertext.size(), + nonce, + mConnectionPublicKey, + arrayPrivateKey + ); + + if (decrypted.size() != 32) { + Utils::warn("Decrypted HANDSHAKE_EXCHANGE_KEY from " + reqAddr + " has invalid size."); + disconnect(); + return; + } + + std::memcpy(mConnectionAESKey.data(), decrypted.data(), decrypted.size()); + + // Make a Session ID + randombytes_buf(&mConnectionSessionID, sizeof(mConnectionSessionID)); + + // Encrypt the Session ID with the established AES key (using symmetric encryption, nonce can be all zeros for this purpose) + Nonce symNonce{}; // All zeros + std::vector encryptedSessionID = Utils::LibSodiumWrapper::encryptMessage( + reinterpret_cast(&mConnectionSessionID), sizeof(mConnectionSessionID), + mConnectionAESKey, symNonce + ); + + mHandler->sendMessage(ServerMessageType::HANDSHAKE_EXCHANGE_KEY_CONFIRM, Utils::uint8ArrayToString(encryptedSessionID.data(), encryptedSessionID.size())); + + } catch (const std::exception& e) { + Utils::error("Failed to decrypt HANDSHAKE_EXCHANGE_KEY from " + reqAddr + ": " + e.what()); + disconnect(); + } + break; } case ClientMessageType::GRACEFUL_DISCONNECT: { @@ -97,5 +170,8 @@ namespace ColumnLynx::Net::TCP { std::shared_ptr mHandler; std::function)> mOnDisconnect; Utils::LibSodiumWrapper *mLibSodiumWrapper; + std::array mConnectionAESKey; + uint64_t mConnectionSessionID; + AsymPublicKey mConnectionPublicKey; }; } \ No newline at end of file diff --git a/include/columnlynx/server/net/tcp/tcp_server.hpp b/include/columnlynx/server/net/tcp/tcp_server.hpp index 1f6f6ae..ee6397e 100644 --- a/include/columnlynx/server/net/tcp/tcp_server.hpp +++ b/include/columnlynx/server/net/tcp/tcp_server.hpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include diff --git a/src/client/main.cpp b/src/client/main.cpp index e31beb8..9dba8e7 100644 --- a/src/client/main.cpp +++ b/src/client/main.cpp @@ -32,8 +32,10 @@ int main(int argc, char** argv) { PanicHandler::init(); try { + LibSodiumWrapper sodiumWrapper = LibSodiumWrapper(); + asio::io_context io; - auto client = std::make_shared(io, "127.0.0.1", std::to_string(serverPort())); + auto client = std::make_shared(io, "127.0.0.1", std::to_string(serverPort()), &sodiumWrapper); client->start(); diff --git a/src/common/libsodium_wrapper.cpp b/src/common/libsodium_wrapper.cpp index bd71617..3db4cc3 100644 --- a/src/common/libsodium_wrapper.cpp +++ b/src/common/libsodium_wrapper.cpp @@ -11,24 +11,29 @@ namespace ColumnLynx::Utils { throw std::runtime_error("Failed to initialize libsodium"); } - if (crypto_kx_keypair(mPublicKey, mPrivateKey) != 0) { + // Generate keypair + if (crypto_sign_keypair(mPublicKey.data(), mPrivateKey.data()) != 0) { throw std::runtime_error("Failed to generate key pair"); } + // Convert to Curve25519 keys for encryption + crypto_sign_ed25519_pk_to_curve25519(mXPublicKey.data(), mPublicKey.data()); + crypto_sign_ed25519_sk_to_curve25519(mXPrivateKey.data(), mPrivateKey.data()); + log("Libsodium initialized and keypair generated"); } uint8_t* LibSodiumWrapper::getPublicKey() { - return mPublicKey; + return mPublicKey.data(); } uint8_t* LibSodiumWrapper::getPrivateKey() { - return mPrivateKey; + return mPrivateKey.data(); } - uint8_t LibSodiumWrapper::generateRandomAESKey() { - uint8_t aesKey[32]; // 256-bit key - randombytes_buf(aesKey, sizeof(aesKey)); - return *aesKey; + std::array LibSodiumWrapper::generateRandom256Bit() { + std::array randbytes; // 256 bits + randombytes_buf(randbytes.data(), randbytes.size()); + return randbytes; } } \ No newline at end of file diff --git a/src/common/utils.cpp b/src/common/utils.cpp index 3fb614a..624a17d 100644 --- a/src/common/utils.cpp +++ b/src/common/utils.cpp @@ -43,4 +43,18 @@ namespace ColumnLynx::Utils { unsigned short serverPort() { return 48042; } + + std::string bytesToHexString(const uint8_t* bytes, size_t length) { + const char hexChars[] = "0123456789ABCDEF"; + std::string hexString; + hexString.reserve(length * 2); + + for (size_t i = 0; i < length; ++i) { + uint8_t byte = bytes[i]; + hexString.push_back(hexChars[(byte >> 4) & 0x0F]); + hexString.push_back(hexChars[byte & 0x0F]); + } + + return hexString; + } } \ No newline at end of file diff --git a/src/server/main.cpp b/src/server/main.cpp index db83036..2c938f6 100644 --- a/src/server/main.cpp +++ b/src/server/main.cpp @@ -21,9 +21,11 @@ int main(int argc, char** argv) { // Generate a temporary keypair, replace with actual CA signed keys later (Note, these are stored in memory) LibSodiumWrapper sodiumWrapper = LibSodiumWrapper(); + log("Server public key: " + bytesToHexString(sodiumWrapper.getPublicKey(), crypto_sign_PUBLICKEYBYTES)); + log("Server private key: " + bytesToHexString(sodiumWrapper.getPrivateKey(), crypto_sign_SECRETKEYBYTES)); // TEMP, remove later asio::io_context io; - auto server = std::make_shared(io, serverPort()); + auto server = std::make_shared(io, serverPort(), &sodiumWrapper); // Run the IO context in a separate thread std::thread ioThread([&io]() { diff --git a/src/server/server/net/tcp/tcp_server.cpp b/src/server/server/net/tcp/tcp_server.cpp index 5c2c815..73bb5d7 100644 --- a/src/server/server/net/tcp/tcp_server.cpp +++ b/src/server/server/net/tcp/tcp_server.cpp @@ -23,7 +23,8 @@ namespace ColumnLynx::Net::TCP { mAcceptor.async_accept( [this](asio::error_code ec, asio::ip::tcp::socket socket) { if (!NetHelper::isExpectedDisconnect(ec)) { - auto client = TCPConnection::create(std::move(socket), mSodiumWrapper, + auto client = TCPConnection::create(std::move(socket), + mSodiumWrapper, [this](std::shared_ptr c) { mClients.erase(c); Utils::log("Client removed.");