From 532a83fe91dfc74ae66573c7618c147ad693e3d9 Mon Sep 17 00:00:00 2001 From: Pax1601 Date: Mon, 11 Dec 2023 19:44:58 +0100 Subject: [PATCH] Added c++ configurator --- build.bat | 6 +- configurator/configurator.sln | 31 + configurator/configurator/configurator.cpp | 170 +++++ .../configurator/configurator.vcxproj | 143 +++++ .../configurator/configurator.vcxproj.filters | 30 + .../third-party/argparse/argparse.hpp | 586 ++++++++++++++++++ .../third-party/hash-library/LICENSE | 10 + .../third-party/hash-library/include/crc32.h | 69 +++ .../third-party/hash-library/include/hash.h | 28 + .../third-party/hash-library/include/hmac.h | 83 +++ .../third-party/hash-library/include/keccak.h | 81 +++ .../third-party/hash-library/include/md5.h | 78 +++ .../third-party/hash-library/include/sha1.h | 78 +++ .../third-party/hash-library/include/sha256.h | 78 +++ .../third-party/hash-library/include/sha3.h | 81 +++ .../third-party/hash-library/readme.md | 51 ++ .../third-party/hash-library/src/sha256.cpp | 411 ++++++++++++ 17 files changed, 2011 insertions(+), 3 deletions(-) create mode 100644 configurator/configurator.sln create mode 100644 configurator/configurator/configurator.cpp create mode 100644 configurator/configurator/configurator.vcxproj create mode 100644 configurator/configurator/configurator.vcxproj.filters create mode 100644 configurator/configurator/third-party/argparse/argparse.hpp create mode 100644 configurator/configurator/third-party/hash-library/LICENSE create mode 100644 configurator/configurator/third-party/hash-library/include/crc32.h create mode 100644 configurator/configurator/third-party/hash-library/include/hash.h create mode 100644 configurator/configurator/third-party/hash-library/include/hmac.h create mode 100644 configurator/configurator/third-party/hash-library/include/keccak.h create mode 100644 configurator/configurator/third-party/hash-library/include/md5.h create mode 100644 configurator/configurator/third-party/hash-library/include/sha1.h create mode 100644 configurator/configurator/third-party/hash-library/include/sha256.h create mode 100644 configurator/configurator/third-party/hash-library/include/sha3.h create mode 100644 configurator/configurator/third-party/hash-library/readme.md create mode 100644 configurator/configurator/third-party/hash-library/src/sha256.cpp diff --git a/build.bat b/build.bat index 7993230f..bf48753d 100644 --- a/build.bat +++ b/build.bat @@ -4,9 +4,9 @@ cd src msbuild olympus.sln /t:Rebuild /p:Configuration=Release cd .. -cd scripts/python/configurator -call build_configurator.bat -cd ../../.. +cd configurator +msbuild configurator.sln /t:Rebuild /p:Configuration=Release +cd .. cd client rmdir /s /q "hgt" diff --git a/configurator/configurator.sln b/configurator/configurator.sln new file mode 100644 index 00000000..19e67b0b --- /dev/null +++ b/configurator/configurator.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.3.32929.385 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "configurator", "configurator\configurator.vcxproj", "{FF484257-F6D1-43E9-B6BA-A892B86E8963}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {FF484257-F6D1-43E9-B6BA-A892B86E8963}.Debug|x64.ActiveCfg = Debug|x64 + {FF484257-F6D1-43E9-B6BA-A892B86E8963}.Debug|x64.Build.0 = Debug|x64 + {FF484257-F6D1-43E9-B6BA-A892B86E8963}.Debug|x86.ActiveCfg = Debug|Win32 + {FF484257-F6D1-43E9-B6BA-A892B86E8963}.Debug|x86.Build.0 = Debug|Win32 + {FF484257-F6D1-43E9-B6BA-A892B86E8963}.Release|x64.ActiveCfg = Release|x64 + {FF484257-F6D1-43E9-B6BA-A892B86E8963}.Release|x64.Build.0 = Release|x64 + {FF484257-F6D1-43E9-B6BA-A892B86E8963}.Release|x86.ActiveCfg = Release|Win32 + {FF484257-F6D1-43E9-B6BA-A892B86E8963}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {19612EF0-B177-4572-A304-ECE142C31BDC} + EndGlobalSection +EndGlobal diff --git a/configurator/configurator/configurator.cpp b/configurator/configurator/configurator.cpp new file mode 100644 index 00000000..539f77bc --- /dev/null +++ b/configurator/configurator/configurator.cpp @@ -0,0 +1,170 @@ +// configurator.cpp : This file contains the 'main' function. Program execution begins and ends there. +// + +#include +#include +#include +#include "argparse/argparse.hpp" +#include "sha256.h" +#include "windows.h" +#include +#include + + +using json = nlohmann::json; +using namespace std; + +struct MyArgs : public argparse::Args { + string& address = kwarg("a", "Address"); + int& clientPort = kwarg("c", "Client port"); + int& backendPort = kwarg("b", "Backend port"); + string& gameMasterPassword = kwarg("p", "Game master password"); + string& blueCommanderPassword = kwarg("bp", "Blue commander password"); + string& redCommanderPassword = kwarg("rp", "Red commander password"); +}; + +void SetStdinEcho(bool enable = true) +{ + HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE); + DWORD mode; + GetConsoleMode(hStdin, &mode); + + if (!enable) + mode &= ~ENABLE_ECHO_INPUT; + else + mode |= ENABLE_ECHO_INPUT; + + SetConsoleMode(hStdin, mode); +} + +int main(int argc, char* argv[]) +{ + cout << "DCS Olympus configurator v1.0.0" << endl << endl << endl; + + SHA256 sha256; + + std::ifstream f("olympus.json"); + json data = json::parse(f); + + string address = data["server"]["address"]; + int clientPort = data["client"]["port"]; + int backendPort = data["server"]["port"]; + string gameMasterPassword = data["authentication"]["gameMasterPassword"]; + string blueCommanderPassword = data["authentication"]["blueCommanderPassword"]; + string redCommanderPassword = data["authentication"]["redCommanderPassword"]; + + if (argc > 1) { + auto args = argparse::parse(argc, argv); + args.print(); // prints all variables + + address = args.address; + clientPort = args.clientPort; + backendPort = args.backendPort; + gameMasterPassword = sha256(args.gameMasterPassword); + blueCommanderPassword = sha256(args.blueCommanderPassword); + redCommanderPassword = sha256(args.redCommanderPassword); + + } else { + cout << "No arguments provided, running in interactive mode" << endl; + + SetStdinEcho(true); + + string newValue; + + cout << "Insert a new address or press Enter to keep current value: " << address << endl << ">"; + getline(cin, newValue); + if (newValue != "") + address = newValue; + + while (true) { + cout << "Insert a new client port or press Enter to keep current value: " << clientPort << endl << ">"; + getline(cin, newValue); + if (newValue != "") { + try { + clientPort = stoi(newValue); + break; + } + catch (...) { + cout << "Insert a valid integer number" << endl; + } + } + else { + break; + } + } + + while (true) { + cout << "Insert a new backend port or press Enter to keep current value: " << backendPort << endl << ">"; + getline(cin, newValue); + if (newValue != "") { + try { + backendPort = stoi(newValue); + break; + } + catch (...) { + cout << "Insert a valid integer number" << endl; + } + } + else { + break; + } + }; + + SetStdinEcho(false); + + while (true) { + cout << "Insert a new Game Master password" << endl << ">"; + getline(cin, newValue); + cout << endl; + if (newValue != "") { + gameMasterPassword = sha256(newValue); + break; + } + else { + cout << "Value can't be empty" << endl; + } + } + + while (true) { + cout << "Insert a new Blue Commander password" << endl << ">"; + getline(cin, newValue); + cout << endl; + if (newValue != "") { + blueCommanderPassword = sha256(newValue); + break; + } + else { + cout << "Value can't be empty" << endl; + } + } + + while (true) { + cout << "Insert a new Red Commander password" << endl << ">"; + getline(cin, newValue); + cout << endl; + if (newValue != "") { + redCommanderPassword = sha256(newValue); + break; + } + else { + cout << "Value can't be empty" << endl; + } + } + } + + data["server"]["address"] = address; + data["server"]["port"] = backendPort; + data["authentication"]["gameMasterPassword"] = gameMasterPassword; + data["authentication"]["blueCommanderPassword"] = blueCommanderPassword; + data["authentication"]["redCommanderPassword"] = redCommanderPassword; + data["client"]["port"] = clientPort; + + std::ofstream o("olympus.json"); + o << std::setw(4) << data << std::endl; + + cout << endl; + cout << "Configuration updated successfully, bye!" << endl; + + this_thread::sleep_for(chrono::milliseconds(2000)); +} + diff --git a/configurator/configurator/configurator.vcxproj b/configurator/configurator/configurator.vcxproj new file mode 100644 index 00000000..24eafd88 --- /dev/null +++ b/configurator/configurator/configurator.vcxproj @@ -0,0 +1,143 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {ff484257-f6d1-43e9-b6ba-a892b86e8963} + configurator + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpp20 + third-party\hash-library\include;third-party + + + Console + true + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpp20 + third-party\hash-library\include;third-party + + + Console + true + true + true + + + + + + + + + + + + + \ No newline at end of file diff --git a/configurator/configurator/configurator.vcxproj.filters b/configurator/configurator/configurator.vcxproj.filters new file mode 100644 index 00000000..b0635bd4 --- /dev/null +++ b/configurator/configurator/configurator.vcxproj.filters @@ -0,0 +1,30 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Source Files + + + + + Header Files + + + \ No newline at end of file diff --git a/configurator/configurator/third-party/argparse/argparse.hpp b/configurator/configurator/third-party/argparse/argparse.hpp new file mode 100644 index 00000000..97b67d4c --- /dev/null +++ b/configurator/configurator/third-party/argparse/argparse.hpp @@ -0,0 +1,586 @@ +#pragma once +// +// @author : Morris Franken +// https://github.com/morrisfranken/argparse +// +// Permission is hereby granted, free of charge, to any person or organization +// obtaining a copy of the software and accompanying documentation covered by +// this license (the "Software") to use, reproduce, display, distribute, +// execute, and transmit the Software, and to prepare derivative works of the +// Software, and to permit third-parties to whom the Software is furnished to +// do so, all subject to the following: +// +// The copyright notices in the Software and this entire statement, including +// the above license grant, this restriction and the following disclaimer, +// must be included in all copies of the Software, in whole or in part, and +// all derivative works of the Software, unless such copies or derivative +// works are solely in the form of machine-executable object code generated by +// a source language processor. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#include // for isdigit, tolower +#include +#include // for size_t, exit +#include // for max, transform, copy, min +#include // for operator<<, setw +#include // for operator<<, basic_ostream, endl, ostream +#include // for ostream_iterator +#include // for operator!=, map, _Rb_tree_iterator +#include // for allocator, shared_ptr, __shared_ptr_ac... +#include // for optional, nullopt +#include // for runtime_error, invalid_argument +#include // for getting program_name from path +#include // for string, operator+, basic_string, char_... +#include // for declval, false_type, true_type, is_enum +#include // for move, pair +#include // for vector + +#if __has_include() +#include // for enum_entries +#define HAS_MAGIC_ENUM +#endif + +#define ARGPARSE_VERSION 4 + +namespace argparse { + class Args; + using std::cout, std::cerr, std::endl, std::setw, std::size_t; + + template struct is_vector : public std::false_type {}; + template struct is_vector> : public std::true_type {}; + + template struct is_optional : public std::false_type {}; + template struct is_optional> : public std::true_type {}; + + template struct is_shared_ptr : public std::false_type {}; + template struct is_shared_ptr> : public std::true_type {}; + + template struct has_ostream_operator : std::false_type {}; + template struct has_ostream_operator() << std::declval()))> : std::true_type {}; + + inline std::string bold(const std::string& input_str) { +#ifdef _WIN32 + return input_str; // no bold for windows +#else + return "\033[1m" + input_str + "\033[0m"; +#endif + } + + template std::string toString(const T &v) { + if constexpr (has_ostream_operator::value) { + return static_cast((std::ostringstream() << std::boolalpha << v)).str(); // https://github.com/stan-dev/math/issues/590#issuecomment-550122627 + } else { + return "unknown"; + } + } + + std::vector inline split(const std::string &str) { + std::vector splits; + std::stringstream ss(str); + std::string key; + while (std::getline(ss, key, ',')) { + if (!key.empty() && key.back() == '\0') + key.pop_back(); // last variables contain a '\0', which is unexpected when comparing to raw string, e.g. value == "test" will fail when the last character is '\0'. Therefore we can remove it + splits.emplace_back(std::move(key)); + } + return splits; + } + + template std::string to_lower(const T &str_) { // both std::string and std::basic_string_view (for magic_enum) are using to_lower + std::string str(str_.size(), '\0'); + std::transform(str_.begin(), str_.end(), str.begin(), ::tolower); + return str; + } + + template inline T get(const std::string &v); + template<> inline std::string get(const std::string &v) { return v; } + template<> inline char get(const std::string &v) { return v.empty()? throw std::invalid_argument("empty string") : v.size() > 1? v.substr(0,2) == "0x"? (char)std::stoul(v, nullptr, 16) : (char)std::stoi(v) : v[0]; } + template<> inline int get(const std::string &v) { return std::stoi(v); } + template<> inline short get(const std::string &v) { return std::stoi(v); } + template<> inline long get(const std::string &v) { return std::stol(v); } + template<> inline bool get(const std::string &v) { return to_lower(v) == "true" || v == "1"; } + template<> inline float get(const std::string &v) { return std::stof(v); } + template<> inline double get(const std::string &v) { return std::stod(v); } + template<> inline unsigned char get(const std::string &v) { return get(v); } + template<> inline unsigned int get(const std::string &v) { return std::stoul(v); } + template<> inline unsigned short get(const std::string &v) { return std::stoul(v); } + template<> inline unsigned long get(const std::string &v) { return std::stoul(v); } + + template inline T get(const std::string &v) { // remaining types + if constexpr (is_vector::value) { + const std::vector splitted = split(v); + T res(splitted.size()); + if (!v.empty()) + std::transform (splitted.begin(), splitted.end(), res.begin(), get); + return res; + } else if constexpr (std::is_pointer::value) { + return new typename std::remove_pointer::type(get::type>(v)); + } else if constexpr (is_shared_ptr::value) { + return std::make_shared(get(v)); + } else if constexpr (is_optional::value) { + return get(v); + } else if constexpr (std::is_enum::value) { // case-insensitive enum conversion +#ifdef HAS_MAGIC_ENUM + constexpr auto& enum_entries = magic_enum::enum_entries(); + const std::string lower_str = to_lower(v); + for (const auto &[value, name] : enum_entries) { + if (to_lower(name) == lower_str) + return value; + } + std::string error = "enum is only accepting ["; + for (size_t i = 0; i < enum_entries.size(); i++) + error += (i==0? "" : ", ") + to_lower(enum_entries[i].second); + error += "]"; + throw std::runtime_error(error); +#else + throw std::runtime_error("Enum not supported, please install magic_enum (https://github.com/Neargye/magic_enum)"); +#endif + } else { + return T(v); + } + } + + struct ConvertBase { + virtual ~ConvertBase() = default; + virtual void convert(const std::string &v) = 0; + virtual void set_default(const std::unique_ptr &default_value, const std::string &default_string) = 0; + [[nodiscard]] virtual size_t get_type_id() const = 0; + [[nodiscard]] virtual std::string get_allowed_entries() const = 0; + }; + + template struct ConvertType : public ConvertBase { + T data; + ~ConvertType() override = default; + ConvertType() : ConvertBase() {}; + explicit ConvertType(const T &value) : ConvertBase(), data(value) {}; + + void convert(const std::string &v) override { + data = get(v); + } + + void set_default(const std::unique_ptr &default_value, const std::string &default_string) override { + if (this->get_type_id() == default_value->get_type_id()) // When the types do not match exactly. resort to string conversion + data = ((ConvertType*)(default_value.get()))->data; + else + data = get(default_string); + } + + [[nodiscard]] size_t get_type_id() const override { + return typeid(T).hash_code(); + } + + [[nodiscard]] std::string get_allowed_entries() const override { + std::stringstream ss; + +#ifdef HAS_MAGIC_ENUM + if constexpr (std::is_enum::value) { + for (const auto &[value, name] : magic_enum::enum_entries()) { + ss << to_lower(name) << ", "; + } + } +#endif + + return ss.str(); + } + }; + + struct Entry { + enum ARG_TYPE {ARG, KWARG, FLAG} type; + + Entry(ARG_TYPE type, const std::string& key, std::string help, std::optional implicit_value=std::nullopt) : + type(type), + keys_(split(key)), + help(std::move(help)), + implicit_value_(std::move(implicit_value)) { + } + + // Allow both string inputs and direct-type inputs. Where a string-input will be converted like it would when using the commandline, and the direct approach is to simply use the value provided. + template Entry &set_default(const T &default_value) { + this->default_str_ = toString(default_value); + if constexpr (!(std::is_array::value || std::is_same::type, char>::value)) { + data_default = std::make_unique>(default_value); + } + return *this; + } + + Entry &multi_argument() { + _is_multi_argument = true; + return *this; + } + + // Magically convert the value string to the requested type + template operator T&() { + // Automatically set the default to nullptr for pointer types and empty for optional types + if constexpr (is_optional::value || std::is_pointer::value || is_shared_ptr::value) { + if (!default_str_.has_value()) { + default_str_ = "none"; + if constexpr(is_optional::value) { + data_default = std::make_unique> (T{std::nullopt}); + } else { + data_default = std::make_unique> ((T) nullptr); + } + } + } + + datap = std::make_unique>(); + return ((ConvertType*)(datap.get()))->data; + } + + // Force an ambiguous error when not using a reference. + template operator T() {} // When you get here because you received an error, make sure all parameters of argparse are references (e.g. with `&`) + + private: + std::vector keys_; + std::string help; + std::optional value_; + std::optional implicit_value_; + std::optional default_str_; + std::string error; + std::unique_ptr datap; + std::unique_ptr data_default; + bool _is_multi_argument = false; + bool is_set_by_user = true; + + [[nodiscard]] std::string _get_keys() const { + std::stringstream ss; + for (size_t i = 0; i < keys_.size(); i++) + ss << (i? "," : "") << (type == ARG? "" : (keys_[i].size() > 1 ? "--" : "-")) + keys_[i]; + return ss.str(); + } + + void _convert(const std::string &value) { + try { + this->value_ = value; + datap->convert(value); + } catch (const std::invalid_argument &e) { + error = "Invalid argument, could not convert \"" + value + "\" for " + _get_keys() + " (" + help + ")"; + } catch (const std::runtime_error &e) { + error = "Invalid argument \"" + value + "\" for " + _get_keys() + " (" + help + "). Error: " + e.what(); + } + } + + void _apply_default() { + is_set_by_user = false; + if (data_default != nullptr) { + value_ = *default_str_; // for printing + datap->set_default(data_default, *default_str_); + } else if (default_str_.has_value()) { // in cases where a string is provided to the `set_default` function + _convert(default_str_.value()); + } else { + error = "Argument missing: " + _get_keys() + " (" + help + ")"; + } + } + + [[nodiscard]] std::string info() const { + const std::string allowed_entries = datap->get_allowed_entries(); + const std::string default_value = default_str_.has_value() ? "default: " + *default_str_ : "required"; + const std::string implicit_value = implicit_value_.has_value() ? "implicit: \"" + *implicit_value_ + "\", ": ""; + const std::string allowed_value = !allowed_entries.empty()? "allowed: <" + allowed_entries.substr(0, allowed_entries.size()-2) + ">, ": ""; + return " [" + allowed_value + implicit_value + default_value + "]"; + } + + friend class Args; + }; + + struct SubcommandEntry { + std::shared_ptr subargs; + std::string subcommand_name; + + explicit SubcommandEntry(std::string subcommand_name) : subcommand_name(std::move(subcommand_name)) {} + + template operator T &() { + static_assert(std::is_base_of_v, "Subcommand type must be a derivative of argparse::Args"); + + std::shared_ptr res = std::make_shared(); + res->program_name = subcommand_name; + subargs = res; + return *(T*)(subargs.get()); + } + + // Force an ambiguous error when not using a reference. + template operator T() {} // When you get here because you received an error, make sure all parameters of argparse are references (e.g. with `&`) + }; + + class Args { + private: + size_t _arg_idx = 0; + std::vector params; + std::vector> all_entries; + std::map> kwarg_entries; + std::vector> arg_entries; + std::map> subcommand_entries; + + public: + std::string program_name; + bool is_valid = false; + + virtual ~Args() = default; + + /* Add a positional argument, the order in which it is defined equals the order in which they are being read. + * help : Description of the variable + * + * Returns a reference to the Entry, which will collapse into the requested type in `Entry::operator T()` + */ + Entry &arg(const std::string &help) { + return arg("arg_" + std::to_string(_arg_idx), help); + } + + /* Add a *named* positional argument, the order in which it is defined equals the order in which they are being read. + * key : The name of the argument, otherwise arg_ will be used + * help : Description of the variable + * + * Returns a reference to the Entry, which will collapse into the requested type in `Entry::operator T()` + */ + Entry &arg(const std::string& key, const std::string &help) { + std::shared_ptr entry = std::make_shared(Entry::ARG, key, help); + // Increasing _arg_idx, so that arg2 will be arg_2, irregardless of whether it is preceded by other positional arguments + _arg_idx++; + arg_entries.emplace_back(entry); + all_entries.emplace_back(entry); + return *entry; + } + + /* Add a Key-Worded argument that takes a variable. + * key : A comma-separated string, e.g. "k,key", which denotes the short (-k) and long(--key) keys_ + * help : Description of the variable + * implicit_value : Implicit values are used when no value is provided. + * + * Returns a reference to the Entry, which will collapse into the requested type in `Entry::operator T()` + */ + Entry &kwarg(const std::string &key, const std::string &help, const std::optional& implicit_value=std::nullopt) { + std::shared_ptr entry = std::make_shared(Entry::KWARG, key, help, implicit_value); + all_entries.emplace_back(entry); + for (const std::string &k : entry->keys_) { + kwarg_entries[k] = entry; + } + return *entry; + } + + /* Add a flag which will be false by default. + * key : A comma-separated string, e.g. "k,key", which denotes the short (-k) and long(--key) keys_ + * help : Description of the variable + * + * Returns reference to Entry like kwarg + */ + Entry &flag(const std::string &key, const std::string &help) { + return kwarg(key, help, "true").set_default(false); + } + + /* Add a a subcommand + * command : name of the subcommand, e.g. 'commit', if you wish to implement a function like 'git commit' + * + * Returns a reference to the Entry, which will collapse into the requested type in `Entry::operator T()` + * Expected type *Must* be an std::shared_ptr of derivative of the argparse::Args class + */ + SubcommandEntry &subcommand(const std::string &command) { + std::shared_ptr entry = std::make_shared(command); + subcommand_entries[command] = entry; + return *entry; + } + + virtual void welcome() {} // Allow to overwrite the `welcome` function to add a welcome-message to the help output + virtual void help() { + welcome(); + cout << "Usage: " << program_name << " "; + for (const auto &entry : arg_entries) + cout << entry->keys_[0] << ' '; + cout << " [options...]"; + if (!subcommand_entries.empty()) { + cout << " [SUBCOMMAND: "; + for (const auto &[subcommand, subentry]: subcommand_entries) { + cout << subcommand << ", "; + } + cout << "]"; + } + cout << endl; + for (const auto &entry : arg_entries) { + cout << setw(17) << entry->keys_[0] << " : " << entry->help << entry->info() << endl; + } + + cout << endl << "Options:" << endl; + for (const auto &entry : all_entries) { + if (entry->type != Entry::ARG) { + cout << setw(17) << entry->_get_keys() << " : " << entry->help << entry->info() << endl; + } + } + + for (const auto &[subcommand, subentry] : subcommand_entries) { + cout << endl << endl << bold("Subcommand: ") << bold(subcommand) << endl; + subentry->subargs->help(); + } + } + + void validate(const bool &raise_on_error) { + for (const auto &entry : all_entries) { + if (!entry->error.empty()) { + if (raise_on_error) { + throw std::runtime_error(entry->error); + } else { + std::cerr << entry->error << std::endl; + exit(-1); + } + } + } + } + + /* parse all parameters and also check for the help_flag which was set in this constructor + * Upon error, it will print the error and exit immediately if validation_action is ValidationAction::EXIT_ON_ERROR + */ + void parse(int argc, const char* const *argv, const bool &raise_on_error) { + auto parse_subcommands = [&]() -> int { + for (int i = 1; i < argc; i++) { + for (auto &[subcommand, subentry] : subcommand_entries) { + if (subcommand == argv[i]) { + subentry->subargs->parse(argc - i, argv + i, raise_on_error); + return i; + } + } + } + return argc; + }; + argc = parse_subcommands(); // argc_ is the number of arguments that should be parsed after the subcommand has finished parsing + + program_name = std::filesystem::path(argv[0]).stem().string(); + params = std::vector(argv + 1, argv + argc); + + bool& _help = flag("?,help", "print help"); + + auto is_value = [&](const size_t &i) -> bool { + return params.size() > i && (params[i][0] != '-' || (params[i].size() > 1 && std::isdigit(params[i][1]))); // check for number to not accidentally mark negative numbers as non-parameter + }; + auto parse_param = [&](size_t &i, const std::string &key, const bool is_short, const std::optional &equal_value=std::nullopt) { + auto itt = kwarg_entries.find(key); + if (itt != kwarg_entries.end()) { + auto &entry = itt->second; + if (equal_value.has_value()) { + entry->_convert(equal_value.value()); + } else if (entry->implicit_value_.has_value()) { + entry->_convert(*entry->implicit_value_); + } else if (!is_short) { // short values are not allowed to look ahead for the next parameter + if (is_value(i + 1)) { + std::string value = params[++i]; + if (entry->_is_multi_argument) { + while (is_value(i + 1)) + value += "," + params[++i]; + } + entry->_convert(value); + } else if (entry->_is_multi_argument) { + entry->_convert(""); // for multiargument parameters, return an empty vector when not passing any more values + } else { + entry->error = "No value provided for: " + key; + } + } else { + entry->error = "No value provided for: " + key; + } + } else { + cerr << "unrecognised commandline argument: " << key << endl; + } + }; + auto add_param = [&](size_t &i, const size_t &start) { + size_t eq_idx = params[i].find('='); // check if value was passed using the '=' sign + if (eq_idx != std::string::npos) { // key/value from = notation + std::string key = params[i].substr(start, eq_idx - start); + std::string value = params[i].substr(eq_idx + 1); + parse_param(i, key, false, value); + } else { + std::string key = std::string(params[i].substr(start)); + parse_param(i, key, false); + } + }; + + std::vector arguments_flat; + for (size_t i = 0; i < params.size(); i++) { + if (!is_value(i)) { + if (params[i].size() > 1 && params[i][1] == '-') { // long -- + add_param(i, 2); + } else { // short - + const size_t j_end = std::min(params[i].size(), params[i].find('=')) - 1; + for (size_t j = 1; j < j_end; j++) { // add possible other flags + const std::string key = std::string(1, params[i][j]); + parse_param(i, key, true); + } + add_param(i, j_end); + } + } else { + arguments_flat.emplace_back(params[i]); + } + } + + // Parse all the positional arguments, making sure multi_argument positional arguments are processed last to enable arguments afterwards + size_t arg_i = 0; + for (; arg_i < arg_entries.size() && !arg_entries[arg_i]->_is_multi_argument; arg_i++) { // iterate over positional arguments until a multi-argument is found + if (arg_i < arguments_flat.size()) + arg_entries[arg_i]->_convert(arguments_flat[arg_i]); + } + size_t arg_j = 1; + for (size_t j_end = arg_entries.size() - arg_i; arg_j <= j_end; arg_j++) { // iterate from back to front, to ensure non-multi-arguments in the front and back are given preference + size_t flat_idx = arguments_flat.size() - arg_j; + if (flat_idx < arguments_flat.size() && flat_idx >= arg_i) { + if (arg_entries[arg_entries.size() - arg_j]->_is_multi_argument) { + std::stringstream s; // Combine multiple arguments into 1 comma-separated string for parsing + copy(&arguments_flat[arg_i],&arguments_flat[flat_idx] + 1, std::ostream_iterator(s,",")); + std::string value = s.str(); + value.back() = '\0'; // remove trailing ',' + arg_entries[arg_i]->_convert(value); + } else { + arg_entries[arg_entries.size() - arg_j]->_convert(arguments_flat[flat_idx]); + } + } + } + + // try to apply default values for arguments which have not been set + for (const auto &entry : all_entries) { + if (!entry->value_.has_value()) { + entry->_apply_default(); + } + } + + if (_help) { + help(); + exit(0); + } + + validate(raise_on_error); + is_valid = true; + } + + void print() const { + for (const auto &entry : all_entries) { + std::string snip = entry->type == Entry::ARG ? "(" + (entry->help.size() > 10 ? entry->help.substr(0, 7) + "..." : entry->help) + ")" : ""; + cout << setw(21) << entry->_get_keys() + snip << " : " << (entry->is_set_by_user? bold(entry->value_.value_or("null")) : entry->value_.value_or("null")) << endl; + } + + for (const auto &[subcommand, subentry] : subcommand_entries) { + if (subentry->subargs->is_valid) { + cout << endl << "--- Subcommand: " << subcommand << endl; + subentry->subargs->print(); + } + } + } + + virtual int run() {return 0;} // For automatically running subcommands + int run_subcommands() { + for (const auto &[subcommand, subentry] : subcommand_entries) { + if (subentry->subargs->is_valid) { + return subentry->subargs->run(); + } + } + + std::cerr << "No subcommand provided" << std::endl; + help(); + return -1; + } + }; + + template T parse(int argc, const char* const *argv, const bool &raise_on_error=false) { + T args = T(); + args.parse(argc, argv, raise_on_error); + return args; + } +} diff --git a/configurator/configurator/third-party/hash-library/LICENSE b/configurator/configurator/third-party/hash-library/LICENSE new file mode 100644 index 00000000..e540da66 --- /dev/null +++ b/configurator/configurator/third-party/hash-library/LICENSE @@ -0,0 +1,10 @@ +zlib License + +Copyright (c) 2014,2015 Stephan Brumme + +This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. + If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. diff --git a/configurator/configurator/third-party/hash-library/include/crc32.h b/configurator/configurator/third-party/hash-library/include/crc32.h new file mode 100644 index 00000000..c8fc77b2 --- /dev/null +++ b/configurator/configurator/third-party/hash-library/include/crc32.h @@ -0,0 +1,69 @@ +// ////////////////////////////////////////////////////////// +// crc32.h +// Copyright (c) 2014,2015 Stephan Brumme. All rights reserved. +// see http://create.stephan-brumme.com/disclaimer.html +// + +#pragma once + +//#include "hash.h" +#include + +// define fixed size integer types +#ifdef _MSC_VER +// Windows +typedef unsigned __int8 uint8_t; +typedef unsigned __int32 uint32_t; +#else +// GCC +#include +#endif + + +/// compute CRC32 hash, based on Intel's Slicing-by-8 algorithm +/** Usage: + CRC32 crc32; + std::string myHash = crc32("Hello World"); // std::string + std::string myHash2 = crc32("How are you", 11); // arbitrary data, 11 bytes + + // or in a streaming fashion: + + CRC32 crc32; + while (more data available) + crc32.add(pointer to fresh data, number of new bytes); + std::string myHash3 = crc32.getHash(); + + Note: + You can find code for the faster Slicing-by-16 algorithm on my website, too: + http://create.stephan-brumme.com/crc32/ + Its unrolled version is about twice as fast but its look-up table doubled in size as well. + */ +class CRC32 //: public Hash +{ +public: + /// hash is 4 bytes long + enum { HashBytes = 4 }; + + /// same as reset() + CRC32(); + + /// compute CRC32 of a memory block + std::string operator()(const void* data, size_t numBytes); + /// compute CRC32 of a string, excluding final zero + std::string operator()(const std::string& text); + + /// add arbitrary number of bytes + void add(const void* data, size_t numBytes); + + /// return latest hash as 8 hex characters + std::string getHash(); + /// return latest hash as bytes + void getHash(unsigned char buffer[HashBytes]); + + /// restart + void reset(); + +private: + /// hash + uint32_t m_hash; +}; diff --git a/configurator/configurator/third-party/hash-library/include/hash.h b/configurator/configurator/third-party/hash-library/include/hash.h new file mode 100644 index 00000000..6c6f2eaa --- /dev/null +++ b/configurator/configurator/third-party/hash-library/include/hash.h @@ -0,0 +1,28 @@ +// ////////////////////////////////////////////////////////// +// hash.h +// Copyright (c) 2014,2015 Stephan Brumme. All rights reserved. +// see http://create.stephan-brumme.com/disclaimer.html +// + +#pragma once + +#include + +/// abstract base class +class Hash +{ +public: + /// compute hash of a memory block + virtual std::string operator()(const void* data, size_t numBytes) = 0; + /// compute hash of a string, excluding final zero + virtual std::string operator()(const std::string& text) = 0; + + /// add arbitrary number of bytes + virtual void add(const void* data, size_t numBytes) = 0; + + /// return latest hash as hex characters + virtual std::string getHash() = 0; + + /// restart + virtual void reset() = 0; +}; diff --git a/configurator/configurator/third-party/hash-library/include/hmac.h b/configurator/configurator/third-party/hash-library/include/hmac.h new file mode 100644 index 00000000..442a1f39 --- /dev/null +++ b/configurator/configurator/third-party/hash-library/include/hmac.h @@ -0,0 +1,83 @@ +// ////////////////////////////////////////////////////////// +// hmac.h +// Copyright (c) 2015 Stephan Brumme. All rights reserved. +// see http://create.stephan-brumme.com/disclaimer.html +// + +#pragma once + +// based on http://tools.ietf.org/html/rfc2104 +// see also http://en.wikipedia.org/wiki/Hash-based_message_authentication_code + +/** Usage: + std::string msg = "The quick brown fox jumps over the lazy dog"; + std::string key = "key"; + std::string md5hmac = hmac< MD5 >(msg, key); + std::string sha1hmac = hmac< SHA1 >(msg, key); + std::string sha2hmac = hmac(msg, key); + + Note: + To keep my code simple, HMAC computation currently needs the whole message at once. + This is in contrast to the hashes MD5, SHA1, etc. where an add() method is available + for incremental computation. + You can use any hash for HMAC as long as it provides: + - constant HashMethod::BlockSize (typically 64) + - constant HashMethod::HashBytes (length of hash in bytes, e.g. 20 for SHA1) + - HashMethod::add(buffer, bufferSize) + - HashMethod::getHash(unsigned char buffer[HashMethod::BlockSize]) + */ + +#include +#include // memcpy + +/// compute HMAC hash of data and key using MD5, SHA1 or SHA256 +template +std::string hmac(const void* data, size_t numDataBytes, const void* key, size_t numKeyBytes) +{ + // initialize key with zeros + unsigned char usedKey[HashMethod::BlockSize] = {0}; + + // adjust length of key: must contain exactly blockSize bytes + if (numKeyBytes <= HashMethod::BlockSize) + { + // copy key + memcpy(usedKey, key, numKeyBytes); + } + else + { + // shorten key: usedKey = hashed(key) + HashMethod keyHasher; + keyHasher.add(key, numKeyBytes); + keyHasher.getHash(usedKey); + } + + // create initial XOR padding + for (size_t i = 0; i < HashMethod::BlockSize; i++) + usedKey[i] ^= 0x36; + + // inside = hash((usedKey ^ 0x36) + data) + unsigned char inside[HashMethod::HashBytes]; + HashMethod insideHasher; + insideHasher.add(usedKey, HashMethod::BlockSize); + insideHasher.add(data, numDataBytes); + insideHasher.getHash(inside); + + // undo usedKey's previous 0x36 XORing and apply a XOR by 0x5C + for (size_t i = 0; i < HashMethod::BlockSize; i++) + usedKey[i] ^= 0x5C ^ 0x36; + + // hash((usedKey ^ 0x5C) + hash((usedKey ^ 0x36) + data)) + HashMethod finalHasher; + finalHasher.add(usedKey, HashMethod::BlockSize); + finalHasher.add(inside, HashMethod::HashBytes); + + return finalHasher.getHash(); +} + + +/// convenience function for std::string +template +std::string hmac(const std::string& data, const std::string& key) +{ + return hmac(data.c_str(), data.size(), key.c_str(), key.size()); +} diff --git a/configurator/configurator/third-party/hash-library/include/keccak.h b/configurator/configurator/third-party/hash-library/include/keccak.h new file mode 100644 index 00000000..8b87a259 --- /dev/null +++ b/configurator/configurator/third-party/hash-library/include/keccak.h @@ -0,0 +1,81 @@ +// ////////////////////////////////////////////////////////// +// keccak.h +// Copyright (c) 2014,2015 Stephan Brumme. All rights reserved. +// see http://create.stephan-brumme.com/disclaimer.html +// + +#pragma once + +//#include "hash.h" +#include + +// define fixed size integer types +#ifdef _MSC_VER +// Windows +typedef unsigned __int8 uint8_t; +typedef unsigned __int64 uint64_t; +#else +// GCC +#include +#endif + + +/// compute Keccak hash (designated SHA3) +/** Usage: + Keccak keccak; + std::string myHash = keccak("Hello World"); // std::string + std::string myHash2 = keccak("How are you", 11); // arbitrary data, 11 bytes + + // or in a streaming fashion: + + Keccak keccak; + while (more data available) + keccak.add(pointer to fresh data, number of new bytes); + std::string myHash3 = keccak.getHash(); + */ +class Keccak //: public Hash +{ +public: + /// algorithm variants + enum Bits { Keccak224 = 224, Keccak256 = 256, Keccak384 = 384, Keccak512 = 512 }; + + /// same as reset() + explicit Keccak(Bits bits = Keccak256); + + /// compute hash of a memory block + std::string operator()(const void* data, size_t numBytes); + /// compute hash of a string, excluding final zero + std::string operator()(const std::string& text); + + /// add arbitrary number of bytes + void add(const void* data, size_t numBytes); + + /// return latest hash as hex characters + std::string getHash(); + + /// restart + void reset(); + +private: + /// process a full block + void processBlock(const void* data); + /// process everything left in the internal buffer + void processBuffer(); + + /// 1600 bits, stored as 25x64 bit, BlockSize is no more than 1152 bits (Keccak224) + enum { StateSize = 1600 / (8 * 8), + MaxBlockSize = 200 - 2 * (224 / 8) }; + + /// hash + uint64_t m_hash[StateSize]; + /// size of processed data in bytes + uint64_t m_numBytes; + /// block size (less or equal to MaxBlockSize) + size_t m_blockSize; + /// valid bytes in m_buffer + size_t m_bufferSize; + /// bytes not processed yet + uint8_t m_buffer[MaxBlockSize]; + /// variant + Bits m_bits; +}; diff --git a/configurator/configurator/third-party/hash-library/include/md5.h b/configurator/configurator/third-party/hash-library/include/md5.h new file mode 100644 index 00000000..f53e2d9e --- /dev/null +++ b/configurator/configurator/third-party/hash-library/include/md5.h @@ -0,0 +1,78 @@ +// ////////////////////////////////////////////////////////// +// md5.h +// Copyright (c) 2014 Stephan Brumme. All rights reserved. +// see http://create.stephan-brumme.com/disclaimer.html +// + +#pragma once + +//#include "hash.h" +#include + +// define fixed size integer types +#ifdef _MSC_VER +// Windows +typedef unsigned __int8 uint8_t; +typedef unsigned __int32 uint32_t; +typedef unsigned __int64 uint64_t; +#else +// GCC +#include +#endif + + +/// compute MD5 hash +/** Usage: + MD5 md5; + std::string myHash = md5("Hello World"); // std::string + std::string myHash2 = md5("How are you", 11); // arbitrary data, 11 bytes + + // or in a streaming fashion: + + MD5 md5; + while (more data available) + md5.add(pointer to fresh data, number of new bytes); + std::string myHash3 = md5.getHash(); + */ +class MD5 //: public Hash +{ +public: + /// split into 64 byte blocks (=> 512 bits), hash is 16 bytes long + enum { BlockSize = 512 / 8, HashBytes = 16 }; + + /// same as reset() + MD5(); + + /// compute MD5 of a memory block + std::string operator()(const void* data, size_t numBytes); + /// compute MD5 of a string, excluding final zero + std::string operator()(const std::string& text); + + /// add arbitrary number of bytes + void add(const void* data, size_t numBytes); + + /// return latest hash as 32 hex characters + std::string getHash(); + /// return latest hash as bytes + void getHash(unsigned char buffer[HashBytes]); + + /// restart + void reset(); + +private: + /// process 64 bytes + void processBlock(const void* data); + /// process everything left in the internal buffer + void processBuffer(); + + /// size of processed data in bytes + uint64_t m_numBytes; + /// valid bytes in m_buffer + size_t m_bufferSize; + /// bytes not processed yet + uint8_t m_buffer[BlockSize]; + + enum { HashValues = HashBytes / 4 }; + /// hash, stored as integers + uint32_t m_hash[HashValues]; +}; diff --git a/configurator/configurator/third-party/hash-library/include/sha1.h b/configurator/configurator/third-party/hash-library/include/sha1.h new file mode 100644 index 00000000..c8eb00d9 --- /dev/null +++ b/configurator/configurator/third-party/hash-library/include/sha1.h @@ -0,0 +1,78 @@ +// ////////////////////////////////////////////////////////// +// sha1.h +// Copyright (c) 2014,2015 Stephan Brumme. All rights reserved. +// see http://create.stephan-brumme.com/disclaimer.html +// + +#pragma once + +//#include "hash.h" +#include + +// define fixed size integer types +#ifdef _MSC_VER +// Windows +typedef unsigned __int8 uint8_t; +typedef unsigned __int32 uint32_t; +typedef unsigned __int64 uint64_t; +#else +// GCC +#include +#endif + + +/// compute SHA1 hash +/** Usage: + SHA1 sha1; + std::string myHash = sha1("Hello World"); // std::string + std::string myHash2 = sha1("How are you", 11); // arbitrary data, 11 bytes + + // or in a streaming fashion: + + SHA1 sha1; + while (more data available) + sha1.add(pointer to fresh data, number of new bytes); + std::string myHash3 = sha1.getHash(); + */ +class SHA1 //: public Hash +{ +public: + /// split into 64 byte blocks (=> 512 bits), hash is 20 bytes long + enum { BlockSize = 512 / 8, HashBytes = 20 }; + + /// same as reset() + SHA1(); + + /// compute SHA1 of a memory block + std::string operator()(const void* data, size_t numBytes); + /// compute SHA1 of a string, excluding final zero + std::string operator()(const std::string& text); + + /// add arbitrary number of bytes + void add(const void* data, size_t numBytes); + + /// return latest hash as 40 hex characters + std::string getHash(); + /// return latest hash as bytes + void getHash(unsigned char buffer[HashBytes]); + + /// restart + void reset(); + +private: + /// process 64 bytes + void processBlock(const void* data); + /// process everything left in the internal buffer + void processBuffer(); + + /// size of processed data in bytes + uint64_t m_numBytes; + /// valid bytes in m_buffer + size_t m_bufferSize; + /// bytes not processed yet + uint8_t m_buffer[BlockSize]; + + enum { HashValues = HashBytes / 4 }; + /// hash, stored as integers + uint32_t m_hash[HashValues]; +}; diff --git a/configurator/configurator/third-party/hash-library/include/sha256.h b/configurator/configurator/third-party/hash-library/include/sha256.h new file mode 100644 index 00000000..aeaf314f --- /dev/null +++ b/configurator/configurator/third-party/hash-library/include/sha256.h @@ -0,0 +1,78 @@ +// ////////////////////////////////////////////////////////// +// sha256.h +// Copyright (c) 2014,2015 Stephan Brumme. All rights reserved. +// see http://create.stephan-brumme.com/disclaimer.html +// + +#pragma once + +//#include "hash.h" +#include + +// define fixed size integer types +#ifdef _MSC_VER +// Windows +typedef unsigned __int8 uint8_t; +typedef unsigned __int32 uint32_t; +typedef unsigned __int64 uint64_t; +#else +// GCC +#include +#endif + + +/// compute SHA256 hash +/** Usage: + SHA256 sha256; + std::string myHash = sha256("Hello World"); // std::string + std::string myHash2 = sha256("How are you", 11); // arbitrary data, 11 bytes + + // or in a streaming fashion: + + SHA256 sha256; + while (more data available) + sha256.add(pointer to fresh data, number of new bytes); + std::string myHash3 = sha256.getHash(); + */ +class SHA256 //: public Hash +{ +public: + /// split into 64 byte blocks (=> 512 bits), hash is 32 bytes long + enum { BlockSize = 512 / 8, HashBytes = 32 }; + + /// same as reset() + SHA256(); + + /// compute SHA256 of a memory block + std::string operator()(const void* data, size_t numBytes); + /// compute SHA256 of a string, excluding final zero + std::string operator()(const std::string& text); + + /// add arbitrary number of bytes + void add(const void* data, size_t numBytes); + + /// return latest hash as 64 hex characters + std::string getHash(); + /// return latest hash as bytes + void getHash(unsigned char buffer[HashBytes]); + + /// restart + void reset(); + +private: + /// process 64 bytes + void processBlock(const void* data); + /// process everything left in the internal buffer + void processBuffer(); + + /// size of processed data in bytes + uint64_t m_numBytes; + /// valid bytes in m_buffer + size_t m_bufferSize; + /// bytes not processed yet + uint8_t m_buffer[BlockSize]; + + enum { HashValues = HashBytes / 4 }; + /// hash, stored as integers + uint32_t m_hash[HashValues]; +}; diff --git a/configurator/configurator/third-party/hash-library/include/sha3.h b/configurator/configurator/third-party/hash-library/include/sha3.h new file mode 100644 index 00000000..31c0bc44 --- /dev/null +++ b/configurator/configurator/third-party/hash-library/include/sha3.h @@ -0,0 +1,81 @@ +// ////////////////////////////////////////////////////////// +// sha3.h +// Copyright (c) 2014,2015 Stephan Brumme. All rights reserved. +// see http://create.stephan-brumme.com/disclaimer.html +// + +#pragma once + +//#include "hash.h" +#include + +// define fixed size integer types +#ifdef _MSC_VER +// Windows +typedef unsigned __int8 uint8_t; +typedef unsigned __int64 uint64_t; +#else +// GCC +#include +#endif + + +/// compute SHA3 hash +/** Usage: + SHA3 sha3; + std::string myHash = sha3("Hello World"); // std::string + std::string myHash2 = sha3("How are you", 11); // arbitrary data, 11 bytes + + // or in a streaming fashion: + + SHA3 sha3; + while (more data available) + sha3.add(pointer to fresh data, number of new bytes); + std::string myHash3 = sha3.getHash(); + */ +class SHA3 //: public Hash +{ +public: + /// algorithm variants + enum Bits { Bits224 = 224, Bits256 = 256, Bits384 = 384, Bits512 = 512 }; + + /// same as reset() + explicit SHA3(Bits bits = Bits256); + + /// compute hash of a memory block + std::string operator()(const void* data, size_t numBytes); + /// compute hash of a string, excluding final zero + std::string operator()(const std::string& text); + + /// add arbitrary number of bytes + void add(const void* data, size_t numBytes); + + /// return latest hash as hex characters + std::string getHash(); + + /// restart + void reset(); + +private: + /// process a full block + void processBlock(const void* data); + /// process everything left in the internal buffer + void processBuffer(); + + /// 1600 bits, stored as 25x64 bit, BlockSize is no more than 1152 bits (Keccak224) + enum { StateSize = 1600 / (8 * 8), + MaxBlockSize = 200 - 2 * (224 / 8) }; + + /// hash + uint64_t m_hash[StateSize]; + /// size of processed data in bytes + uint64_t m_numBytes; + /// block size (less or equal to MaxBlockSize) + size_t m_blockSize; + /// valid bytes in m_buffer + size_t m_bufferSize; + /// bytes not processed yet + uint8_t m_buffer[MaxBlockSize]; + /// variant + Bits m_bits; +}; diff --git a/configurator/configurator/third-party/hash-library/readme.md b/configurator/configurator/third-party/hash-library/readme.md new file mode 100644 index 00000000..8be9d01b --- /dev/null +++ b/configurator/configurator/third-party/hash-library/readme.md @@ -0,0 +1,51 @@ +# Portable C++ Hashing Library + +This is a mirror of my library hosted at https://create.stephan-brumme.com/hash-library/ + +In a nutshell: + +- computes CRC32, MD5, SHA1 and SHA256 (most common member of the SHA2 functions), Keccak and its SHA3 sibling +- optional HMAC (keyed-hash message authentication code) +- no external dependencies, small code size +- can work chunk-wise (for example when reading streams block-by-block) +- portable: supports Windows and Linux, tested on Little Endian and Big Endian CPUs +- roughly as fast as Linux core hashing functions +- open source, zlib license + +You can find code examples, benchmarks and much more on my website https://create.stephan-brumme.com/hash-library/ + +# How to use + +This example computes SHA256 hashes but the API is more or less identical for all hash algorithms: + +``` cpp +// SHA2 test program +#include "sha256.h" +#include // for std::cout only, not needed for hashing library + +int main(int, char**) +{ + // create a new hashing object + SHA256 sha256; + + // hashing an std::string + std::cout << sha256("Hello World") << std::endl; + // => a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e + + // hashing a buffer of bytes + const char* buffer = "How are you"; + std::cout << sha256(buffer, 11) << std::endl; + // => 9c7d5b046878838da72e40ceb3179580958df544b240869b80d0275cc07209cc + + // or in a streaming fashion (re-use "How are you") + SHA256 sha256stream; + const char* url = "create.stephan-brumme.com"; // 25 bytes + int step = 5; + for (int i = 0; i < 25; i += step) + sha256stream.add(url + i, step); // add five bytes at a time + std::cout << sha256stream.getHash() << std::endl; + // => 82aa771f1183c52f973c798c9243a1c73833ea40961c73e55e12430ec77b69f6 + + return 0; +} +``` diff --git a/configurator/configurator/third-party/hash-library/src/sha256.cpp b/configurator/configurator/third-party/hash-library/src/sha256.cpp new file mode 100644 index 00000000..0c6138cd --- /dev/null +++ b/configurator/configurator/third-party/hash-library/src/sha256.cpp @@ -0,0 +1,411 @@ +// ////////////////////////////////////////////////////////// +// sha256.cpp +// Copyright (c) 2014,2015 Stephan Brumme. All rights reserved. +// see http://create.stephan-brumme.com/disclaimer.html +// + +#include "sha256.h" + +// big endian architectures need #define __BYTE_ORDER __BIG_ENDIAN +#ifndef _MSC_VER +#include +#endif + + +/// same as reset() +SHA256::SHA256() +{ + reset(); +} + + +/// restart +void SHA256::reset() +{ + m_numBytes = 0; + m_bufferSize = 0; + + // according to RFC 1321 + m_hash[0] = 0x6a09e667; + m_hash[1] = 0xbb67ae85; + m_hash[2] = 0x3c6ef372; + m_hash[3] = 0xa54ff53a; + m_hash[4] = 0x510e527f; + m_hash[5] = 0x9b05688c; + m_hash[6] = 0x1f83d9ab; + m_hash[7] = 0x5be0cd19; +} + + +namespace +{ + inline uint32_t rotate(uint32_t a, uint32_t c) + { + return (a >> c) | (a << (32 - c)); + } + + inline uint32_t swap(uint32_t x) + { +#if defined(__GNUC__) || defined(__clang__) + return __builtin_bswap32(x); +#endif +#ifdef MSC_VER + return _byteswap_ulong(x); +#endif + + return (x >> 24) | + ((x >> 8) & 0x0000FF00) | + ((x << 8) & 0x00FF0000) | + (x << 24); + } + + // mix functions for processBlock() + inline uint32_t f1(uint32_t e, uint32_t f, uint32_t g) + { + uint32_t term1 = rotate(e, 6) ^ rotate(e, 11) ^ rotate(e, 25); + uint32_t term2 = (e & f) ^ (~e & g); //(g ^ (e & (f ^ g))) + return term1 + term2; + } + + inline uint32_t f2(uint32_t a, uint32_t b, uint32_t c) + { + uint32_t term1 = rotate(a, 2) ^ rotate(a, 13) ^ rotate(a, 22); + uint32_t term2 = ((a | b) & c) | (a & b); //(a & (b ^ c)) ^ (b & c); + return term1 + term2; + } +} + + +/// process 64 bytes +void SHA256::processBlock(const void* data) +{ + // get last hash + uint32_t a = m_hash[0]; + uint32_t b = m_hash[1]; + uint32_t c = m_hash[2]; + uint32_t d = m_hash[3]; + uint32_t e = m_hash[4]; + uint32_t f = m_hash[5]; + uint32_t g = m_hash[6]; + uint32_t h = m_hash[7]; + + // data represented as 16x 32-bit words + const uint32_t* input = (uint32_t*) data; + // convert to big endian + uint32_t words[64]; + int i; + for (i = 0; i < 16; i++) +#if defined(__BYTE_ORDER) && (__BYTE_ORDER != 0) && (__BYTE_ORDER == __BIG_ENDIAN) + words[i] = input[i]; +#else + words[i] = swap(input[i]); +#endif + + uint32_t x,y; // temporaries + + // first round + x = h + f1(e,f,g) + 0x428a2f98 + words[ 0]; y = f2(a,b,c); d += x; h = x + y; + x = g + f1(d,e,f) + 0x71374491 + words[ 1]; y = f2(h,a,b); c += x; g = x + y; + x = f + f1(c,d,e) + 0xb5c0fbcf + words[ 2]; y = f2(g,h,a); b += x; f = x + y; + x = e + f1(b,c,d) + 0xe9b5dba5 + words[ 3]; y = f2(f,g,h); a += x; e = x + y; + x = d + f1(a,b,c) + 0x3956c25b + words[ 4]; y = f2(e,f,g); h += x; d = x + y; + x = c + f1(h,a,b) + 0x59f111f1 + words[ 5]; y = f2(d,e,f); g += x; c = x + y; + x = b + f1(g,h,a) + 0x923f82a4 + words[ 6]; y = f2(c,d,e); f += x; b = x + y; + x = a + f1(f,g,h) + 0xab1c5ed5 + words[ 7]; y = f2(b,c,d); e += x; a = x + y; + + // secound round + x = h + f1(e,f,g) + 0xd807aa98 + words[ 8]; y = f2(a,b,c); d += x; h = x + y; + x = g + f1(d,e,f) + 0x12835b01 + words[ 9]; y = f2(h,a,b); c += x; g = x + y; + x = f + f1(c,d,e) + 0x243185be + words[10]; y = f2(g,h,a); b += x; f = x + y; + x = e + f1(b,c,d) + 0x550c7dc3 + words[11]; y = f2(f,g,h); a += x; e = x + y; + x = d + f1(a,b,c) + 0x72be5d74 + words[12]; y = f2(e,f,g); h += x; d = x + y; + x = c + f1(h,a,b) + 0x80deb1fe + words[13]; y = f2(d,e,f); g += x; c = x + y; + x = b + f1(g,h,a) + 0x9bdc06a7 + words[14]; y = f2(c,d,e); f += x; b = x + y; + x = a + f1(f,g,h) + 0xc19bf174 + words[15]; y = f2(b,c,d); e += x; a = x + y; + + // extend to 24 words + for (; i < 24; i++) + words[i] = words[i-16] + + (rotate(words[i-15], 7) ^ rotate(words[i-15], 18) ^ (words[i-15] >> 3)) + + words[i-7] + + (rotate(words[i- 2], 17) ^ rotate(words[i- 2], 19) ^ (words[i- 2] >> 10)); + + // third round + x = h + f1(e,f,g) + 0xe49b69c1 + words[16]; y = f2(a,b,c); d += x; h = x + y; + x = g + f1(d,e,f) + 0xefbe4786 + words[17]; y = f2(h,a,b); c += x; g = x + y; + x = f + f1(c,d,e) + 0x0fc19dc6 + words[18]; y = f2(g,h,a); b += x; f = x + y; + x = e + f1(b,c,d) + 0x240ca1cc + words[19]; y = f2(f,g,h); a += x; e = x + y; + x = d + f1(a,b,c) + 0x2de92c6f + words[20]; y = f2(e,f,g); h += x; d = x + y; + x = c + f1(h,a,b) + 0x4a7484aa + words[21]; y = f2(d,e,f); g += x; c = x + y; + x = b + f1(g,h,a) + 0x5cb0a9dc + words[22]; y = f2(c,d,e); f += x; b = x + y; + x = a + f1(f,g,h) + 0x76f988da + words[23]; y = f2(b,c,d); e += x; a = x + y; + + // extend to 32 words + for (; i < 32; i++) + words[i] = words[i-16] + + (rotate(words[i-15], 7) ^ rotate(words[i-15], 18) ^ (words[i-15] >> 3)) + + words[i-7] + + (rotate(words[i- 2], 17) ^ rotate(words[i- 2], 19) ^ (words[i- 2] >> 10)); + + // fourth round + x = h + f1(e,f,g) + 0x983e5152 + words[24]; y = f2(a,b,c); d += x; h = x + y; + x = g + f1(d,e,f) + 0xa831c66d + words[25]; y = f2(h,a,b); c += x; g = x + y; + x = f + f1(c,d,e) + 0xb00327c8 + words[26]; y = f2(g,h,a); b += x; f = x + y; + x = e + f1(b,c,d) + 0xbf597fc7 + words[27]; y = f2(f,g,h); a += x; e = x + y; + x = d + f1(a,b,c) + 0xc6e00bf3 + words[28]; y = f2(e,f,g); h += x; d = x + y; + x = c + f1(h,a,b) + 0xd5a79147 + words[29]; y = f2(d,e,f); g += x; c = x + y; + x = b + f1(g,h,a) + 0x06ca6351 + words[30]; y = f2(c,d,e); f += x; b = x + y; + x = a + f1(f,g,h) + 0x14292967 + words[31]; y = f2(b,c,d); e += x; a = x + y; + + // extend to 40 words + for (; i < 40; i++) + words[i] = words[i-16] + + (rotate(words[i-15], 7) ^ rotate(words[i-15], 18) ^ (words[i-15] >> 3)) + + words[i-7] + + (rotate(words[i- 2], 17) ^ rotate(words[i- 2], 19) ^ (words[i- 2] >> 10)); + + // fifth round + x = h + f1(e,f,g) + 0x27b70a85 + words[32]; y = f2(a,b,c); d += x; h = x + y; + x = g + f1(d,e,f) + 0x2e1b2138 + words[33]; y = f2(h,a,b); c += x; g = x + y; + x = f + f1(c,d,e) + 0x4d2c6dfc + words[34]; y = f2(g,h,a); b += x; f = x + y; + x = e + f1(b,c,d) + 0x53380d13 + words[35]; y = f2(f,g,h); a += x; e = x + y; + x = d + f1(a,b,c) + 0x650a7354 + words[36]; y = f2(e,f,g); h += x; d = x + y; + x = c + f1(h,a,b) + 0x766a0abb + words[37]; y = f2(d,e,f); g += x; c = x + y; + x = b + f1(g,h,a) + 0x81c2c92e + words[38]; y = f2(c,d,e); f += x; b = x + y; + x = a + f1(f,g,h) + 0x92722c85 + words[39]; y = f2(b,c,d); e += x; a = x + y; + + // extend to 48 words + for (; i < 48; i++) + words[i] = words[i-16] + + (rotate(words[i-15], 7) ^ rotate(words[i-15], 18) ^ (words[i-15] >> 3)) + + words[i-7] + + (rotate(words[i- 2], 17) ^ rotate(words[i- 2], 19) ^ (words[i- 2] >> 10)); + + // sixth round + x = h + f1(e,f,g) + 0xa2bfe8a1 + words[40]; y = f2(a,b,c); d += x; h = x + y; + x = g + f1(d,e,f) + 0xa81a664b + words[41]; y = f2(h,a,b); c += x; g = x + y; + x = f + f1(c,d,e) + 0xc24b8b70 + words[42]; y = f2(g,h,a); b += x; f = x + y; + x = e + f1(b,c,d) + 0xc76c51a3 + words[43]; y = f2(f,g,h); a += x; e = x + y; + x = d + f1(a,b,c) + 0xd192e819 + words[44]; y = f2(e,f,g); h += x; d = x + y; + x = c + f1(h,a,b) + 0xd6990624 + words[45]; y = f2(d,e,f); g += x; c = x + y; + x = b + f1(g,h,a) + 0xf40e3585 + words[46]; y = f2(c,d,e); f += x; b = x + y; + x = a + f1(f,g,h) + 0x106aa070 + words[47]; y = f2(b,c,d); e += x; a = x + y; + + // extend to 56 words + for (; i < 56; i++) + words[i] = words[i-16] + + (rotate(words[i-15], 7) ^ rotate(words[i-15], 18) ^ (words[i-15] >> 3)) + + words[i-7] + + (rotate(words[i- 2], 17) ^ rotate(words[i- 2], 19) ^ (words[i- 2] >> 10)); + + // seventh round + x = h + f1(e,f,g) + 0x19a4c116 + words[48]; y = f2(a,b,c); d += x; h = x + y; + x = g + f1(d,e,f) + 0x1e376c08 + words[49]; y = f2(h,a,b); c += x; g = x + y; + x = f + f1(c,d,e) + 0x2748774c + words[50]; y = f2(g,h,a); b += x; f = x + y; + x = e + f1(b,c,d) + 0x34b0bcb5 + words[51]; y = f2(f,g,h); a += x; e = x + y; + x = d + f1(a,b,c) + 0x391c0cb3 + words[52]; y = f2(e,f,g); h += x; d = x + y; + x = c + f1(h,a,b) + 0x4ed8aa4a + words[53]; y = f2(d,e,f); g += x; c = x + y; + x = b + f1(g,h,a) + 0x5b9cca4f + words[54]; y = f2(c,d,e); f += x; b = x + y; + x = a + f1(f,g,h) + 0x682e6ff3 + words[55]; y = f2(b,c,d); e += x; a = x + y; + + // extend to 64 words + for (; i < 64; i++) + words[i] = words[i-16] + + (rotate(words[i-15], 7) ^ rotate(words[i-15], 18) ^ (words[i-15] >> 3)) + + words[i-7] + + (rotate(words[i- 2], 17) ^ rotate(words[i- 2], 19) ^ (words[i- 2] >> 10)); + + // eigth round + x = h + f1(e,f,g) + 0x748f82ee + words[56]; y = f2(a,b,c); d += x; h = x + y; + x = g + f1(d,e,f) + 0x78a5636f + words[57]; y = f2(h,a,b); c += x; g = x + y; + x = f + f1(c,d,e) + 0x84c87814 + words[58]; y = f2(g,h,a); b += x; f = x + y; + x = e + f1(b,c,d) + 0x8cc70208 + words[59]; y = f2(f,g,h); a += x; e = x + y; + x = d + f1(a,b,c) + 0x90befffa + words[60]; y = f2(e,f,g); h += x; d = x + y; + x = c + f1(h,a,b) + 0xa4506ceb + words[61]; y = f2(d,e,f); g += x; c = x + y; + x = b + f1(g,h,a) + 0xbef9a3f7 + words[62]; y = f2(c,d,e); f += x; b = x + y; + x = a + f1(f,g,h) + 0xc67178f2 + words[63]; y = f2(b,c,d); e += x; a = x + y; + + // update hash + m_hash[0] += a; + m_hash[1] += b; + m_hash[2] += c; + m_hash[3] += d; + m_hash[4] += e; + m_hash[5] += f; + m_hash[6] += g; + m_hash[7] += h; +} + + +/// add arbitrary number of bytes +void SHA256::add(const void* data, size_t numBytes) +{ + const uint8_t* current = (const uint8_t*) data; + + if (m_bufferSize > 0) + { + while (numBytes > 0 && m_bufferSize < BlockSize) + { + m_buffer[m_bufferSize++] = *current++; + numBytes--; + } + } + + // full buffer + if (m_bufferSize == BlockSize) + { + processBlock(m_buffer); + m_numBytes += BlockSize; + m_bufferSize = 0; + } + + // no more data ? + if (numBytes == 0) + return; + + // process full blocks + while (numBytes >= BlockSize) + { + processBlock(current); + current += BlockSize; + m_numBytes += BlockSize; + numBytes -= BlockSize; + } + + // keep remaining bytes in buffer + while (numBytes > 0) + { + m_buffer[m_bufferSize++] = *current++; + numBytes--; + } +} + + +/// process final block, less than 64 bytes +void SHA256::processBuffer() +{ + // the input bytes are considered as bits strings, where the first bit is the most significant bit of the byte + + // - append "1" bit to message + // - append "0" bits until message length in bit mod 512 is 448 + // - append length as 64 bit integer + + // number of bits + size_t paddedLength = m_bufferSize * 8; + + // plus one bit set to 1 (always appended) + paddedLength++; + + // number of bits must be (numBits % 512) = 448 + size_t lower11Bits = paddedLength & 511; + if (lower11Bits <= 448) + paddedLength += 448 - lower11Bits; + else + paddedLength += 512 + 448 - lower11Bits; + // convert from bits to bytes + paddedLength /= 8; + + // only needed if additional data flows over into a second block + unsigned char extra[BlockSize]; + + // append a "1" bit, 128 => binary 10000000 + if (m_bufferSize < BlockSize) + m_buffer[m_bufferSize] = 128; + else + extra[0] = 128; + + size_t i; + for (i = m_bufferSize + 1; i < BlockSize; i++) + m_buffer[i] = 0; + for (; i < paddedLength; i++) + extra[i - BlockSize] = 0; + + // add message length in bits as 64 bit number + uint64_t msgBits = 8 * (m_numBytes + m_bufferSize); + // find right position + unsigned char* addLength; + if (paddedLength < BlockSize) + addLength = m_buffer + paddedLength; + else + addLength = extra + paddedLength - BlockSize; + + // must be big endian + *addLength++ = (unsigned char)((msgBits >> 56) & 0xFF); + *addLength++ = (unsigned char)((msgBits >> 48) & 0xFF); + *addLength++ = (unsigned char)((msgBits >> 40) & 0xFF); + *addLength++ = (unsigned char)((msgBits >> 32) & 0xFF); + *addLength++ = (unsigned char)((msgBits >> 24) & 0xFF); + *addLength++ = (unsigned char)((msgBits >> 16) & 0xFF); + *addLength++ = (unsigned char)((msgBits >> 8) & 0xFF); + *addLength = (unsigned char)( msgBits & 0xFF); + + // process blocks + processBlock(m_buffer); + // flowed over into a second block ? + if (paddedLength > BlockSize) + processBlock(extra); +} + + +/// return latest hash as 64 hex characters +std::string SHA256::getHash() +{ + // compute hash (as raw bytes) + unsigned char rawHash[HashBytes]; + getHash(rawHash); + + // convert to hex string + std::string result; + result.reserve(2 * HashBytes); + for (int i = 0; i < HashBytes; i++) + { + static const char dec2hex[16+1] = "0123456789abcdef"; + result += dec2hex[(rawHash[i] >> 4) & 15]; + result += dec2hex[ rawHash[i] & 15]; + } + + return result; +} + + +/// return latest hash as bytes +void SHA256::getHash(unsigned char buffer[SHA256::HashBytes]) +{ + // save old hash if buffer is partially filled + uint32_t oldHash[HashValues]; + for (int i = 0; i < HashValues; i++) + oldHash[i] = m_hash[i]; + + // process remaining bytes + processBuffer(); + + unsigned char* current = buffer; + for (int i = 0; i < HashValues; i++) + { + *current++ = (m_hash[i] >> 24) & 0xFF; + *current++ = (m_hash[i] >> 16) & 0xFF; + *current++ = (m_hash[i] >> 8) & 0xFF; + *current++ = m_hash[i] & 0xFF; + + // restore old hash + m_hash[i] = oldHash[i]; + } +} + + +/// compute SHA256 of a memory block +std::string SHA256::operator()(const void* data, size_t numBytes) +{ + reset(); + add(data, numBytes); + return getHash(); +} + + +/// compute SHA256 of a string, excluding final zero +std::string SHA256::operator()(const std::string& text) +{ + reset(); + add(text.c_str(), text.size()); + return getHash(); +}