use protocol buffers to do something cool

This commit is contained in:
6arelyFuture 2022-09-02 17:39:26 +02:00
parent 210da7c6a5
commit d8919ba684
Signed by: Future
GPG Key ID: FA77F074E98D98A5
25 changed files with 1219 additions and 24 deletions

View File

@ -1,5 +1,4 @@

---
---
Language: Cpp
BasedOnStyle: LLVM
DerivePointerAlignment: false
@ -10,3 +9,5 @@ SortIncludes: false
IncludeBlocks: Preserve
---
Language: Proto
BasedOnStyle: Google

10
.gitmodules vendored
View File

@ -1,3 +1,13 @@
[submodule "deps/GSL"]
path = deps/GSL
url = https://github.com/microsoft/GSL.git
[submodule "deps/libtomcrypt"]
path = deps/libtomcrypt
url = https://github.com/libtom/libtomcrypt.git
[submodule "deps/libtommath"]
path = deps/libtommath
url = https://github.com/libtom/libtommath.git
[submodule "deps/protobuf"]
path = deps/protobuf
url = https://github.com/google/protobuf.git
branch = 3.20.x

1
deps/libtomcrypt vendored Submodule

@ -0,0 +1 @@
Subproject commit ddfe2e8aa7c4239463a8a1d26724aef123333549

1
deps/libtommath vendored Submodule

@ -0,0 +1 @@
Subproject commit 4b47368501321c795d5b54d87a5bab35a21a7940

61
deps/premake/libtomcrypt.lua vendored Normal file
View File

@ -0,0 +1,61 @@
libtomcrypt = {
source = path.join(dependencies.basePath, "libtomcrypt"),
}
function libtomcrypt.import()
links {
"libtomcrypt"
}
libtomcrypt.includes()
end
function libtomcrypt.includes()
includedirs {
path.join(libtomcrypt.source, "src/headers")
}
defines {
"LTC_NO_FAST",
"LTC_NO_PROTOTYPES",
"LTC_NO_RSA_BLINDING",
}
end
function libtomcrypt.project()
project "libtomcrypt"
language "C"
libtomcrypt.includes()
libtommath.import()
files {
path.join(libtomcrypt.source, "src/**.c"),
}
removefiles {
path.join(libtomcrypt.source, "src/**/*tab.c"),
path.join(libtomcrypt.source, "src/encauth/ocb3/**.c"),
}
defines {
"_CRT_SECURE_NO_WARNINGS",
"LTC_SOURCE",
"_LIB",
"USE_LTM"
}
removedefines {
"_DLL",
"_USRDLL"
}
linkoptions {
"-IGNORE:4221"
}
warnings "Off"
kind "StaticLib"
end
table.insert(dependencies, libtomcrypt)

52
deps/premake/libtommath.lua vendored Normal file
View File

@ -0,0 +1,52 @@
libtommath = {
source = path.join(dependencies.basePath, "libtommath"),
}
function libtommath.import()
links {
"libtommath"
}
libtommath.includes()
end
function libtommath.includes()
includedirs {
libtommath.source
}
defines {
"LTM_DESC",
"__STDC_IEC_559__",
"MP_NO_DEV_URANDOM",
}
end
function libtommath.project()
project "libtommath"
language "C"
libtommath.includes()
files {
path.join(libtommath.source, "*.c"),
}
defines {
"_LIB"
}
removedefines {
"_DLL",
"_USRDLL"
}
linkoptions {
"-IGNORE:4221"
}
warnings "Off"
kind "StaticLib"
end
table.insert(dependencies, libtommath)

60
deps/premake/protobuf.lua vendored Normal file
View File

@ -0,0 +1,60 @@
protobuf = {
source = path.join(dependencies.basePath, "protobuf"),
}
function protobuf.import()
links {
"protobuf"
}
protobuf.includes()
end
function protobuf.includes()
includedirs {
path.join(protobuf.source, "src"),
}
end
function protobuf.project()
project "protobuf"
language "C++"
protobuf.includes()
files {
path.join(protobuf.source, "src/**.cc"),
"./src/**.proto",
}
removefiles {
path.join(protobuf.source, "src/**/*test.cc"),
path.join(protobuf.source, "src/google/protobuf/*test*.cc"),
path.join(protobuf.source, "src/google/protobuf/testing/**.cc"),
path.join(protobuf.source, "src/google/protobuf/compiler/**.cc"),
path.join(protobuf.source, "src/google/protobuf/arena_nc.cc"),
path.join(protobuf.source, "src/google/protobuf/util/internal/error_listener.cc"),
path.join(protobuf.source, "**/*_gcc.cc"),
}
rules {
"ProtobufCompiler"
}
defines {
"_SCL_SECURE_NO_WARNINGS",
"_SILENCE_ALL_CXX17_DEPRECATION_WARNINGS",
"_SILENCE_ALL_CXX20_DEPRECATION_WARNINGS",
}
linkoptions {
"-IGNORE:4221"
}
warnings "Off"
kind "StaticLib"
end
table.insert(dependencies, protobuf)

1
deps/protobuf vendored Submodule

@ -0,0 +1 @@
Subproject commit 50bdb17409d4133d51ab6cfa095700f4c816576e

View File

@ -31,6 +31,7 @@ end
dependencies.load()
workspace "mw3-server-freezer"
startproject "client"
location "./build"
objdir "%{wks.location}/obj"
targetdir "%{wks.location}/bin/%{cfg.platform}/%{cfg.buildcfg}"
@ -104,3 +105,13 @@ dependencies.imports()
group "Dependencies"
dependencies.projects()
rule "ProtobufCompiler"
display "Protobuf compiler"
location "./build"
fileExtension ".proto"
buildmessage "Compiling %(Identity) with protoc..."
buildcommands {'@echo off', 'path "$(SolutionDir)\\..\\tools"',
'if not exist "$(ProjectDir)\\src\\proto" mkdir "$(ProjectDir)\\src\\proto"',
'protoc --error_format=msvs -I=%(RelativeDir) --cpp_out=src\\proto %(Identity)'}
buildoutputs {'$(ProjectDir)\\src\\proto\\%(Filename).pb.cc', '$(ProjectDir)\\src\\proto\\%(Filename).pb.h'}

View File

@ -0,0 +1,62 @@
#include <std_include.hpp>
#include "crypto_key.hpp"
#include "console.hpp"
#include <utils/io.hpp>
namespace crypto_key {
namespace {
bool load_key(utils::cryptography::ecc::key& key) {
std::string data{};
if (!utils::io::read_file("./private.key", &data)) {
return false;
}
key.deserialize(data);
if (!key.is_valid()) {
console::info("Loaded key is invalid!");
return false;
}
return true;
}
utils::cryptography::ecc::key generate_key() {
auto key = utils::cryptography::ecc::generate_key(512);
if (!key.is_valid()) {
throw std::runtime_error("Failed to generate server key!");
}
if (!utils::io::write_file("./private.key", key.serialize())) {
throw std::runtime_error("Failed to write server key!");
}
console::info("Generated cryptographic key: {}", key.get_hash());
return key;
}
utils::cryptography::ecc::key load_or_generate_key() {
utils::cryptography::ecc::key key{};
if (load_key(key)) {
console::info("Loaded cryptographic key: {}", key.get_hash());
return key;
}
return generate_key();
}
utils::cryptography::ecc::key get_key_internal() {
auto key = load_or_generate_key();
if (!utils::io::write_file("./public.key", key.get_public_key())) {
console::info("Failed to write public key!");
}
return key;
}
} // namespace
const utils::cryptography::ecc::key& get() {
static auto key = get_key_internal();
return key;
}
} // namespace crypto_key

View File

@ -0,0 +1,7 @@
#pragma once
#include "utils/cryptography.hpp"
namespace crypto_key {
const utils::cryptography::ecc::key& get();
}

View File

@ -1,8 +0,0 @@
#pragma once
namespace network {
using callback =
std::function<void(const game::netadr_s&, const std::string_view&)>;
void on_packet(const std::string& command, const callback& callback);
} // namespace network

View File

@ -1,15 +1,17 @@
#include <std_include.hpp>
#include "../loader/component_loader.hpp"
#include "../../loader/component_loader.hpp"
#include <utils/hook.hpp>
#include <utils/string.hpp>
#include "../console.hpp"
#include "network.hpp"
namespace network {
namespace {
std::unordered_map<std::string, network::callback>& get_callbacks() {
static std::unordered_map<std::string, network::callback> network_callbacks{};
std::unordered_map<std::string, callback>& get_callbacks() {
static std::unordered_map<std::string, callback> network_callbacks{};
return network_callbacks;
}
@ -32,7 +34,6 @@ bool handle_command(game::netadr_s* address, const char* command,
handler->second(*address, data);
return true;
}
} // namespace
int packet_interception_handler(game::netadr_s* from, const char* command,
game::msg_t* message) {
@ -42,12 +43,61 @@ int packet_interception_handler(game::netadr_s* from, const char* command,
return TRUE;
}
} // namespace
void send(const game::netadr_s& address, const std::string& command,
const std::string& data, const char separator) {
std::string packet = "\xFF\xFF\xFF\xFF";
packet.append(command);
packet.push_back(separator);
packet.append(data);
send_data(address, packet);
}
void send_data(const game::netadr_s& address, const std::string& data) {
auto size = static_cast<int>(data.size());
if (address.type == game::NA_LOOPBACK) {
// TODO: Fix this for loopback
if (size > 1280) {
console::info("Packet was too long. Truncated!\n");
size = 1280;
}
game::NET_SendLoopPacket(game::NS_CLIENT1, size, data.data(), address);
} else {
game::Sys_SendPacket(size, data.data(), address);
}
}
void on_packet(const std::string& command, const callback& callback) {
get_callbacks()[utils::string::to_lower(command)] = callback;
}
class component final : public component_interface {
const char* net_adr_to_string(const game::netadr_s& a) {
if (a.type == game::netadrtype_t::NA_LOOPBACK) {
return "loopback";
}
if (a.type == game::netadrtype_t::NA_BOT) {
return "bot";
}
if (a.type == game::netadrtype_t::NA_IP ||
a.type == game::netadrtype_t::NA_BROADCAST) {
if (a.port) {
return utils::string::va("%u.%u.%u.%u:%u", a.ip[0], a.ip[1], a.ip[2],
a.ip[3], htons(a.port));
}
return utils::string::va("%u.%u.%u.%u", a.ip[0], a.ip[1], a.ip[2], a.ip[3]);
}
return "bad";
}
class network final : public component_interface {
public:
void post_unpack() override {
add_network_commands();
@ -67,4 +117,4 @@ private:
};
} // namespace network
REGISTER_COMPONENT(network::component)
REGISTER_COMPONENT(network::network)

View File

@ -0,0 +1,13 @@
#pragma once
namespace network {
void send(const game::netadr_s& address, const std::string& command,
const std::string& data = {}, char separator = ' ');
void send_data(const game::netadr_s& address, const std::string& data);
using callback =
std::function<void(const game::netadr_s&, const std::string_view&)>;
void on_packet(const std::string& command, const callback& callback);
const char* net_adr_to_string(const game::netadr_s& a);
} // namespace network

View File

@ -0,0 +1,54 @@
#include <std_include.hpp>
#include "../loader/component_loader.hpp"
#include <utils/cryptography.hpp>
#include "network/network.hpp"
#include "crypto_key.hpp"
#include "key_catcher.hpp"
#include <proto/rcon.pb.h>
namespace rcon {
namespace {
utils::cryptography::ecc::key key;
std::string commands;
} // namespace
class component final : public component_interface {
public:
void post_unpack() override {
key = crypto_key::get();
add_key_hooks();
add_commands();
}
private:
static void add_key_hooks() {
// Why commands don't work? I don't know!
key_catcher::on_key_press(
"7", []([[maybe_unused]] const game::LocalClientNum_t& local_client) {
commands = "quit";
network::send(game::localClientConnection->serverAddress,
"rcon_request");
});
}
static void add_commands() {
network::on_packet("rcon_authorization", [](const game::netadr_s& adr,
const std::string_view& data) {
const auto signed_msg =
utils::cryptography::ecc::sign_message(key, std::string(data));
proto::rcon::command info;
info.set_commands(commands);
info.set_signature(signed_msg);
network::send(adr, "rcon_execute", info.SerializeAsString());
});
}
};
} // namespace rcon
REGISTER_COMPONENT(rcon::component)

View File

@ -69,7 +69,7 @@ struct netadr_s {
unsigned int addrHandleIndex;
};
static_assert(sizeof(netadr_s) == 24);
static_assert(sizeof(netadr_s) == 0x18);
typedef enum {
ERR_FATAL = 0x0,

View File

@ -54,7 +54,9 @@ WEAK symbol<bool(netsrc_t, netadr_s dest, unsigned char* data, int size)>
WEAK symbol<void(netadr_s*, sockaddr*)> NetadrToSockadr{0x48B460};
WEAK symbol<int(const char* serverName, netadr_s serverRemote)> NET_StringToAdr{
0x4E09A0};
WEAK symbol<SOCKET> query_socket{0x5A861EC};
WEAK symbol<bool(int length, const void* voiddata, netadr_s to)> Sys_SendPacket{0x5145C0};
WEAK symbol<void(netsrc_t sock, int length, const void* data, netadr_s to)> NET_SendLoopPacket{0x4B9DF0};
WEAK symbol<void()> Com_Quit_f{0x556060};
WEAK symbol<void(const msg_t*, unsigned char*, int)> MSG_Init{0x40E030};
@ -74,11 +76,6 @@ WEAK symbol<int(unsigned __int64, const void*, unsigned int)>
WEAK symbol<XAssetHeader(int type, const char* name, int allowCreateDefault)>
DB_FindXAssetHeader{0x4B25C0};
WEAK symbol<void(const char* text, int maxChars, Font_s* font, float x, float y,
float xScale, float yScale, float rotation, const float* color,
int style)>
R_AddCmdDrawText{0x42C970};
// Variables
WEAK symbol<CmdArgs> cmd_args{0x1C96850};
WEAK symbol<PlayerKeyState> playerKeys{0xB3A38C};

View File

@ -0,0 +1,8 @@
syntax = "proto3";
package proto.rcon;
message command {
bytes commands = 1;
bytes signature = 2;
}

View File

@ -1 +1,11 @@
#include "std_include.hpp"
extern "C" {
int s_read_arc4random(void*, std::size_t) { return -1; }
int s_read_getrandom(void*, std::size_t) { return -1; }
int s_read_urandom(void*, std::size_t) { return -1; }
int s_read_ltm_rng(void*, std::size_t) { return -1; }
}

View File

@ -4,8 +4,10 @@
#define WIN32_LEAN_AND_MEAN
#include <WinSock2.h>
#include <Windows.h>
#include <WinSock2.h>
#include <WS2tcpip.h>
#include <corecrt_io.h>
#include <fcntl.h>
@ -15,10 +17,12 @@
#include <iostream>
#include <mutex>
#include <string>
#include <string_view>
#include <source_location>
#include <queue>
#pragma comment(lib, "ntdll.lib")
#pragma comment(lib, "ws2_32.lib")
using namespace std::literals;

View File

@ -0,0 +1,556 @@
#include "string.hpp"
#include "cryptography.hpp"
#include "nt.hpp"
#include <gsl/gsl>
#undef max
using namespace std::string_literals;
/// http://www.opensource.apple.com/source/CommonCrypto/CommonCrypto-55010/Source/libtomcrypt/doc/libTomCryptDoc.pdf
namespace utils::cryptography {
namespace {
struct __ {
__() {
ltc_mp = ltm_desc;
register_cipher(&aes_desc);
register_cipher(&des3_desc);
register_prng(&sprng_desc);
register_prng(&fortuna_desc);
register_prng(&yarrow_desc);
register_hash(&sha1_desc);
register_hash(&sha256_desc);
register_hash(&sha512_desc);
}
} ___;
[[maybe_unused]] const char* cs(const std::uint8_t* data) {
return reinterpret_cast<const char*>(data);
}
[[maybe_unused]] char* cs(std::uint8_t* data) {
return reinterpret_cast<char*>(data);
}
[[maybe_unused]] const std::uint8_t* cs(const char* data) {
return reinterpret_cast<const std::uint8_t*>(data);
}
[[maybe_unused]] std::uint8_t* cs(char* data) {
return reinterpret_cast<std::uint8_t*>(data);
}
[[maybe_unused]] unsigned long ul(const std::size_t value) {
return static_cast<unsigned long>(value);
}
class prng {
public:
prng(const ltc_prng_descriptor& descriptor, const bool autoseed = true)
: state_(std::make_unique<prng_state>()), descriptor_(descriptor) {
this->id_ = register_prng(&descriptor);
if (this->id_ == -1) {
throw std::runtime_error("PRNG "s + this->descriptor_.name +
" could not be registered!");
}
if (autoseed) {
this->auto_seed();
} else {
this->descriptor_.start(this->state_.get());
}
}
~prng() { this->descriptor_.done(this->state_.get()); }
[[nodiscard]] prng_state* get_state() const {
this->descriptor_.ready(this->state_.get());
return this->state_.get();
}
[[nodiscard]] int get_id() const { return this->id_; }
void add_entropy(const void* data, const std::size_t length) const {
this->descriptor_.add_entropy(static_cast<const std::uint8_t*>(data),
ul(length), this->state_.get());
}
void read(void* data, const std::size_t length) const {
this->descriptor_.read(static_cast<std::uint8_t*>(data), ul(length),
this->get_state());
}
private:
int id_;
std::unique_ptr<prng_state> state_;
const ltc_prng_descriptor& descriptor_;
void auto_seed() const {
rng_make_prng(128, this->id_, this->state_.get(), nullptr);
int i[4]; // uninitialized data
auto* i_ptr = &i;
this->add_entropy(reinterpret_cast<std::uint8_t*>(&i), sizeof(i));
this->add_entropy(reinterpret_cast<std::uint8_t*>(&i_ptr), sizeof(i_ptr));
auto t = time(nullptr);
this->add_entropy(reinterpret_cast<std::uint8_t*>(&t), sizeof(t));
}
};
const prng prng_(fortuna_desc);
} // namespace
ecc::key::key() { ZeroMemory(&this->key_storage_, sizeof(this->key_storage_)); }
ecc::key::~key() { this->free(); }
ecc::key::key(key&& obj) noexcept : key() { this->operator=(std::move(obj)); }
ecc::key::key(const key& obj) : key() { this->operator=(obj); }
ecc::key& ecc::key::operator=(key&& obj) noexcept {
if (this != &obj) {
std::memmove(&this->key_storage_, &obj.key_storage_,
sizeof(this->key_storage_));
ZeroMemory(&obj.key_storage_, sizeof(obj.key_storage_));
}
return *this;
}
ecc::key& ecc::key::operator=(const key& obj) {
if (this != &obj && obj.is_valid()) {
this->deserialize(obj.serialize(obj.key_storage_.type));
}
return *this;
}
bool ecc::key::operator==(key& key) const {
return (this->is_valid() && key.is_valid() &&
this->serialize(PK_PUBLIC) == key.serialize(PK_PUBLIC));
}
bool ecc::key::is_valid() const {
return (!memory::is_set(&this->key_storage_, 0, sizeof(this->key_storage_)));
}
ecc_key& ecc::key::get() { return this->key_storage_; }
const ecc_key& ecc::key::get() const { return this->key_storage_; }
std::string ecc::key::get_public_key() const {
std::uint8_t buffer[512] = {0};
unsigned long length = sizeof(buffer);
if (ecc_ansi_x963_export(&this->key_storage_, buffer, &length) == CRYPT_OK) {
return {cs(buffer), length};
}
return {};
}
void ecc::key::set(const std::string& pub_key_buffer) {
this->free();
if (ecc_ansi_x963_import(cs(pub_key_buffer.data()), ul(pub_key_buffer.size()),
&this->key_storage_) != CRYPT_OK) {
ZeroMemory(&this->key_storage_, sizeof(this->key_storage_));
}
}
void ecc::key::deserialize(const std::string& key) {
this->free();
if (ecc_import(cs(key.data()), ul(key.size()), &this->key_storage_) !=
CRYPT_OK) {
ZeroMemory(&this->key_storage_, sizeof(this->key_storage_));
}
}
std::string ecc::key::serialize(const int type) const {
std::uint8_t buffer[4096] = {0};
unsigned long length = sizeof(buffer);
if (ecc_export(buffer, &length, type, &this->key_storage_) == CRYPT_OK) {
return {cs(buffer), length};
}
return {};
}
void ecc::key::free() {
if (this->is_valid()) {
ecc_free(&this->key_storage_);
}
ZeroMemory(&this->key_storage_, sizeof(this->key_storage_));
}
std::uint64_t ecc::key::get_hash() const {
const auto hash = sha1::compute(this->get_public_key());
if (hash.size() >= 8) {
return *reinterpret_cast<const std::uint64_t*>(hash.data());
}
return 0;
}
ecc::key ecc::generate_key(const int bits) {
key key;
ecc_make_key(prng_.get_state(), prng_.get_id(), bits / 8, &key.get());
return key;
}
ecc::key ecc::generate_key(const int bits, const std::string& entropy) {
key key{};
const prng yarrow(yarrow_desc, false);
yarrow.add_entropy(entropy.data(), entropy.size());
ecc_make_key(yarrow.get_state(), yarrow.get_id(), bits / 8, &key.get());
return key;
}
std::string ecc::sign_message(const key& key, const std::string& message) {
if (!key.is_valid())
return {};
std::uint8_t buffer[512];
unsigned long length = sizeof(buffer);
ecc_sign_hash(cs(message.data()), ul(message.size()), buffer, &length,
prng_.get_state(), prng_.get_id(), &key.get());
return {cs(buffer), length};
}
bool ecc::verify_message(const key& key, const std::string& message,
const std::string& signature) {
if (!key.is_valid())
return false;
auto result = 0;
return (ecc_verify_hash(cs(signature.data()), ul(signature.size()),
cs(message.data()), ul(message.size()), &result,
&key.get()) == CRYPT_OK &&
result != 0);
}
bool ecc::encrypt(const key& key, std::string& data) {
std::string out_data{};
out_data.resize(std::max(ul(data.size() * 3), ul(0x100)));
auto out_len = ul(out_data.size());
auto crypt = [&]() {
return ecc_encrypt_key(cs(data.data()), ul(data.size()),
cs(out_data.data()), &out_len, prng_.get_state(),
prng_.get_id(), find_hash("sha512"), &key.get());
};
auto res = crypt();
if (res == CRYPT_BUFFER_OVERFLOW) {
out_data.resize(out_len);
res = crypt();
}
if (res != CRYPT_OK) {
return false;
}
out_data.resize(out_len);
data = std::move(out_data);
return true;
}
bool ecc::decrypt(const key& key, std::string& data) {
std::string out_data{};
out_data.resize(std::max(ul(data.size() * 3), ul(0x100)));
auto out_len = ul(out_data.size());
auto crypt = [&]() {
return ecc_decrypt_key(cs(data.data()), ul(data.size()),
cs(out_data.data()), &out_len, &key.get());
};
auto res = crypt();
if (res == CRYPT_BUFFER_OVERFLOW) {
out_data.resize(out_len);
res = crypt();
}
if (res != CRYPT_OK) {
return false;
}
out_data.resize(out_len);
data = std::move(out_data);
return true;
}
std::string rsa::encrypt(const std::string& data, const std::string& hash,
const std::string& key) {
rsa_key new_key;
rsa_import(cs(key.data()), ul(key.size()), &new_key);
const auto _ = gsl::finally([&]() { rsa_free(&new_key); });
std::string out_data{};
out_data.resize(std::max(ul(data.size() * 3), ul(0x100)));
auto out_len = ul(out_data.size());
auto crypt = [&]() {
return rsa_encrypt_key(cs(data.data()), ul(data.size()),
cs(out_data.data()), &out_len, cs(hash.data()),
ul(hash.size()), prng_.get_state(), prng_.get_id(),
find_hash("sha512"), &new_key);
};
auto res = crypt();
if (res == CRYPT_BUFFER_OVERFLOW) {
out_data.resize(out_len);
res = crypt();
}
if (res == CRYPT_OK) {
out_data.resize(out_len);
return out_data;
}
return {};
}
std::string des3::encrypt(const std::string& data, const std::string& iv,
const std::string& key) {
std::string enc_data;
enc_data.resize(data.size());
symmetric_CBC cbc;
const auto des3 = find_cipher("3des");
cbc_start(des3, cs(iv.data()), cs(key.data()), static_cast<int>(key.size()),
0, &cbc);
cbc_encrypt(cs(data.data()), cs(enc_data.data()), ul(data.size()), &cbc);
cbc_done(&cbc);
return enc_data;
}
std::string des3::decrypt(const std::string& data, const std::string& iv,
const std::string& key) {
std::string dec_data;
dec_data.resize(data.size());
symmetric_CBC cbc;
const auto des3 = find_cipher("3des");
cbc_start(des3, cs(iv.data()), cs(key.data()), static_cast<int>(key.size()),
0, &cbc);
cbc_decrypt(cs(data.data()), cs(dec_data.data()), ul(data.size()), &cbc);
cbc_done(&cbc);
return dec_data;
}
std::string tiger::compute(const std::string& data, const bool hex) {
return compute(cs(data.data()), data.size(), hex);
}
std::string tiger::compute(const uint8_t* data, const size_t length,
const bool hex) {
std::uint8_t buffer[24] = {0};
hash_state state;
tiger_init(&state);
tiger_process(&state, data, ul(length));
tiger_done(&state, buffer);
std::string hash(cs(buffer), sizeof(buffer));
if (!hex)
return hash;
return string::dump_hex(hash, "");
}
std::string aes::encrypt(const std::string& data, const std::string& iv,
const std::string& key) {
std::string enc_data;
enc_data.resize(data.size());
symmetric_CBC cbc;
const auto aes = find_cipher("aes");
cbc_start(aes, cs(iv.data()), cs(key.data()), static_cast<int>(key.size()), 0,
&cbc);
cbc_encrypt(cs(data.data()), cs(enc_data.data()), ul(data.size()), &cbc);
cbc_done(&cbc);
return enc_data;
}
std::string aes::decrypt(const std::string& data, const std::string& iv,
const std::string& key) {
std::string dec_data;
dec_data.resize(data.size());
symmetric_CBC cbc;
const auto aes = find_cipher("aes");
cbc_start(aes, cs(iv.data()), cs(key.data()), static_cast<int>(key.size()), 0,
&cbc);
cbc_decrypt(cs(data.data()), cs(dec_data.data()), ul(data.size()), &cbc);
cbc_done(&cbc);
return dec_data;
}
std::string hmac_sha1::compute(const std::string& data,
const std::string& key) {
std::string buffer;
buffer.resize(20);
hmac_state state;
hmac_init(&state, find_hash("sha1"), cs(key.data()), ul(key.size()));
hmac_process(&state, cs(data.data()), static_cast<int>(data.size()));
auto out_len = ul(buffer.size());
hmac_done(&state, cs(buffer.data()), &out_len);
buffer.resize(out_len);
return buffer;
}
std::string sha1::compute(const std::string& data, const bool hex) {
return compute(cs(data.data()), data.size(), hex);
}
std::string sha1::compute(const std::uint8_t* data, const std::size_t length,
const bool hex) {
std::uint8_t buffer[20] = {0};
hash_state state;
sha1_init(&state);
sha1_process(&state, data, ul(length));
sha1_done(&state, buffer);
std::string hash(cs(buffer), sizeof(buffer));
if (!hex)
return hash;
return string::dump_hex(hash, "");
}
std::string sha256::compute(const std::string& data, const bool hex) {
return compute(cs(data.data()), data.size(), hex);
}
std::string sha256::compute(const std::uint8_t* data, const std::size_t length,
const bool hex) {
std::uint8_t buffer[32] = {0};
hash_state state;
sha256_init(&state);
sha256_process(&state, data, ul(length));
sha256_done(&state, buffer);
std::string hash(cs(buffer), sizeof(buffer));
if (!hex)
return hash;
return string::dump_hex(hash, "");
}
std::string sha512::compute(const std::string& data, const bool hex) {
return compute(cs(data.data()), data.size(), hex);
}
std::string sha512::compute(const std::uint8_t* data, const std::size_t length,
const bool hex) {
std::uint8_t buffer[64] = {0};
hash_state state;
sha512_init(&state);
sha512_process(&state, data, ul(length));
sha512_done(&state, buffer);
std::string hash(cs(buffer), sizeof(buffer));
if (!hex)
return hash;
return string::dump_hex(hash, "");
}
std::string base64::encode(const std::uint8_t* data, const std::size_t len) {
std::string result;
result.resize((len + 2) * 2);
auto out_len = ul(result.size());
if (base64_encode(data, ul(len), result.data(), &out_len) != CRYPT_OK) {
return {};
}
result.resize(out_len);
return result;
}
std::string base64::encode(const std::string& data) {
return base64::encode(cs(data.data()), data.size());
}
std::string base64::decode(const std::string& data) {
std::string result;
result.resize((data.size() + 2) * 2);
auto out_len = ul(result.size());
if (base64_decode(data.data(), ul(data.size()), cs(result.data()),
&out_len) != CRYPT_OK) {
return {};
}
result.resize(out_len);
return result;
}
unsigned int jenkins_one_at_a_time::compute(const std::string& data) {
return compute(data.data(), data.size());
}
unsigned int jenkins_one_at_a_time::compute(const char* key,
const std::size_t len) {
unsigned int hash, i;
for (hash = i = 0; i < len; ++i) {
hash += key[i];
hash += (hash << 10);
hash ^= (hash >> 6);
}
hash += (hash << 3);
hash ^= (hash >> 11);
hash += (hash << 15);
return hash;
}
std::uint32_t random::get_integer() {
std::uint32_t result;
random::get_data(&result, sizeof(result));
return result;
}
std::string random::get_challenge() {
std::string result;
result.resize(sizeof(std::uint32_t));
random::get_data(result.data(), result.size());
return string::dump_hex(result, "");
}
void random::get_data(void* data, const std::size_t size) {
prng_.read(data, size);
}
} // namespace utils::cryptography

View File

@ -0,0 +1,113 @@
#pragma once
#include <string>
#include <tomcrypt.h>
namespace utils::cryptography {
namespace ecc {
class key final {
public:
key();
~key();
key(key&& obj) noexcept;
key(const key& obj);
key& operator=(key&& obj) noexcept;
key& operator=(const key& obj);
bool operator==(key& key) const;
[[nodiscard]] bool is_valid() const;
ecc_key& get();
[[nodiscard]] const ecc_key& get() const;
[[nodiscard]] std::string get_public_key() const;
void set(const std::string& pub_key_buffer);
void deserialize(const std::string& key);
[[nodiscard]] std::string serialize(int type = PK_PRIVATE) const;
void free();
[[nodiscard]] std::uint64_t get_hash() const;
private:
ecc_key key_storage_{};
};
key generate_key(int bits);
key generate_key(int bits, const std::string& entropy);
std::string sign_message(const key& key, const std::string& message);
bool verify_message(const key& key, const std::string& message,
const std::string& signature);
bool encrypt(const key& key, std::string& data);
bool decrypt(const key& key, std::string& data);
} // namespace ecc
namespace rsa {
std::string encrypt(const std::string& data, const std::string& hash,
const std::string& key);
}
namespace des3 {
std::string encrypt(const std::string& data, const std::string& iv,
const std::string& key);
std::string decrypt(const std::string& data, const std::string& iv,
const std::string& key);
} // namespace des3
namespace tiger {
std::string compute(const std::string& data, bool hex = false);
std::string compute(const std::uint8_t* data, std::size_t length,
bool hex = false);
} // namespace tiger
namespace aes {
std::string encrypt(const std::string& data, const std::string& iv,
const std::string& key);
std::string decrypt(const std::string& data, const std::string& iv,
const std::string& key);
} // namespace aes
namespace hmac_sha1 {
std::string compute(const std::string& data, const std::string& key);
}
namespace sha1 {
std::string compute(const std::string& data, bool hex = false);
std::string compute(const std::uint8_t* data, std::size_t length,
bool hex = false);
} // namespace sha1
namespace sha256 {
std::string compute(const std::string& data, bool hex = false);
std::string compute(const std::uint8_t* data, std::size_t length,
bool hex = false);
} // namespace sha256
namespace sha512 {
std::string compute(const std::string& data, bool hex = false);
std::string compute(const std::uint8_t* data, std::size_t length,
bool hex = false);
} // namespace sha512
namespace base64 {
std::string encode(const std::uint8_t* data, std::size_t len);
std::string encode(const std::string& data);
std::string decode(const std::string& data);
} // namespace base64
namespace jenkins_one_at_a_time {
unsigned int compute(const std::string& data);
unsigned int compute(const char* key, std::size_t len);
}; // namespace jenkins_one_at_a_time
namespace random {
std::uint32_t get_integer();
std::string get_challenge();
void get_data(void* data, std::size_t size);
} // namespace random
} // namespace utils::cryptography

109
src/common/utils/io.cpp Normal file
View File

@ -0,0 +1,109 @@
#include "io.hpp"
#include "nt.hpp"
#include <fstream>
namespace utils::io {
bool remove_file(const std::string& file) {
return DeleteFileA(file.data()) == TRUE;
}
bool move_file(const std::string& src, const std::string& target) {
return MoveFileA(src.data(), target.data()) == TRUE;
}
bool file_exists(const std::string& file) {
return std::filesystem::exists(file);
}
bool write_file(const std::string& file, const std::string& data,
const bool append) {
const auto pos = file.find_last_of("/\\");
if (pos != std::string::npos) {
create_directory(file.substr(0, pos));
}
std::ofstream stream(file, std::ios::binary | std::ofstream::out |
(append ? std::ofstream::app : 0));
if (stream.is_open()) {
stream.write(data.data(), data.size());
stream.close();
return true;
}
return false;
}
std::string read_file(const std::string& file) {
std::string data;
read_file(file, &data);
return data;
}
bool read_file(const std::string& file, std::string* data) {
if (!data)
return false;
data->clear();
if (file_exists(file)) {
std::ifstream stream(file, std::ios::binary);
if (!stream.is_open())
return false;
stream.seekg(0, std::ios::end);
const std::streamsize size = stream.tellg();
stream.seekg(0, std::ios::beg);
if (size > -1) {
data->resize(static_cast<uint32_t>(size));
stream.read(data->data(), size);
stream.close();
return true;
}
}
return false;
}
size_t file_size(const std::string& file) {
if (file_exists(file)) {
std::ifstream stream(file, std::ios::binary);
if (stream.good()) {
stream.seekg(0, std::ios::end);
return static_cast<size_t>(stream.tellg());
}
}
return 0;
}
bool create_directory(const std::string& directory) {
return std::filesystem::create_directories(directory);
}
bool directory_exists(const std::string& directory) {
return std::filesystem::is_directory(directory);
}
bool directory_is_empty(const std::string& directory) {
return std::filesystem::is_empty(directory);
}
std::vector<std::string> list_files(const std::string& directory) {
std::vector<std::string> files;
for (auto& file : std::filesystem::directory_iterator(directory)) {
files.push_back(file.path().generic_string());
}
return files;
}
void copy_folder(const std::filesystem::path& src,
const std::filesystem::path& target) {
std::filesystem::copy(src, target,
std::filesystem::copy_options::overwrite_existing |
std::filesystem::copy_options::recursive);
}
} // namespace utils::io

22
src/common/utils/io.hpp Normal file
View File

@ -0,0 +1,22 @@
#pragma once
#include <string>
#include <vector>
#include <filesystem>
namespace utils::io {
bool remove_file(const std::string& file);
bool move_file(const std::string& src, const std::string& target);
bool file_exists(const std::string& file);
bool write_file(const std::string& file, const std::string& data,
bool append = false);
bool read_file(const std::string& file, std::string* data);
std::string read_file(const std::string& file);
size_t file_size(const std::string& file);
bool create_directory(const std::string& directory);
bool directory_exists(const std::string& directory);
bool directory_is_empty(const std::string& directory);
std::vector<std::string> list_files(const std::string& directory);
void copy_folder(const std::filesystem::path& src,
const std::filesystem::path& target);
} // namespace utils::io

BIN
tools/protoc.exe Normal file

Binary file not shown.