Compare commits

..

No commits in common. "55c63312b117bfb4a7e2d95334f9ff11af6bfe1a" and "101d929b116a09c45918e88ebc9fbbd3b0afdf25" have entirely different histories.

3 changed files with 120 additions and 295 deletions

1
.gitignore vendored
View File

@ -1,3 +1,2 @@
*.o *.o
cerberus cerberus
out/

View File

@ -6,24 +6,15 @@
#include <sstream> #include <sstream>
#include <iomanip> #include <iomanip>
#include <cstdio> #include <cstdio>
#include <stdexcept>
extern "C" { extern "C" {
#include "monocypher.h" #include "monocypher.h"
} }
// Utility functions static const size_t NONCE_SIZE = 24;
namespace { static const size_t KEY_SIZE = 32;
class HexError : public std::runtime_error { static const size_t MAC_SIZE = 16;
public:
explicit HexError(const std::string& msg) : std::runtime_error(msg) {}
};
class FileError : public std::runtime_error { static std::string toHex(const uint8_t* data, size_t len) {
public:
explicit FileError(const std::string& msg) : std::runtime_error(msg) {}
};
std::string toHex(const uint8_t* data, size_t len) {
std::ostringstream oss; std::ostringstream oss;
oss << std::hex; oss << std::hex;
for (size_t i=0;i<len;i++){ for (size_t i=0;i<len;i++){
@ -32,55 +23,23 @@ namespace {
return oss.str(); return oss.str();
} }
std::vector<uint8_t> fromHex(const std::string& hex) { static std::vector<uint8_t> fromHex(const std::string& hex) {
if (hex.size() % 2 != 0) {
throw HexError("Hex string length must be even");
}
std::vector<uint8_t> data; std::vector<uint8_t> data;
data.reserve(hex.size()/2); data.reserve(hex.size()/2);
for(size_t i=0;i<hex.size();i+=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); uint8_t val=(uint8_t)std::stoi(hex.substr(i,2),nullptr,16);
data.push_back(val); 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; return data;
} }
void readRandomBytes(uint8_t* buffer, size_t size) { static void hexStrToKey(const std::string &hex, uint8_t outKey[32]) {
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<uint8_t> buf=fromHex(hex); std::vector<uint8_t> buf=fromHex(hex);
std::copy(buf.begin(), buf.end(), outKey); if(buf.size()==32){
for(int i=0;i<32;i++) outKey[i]=buf[i];
} }
} }
static const size_t NONCE_SIZE = 24;
static const size_t KEY_SIZE = 32;
static const size_t MAC_SIZE = 16;
static void handleNickCommand(const std::string &args, AppConfig &config) { static void handleNickCommand(const std::string &args, AppConfig &config) {
std::istringstream iss(args); std::istringstream iss(args);
std::string sub; std::string sub;
@ -89,22 +48,15 @@ static void handleNickCommand(const std::string &args, AppConfig &config) {
std::string name; std::string name;
std::getline(iss,name); std::getline(iss,name);
if(!name.empty() && name[0]==' ') name.erase(0,1); if(!name.empty() && name[0]==' ') name.erase(0,1);
if(name.empty()) {
std::cout << CLR_RED << "[nick] Error: Nickname cannot be empty" << CLR_RESET << "\n";
return;
}
config.nickname=name; config.nickname=name;
std::cout << CLR_GREEN << "[nick] Nickname set to: " << config.nickname << CLR_RESET << "\n"; std::cout<<"[nick] set: "<<config.nickname<<"\n";
} else if(sub=="generatekey"){ } else if(sub=="generatekey"){
try { FILE* f=fopen("/dev/urandom","rb");
readRandomBytes(config.sharedSecret, KEY_SIZE); if(!f)return;
fread(config.sharedSecret,1,KEY_SIZE,f);
fclose(f);
config.haveSharedSecret=true; config.haveSharedSecret=true;
std::cout << CLR_GREEN << "[nick] 256-bit key generated: " << toHex(config.sharedSecret, 32) << CLR_RESET << "\n"; std::cout<<"[nick] 256-bit key generated: "<<toHex(config.sharedSecret,32)<<"\n";
} catch (const FileError& e) {
std::cout << CLR_RED << "[nick] Error: " << e.what() << CLR_RESET << "\n";
}
} else {
std::cout << CLR_RED << "[nick] Error: Unknown subcommand '" << sub << "'" << CLR_RESET << "\n";
} }
} }
@ -115,47 +67,29 @@ static void handleMakeTea(const std::string& input, AppConfig& config) {
std::istringstream iss(input); std::istringstream iss(input);
std::string plaintext; std::string plaintext;
iss >> plaintext; iss >> plaintext;
if(plaintext.empty()) {
std::cout << CLR_RED << "[makeTea] Error: Text to encrypt cannot be empty" << CLR_RESET << "\n";
return;
}
std::string keyHex; std::string keyHex;
iss >> keyHex; iss >> keyHex;
uint8_t localKey[32]; uint8_t localKey[32];
bool useLocal=false; bool useLocal=false;
if(!keyHex.empty()){ if(!keyHex.empty()){
try {
hexStrToKey(keyHex, localKey); hexStrToKey(keyHex, localKey);
useLocal=true; useLocal=true;
} catch (const HexError& e) {
std::cout << CLR_RED << "[makeTea] Error: " << e.what() << CLR_RESET << "\n";
return;
} }
}
if(!config.haveSharedSecret && !useLocal){ if(!config.haveSharedSecret && !useLocal){
try { FILE* f=fopen("/dev/urandom","rb");
readRandomBytes(config.sharedSecret, KEY_SIZE); if(!f)return;
fread(config.sharedSecret,1,KEY_SIZE,f);
fclose(f);
config.haveSharedSecret=true; config.haveSharedSecret=true;
std::cout << CLR_YELLOW << "[makeTea] No key found, random generated: " << toHex(config.sharedSecret, 32) << CLR_RESET << "\n"; std::cout<<"[makeTea] No key found, random generated: "<<toHex(config.sharedSecret,32)<<"\n";
} catch (const FileError& e) {
std::cout << CLR_RED << "[makeTea] Error: " << e.what() << CLR_RESET << "\n";
return;
} }
}
std::vector<uint8_t> nonce(NONCE_SIZE), mac(MAC_SIZE); std::vector<uint8_t> nonce(NONCE_SIZE), mac(MAC_SIZE);
std::vector<uint8_t> ciphertext(plaintext.size()); std::vector<uint8_t> ciphertext(plaintext.size());
{
try { FILE* f=fopen("/dev/urandom","rb");
readRandomBytes(nonce.data(), NONCE_SIZE); fread(nonce.data(),1,NONCE_SIZE,f);
} catch (const FileError& e) { fclose(f);
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( crypto_aead_lock(
ciphertext.data(), ciphertext.data(),
@ -166,63 +100,37 @@ static void handleMakeTea(const std::string& input, AppConfig& config) {
(const uint8_t*)plaintext.data(), (const uint8_t*)plaintext.data(),
plaintext.size() plaintext.size()
); );
std::vector<uint8_t> out; std::vector<uint8_t> out;
out.insert(out.end(),nonce.begin(),nonce.end()); out.insert(out.end(),nonce.begin(),nonce.end());
out.insert(out.end(),mac.begin(),mac.end()); out.insert(out.end(),mac.begin(),mac.end());
out.insert(out.end(),ciphertext.begin(),ciphertext.end()); out.insert(out.end(),ciphertext.begin(),ciphertext.end());
std::cout<<"[makeTea] keyUsed="<<toHex(usedKey,32)<<"\n";
std::cout << CLR_GREEN << "[makeTea] keyUsed=" << toHex(usedKey, 32) << CLR_RESET << "\n"; std::ostringstream oss;
std::cout << CLR_GREEN << "[makeTea] encrypted: " << toHex(out.data(), out.size()) << CLR_RESET << "\n"; for(auto &x: out){
oss<<std::hex<<std::setw(2)<<std::setfill('0')<<(int)x;
}
std::cout<<"[makeTea] encrypted: "<<oss.str()<<"\n";
} }
static void handleDrinkTea(const std::string& input, AppConfig& config) { static void handleDrinkTea(const std::string& input, AppConfig& config) {
std::istringstream iss(input); std::istringstream iss(input);
std::string hexIn; std::string hexIn;
iss >> hexIn; iss >> hexIn;
if(hexIn.empty()) {
std::cout << CLR_RED << "[drinkTea] Error: Encrypted data cannot be empty" << CLR_RESET << "\n";
return;
}
std::string keyHex; std::string keyHex;
iss >> keyHex; iss >> keyHex;
uint8_t localKey[32]; uint8_t localKey[32];
bool useLocal=false; bool useLocal=false;
if(!keyHex.empty()){ if(!keyHex.empty()){
try {
hexStrToKey(keyHex, localKey); hexStrToKey(keyHex, localKey);
useLocal=true; 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(!config.haveSharedSecret && !useLocal) { if(data.size()<NONCE_SIZE+MAC_SIZE) return;
std::cout << CLR_RED << "[drinkTea] Error: No key available for decryption" << CLR_RESET << "\n";
return;
}
std::vector<uint8_t> 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<uint8_t> nonce(data.begin(), data.begin()+NONCE_SIZE); std::vector<uint8_t> nonce(data.begin(), data.begin()+NONCE_SIZE);
std::vector<uint8_t> mac(data.begin()+NONCE_SIZE, data.begin()+NONCE_SIZE+MAC_SIZE); std::vector<uint8_t> mac(data.begin()+NONCE_SIZE, data.begin()+NONCE_SIZE+MAC_SIZE);
std::vector<uint8_t> cipher(data.begin()+NONCE_SIZE+MAC_SIZE, data.end()); std::vector<uint8_t> cipher(data.begin()+NONCE_SIZE+MAC_SIZE, data.end());
std::vector<uint8_t> plain(cipher.size()); std::vector<uint8_t> plain(cipher.size());
const uint8_t* usedKey= useLocal? localKey : config.sharedSecret; const uint8_t* usedKey= useLocal? localKey : config.sharedSecret;
int rc=crypto_aead_unlock( int rc=crypto_aead_unlock(
plain.data(), plain.data(),
@ -233,144 +141,67 @@ static void handleDrinkTea(const std::string& input, AppConfig& config) {
cipher.data(), cipher.data(),
cipher.size() cipher.size()
); );
if(rc!=0){ if(rc!=0){
std::cout << CLR_RED << "[drinkTea] Error: MAC verification failed" << CLR_RESET << "\n"; std::cerr<<"[drinkTea] MAC error\n";
return; return;
} }
std::string s((char*)plain.data(),plain.size()); std::string s((char*)plain.data(),plain.size());
std::cout << CLR_GREEN << "[drinkTea] keyUsed=" << toHex(usedKey, 32) << CLR_RESET << "\n"; std::cout<<"[drinkTea] keyUsed="<<toHex(usedKey,32)<<"\n";
std::cout << CLR_GREEN << "[drinkTea] decrypted: " << s << CLR_RESET << "\n"; std::cout<<"[drinkTea] decrypted: "<<s<<"\n";
} }
void processCommand(const std::string& input, AppConfig& config) { void processCommand(const std::string& input, AppConfig& config) {
if(input.empty()) {
return;
}
try {
if(input.rfind("nick ",0)==0){ if(input.rfind("nick ",0)==0){
handleNickCommand(input.substr(5), config); handleNickCommand(input.substr(5), config);
} else if(input.rfind("web ", 0) == 0) { return;
}
if(input.rfind("web ",0)==0){
handleWebCommand(input.substr(4), config); handleWebCommand(input.substr(4), config);
} else if(input.rfind("sound ", 0) == 0) { return;
}
if(input.rfind("sound ",0)==0){
handleSoundCommand(input.substr(6), config); handleSoundCommand(input.substr(6), config);
} else if(input.rfind("cerber maketea ", 0) == 0) { return;
}
if(input.rfind("cerber maketea ",0)==0){
handleMakeTea(input.substr(15), config); handleMakeTea(input.substr(15), config);
} else if(input.rfind("cerber drinktea ", 0) == 0) { return;
}
if(input.rfind("cerber drinktea ",0)==0){
handleDrinkTea(input.substr(16), config); handleDrinkTea(input.substr(16), config);
} else if(input == "exit") { return;
std::cout << CLR_CYAN << "[cli] Exiting..." << CLR_RESET << "\n"; }
if(input=="exit"){
std::cout<<"[cli] exit\n";
exit(0); exit(0);
} else if(input == "help") {
std::cout << CLR_CYAN << "Available commands:\n"
<< " nick set <usernick> - Set your nickname\n"
<< " nick generatekey - Generate a new encryption key\n"
<< " web start - Start the web server\n"
<< " web connect <type> <ip> - 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 <text> [hexKey] - Encrypt text\n"
<< " cerber drinktea <hex> [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";
} }
std::cout<<"[cli] Unknown: "<<input<<"\n";
} }
static void handleWebCommand(const std::string &args, AppConfig &config){ static void handleWebCommand(const std::string &args, AppConfig &config){
std::istringstream iss(args); std::istringstream iss(args);
std::string cmd; std::string cmd; iss>>cmd;
iss >> cmd;
if(cmd=="start"){ 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&); extern void webServerStart(AppConfig&);
try {
webServerStart(config); 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"){ } else if(cmd=="connect"){
std::string type, ip; std::string t,ip; iss>>t>>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&); extern void webServerConnect(AppConfig&,const std::string&,const std::string&);
try { webServerConnect(config,t,ip);
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"){ } 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&); extern void webServerStop(AppConfig&);
try {
webServerStop(config); 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::istringstream iss(args);
std::string cmd; std::string cmd; iss>>cmd;
iss >> cmd;
if(cmd=="find"){ 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&); extern void soundFind(AppConfig&);
try {
soundFind(config); 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"){ } 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&); extern void soundLose(AppConfig&);
try {
soundLose(config); 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";
} }
} }

View File

@ -1,5 +0,0 @@
nick set nick
nick generatekey
cerber maketea ea616c501f231fb32428ada33d9c4fb96b696459ee6902902c6ed271242da
cerber maketea platonpidor
exit