This commit is contained in:
wheelchairy 2025-01-28 22:33:54 +03:00
parent 7ed2435811
commit 118875234a
24 changed files with 461 additions and 478 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*.o
cerberus

View File

@ -1,19 +1,18 @@
CC = clang CC = clang
CXX = clang++ CXX = clang++
CFLAGS = -O2 -Wall -I./libs -pthread
CFLAGS = -O2 -Wall -I./libs CXXFLAGS = -std=c++17 -O2 -Wall -I./libs -pthread
CXXFLAGS = -std=c++17 -O2 -Wall -I./libs
UNAME_S := $(shell uname -s) UNAME_S := $(shell uname -s)
BREW_PREFIX := $(shell brew --prefix portaudio 2>/dev/null) BREW_PREFIX := $(shell brew --prefix portaudio 2>/dev/null)
ifneq ($(UNAME_S),)
ifeq ($(UNAME_S),Darwin) ifeq ($(UNAME_S),Darwin)
ifneq ($(BREW_PREFIX),) ifneq ($(BREW_PREFIX),)
CFLAGS += -I$(BREW_PREFIX)/include CFLAGS += -I$(BREW_PREFIX)/include
CXXFLAGS += -I$(BREW_PREFIX)/include CXXFLAGS += -I$(BREW_PREFIX)/include
LIBS += -L$(BREW_PREFIX)/lib -lportaudio LIBS += -L$(BREW_PREFIX)/lib -lportaudio
else else
$(warning [Makefile] PortAudio не найдена через Homebrew. Установите её: brew install portaudio) $(warning PortAudio not found via brew)
endif endif
else ifeq ($(UNAME_S),Linux) else ifeq ($(UNAME_S),Linux)
HAVE_PKG := $(shell pkg-config --exists portaudio-2.0 && echo yes || echo no) HAVE_PKG := $(shell pkg-config --exists portaudio-2.0 && echo yes || echo no)
@ -24,19 +23,16 @@ else ifeq ($(UNAME_S),Linux)
CXXFLAGS += $(PORTAUDIO_CFLAGS) CXXFLAGS += $(PORTAUDIO_CFLAGS)
LIBS += $(PORTAUDIO_LIBS) LIBS += $(PORTAUDIO_LIBS)
else else
$(warning [Makefile] portaudio-2.0 не найдена через pkg-config. Использую -lportaudio) $(warning portaudio-2.0 not found, using -lportaudio)
LIBS += -lportaudio LIBS += -lportaudio
endif endif
else endif
$(warning [Makefile] Unsupported OS: $(UNAME_S))
endif endif
SRCS_CPP = main.cpp commands.cpp cli.cpp webserver.cpp sound.cpp bfsk.cpp x25519_handshake.cpp SRCS_CPP = main.cpp commands.cpp cli.cpp webserver.cpp sound.cpp bfsk.cpp x25519_handshake.cpp
SRCS_C = libs/monocypher.c libs/linenoise.c SRCS_C = libs/monocypher.c libs/linenoise.c
OBJS_CPP = $(SRCS_CPP:.cpp=.o) OBJS_CPP = $(SRCS_CPP:.cpp=.o)
OBJS_C = $(SRCS_C:.c=.o) OBJS_C = $(SRCS_C:.c=.o)
TARGET = cerberus TARGET = cerberus
.PHONY: all clean .PHONY: all clean

View File

@ -1,78 +1,53 @@
#include "bfsk.hpp" #include "bfsk.hpp"
#include <cmath> #include <cmath>
#include <cstdlib> #include <cstdlib>
#include <iostream>
std::vector<float> bfskModulate(const std::vector<unsigned char> &data) { std::vector<float> bfskModulate(const std::vector<uint8_t> &data){
int samplesPerBit = (int)((double)SAMPLE_RATE / BFSK_BAUD); int spb=(int)(SAMPLE_RATE/BFSK_BAUD);
size_t totalBits = data.size() * 8; size_t tb=data.size()*8;
size_t totalSamples = totalBits * samplesPerBit; size_t ts=tb*spb;
std::vector<float> out(totalSamples * 2, 0.0f); std::vector<float> out(ts*2,0.0f);
double phase0=0.0,phase1=0.0;
double phase0 = 0.0;
double phase1 = 0.0;
double inc0=2.0*M_PI*BFSK_FREQ0/(double)SAMPLE_RATE; double inc0=2.0*M_PI*BFSK_FREQ0/(double)SAMPLE_RATE;
double inc1=2.0*M_PI*BFSK_FREQ1/(double)SAMPLE_RATE; double inc1=2.0*M_PI*BFSK_FREQ1/(double)SAMPLE_RATE;
size_t idx=0;
size_t sampleIndex = 0;
for(auto byteVal:data){ for(auto byteVal:data){
for(int b=0;b<8;b++){ for(int b=0;b<8;b++){
int bit=(byteVal>>b)&1; int bit=(byteVal>>b)&1;
for (int s = 0; s < samplesPerBit; s++) { for(int s=0;s<spb;s++){
float val; float val=bit? sinf((float)phase1): sinf((float)phase0);
if (bit == 0) { if(bit) phase1+=inc1; else phase0+=inc0;
val = sinf((float)phase0); out[idx*2+0]=val*0.3f;
phase0 += inc0; out[idx*2+1]=val*0.3f;
} else { idx++;
val = sinf((float)phase1);
phase1 += inc1;
}
out[sampleIndex*2 + 0] = val * 0.3f;
out[sampleIndex*2 + 1] = val * 0.3f;
sampleIndex++;
} }
} }
} }
return out; return out;
} }
std::vector<unsigned char> bfskDemodulate(const std::vector<float> &monoData) { std::vector<uint8_t> bfskDemodulate(const std::vector<float> &monoData){
int samplesPerBit = (int)((double)SAMPLE_RATE / BFSK_BAUD); int spb=(int)(SAMPLE_RATE/BFSK_BAUD);
size_t totalBits = monoData.size() / samplesPerBit; size_t tb=monoData.size()/spb;
size_t totalBytes = totalBits / 8; size_t tbytes=tb/8;
std::vector<uint8_t> res(tbytes,0);
double inc0=2.0*M_PI*BFSK_FREQ0/(double)SAMPLE_RATE; double inc0=2.0*M_PI*BFSK_FREQ0/(double)SAMPLE_RATE;
double inc1=2.0*M_PI*BFSK_FREQ1/(double)SAMPLE_RATE; double inc1=2.0*M_PI*BFSK_FREQ1/(double)SAMPLE_RATE;
size_t bitIdx=0;
std::vector<unsigned char> result(totalBytes, 0); for(size_t b=0;b<tb;b++){
double sum0=0.0,sum1=0.0;
size_t bitIndex = 0; double ph0=0.0,ph1=0.0;
for (size_t b = 0; b < totalBits; b++) { size_t st=b*spb;
double sum0 = 0.0; for(int s=0;s<spb;s++){
double sum1 = 0.0; float sample=monoData[st+s];
double phase0 = 0.0; float r0=sinf((float)ph0), r1=sinf((float)ph1);
double phase1 = 0.0; sum0+=sample*r0; sum1+=sample*r1;
ph0+=inc0; ph1+=inc1;
size_t startSample = b * samplesPerBit;
for (int s = 0; s < samplesPerBit; s++) {
float sample = monoData[startSample + s];
float ref0 = sinf((float)phase0);
float ref1 = sinf((float)phase1);
sum0 += sample * ref0;
sum1 += sample * ref1;
phase0 += inc0;
phase1 += inc1;
} }
int bit=(std::fabs(sum1)>std::fabs(sum0))?1:0; int bit=(std::fabs(sum1)>std::fabs(sum0))?1:0;
size_t bytePos = bitIndex / 8; size_t bytePos=bitIdx/8; int bitPos=bitIdx%8;
int bitPos = bitIndex % 8; if(bytePos<res.size()) res[bytePos]|=(bit<<bitPos);
if (bytePos < result.size()) { bitIdx++;
result[bytePos] |= (bit << bitPos);
} }
bitIndex++; return res;
}
return result;
} }

View File

@ -1,11 +1,11 @@
#pragma once #pragma once
#include <vector> #include <vector>
#include <cstdint>
constexpr double BFSK_FREQ0=1000.0; constexpr double BFSK_FREQ0=1000.0;
constexpr double BFSK_FREQ1=2000.0; constexpr double BFSK_FREQ1=2000.0;
constexpr double BFSK_BAUD=100.0; constexpr double BFSK_BAUD=100.0;
constexpr int SAMPLE_RATE=44100; constexpr int SAMPLE_RATE=44100;
std::vector<float> bfskModulate(const std::vector<unsigned char> &data); std::vector<float> bfskModulate(const std::vector<uint8_t> &data);
std::vector<uint8_t> bfskDemodulate(const std::vector<float> &monoData);
std::vector<unsigned char> bfskDemodulate(const std::vector<float> &monoData);

BIN
bfsk.o

Binary file not shown.

BIN
cerberus

Binary file not shown.

62
cli.cpp
View File

@ -1,60 +1,58 @@
#include "cli.hpp" #include "cli.hpp"
#include "commands.hpp" #include "commands.hpp"
#include "config.hpp" #include "config.hpp"
#include <iostream> #include <iostream>
#include <cstdlib> #include <cstdlib>
#include <string> #include <string>
#include <cstring> #include <cstring>
#include <algorithm>
#include "libs/linenoise.h" #include "libs/linenoise.h"
#define CLR_RESET "\x1b[0m" static const char* asciiArt =
#define CLR_CYAN "\x1b[36m" " (`-') _ (`-') <-.(`-') (`-') _ (`-') (`-').->\n"
" _ ( OO).-/<-.(OO ) __( OO) ( OO).-/<-.(OO ) .-> ( OO)_ \n"
" \\-,-----.(,------.,------,)'-'---.\\ (,------.,------,),--.(,--. (_)--\\_) \n"
" | .--./ | .---'| /`. '| .-. (/ | .---'| /`. '| | |(`-')/ _ / \n"
" /_) (`-')(| '--. | |_.' || '-' `.)(| '--. | |_.' || | |(OO )\\_..`--. \n"
" || |OO ) | .--' | . .'| /`'. | | .--' | . .'| | | | \\.-._) \\\n"
"(_' '--'\\ | `---.| |\\ \\ | '--' / | `---.| |\\ \\ \\ '-'(_ .'\\ /\n"
" `-----' `------'`--' '--'`------' `------'`--' '--' `-----' `-----'\n";
static void completionCallback(const char *input, linenoiseCompletions *completions) { static void completionCallback(const char *input, linenoiseCompletions *lc) {
if (strncmp(input, "nick", 4) == 0) { if(strncmp(input,"nick ",5)==0) linenoiseAddCompletion(lc,"nick set ");
linenoiseAddCompletion(completions, "nick set "); else if(strncmp(input,"nick g",6)==0) linenoiseAddCompletion(lc,"nick generatekey");
linenoiseAddCompletion(completions, "nick generatekey"); else if(strncmp(input,"web s",5)==0) linenoiseAddCompletion(lc,"web start");
} else if(strncmp(input,"web c",5)==0) linenoiseAddCompletion(lc,"web connect pm 127.0.0.1");
else if (strncmp(input, "web", 3) == 0) { else if(strncmp(input,"web s",5)==0) linenoiseAddCompletion(lc,"web stop");
linenoiseAddCompletion(completions, "web start"); else if(strncmp(input,"sound f",7)==0) linenoiseAddCompletion(lc,"sound find");
linenoiseAddCompletion(completions, "web connect pm 127.0.0.1"); else if(strncmp(input,"sound l",7)==0) linenoiseAddCompletion(lc,"sound lose");
linenoiseAddCompletion(completions, "web stop"); else if(strncmp(input,"cerber ma",9)==0) linenoiseAddCompletion(lc,"cerber maketea <text> [key]");
} else if(strncmp(input,"cerber dr",9)==0) linenoiseAddCompletion(lc,"cerber drinktea <hex> [key]");
else if (strncmp(input, "sound", 5) == 0) { else if(strncmp(input,"exit",4)==0) linenoiseAddCompletion(lc,"exit");
linenoiseAddCompletion(completions, "sound find");
linenoiseAddCompletion(completions, "sound lose");
}
} }
void runCLI(AppConfig &config) { void runCLI(AppConfig &config) {
linenoiseHistoryLoad("history.txt"); linenoiseHistoryLoad("history.txt");
linenoiseSetCompletionCallback(completionCallback); linenoiseSetCompletionCallback(completionCallback);
std::cout << CLR_CYAN "Cerberus BFSK Demo (linenoise + color)\n" std::cout << asciiArt << "\n"
<< "Доступные команды:\n" << CLR_CYAN << "Cerberus BFSK:\n"
<< " nick set <usernick>\n" << " nick set <usernick>\n"
<< " nick generatekey\n" << " nick generatekey\n"
<< " web start / web connect pm|server <ip> / web stop\n" << " web start/connect/stop\n"
<< " sound find / sound lose\n" << " sound find/lose\n"
<< " exit\n\n" CLR_RESET; << " cerber maketea <text> [hexKey]\n"
<< " cerber drinktea <hex> [hexKey]\n"
<< " exit\n"
<< CLR_RESET;
while(true){ while(true){
char *line = linenoise(CLR_CYAN ">_ " CLR_RESET); char* line = linenoise(">_ ");
if (!line) { if(!line) break;
std::cout << "\n[cli] EOF/Ctrl+C - выходим.\n";
break;
}
std::string input(line); std::string input(line);
free(line); free(line);
if(!input.empty()){ if(!input.empty()){
linenoiseHistoryAdd(input.c_str()); linenoiseHistoryAdd(input.c_str());
linenoiseHistorySave("history.txt"); linenoiseHistorySave("history.txt");
processCommand(input, config); processCommand(input, config);
} }
} }

View File

@ -1,5 +1,4 @@
#pragma once #pragma once
#include "config.hpp" #include "config.hpp"
void runCLI(AppConfig &config); void runCLI(AppConfig &config);

BIN
cli.o

Binary file not shown.

View File

@ -1,113 +1,207 @@
#include "commands.hpp" #include "commands.hpp"
#include "config.hpp" #include "config.hpp"
#include "webserver.hpp"
#include "sound.hpp"
#include <iostream> #include <iostream>
#include <sstream> #include <string>
#include <iterator>
#include <vector> #include <vector>
#include <sstream>
#include <iomanip>
#include <cstdio> #include <cstdio>
extern "C" {
static std::vector<std::string> splitTokens(const std::string &line) { #include "monocypher.h"
std::istringstream iss(line);
return { std::istream_iterator<std::string>(iss),
std::istream_iterator<std::string>() };
} }
static void generateKey(AppConfig &config) { static const size_t NONCE_SIZE = 24;
config.key.resize(32); 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<len;i++){
oss << std::setw(2)<<std::setfill('0')<<(int)data[i];
}
return oss.str();
}
static std::vector<uint8_t> fromHex(const std::string& hex) {
std::vector<uint8_t> data;
data.reserve(hex.size()/2);
for(size_t i=0;i<hex.size();i+=2){
uint8_t val=(uint8_t)std::stoi(hex.substr(i,2),nullptr,16);
data.push_back(val);
}
return data;
}
static void hexStrToKey(const std::string &hex, uint8_t outKey[32]) {
std::vector<uint8_t> 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"){
std::string name;
std::getline(iss,name);
if(!name.empty() && name[0]==' ') name.erase(0,1);
config.nickname=name;
std::cout<<"[nick] set: "<<config.nickname<<"\n";
} else if(sub=="generatekey"){
FILE* f=fopen("/dev/urandom","rb"); FILE* f=fopen("/dev/urandom","rb");
if (!f) { if(!f)return;
std::cerr << CLR_RED "[nick] Не удалось открыть /dev/urandom\n" CLR_RESET; fread(config.sharedSecret,1,KEY_SIZE,f);
fclose(f);
config.haveSharedSecret=true;
std::cout<<"[nick] 256-bit key generated: "<<toHex(config.sharedSecret,32)<<"\n";
}
}
static void handleWebCommand(const std::string &args, AppConfig &config);
static void handleSoundCommand(const std::string &args, AppConfig &config);
static void handleMakeTea(const std::string& input, AppConfig& config) {
std::istringstream iss(input);
std::string plaintext;
iss >> plaintext;
std::string keyHex;
iss >> keyHex;
uint8_t localKey[32];
bool useLocal=false;
if(!keyHex.empty()){
hexStrToKey(keyHex, localKey);
useLocal=true;
}
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: "<<toHex(config.sharedSecret,32)<<"\n";
}
std::vector<uint8_t> nonce(NONCE_SIZE), mac(MAC_SIZE);
std::vector<uint8_t> ciphertext(plaintext.size());
{
FILE* f=fopen("/dev/urandom","rb");
fread(nonce.data(),1,NONCE_SIZE,f);
fclose(f);
}
const uint8_t* usedKey= useLocal? localKey : config.sharedSecret;
crypto_aead_lock(
ciphertext.data(),
mac.data(),
usedKey,
nonce.data(),
nullptr,0,
(const uint8_t*)plaintext.data(),
plaintext.size()
);
std::vector<uint8_t> 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="<<toHex(usedKey,32)<<"\n";
std::ostringstream oss;
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) {
std::istringstream iss(input);
std::string hexIn;
iss >> hexIn;
std::string keyHex;
iss >> keyHex;
uint8_t localKey[32];
bool useLocal=false;
if(!keyHex.empty()){
hexStrToKey(keyHex, localKey);
useLocal=true;
}
if(!config.haveSharedSecret && !useLocal) return;
auto data=fromHex(hexIn);
if(data.size()<NONCE_SIZE+MAC_SIZE) return;
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> cipher(data.begin()+NONCE_SIZE+MAC_SIZE, data.end());
std::vector<uint8_t> plain(cipher.size());
const uint8_t* usedKey= useLocal? localKey : config.sharedSecret;
int rc=crypto_aead_unlock(
plain.data(),
mac.data(),
usedKey,
nonce.data(),
nullptr,0,
cipher.data(),
cipher.size()
);
if(rc!=0){
std::cerr<<"[drinkTea] MAC error\n";
return; return;
} }
fread(config.key.data(), 1, 32, f); std::string s((char*)plain.data(),plain.size());
fclose(f); std::cout<<"[drinkTea] keyUsed="<<toHex(usedKey,32)<<"\n";
std::cout<<"[drinkTea] decrypted: "<<s<<"\n";
std::cout << CLR_GREEN "[nick] 256-битный ключ сгенерирован!\n" CLR_RESET;
} }
void processCommand(const std::string& input, AppConfig& config) { void processCommand(const std::string& input, AppConfig& config) {
if (input.empty()) return; if(input.rfind("nick ",0)==0){
handleNickCommand(input.substr(5), config);
auto tokens = splitTokens(input);
if (tokens.empty()) return;
std::string cmd = tokens[0];
if (cmd == "nick") {
if (tokens.size() < 2) {
std::cout << CLR_YELLOW "[nick] Доступно: set <name>, generatekey\n" CLR_RESET;
return; return;
} }
std::string sub = tokens[1]; if(input.rfind("web ",0)==0){
if (sub == "set") { handleWebCommand(input.substr(4), config);
if (tokens.size() < 3) {
std::cout << CLR_YELLOW "[nick] Использование: nick set <usernick>\n" CLR_RESET;
return; return;
} }
std::string newNick; if(input.rfind("sound ",0)==0){
for (size_t i = 2; i < tokens.size(); i++) { handleSoundCommand(input.substr(6), config);
if (i > 2) newNick += " ";
newNick += tokens[i];
}
config.nickname = newNick;
std::cout << CLR_GREEN "[nick] Установлен ник: " << newNick << "\n" CLR_RESET;
}
else if (sub == "generatekey") {
generateKey(config);
}
else {
std::cout << CLR_YELLOW "[nick] Неизвестная подкоманда: " << sub << "\n" CLR_RESET;
}
}
else if (cmd == "web") {
if (tokens.size() < 2) {
std::cout << CLR_YELLOW "[web] Доступно: start, connect pm|server <ip>, stop\n" CLR_RESET;
return; return;
} }
std::string sub = tokens[1]; if(input.rfind("cerber maketea ",0)==0){
if (sub == "start") { handleMakeTea(input.substr(15), config);
webServerStart(config);
}
else if (sub == "connect") {
if (tokens.size() < 4) {
std::cout << CLR_YELLOW "[web] Использование: web connect <pm|server> <ip>\n" CLR_RESET;
return; return;
} }
std::string ctype = tokens[2]; if(input.rfind("cerber drinktea ",0)==0){
std::string ip = tokens[3]; handleDrinkTea(input.substr(16), config);
webServerConnect(config, ctype, ip);
}
else if (sub == "stop") {
webServerStop(config);
}
else {
std::cout << CLR_YELLOW "[web] Неизвестная подкоманда: " << sub << "\n" CLR_RESET;
}
}
else if (cmd == "sound") {
if (tokens.size() < 2) {
std::cout << CLR_YELLOW "[sound] Доступно: find, lose\n" CLR_RESET;
return; return;
} }
std::string sub = tokens[1]; if(input=="exit"){
if (sub == "find") { std::cout<<"[cli] exit\n";
soundFind(config);
}
else if (sub == "lose") {
soundLose(config);
}
else {
std::cout << CLR_YELLOW "[sound] Неизвестная подкоманда: " << sub << "\n" CLR_RESET;
}
}
else if (cmd == "exit") {
std::cout << CLR_CYAN "[cli] Завершаем работу по команде 'exit'\n" CLR_RESET;
exit(0); exit(0);
} }
else { std::cout<<"[cli] Unknown: "<<input<<"\n";
std::cout << CLR_RED "Неизвестная команда: " << cmd << CLR_RESET << "\n"; }
static void handleWebCommand(const std::string &args, AppConfig &config){
std::istringstream iss(args);
std::string cmd; iss>>cmd;
if(cmd=="start"){
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"){
extern void webServerStop(AppConfig&);
webServerStop(config);
}
}
static void handleSoundCommand(const std::string &args, AppConfig &config){
std::istringstream iss(args);
std::string cmd; iss>>cmd;
if(cmd=="find"){
extern void soundFind(AppConfig&);
soundFind(config);
} else if(cmd=="lose"){
extern void soundLose(AppConfig&);
soundLose(config);
} }
} }

View File

@ -1,6 +1,5 @@
#pragma once #pragma once
#include <string> #include <string>
#include "config.hpp"
struct AppConfig;
void processCommand(const std::string& input, AppConfig& config); void processCommand(const std::string& input, AppConfig& config);

Binary file not shown.

View File

@ -1,6 +1,5 @@
#pragma once #pragma once
#include <string> #include <string>
#include <vector>
#include <cstdint> #include <cstdint>
#define CLR_RESET "\x1b[0m" #define CLR_RESET "\x1b[0m"
@ -9,22 +8,15 @@
#define CLR_GREEN "\x1b[32m" #define CLR_GREEN "\x1b[32m"
#define CLR_YELLOW "\x1b[33m" #define CLR_YELLOW "\x1b[33m"
#define CLR_BLUE "\x1b[34m" #define CLR_BLUE "\x1b[34m"
#define CLR_MAGENTA "\x1b[35m"
#define CLR_CYAN "\x1b[36m" #define CLR_CYAN "\x1b[36m"
#define CLR_WHITE "\x1b[37m"
struct AppConfig { struct AppConfig {
std::string nickname = "noname"; std::string nickname;
bool webServerRunning;
std::vector<unsigned char> key; bool soundExchangeActive;
bool webServerRunning = false;
bool soundExchangeActive = false;
uint8_t ephemeralSec[32]; uint8_t ephemeralSec[32];
uint8_t ephemeralPub[32]; uint8_t ephemeralPub[32];
uint8_t sharedSecret[32]; uint8_t sharedSecret[32];
bool haveSharedSecret = false; bool haveSharedSecret;
AppConfig(): nickname("noname"), webServerRunning(false), soundExchangeActive(false), haveSharedSecret(false) {}
}; };

View File

@ -13,3 +13,59 @@ web find
web find web find
sound find sound find
exit exit
sound find
sound lose
exit
ls
help
sound
nick set
nick set platon
nick generatekey
cerber maketea
cerber maketea aboba
cerber drinktea fa93d14d8702e215e805098b5ab58c518bf082c6e882bc6952674750b600000000000000000000005167bc15e8
sound find
sound find
sound lose
exirt
exit
sound find
sound lose
exit
nick set platon
nick generatekey
nick maketea textual
cerber maketea xuy
cerber drinktea 60551f937ad549d2aa2492e05ba684acc67a90f95ab713bcdce90200000000000000000000000000a86932
nick generatekey
cerber maketea mother
cerber drinktea a13f5ea8ff23673d45c7e43f690752e9d48360a00a576554b80829668888a1e3bcbec311c5c8afad373e62289c57
nick set Alice
nick generatekey
cerber maketea Hi
nick set Bob
nick generatekey
cerber drinktea 930bd444ebfb9794517067f83a0c50ec678ed8b2632ba7c69201f76b62a43b395457eba05939b7c78ca6
exit
nick generatekey
cerber maketea hi
cerber drinktea 1d5068faafb248389fc305fef3df85d9bd2cbc93f552cfe77a7521ed4a1cbf27c985dc48865f45e13b05
nick set bob
nick generatekey
cerber drinktea 1d5068faafb248389fc305fef3df85d9bd2cbc93f552cfe77a7521ed4a1cbf27c985dc48865f45e13b05
nick set noname
nick generatekey
cerber drinktea 1d5068faafb248389fc305fef3df85d9bd2cbc93f552cfe77a7521ed4a1cbf27c985dc48865f45e13b05
Ńexit
exit
nick generatekey
exit
cerber maketea pidor
cerber drinktea 4c2f53f936e6d576e54e5a0ae88c9e87441fc77d852c0461b5cfb8a14358c929407a181a2f65ea93528f741057
exit
nick generatekey
cerber maketea penis 424cbf882606a41382aad5ce96d24688e04c7adf6cceca8d57bcb6a83a02adcb
cerber drinktea 209a9ef75a9a84434bb9ec84cd71c6fc0b6024da3f9ea550b4bb868f5efed3400cb06f4623a904618cb9317f35 keyUsed=424cbf882606a41382aad5ce96d24688e04c7adf6cceca8d57bcb6a83a02adcb
cerber drinktea 209a9ef75a9a84434bb9ec84cd71c6fc0b6024da3f9ea550b4bb868f5efed3400cb06f4623a904618cb9317f35 424cbf882606a41382aad5ce96d24688e04c7adf6cceca8d57bcb6a83a02adcb
exit

BIN
main.o

Binary file not shown.

119
sound.cpp
View File

@ -1,48 +1,29 @@
#include "sound.hpp" #include "sound.hpp"
#include "bfsk.hpp" #include "bfsk.hpp"
#include "config.hpp" #include "config.hpp"
#include "x25519_handshake.hpp" #include "x25519_handshake.hpp"
#include <iostream> #include <iostream>
#include <thread> #include <thread>
#include <atomic> #include <atomic>
#include <vector> #include <vector>
#include <cmath> #include <cmath>
#include <portaudio.h> #include <portaudio.h>
extern "C" {
#include "monocypher.h"
}
constexpr int CAPTURE_SECONDS=3; constexpr int CAPTURE_SECONDS=3;
static std::atomic<bool> gSoundActive{false}; static std::atomic<bool> gSoundActive{false};
static std::thread gSoundThread; static std::thread gSoundThread;
static std::vector<float> gInputBuffer; static std::vector<float> gInputBuffer;
static size_t gWritePos=0; static size_t gWritePos=0;
static std::vector<float> gOutputBuffer; static std::vector<float> gOutputBuffer;
static size_t gReadPos=0; static size_t gReadPos=0;
static int audioCallback(const void *input, static int audioCallback(const void *input,void *output,unsigned long frameCount,const PaStreamCallbackTimeInfo*,PaStreamCallbackFlags,void*){
void *output, const float *in=(const float*)input;
unsigned long frameCount, float *out=(float*)output;
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData)
{
(void)timeInfo; (void)statusFlags; (void)userData;
const float *in = static_cast<const float*>(input);
float *out = static_cast<float*>(output);
for(unsigned long i=0;i<frameCount;i++){ for(unsigned long i=0;i<frameCount;i++){
if(gWritePos<gInputBuffer.size()){ if(gWritePos<gInputBuffer.size()){
gInputBuffer[gWritePos++]=in?in[i]:0.0f; gInputBuffer[gWritePos++]=in?in[i]:0.0f;
} }
float sample=0.0f; float sample=0.0f;
if(gReadPos<gOutputBuffer.size()){ if(gReadPos<gOutputBuffer.size()){
sample=gOutputBuffer[gReadPos++]; sample=gOutputBuffer[gReadPos++];
@ -50,131 +31,65 @@ static int audioCallback(const void *input,
out[i*2+0]=sample; out[i*2+0]=sample;
out[i*2+1]=sample; out[i*2+1]=sample;
} }
return paContinue; return paContinue;
} }
static void soundThreadFunc(AppConfig config){ static void soundThreadFunc(AppConfig config){
PaError err=Pa_Initialize(); PaError err=Pa_Initialize();
if (err != paNoError) { if(err!=paNoError)return;
std::cerr << CLR_RED "[sound] Pa_Initialize error: " << Pa_GetErrorText(err) << CLR_RESET "\n";
return;
}
PaStreamParameters inParams,outParams; PaStreamParameters inParams,outParams;
inParams.device=Pa_GetDefaultInputDevice(); inParams.device=Pa_GetDefaultInputDevice();
if (inParams.device == paNoDevice) { if(inParams.device==paNoDevice){Pa_Terminate();return;}
std::cerr << CLR_RED "[sound] Нет устройства ввода.\n" CLR_RESET;
Pa_Terminate();
return;
}
inParams.channelCount=1; inParams.channelCount=1;
inParams.sampleFormat=paFloat32; inParams.sampleFormat=paFloat32;
inParams.suggestedLatency=Pa_GetDeviceInfo(inParams.device)->defaultLowInputLatency; inParams.suggestedLatency=Pa_GetDeviceInfo(inParams.device)->defaultLowInputLatency;
inParams.hostApiSpecificStreamInfo=nullptr; inParams.hostApiSpecificStreamInfo=nullptr;
outParams.device=Pa_GetDefaultOutputDevice(); outParams.device=Pa_GetDefaultOutputDevice();
if (outParams.device == paNoDevice) { if(outParams.device==paNoDevice){Pa_Terminate();return;}
std::cerr << CLR_RED "[sound] Нет устройства вывода.\n" CLR_RESET;
Pa_Terminate();
return;
}
outParams.channelCount=2; outParams.channelCount=2;
outParams.sampleFormat=paFloat32; outParams.sampleFormat=paFloat32;
outParams.suggestedLatency=Pa_GetDeviceInfo(outParams.device)->defaultLowOutputLatency; outParams.suggestedLatency=Pa_GetDeviceInfo(outParams.device)->defaultLowOutputLatency;
outParams.hostApiSpecificStreamInfo=nullptr; outParams.hostApiSpecificStreamInfo=nullptr;
PaStream* stream=nullptr; PaStream* stream=nullptr;
err=Pa_OpenStream(&stream,&inParams,&outParams,SAMPLE_RATE,256,paNoFlag,audioCallback,nullptr);
err = Pa_OpenStream(&stream, if(err!=paNoError){Pa_Terminate();return;}
&inParams,
&outParams,
SAMPLE_RATE,
256,
paNoFlag,
audioCallback,
nullptr);
if (err != paNoError) {
std::cerr << CLR_RED "[sound] Pa_OpenStream error: " << Pa_GetErrorText(err) << CLR_RESET "\n";
Pa_Terminate();
return;
}
err=Pa_StartStream(stream); err=Pa_StartStream(stream);
if (err != paNoError) { if(err!=paNoError){Pa_CloseStream(stream);Pa_Terminate();return;}
std::cerr << CLR_RED "[sound] Pa_StartStream error: " << Pa_GetErrorText(err) << CLR_RESET "\n";
Pa_CloseStream(stream);
Pa_Terminate();
return;
}
std::cout << CLR_BLUE "[sound] Старт записи/воспроизведения (3 сек)...\n" CLR_RESET;
Pa_Sleep(CAPTURE_SECONDS*1000); Pa_Sleep(CAPTURE_SECONDS*1000);
Pa_StopStream(stream); Pa_StopStream(stream);
Pa_CloseStream(stream); Pa_CloseStream(stream);
Pa_Terminate(); Pa_Terminate();
std::cout << CLR_BLUE "[sound] Остановка аудиопотока...\n" CLR_RESET;
auto received=bfskDemodulate(gInputBuffer); auto received=bfskDemodulate(gInputBuffer);
if(!received.empty()){ if(!received.empty()){
if(received.size()>=33 && received[0]=='E'){ if(received.size()>=33 && received[0]=='E'){
uint8_t otherPub[32]; uint8_t otherPub[32];
std::memcpy(otherPub, received.data() + 1, 32); for(int i=0;i<32;i++){
otherPub[i]=received[1+i];
}
x25519ComputeShared(config,otherPub); x25519ComputeShared(config,otherPub);
std::cout << CLR_GREEN "[x25519] Общий сеансовый ключ вычислен!\n" CLR_RESET;
} else {
std::cout << CLR_YELLOW "[sound] Получены " << received.size()
<< " байт, но не формат 'E' + 32 байта.\n" CLR_RESET;
} }
} else {
std::cout << CLR_YELLOW "[sound] Ничего не демодулировано.\n" CLR_RESET;
} }
gSoundActive=false; gSoundActive=false;
} }
void soundFind(AppConfig &config){ void soundFind(AppConfig &config){
if (config.soundExchangeActive) { if(config.soundExchangeActive)return;
std::cout << CLR_YELLOW "[sound] Уже идёт процесс.\n" CLR_RESET;
return;
}
config.soundExchangeActive=true; config.soundExchangeActive=true;
gSoundActive=true; gSoundActive=true;
x25519GenerateEphemeral(config); x25519GenerateEphemeral(config);
std::vector<uint8_t> packet;packet.push_back('E');
std::vector<unsigned char> packet;
packet.push_back('E');
packet.insert(packet.end(),config.ephemeralPub,config.ephemeralPub+32); packet.insert(packet.end(),config.ephemeralPub,config.ephemeralPub+32);
gOutputBuffer=bfskModulate(packet); gOutputBuffer=bfskModulate(packet);
gReadPos=0; gReadPos=0;
gInputBuffer.clear(); gInputBuffer.clear();
gInputBuffer.resize(SAMPLE_RATE*CAPTURE_SECONDS,0.0f); gInputBuffer.resize(SAMPLE_RATE*CAPTURE_SECONDS,0.0f);
gWritePos=0; gWritePos=0;
gSoundThread=std::thread(soundThreadFunc,config); gSoundThread=std::thread(soundThreadFunc,config);
std::cout << CLR_GREEN "[sound] Отправляем свой публичный ключ X25519 и слушаем!\n" CLR_RESET;
} }
void soundLose(AppConfig &config){ void soundLose(AppConfig &config){
if (!config.soundExchangeActive) { if(!config.soundExchangeActive)return;
std::cout << CLR_YELLOW "[sound] Процесс не активен.\n" CLR_RESET;
return;
}
config.soundExchangeActive=false; config.soundExchangeActive=false;
if(gSoundActive)gSoundActive=false;
if (gSoundActive) { if(gSoundThread.joinable())gSoundThread.join();
gSoundActive = false;
}
if (gSoundThread.joinable()) {
gSoundThread.join();
}
std::cout << CLR_GREEN "[sound] Процесс остановлен.\n" CLR_RESET;
} }

View File

@ -2,5 +2,4 @@
#include "config.hpp" #include "config.hpp"
void soundFind(AppConfig &config); void soundFind(AppConfig &config);
void soundLose(AppConfig &config); void soundLose(AppConfig &config);

BIN
sound.o

Binary file not shown.

View File

@ -1,10 +1,8 @@
#include "webserver.hpp" #include "webserver.hpp"
#include "config.hpp" #include "config.hpp"
#include <iostream> #include <iostream>
#include <thread> #include <thread>
#include <atomic> #include <atomic>
#include "libs/httplib.h" #include "libs/httplib.h"
static std::atomic<bool> g_serverRunning{false}; static std::atomic<bool> g_serverRunning{false};
@ -15,52 +13,28 @@ static void serverThreadFunc() {
svr.Get("/",[](const httplib::Request&,httplib::Response &res){ svr.Get("/",[](const httplib::Request&,httplib::Response &res){
res.set_content("Hello from Cerberus BFSK!","text/plain"); res.set_content("Hello from Cerberus BFSK!","text/plain");
}); });
if(!svr.listen("0.0.0.0",8080)){}
if (!svr.listen("0.0.0.0", 8080)) {
std::cerr << CLR_RED "[web] Ошибка listen(8080). Возможно, порт занят.\n" CLR_RESET;
}
g_serverRunning=false; g_serverRunning=false;
} }
void webServerStart(AppConfig &config){ void webServerStart(AppConfig &config){
if (config.webServerRunning) { if(config.webServerRunning)return;
std::cout << CLR_YELLOW "[web] Сервер уже запущен.\n" CLR_RESET;
return;
}
g_serverRunning=true; g_serverRunning=true;
g_serverThread=std::thread(serverThreadFunc); g_serverThread=std::thread(serverThreadFunc);
config.webServerRunning=true; config.webServerRunning=true;
std::cout << CLR_GREEN "[web] Сервер запущен на порту 8080.\n" CLR_RESET;
} }
void webServerConnect(AppConfig &config,const std::string &type,const std::string &ip){ void webServerConnect(AppConfig &config,const std::string &type,const std::string &ip){
if (!config.webServerRunning) { if(!config.webServerRunning)return;
std::cout << CLR_YELLOW "[web] Сначала запустите сервер (web start)\n" CLR_RESET;
return;
}
httplib::Client cli(ip.c_str(),8080); httplib::Client cli(ip.c_str(),8080);
if(auto res=cli.Get("/")){ if(auto res=cli.Get("/")){
if (res->status == 200) { if(res->status==200){}
std::cout << CLR_CYAN "[web] Ответ от " << ip << ": " << res->body << CLR_RESET "\n";
} else {
std::cout << CLR_YELLOW "[web] Подключились, статус: " << res->status << CLR_RESET "\n";
}
} else {
std::cout << CLR_RED "[web] Не удалось подключиться к " << ip << ":8080.\n" CLR_RESET;
} }
} }
void webServerStop(AppConfig &config){ void webServerStop(AppConfig &config){
if (!config.webServerRunning) { if(!config.webServerRunning)return;
std::cout << CLR_YELLOW "[web] Сервер не запущен.\n" CLR_RESET; if(g_serverThread.joinable())g_serverThread.detach();
return;
}
g_serverRunning=false; g_serverRunning=false;
if (g_serverThread.joinable()) {
g_serverThread.detach();
}
config.webServerRunning=false; config.webServerRunning=false;
std::cout << CLR_GREEN "[web] Сервер остановлен (демо).\n" CLR_RESET;
} }

Binary file not shown.

View File

@ -1,29 +1,17 @@
#include "x25519_handshake.hpp" #include "x25519_handshake.hpp"
#include "config.hpp" #include <cstdio>
#include <cstring>
#include <cstring> // Для std::memcpy и std::memset
#include <iostream> #include <iostream>
// Подключаем Monocypher
extern "C" { extern "C" {
#include "monocypher.h" #include "monocypher.h"
} }
void x25519GenerateEphemeral(AppConfig &config){ void x25519GenerateEphemeral(AppConfig &config){
FILE* f=fopen("/dev/urandom","rb"); FILE* f=fopen("/dev/urandom","rb");
if (!f) { if(!f)return;
std::cerr << CLR_RED "[x25519] Не удалось открыть /dev/urandom\n" CLR_RESET; fread(config.ephemeralSec,1,32,f);
return;
}
size_t read = fread(config.ephemeralSec, 1, 32, f);
fclose(f); fclose(f);
if (read != 32) {
std::cerr << CLR_RED "[x25519] Не удалось прочитать 32 байта из /dev/urandom\n" CLR_RESET;
return;
}
crypto_x25519_public_key(config.ephemeralPub,config.ephemeralSec); crypto_x25519_public_key(config.ephemeralPub,config.ephemeralSec);
std::memset(config.sharedSecret,0,32); std::memset(config.sharedSecret,0,32);
config.haveSharedSecret=false; config.haveSharedSecret=false;
} }
@ -32,7 +20,6 @@ void x25519ComputeShared(AppConfig &config, const uint8_t otherPub[32]) {
uint8_t shared[32]; uint8_t shared[32];
crypto_x25519(shared,config.ephemeralSec,otherPub); crypto_x25519(shared,config.ephemeralSec,otherPub);
std::memcpy(config.sharedSecret,shared,32); std::memcpy(config.sharedSecret,shared,32);
config.haveSharedSecret=true; config.haveSharedSecret=true;
std::cout << CLR_GREEN "[x25519] Общий сеансовый ключ вычислен!\n" CLR_RESET; std::cout<<CLR_GREEN<<"[x25519] key computed!\n"<<CLR_RESET;
} }

View File

@ -1,9 +1,6 @@
#pragma once #pragma once
#include <cstdint> #include <cstdint>
#include <vector>
#include "config.hpp" #include "config.hpp"
void x25519GenerateEphemeral(AppConfig &config); void x25519GenerateEphemeral(AppConfig &config);
void x25519ComputeShared(AppConfig &config,const uint8_t otherPub[32]); void x25519ComputeShared(AppConfig &config,const uint8_t otherPub[32]);

Binary file not shown.