From e3df3cd0a7949325e8d0c1057fabd37c21be515a Mon Sep 17 00:00:00 2001 From: DcruBro Date: Tue, 25 Nov 2025 21:45:10 +0100 Subject: [PATCH] Switched to C++23 as the project standard. Added a basic parser for client_config and server_config, and added some basic authorization. Need to work on verification of the server. --- CMakeLists.txt | 2 +- .../columnlynx/client/net/tcp/tcp_client.hpp | 21 ++++++++++++- .../columnlynx/common/libsodium_wrapper.hpp | 10 +++++++ include/columnlynx/common/utils.hpp | 4 +++ .../columnlynx/server/net/tcp/tcp_server.hpp | 4 +++ src/client/main.cpp | 5 ++++ src/client/net/tcp/tcp_client.cpp | 6 +++- src/common/utils.cpp | 30 +++++++++++++++++++ src/server/main.cpp | 3 ++ src/server/server/net/tcp/tcp_connection.cpp | 13 ++++++-- 10 files changed, 93 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7d72dab..69570c9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,7 +13,7 @@ project(ColumnLynx # --------------------------------------------------------- # General C++ setup # --------------------------------------------------------- -set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) #set(CMAKE_CXX_FLAGS_DEBUG "-g") diff --git a/include/columnlynx/client/net/tcp/tcp_client.hpp b/include/columnlynx/client/net/tcp/tcp_client.hpp index 820f534..e6c2273 100644 --- a/include/columnlynx/client/net/tcp/tcp_client.hpp +++ b/include/columnlynx/client/net/tcp/tcp_client.hpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -42,7 +43,24 @@ namespace ColumnLynx::Net::TCP { mLastHeartbeatReceived(std::chrono::steady_clock::now()), mLastHeartbeatSent(std::chrono::steady_clock::now()), mTun(tun) - {} + { + // Preload the config map + mRawClientConfig = Utils::getConfigMap("client_config"); + + if (!mRawClientConfig.empty()) { + Utils::debug("Loading the keys"); + + PrivateKey sk; + PublicKey pk; + std::copy_n(Utils::hexStringToBytes(mRawClientConfig.find("CLIENT_PRIVATE_KEY")->second).begin(), sk.size(), sk.begin()); // This is extremely stupid, but the C++ compiler has forced my hand (I would've just used to_array, but fucking asio decls) + std::copy_n(Utils::hexStringToBytes(mRawClientConfig.find("CLIENT_PUBLIC_KEY")->second).begin(), pk.size(), pk.begin()); + + mLibSodiumWrapper->setKeys(pk, sk); + + Utils::debug("Newly-Loaded Public Key: " + Utils::bytesToHexString(mLibSodiumWrapper->getPublicKey(), 32)); + Utils::debug("Newly-Loaded Private Key: " + Utils::bytesToHexString(mLibSodiumWrapper->getPrivateKey(), 64)); + } + } void start(); void sendMessage(ClientMessageType type, const std::string& data = ""); @@ -76,5 +94,6 @@ namespace ColumnLynx::Net::TCP { bool mIsHostDomain; Protocol::TunConfig mTunConfig; std::shared_ptr mTun = nullptr; + std::unordered_map mRawClientConfig; }; } \ No newline at end of file diff --git a/include/columnlynx/common/libsodium_wrapper.hpp b/include/columnlynx/common/libsodium_wrapper.hpp index 435f617..4759cc5 100644 --- a/include/columnlynx/common/libsodium_wrapper.hpp +++ b/include/columnlynx/common/libsodium_wrapper.hpp @@ -40,6 +40,16 @@ namespace ColumnLynx::Utils { uint8_t* getXPublicKey() { return mXPublicKey.data(); } uint8_t* getXPrivateKey() { return mXPrivateKey.data(); } + // Dangerous! + void setKeys(PublicKey pk, PrivateKey sk) { + mPublicKey = pk; + mPrivateKey = sk; + + // 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()); + } + // Helper section // Generates a random 256-bit (32-byte) array diff --git a/include/columnlynx/common/utils.hpp b/include/columnlynx/common/utils.hpp index d910b24..bc690ac 100644 --- a/include/columnlynx/common/utils.hpp +++ b/include/columnlynx/common/utils.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #ifdef _WIN32 #include @@ -64,4 +65,7 @@ namespace ColumnLynx::Utils { inline constexpr uint64_t cbe64toh(uint64_t x) { return cbswap64(x); // big-endian -> host (for little-endian hosts) } + + // Returns the config file in an unordered_map format. This purely reads the config file, you still need to parse it manually. + std::unordered_map getConfigMap(std::string path); }; \ 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 50a890f..d1f059c 100644 --- a/include/columnlynx/server/net/tcp/tcp_server.hpp +++ b/include/columnlynx/server/net/tcp/tcp_server.hpp @@ -31,6 +31,9 @@ namespace ColumnLynx::Net::TCP { mSodiumWrapper(sodiumWrapper), mHostRunning(hostRunning) { + // Preload the config map + mRawServerConfig = Utils::getConfigMap("server_config"); + asio::error_code ec; if (!ipv4Only) { @@ -68,6 +71,7 @@ namespace ColumnLynx::Net::TCP { std::unordered_set mClients; Utils::LibSodiumWrapper *mSodiumWrapper; bool* mHostRunning; + std::unordered_map mRawServerConfig; }; } \ No newline at end of file diff --git a/src/client/main.cpp b/src/client/main.cpp index fb4a6d3..f6da9cf 100644 --- a/src/client/main.cpp +++ b/src/client/main.cpp @@ -49,6 +49,9 @@ int main(int argc, char** argv) { auto result = options.parse(argc, argv); if (result.count("help")) { std::cout << options.help() << std::endl; + std::cout << "This software is licensed under the GPLv2-only license OR the GPLv3 license.\n"; + std::cout << "Copyright (C) 2025, The ColumnLynx Contributors.\n"; + std::cout << "This software is provided under ABSOLUTELY NO WARRANTY, to the extent permitted by law.\n"; return 0; } @@ -67,6 +70,8 @@ int main(int argc, char** argv) { log("Using virtual interface: " + tun->getName()); LibSodiumWrapper sodiumWrapper = LibSodiumWrapper(); + debug("Public Key: " + Utils::bytesToHexString(sodiumWrapper.getPublicKey(), 32)); + debug("Private Key: " + Utils::bytesToHexString(sodiumWrapper.getPrivateKey(), 64)); std::array aesKey = {0}; // Defualt zeroed state until modified by handshake uint64_t sessionID = 0; diff --git a/src/client/net/tcp/tcp_client.cpp b/src/client/net/tcp/tcp_client.cpp index 4e232b2..55c1457 100644 --- a/src/client/net/tcp/tcp_client.cpp +++ b/src/client/net/tcp/tcp_client.cpp @@ -33,9 +33,13 @@ namespace ColumnLynx::Net::TCP { std::vector payload; payload.reserve(1 + crypto_box_PUBLICKEYBYTES); payload.push_back(Utils::protocolVersion()); - payload.insert(payload.end(), + /*payload.insert(payload.end(), mLibSodiumWrapper->getXPublicKey(), mLibSodiumWrapper->getXPublicKey() + crypto_box_PUBLICKEYBYTES + );*/ + payload.insert(payload.end(), + mLibSodiumWrapper->getPublicKey(), + mLibSodiumWrapper->getPublicKey() + crypto_sign_PUBLICKEYBYTES ); mHandler->sendMessage(ClientMessageType::HANDSHAKE_INIT, Utils::uint8ArrayToString(payload.data(), payload.size())); diff --git a/src/common/utils.cpp b/src/common/utils.cpp index 5d35a90..4ee4462 100644 --- a/src/common/utils.cpp +++ b/src/common/utils.cpp @@ -113,4 +113,34 @@ namespace ColumnLynx::Utils { return out; } + + std::unordered_map getConfigMap(std::string path) { + // TODO: Currently re-reads every time. + std::vector readLines; + + std::ifstream file(path); + std::string line; + + while (std::getline(file, line)) { + readLines.push_back(line); + } + + // Parse them into the struct + std::unordered_map config; + char delimiter = '='; + + for (std::string str : readLines) { + std::stringstream ss(str); + + std::string key; + std::string val; + + std::getline(ss, key, delimiter); + std::getline(ss, val, delimiter); + + config.insert({ key, val }); + } + + return config; + } } \ No newline at end of file diff --git a/src/server/main.cpp b/src/server/main.cpp index f09fc9d..4cda3de 100644 --- a/src/server/main.cpp +++ b/src/server/main.cpp @@ -50,6 +50,9 @@ int main(int argc, char** argv) { auto result = options.parse(argc, argv); if (result.count("help")) { std::cout << options.help() << std::endl; + std::cout << "This software is licensed under the GPLv2-only license OR the GPLv3 license.\n"; + std::cout << "Copyright (C) 2025, The ColumnLynx Contributors.\n"; + std::cout << "This software is provided under ABSOLUTELY NO WARRANTY, to the extent permitted by law.\n"; return 0; } diff --git a/src/server/server/net/tcp/tcp_connection.cpp b/src/server/server/net/tcp/tcp_connection.cpp index 3722cce..a72c6fd 100644 --- a/src/server/server/net/tcp/tcp_connection.cpp +++ b/src/server/server/net/tcp/tcp_connection.cpp @@ -114,15 +114,24 @@ namespace ColumnLynx::Net::TCP { Utils::log("Client protocol version " + std::to_string(clientProtoVer) + " accepted from " + reqAddr + "."); - std::memcpy(mConnectionPublicKey.data(), data.data() + 1, std::min(data.size() - 1, sizeof(mConnectionPublicKey))); // Store the client's public key (for identification) + PublicKey signPk; + std::memcpy(signPk.data(), data.data() + 1, std::min(data.size() - 1, sizeof(signPk))); // Store the client's public key (for identification) + + crypto_sign_ed25519_pk_to_curve25519(mConnectionPublicKey.data(), signPk.data()); + + Utils::debug("Key attempted connect: " + Utils::bytesToHexString(signPk.data(), signPk.size())); std::vector whitelistedKeys = Utils::getWhitelistedKeys(); - if (std::find(whitelistedKeys.begin(), whitelistedKeys.end(), Utils::bytesToHexString(mConnectionPublicKey.data(), mConnectionPublicKey.size())) == whitelistedKeys.end()) { + if (std::find(whitelistedKeys.begin(), whitelistedKeys.end(), Utils::bytesToHexString(signPk.data(), signPk.size())) == whitelistedKeys.end()) { Utils::warn("Non-whitelisted client attempted to connect, terminating. Client IP: " + reqAddr); disconnect(); + + return; } + Utils::debug("Client " + reqAddr + " passed authorized_keys"); + mHandler->sendMessage(ServerMessageType::HANDSHAKE_IDENTIFY, Utils::uint8ArrayToString(mLibSodiumWrapper->getPublicKey(), crypto_sign_PUBLICKEYBYTES)); // This public key should always exist break; }