From 71c40250c2d764c02d9da07bf8dd2b3e4cb7653f Mon Sep 17 00:00:00 2001 From: yet another researcher Date: Thu, 24 Apr 2025 21:33:15 +0300 Subject: [PATCH] =?UTF-8?q?=D0=BE=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D1=81=D0=BF=D1=83=D1=81=D1=82=D1=8F=20?= =?UTF-8?q?=D0=B1=D0=BE=D0=B6=D0=B5=D1=81=D1=82=D0=B2=D0=B5=D0=BD=D0=BD?= =?UTF-8?q?=D1=8B=D0=B5=20=D1=82=D1=80=D0=B8=20=D0=BC=D0=B5=D1=81=D1=8F?= =?UTF-8?q?=D1=86=D0=B0=20=D0=B1=D0=BB=D1=8F=D1=82=D1=8C,=20=D1=8F=20?= =?UTF-8?q?=D1=81=D0=BA=D0=BE=D1=80=D0=BE=20=D1=81=D0=B4=D0=BE=D1=85=D0=BD?= =?UTF-8?q?=D1=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- commands.cpp | 409 ++++++++++++++++++++++++++++++++++-------------- out/history.txt | 5 + 2 files changed, 294 insertions(+), 120 deletions(-) create mode 100644 out/history.txt diff --git a/commands.cpp b/commands.cpp index d566486..7b7c8fd 100644 --- a/commands.cpp +++ b/commands.cpp @@ -6,57 +6,105 @@ #include #include #include +#include extern "C" { #include "monocypher.h" } +// Utility functions +namespace { + class HexError : public std::runtime_error { + public: + explicit HexError(const std::string& msg) : std::runtime_error(msg) {} + }; + + class FileError : public std::runtime_error { + public: + explicit FileError(const std::string& msg) : std::runtime_error(msg) {} + }; + + std::string toHex(const uint8_t* data, size_t len) { + std::ostringstream oss; + oss << std::hex; + for (size_t i = 0; i < len; i++) { + oss << std::setw(2) << std::setfill('0') << (int)data[i]; + } + return oss.str(); + } + + std::vector fromHex(const std::string& hex) { + if (hex.size() % 2 != 0) { + throw HexError("Hex string length must be even"); + } + + std::vector data; + data.reserve(hex.size()/2); + + for(size_t i = 0; i < hex.size(); i += 2) { + try { + uint8_t val = (uint8_t)std::stoi(hex.substr(i, 2), nullptr, 16); + data.push_back(val); + } catch (const std::invalid_argument&) { + throw HexError("Invalid hex character in string"); + } catch (const std::out_of_range&) { + throw HexError("Hex value out of range"); + } + } + return data; + } + + void readRandomBytes(uint8_t* buffer, size_t size) { + FILE* f = fopen("/dev/urandom", "rb"); + if (!f) { + throw FileError("Failed to open /dev/urandom"); + } + + size_t bytesRead = fread(buffer, 1, size, f); + fclose(f); + + if (bytesRead != size) { + throw FileError("Failed to read enough random bytes"); + } + } + + void hexStrToKey(const std::string &hex, uint8_t outKey[32]) { + if (hex.size() != 64) { + throw HexError("Key must be 64 hex characters (32 bytes)"); + } + + std::vector buf = fromHex(hex); + std::copy(buf.begin(), buf.end(), outKey); + } +} + static const size_t NONCE_SIZE = 24; static const size_t KEY_SIZE = 32; static const size_t MAC_SIZE = 16; -static std::string toHex(const uint8_t* data, size_t len) { - std::ostringstream oss; - oss << std::hex; - for (size_t i=0;i fromHex(const std::string& hex) { - std::vector data; - data.reserve(hex.size()/2); - for(size_t i=0;i buf=fromHex(hex); - if(buf.size()==32){ - for(int i=0;i<32;i++) outKey[i]=buf[i]; - } -} - static void handleNickCommand(const std::string &args, AppConfig &config) { std::istringstream iss(args); std::string sub; iss >> sub; - if(sub=="set"){ + if(sub == "set") { std::string name; - std::getline(iss,name); - if(!name.empty() && name[0]==' ') name.erase(0,1); - config.nickname=name; - std::cout<<"[nick] set: "<> plaintext; + if(plaintext.empty()) { + std::cout << CLR_RED << "[makeTea] Error: Text to encrypt cannot be empty" << CLR_RESET << "\n"; + return; + } + std::string keyHex; iss >> keyHex; uint8_t localKey[32]; - bool useLocal=false; - if(!keyHex.empty()){ - hexStrToKey(keyHex, localKey); - useLocal=true; + bool useLocal = false; + + if(!keyHex.empty()) { + try { + hexStrToKey(keyHex, localKey); + useLocal = true; + } catch (const HexError& e) { + std::cout << CLR_RED << "[makeTea] Error: " << e.what() << CLR_RESET << "\n"; + return; + } } - if(!config.haveSharedSecret && !useLocal){ - FILE* f=fopen("/dev/urandom","rb"); - if(!f)return; - fread(config.sharedSecret,1,KEY_SIZE,f); - fclose(f); - config.haveSharedSecret=true; - std::cout<<"[makeTea] No key found, random generated: "< nonce(NONCE_SIZE), mac(MAC_SIZE); std::vector ciphertext(plaintext.size()); - { - FILE* f=fopen("/dev/urandom","rb"); - fread(nonce.data(),1,NONCE_SIZE,f); - fclose(f); + + try { + readRandomBytes(nonce.data(), NONCE_SIZE); + } catch (const FileError& e) { + std::cout << CLR_RED << "[makeTea] Error: " << e.what() << CLR_RESET << "\n"; + return; } - const uint8_t* usedKey= useLocal? localKey : config.sharedSecret; + + const uint8_t* usedKey = useLocal ? localKey : config.sharedSecret; crypto_aead_lock( ciphertext.data(), mac.data(), usedKey, nonce.data(), - nullptr,0, + nullptr, 0, (const uint8_t*)plaintext.data(), plaintext.size() ); + std::vector out; - out.insert(out.end(),nonce.begin(),nonce.end()); - out.insert(out.end(),mac.begin(),mac.end()); - out.insert(out.end(),ciphertext.begin(),ciphertext.end()); - std::cout<<"[makeTea] keyUsed="<> hexIn; + if(hexIn.empty()) { + std::cout << CLR_RED << "[drinkTea] Error: Encrypted data cannot be empty" << CLR_RESET << "\n"; + return; + } + std::string keyHex; iss >> keyHex; uint8_t localKey[32]; - bool useLocal=false; - if(!keyHex.empty()){ - hexStrToKey(keyHex, localKey); - useLocal=true; + bool useLocal = false; + + if(!keyHex.empty()) { + try { + hexStrToKey(keyHex, localKey); + useLocal = true; + } catch (const HexError& e) { + std::cout << CLR_RED << "[drinkTea] Error: " << e.what() << CLR_RESET << "\n"; + return; + } } - if(!config.haveSharedSecret && !useLocal) return; - auto data=fromHex(hexIn); - if(data.size() nonce(data.begin(), data.begin()+NONCE_SIZE); - std::vector mac(data.begin()+NONCE_SIZE, data.begin()+NONCE_SIZE+MAC_SIZE); - std::vector cipher(data.begin()+NONCE_SIZE+MAC_SIZE, data.end()); + + if(!config.haveSharedSecret && !useLocal) { + std::cout << CLR_RED << "[drinkTea] Error: No key available for decryption" << CLR_RESET << "\n"; + return; + } + + std::vector data; + try { + data = fromHex(hexIn); + } catch (const HexError& e) { + std::cout << CLR_RED << "[drinkTea] Error: " << e.what() << CLR_RESET << "\n"; + return; + } + + if(data.size() < NONCE_SIZE + MAC_SIZE) { + std::cout << CLR_RED << "[drinkTea] Error: Input data too short" << CLR_RESET << "\n"; + return; + } + + std::vector nonce(data.begin(), data.begin() + NONCE_SIZE); + std::vector mac(data.begin() + NONCE_SIZE, data.begin() + NONCE_SIZE + MAC_SIZE); + std::vector cipher(data.begin() + NONCE_SIZE + MAC_SIZE, data.end()); std::vector plain(cipher.size()); - const uint8_t* usedKey= useLocal? localKey : config.sharedSecret; - int rc=crypto_aead_unlock( + + const uint8_t* usedKey = useLocal ? localKey : config.sharedSecret; + int rc = crypto_aead_unlock( plain.data(), mac.data(), usedKey, nonce.data(), - nullptr,0, + nullptr, 0, cipher.data(), cipher.size() ); - if(rc!=0){ - std::cerr<<"[drinkTea] MAC error\n"; + + if(rc != 0) { + std::cout << CLR_RED << "[drinkTea] Error: MAC verification failed" << CLR_RESET << "\n"; return; } - std::string s((char*)plain.data(),plain.size()); - std::cout<<"[drinkTea] keyUsed="< - Set your nickname\n" + << " nick generatekey - Generate a new encryption key\n" + << " web start - Start the web server\n" + << " web connect - Connect to a web server\n" + << " web stop - Stop the web server\n" + << " sound find - Start listening for sound signals\n" + << " sound lose - Stop listening for sound signals\n" + << " cerber maketea [hexKey] - Encrypt text\n" + << " cerber drinktea [hexKey] - Decrypt text\n" + << " help - Show this help message\n" + << " exit - Exit the program\n" + << CLR_RESET; + } else { + std::cout << CLR_RED << "[cli] Error: Unknown command. Type 'help' for available commands." << CLR_RESET << "\n"; + } + } catch (const std::exception& e) { + std::cout << CLR_RED << "[cli] Error: " << e.what() << CLR_RESET << "\n"; + } catch (...) { + std::cout << CLR_RED << "[cli] Error: Unknown error occurred" << CLR_RESET << "\n"; } - if(input.rfind("sound ",0)==0){ - handleSoundCommand(input.substr(6), config); - return; - } - if(input.rfind("cerber maketea ",0)==0){ - handleMakeTea(input.substr(15), config); - return; - } - if(input.rfind("cerber drinktea ",0)==0){ - handleDrinkTea(input.substr(16), config); - return; - } - if(input=="exit"){ - std::cout<<"[cli] exit\n"; - exit(0); - } - std::cout<<"[cli] Unknown: "<>cmd; - if(cmd=="start"){ + std::string cmd; + iss >> cmd; + + if(cmd == "start") { + if(config.webServerRunning) { + std::cout << CLR_YELLOW << "[web] Warning: Web server is already running" << CLR_RESET << "\n"; + return; + } extern void webServerStart(AppConfig&); - webServerStart(config); - } else if(cmd=="connect"){ - std::string t,ip; iss>>t>>ip; - extern void webServerConnect(AppConfig&,const std::string&,const std::string&); - webServerConnect(config,t,ip); - } else if(cmd=="stop"){ + try { + webServerStart(config); + config.webServerRunning = true; + std::cout << CLR_GREEN << "[web] Server started successfully" << CLR_RESET << "\n"; + } catch (const std::exception& e) { + std::cout << CLR_RED << "[web] Error: " << e.what() << CLR_RESET << "\n"; + } + } else if(cmd == "connect") { + std::string type, ip; + iss >> type >> ip; + + if(type.empty() || ip.empty()) { + std::cout << CLR_RED << "[web] Error: Type and IP address are required" << CLR_RESET << "\n"; + return; + } + + extern void webServerConnect(AppConfig&, const std::string&, const std::string&); + try { + webServerConnect(config, type, ip); + std::cout << CLR_GREEN << "[web] Connected to " << ip << " as " << type << CLR_RESET << "\n"; + } catch (const std::exception& e) { + std::cout << CLR_RED << "[web] Error: " << e.what() << CLR_RESET << "\n"; + } + } else if(cmd == "stop") { + if(!config.webServerRunning) { + std::cout << CLR_YELLOW << "[web] Warning: Web server is not running" << CLR_RESET << "\n"; + return; + } extern void webServerStop(AppConfig&); - webServerStop(config); + try { + webServerStop(config); + config.webServerRunning = false; + std::cout << CLR_GREEN << "[web] Server stopped successfully" << CLR_RESET << "\n"; + } catch (const std::exception& e) { + std::cout << CLR_RED << "[web] Error: " << e.what() << CLR_RESET << "\n"; + } + } else { + std::cout << CLR_RED << "[web] Error: Unknown command '" << cmd << "'" << CLR_RESET << "\n"; } } -static void handleSoundCommand(const std::string &args, AppConfig &config){ +static void handleSoundCommand(const std::string &args, AppConfig &config) { std::istringstream iss(args); - std::string cmd; iss>>cmd; - if(cmd=="find"){ + std::string cmd; + iss >> cmd; + + if(cmd == "find") { + if(config.soundExchangeActive) { + std::cout << CLR_YELLOW << "[sound] Warning: Sound exchange is already active" << CLR_RESET << "\n"; + return; + } extern void soundFind(AppConfig&); - soundFind(config); - } else if(cmd=="lose"){ + try { + soundFind(config); + config.soundExchangeActive = true; + std::cout << CLR_GREEN << "[sound] Started listening for sound signals" << CLR_RESET << "\n"; + } catch (const std::exception& e) { + std::cout << CLR_RED << "[sound] Error: " << e.what() << CLR_RESET << "\n"; + } + } else if(cmd == "lose") { + if(!config.soundExchangeActive) { + std::cout << CLR_YELLOW << "[sound] Warning: Sound exchange is not active" << CLR_RESET << "\n"; + return; + } extern void soundLose(AppConfig&); - soundLose(config); + try { + soundLose(config); + config.soundExchangeActive = false; + std::cout << CLR_GREEN << "[sound] Stopped listening for sound signals" << CLR_RESET << "\n"; + } catch (const std::exception& e) { + std::cout << CLR_RED << "[sound] Error: " << e.what() << CLR_RESET << "\n"; + } + } else { + std::cout << CLR_RED << "[sound] Error: Unknown command '" << cmd << "'" << CLR_RESET << "\n"; } } diff --git a/out/history.txt b/out/history.txt new file mode 100644 index 0000000..5d0330f --- /dev/null +++ b/out/history.txt @@ -0,0 +1,5 @@ +nick set nick +nick generatekey +cerber maketea ea616c501f231fb32428ada33d9c4fb96b696459ee6902902c6ed271242da +cerber maketea platonpidor +exit