diff --git a/.gitignore b/.gitignore index f47441e..8ec383f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ build/ *.swp +*_/ diff --git a/.gitmodules b/.gitmodules index 64f5789..79eba58 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,9 @@ [submodule "GeneralUtility"] path = GeneralUtility url = https://gitea.leonetienne.de/leonetienne/GeneralUtility.git +[submodule "GCryptLib/exec/Eule"] + path = GCryptLib/exec/Eule + url = https://gitea.leonetienne.de/leonetienne/Eule.git +[submodule "GCryptLib/exec/BmpPP"] + path = GCryptLib/exec/BmpPP + url = https://gitea.leonetienne.de/leonetienne/BmpPP.git diff --git a/GCryptCLI/CMakeLists.txt b/GCryptCLI/CMakeLists.txt index 9541a68..06669fe 100644 --- a/GCryptCLI/CMakeLists.txt +++ b/GCryptCLI/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.16) -project(gecrypt) +project(gcrypt) set(CMAKE_CXX_STANDARD 17) @@ -42,3 +42,37 @@ target_include_directories(${PROJECT_NAME} PRIVATE ${gcrypt_include} ) +target_compile_options(${PROJECT_NAME} PRIVATE + -Werror + -fdiagnostics-color=always +) + +######### +# Tests # +######### +LIST(FILTER main_src EXCLUDE REGEX ".*/main.cpp") +FILE(GLOB test_src test/*.cpp) + +add_executable(test + test/Catch2.h + ${test_src} + ${main_src} + ${stringtools_src} + ${generalutility_src} + ${hazelnupp_src} + ${gcrypt_src} +) + +target_include_directories(test PRIVATE + include + ${stringtools_include} + ${generalutility_include} + ${hazelnupp_include} + ${gcrypt_include} +) + +target_compile_options(test PRIVATE + -Werror + -fdiagnostics-color=always +) + diff --git a/GCryptCLI/include/Bases.h b/GCryptCLI/include/Bases.h index d457bd2..93eb1f2 100644 --- a/GCryptCLI/include/Bases.h +++ b/GCryptCLI/include/Bases.h @@ -1,6 +1,28 @@ -#pragma once +#ifndef GCRYPTCLI_BASES_H +#define GCRYPTCLI_BASES_H + #include #include +#include + +// This lookup table holds how many digits a block is long +// in any iobase. +// This cannot be calculated on the fly, as it involves +// arithmetic with involving REALLY big numbers (like, 2^512). +// Here's how to calculate these numbers: +// Print an all 1's block in this format, and check the string size. +// That's it. +static auto blockLengthByBase = + std::map({ + std::make_pair(Configuration::IOBASE_FORMAT::BASE_BYTES, 64), + std::make_pair(Configuration::IOBASE_FORMAT::BASE_2, 512), + std::make_pair(Configuration::IOBASE_FORMAT::BASE_8, 171), + std::make_pair(Configuration::IOBASE_FORMAT::BASE_10, 155), + std::make_pair(Configuration::IOBASE_FORMAT::BASE_16, 128), + std::make_pair(Configuration::IOBASE_FORMAT::BASE_64, 86), + std::make_pair(Configuration::IOBASE_FORMAT::BASE_UWU, 81), + std::make_pair(Configuration::IOBASE_FORMAT::BASE_UGH, 126) + }); // Actually useful bases static const std::vector BASE_2 = { "0","1" }; @@ -116,3 +138,6 @@ static const std::vector BASE_UGH = { "Bah!", "Wha-?" }; + +#endif + diff --git a/GCryptCLI/include/CommandlineInterface.h b/GCryptCLI/include/CommandlineInterface.h index 54d0990..9dd5620 100644 --- a/GCryptCLI/include/CommandlineInterface.h +++ b/GCryptCLI/include/CommandlineInterface.h @@ -1,4 +1,6 @@ -#pragma once +#ifndef GCRYPTCLI_CLIINTERFACE_H +#define GCRYPTCLI_CLIINTERFACE_H + #include class CommandlineInterface @@ -14,8 +16,11 @@ private: static void CrashWithMsg(const std::string& msg); static void CatchVersionQueries(); - CommandlineInterface() {}; - static Hazelnp::CmdArgsInterface nupp; + + // No instanciation! >:( + CommandlineInterface() {}; }; +#endif + diff --git a/GCryptCLI/include/Configuration.h b/GCryptCLI/include/Configuration.h new file mode 100644 index 0000000..6af4f3e --- /dev/null +++ b/GCryptCLI/include/Configuration.h @@ -0,0 +1,61 @@ +#ifndef GCRYPTCLI_CONFIGURATION_H +#define GCRYPTCLI_CONFIGURATION_H + +#include + +class Configuration { + public: + static enum class INPUT_FROM { + STDIN, + FILE, + PARAMETER + } inputFrom; + + static enum class OUTPUT_TO { + STDOUT, + FILE + } outputTo; + + static enum class IOBASE_FORMAT { + BASE_BYTES, + BASE_2, + BASE_8, + BASE_10, + BASE_16, + BASE_64, + BASE_UWU, + BASE_UGH + } + formatIn, + formatOut; + + static std::string inputFilename; + static std::string outputFilename; + + static enum class MODULE { + ENCRYPTION, + DECRYPTION, + HASH, + GENERATE_KEY + } activeModule; + + //! Will analyze the supplied cli parameters, + //! and decide what the configuration will be. + static void Parse(); + + private: + static void DecideInputFrom(); + static void DecideOutputTo(); + static void DecideCiphertextFormat(); + static void MapCiphertextFormatToIOBases(); + static void DecideModule(); + + // This is just an intermediary value, used between methods + static IOBASE_FORMAT ciphertextFormat; + + // No instanciation! >:( + Configuration() {}; +}; + +#endif + diff --git a/GCryptCLI/include/DataFormatter.h b/GCryptCLI/include/DataFormatter.h new file mode 100644 index 0000000..a9ae0fe --- /dev/null +++ b/GCryptCLI/include/DataFormatter.h @@ -0,0 +1,58 @@ +#ifndef GCRYPTCLI_DATAFORMATTER_H +#define GCRYPTCLI_DATAFORMATTER_H + +#include +#include +#include +#include "Configuration.h" + +using namespace Leonetienne::GCrypt; + +// This class has the task to format Blocks to various formats. +class DataFormatter { + public: + //! Will format a single block to a given iobase + static std::string FormatBlock( + const Block& block, + const Configuration::IOBASE_FORMAT base + ); + + //! Will parse a string of a given iobase to a block + static Block DecodeFormat( + const std::string& str, + const Configuration::IOBASE_FORMAT base + ); + + //! Will format a vector of blocks to a given iobase + static std::string FormatBlocks( + const std::vector& blocks, + const Configuration::IOBASE_FORMAT base + ); + + //! Will format a string making up multiple block in a given iobase into a vector of block + static std::vector DecodeFormatMultiblock( + const std::string& str, + const Configuration::IOBASE_FORMAT base + ); + + + private: + static std::string Bin2CustomBase( + const std::string& bin, + const std::vector& customSet, + const std::size_t minLen, + const std::string& seperator = "" + ); + + static std::string CustomBase2Bin( + const std::string& in, + const std::vector& customSet, + const std::string& seperator = "" + ); + + // No instanciation! >:( + DataFormatter() {}; +}; + +#endif + diff --git a/GCryptCLI/include/DataIngestionLayer.h b/GCryptCLI/include/DataIngestionLayer.h new file mode 100644 index 0000000..e8a8ab2 --- /dev/null +++ b/GCryptCLI/include/DataIngestionLayer.h @@ -0,0 +1,75 @@ +#ifndef GCRYPTCLI_DATAINGESTIONLAYER_H +#define GCRYPTCLI_DATAINGESTIONLAYER_H + +#include +#include +#include +#include "Configuration.h" + +using namespace Leonetienne::GCrypt; + +namespace IO { + + // This class is used to read in data. + class DataIngestionLayer { + public: + //! Will initialize the ingestion + static void Init(); + + //! Will destruct the ingestion layer (like, closing file handles) + static void Destruct(); + + //! Will attempt to read a data block. + //! Requires Init() to have been called + static void ReadBlock(); + + //! Have we read in all available blocks? + static bool ReachedEOF(); + + //! Returns true if there are blocks to be fetched (GetNextBlock()) + static bool IsBlockReady(); + + //! Will return the next block in the queue + static Block GetNextBlock(); + + //! Returns true, if EOF is reached, and there are no more blocks to fetch (GetNextBlock()) + static bool IsFinished(); + + //! Returns how many blocks have been read, in total + static std::size_t NBlocksRead(); + + private: + static std::istream* in; + + // Will read n bytes from the input. + // If EOF is reached, it will return a string of length <= 5 + // and will set the approriate flags. + static std::string ReadBytes(const std::size_t n, std::size_t& out_bytes_read); + + // We have to hold on to a reference to a filestream, + // even if we're always just reading from in. + // We still have to CLOSE the file handle afterwards! + static std::ifstream ifs; + static std::istringstream iss; + + // Indicates whether EOF has been reached + static bool reachedEof; + // Indicates whether this class has been initialized + static bool initialized; + + // Are we reading ciphertext or regular text? + static bool isReadingCiphertext; + + // How many blocks have been read in total + static std::size_t nBlocksRead; + + // All read blocks, that haven't been given out yet + static std::queue blocks; + + // No instanciation! >:( + DataIngestionLayer(); + }; +} + +#endif + diff --git a/GCryptCLI/include/DataOutputLayer.h b/GCryptCLI/include/DataOutputLayer.h new file mode 100644 index 0000000..9b80679 --- /dev/null +++ b/GCryptCLI/include/DataOutputLayer.h @@ -0,0 +1,61 @@ +#ifndef GCRYPTCLI_DATAOUTPUTLAYER_H +#define GCRYPTCLI_DATAOUTPUTLAYER_H + +#include +#include +#include +#include "Configuration.h" + +using namespace Leonetienne::GCrypt; + +namespace IO { + + // This class is used to read in data. + class DataOutputLayer { + public: + //! Will initialize the output + static void Init(); + + //! Will destruct the output layer (like, closing file handles) + static void Destruct(); + + //! Will queue a block for writing + static void Enqueue(const Block& block); + + //! Will attempt to write the next block + static void WriteBlock(); + + //! Indicates that no more blocks will be enqueued + static void ReachedEOF(); + + //! Returns true, if all blocks have been written, and an EOF signal as been received + static bool IsFinished(); + + private: + //! If we are finished, and are outputting to stdout, + //! and the user didn't specifically opt out, print a newline + static void AddTrailingLinebreakIfRequired(); + + static std::ostream* out; + + // We have to hold on to a reference to a filestream, + // even if we're always just reading from in. + // We still have to CLOSE the file handle afterwards! + static std::ofstream ofs; + + // Indicates whether EOF has been reached + static bool reachedEof; + + // Indicates whether this class has been initialized + static bool initialized; + + // All blocks, that haven't been written yet + static std::queue blocks; + + //! No instanciation >:( + DataOutputLayer() {}; + }; +} + +#endif + diff --git a/GCryptCLI/include/KeyManager.h b/GCryptCLI/include/KeyManager.h new file mode 100644 index 0000000..926a232 --- /dev/null +++ b/GCryptCLI/include/KeyManager.h @@ -0,0 +1,30 @@ +#ifndef GCRYPTCLI_KEYMANAGER_H +#define GCRYPTCLI_KEYMANAGER_H + +#include + +using namespace Leonetienne::GCrypt; + +// This class has the task to prepare and supply the encryption key. +class KeyManager { + public: + //! Will prepare the key. Be it from cli, a file, or, random, or whatever. + static void PrepareKey(); + + //! Will return the key, if prepared. + static const Key& GetKey(); + + private: + //! Will ask for a password on stdin, + //! hiding the input. + static std::string PasswordPrompt(); + + //! The encryption key + static Key key; + + // No instanciation! >:( + KeyManager() {}; +}; + +#endif + diff --git a/GCryptCLI/include/ModuleDecryption.h b/GCryptCLI/include/ModuleDecryption.h new file mode 100644 index 0000000..79d909f --- /dev/null +++ b/GCryptCLI/include/ModuleDecryption.h @@ -0,0 +1,18 @@ +#ifndef GCRYPTCLI_MODULE_DECRYPTION_H +#define GCRYPTCLI_MODULE_DECRYPTION_H + +namespace Module { + //! This module will decrypt supplied input + class Decryption { + public: + //! Will run the module + static void Run(); + + private: + // No instanciation! >:( + Decryption() {}; + }; +} + +#endif + diff --git a/GCryptCLI/include/ModuleEncryption.h b/GCryptCLI/include/ModuleEncryption.h new file mode 100644 index 0000000..069a65a --- /dev/null +++ b/GCryptCLI/include/ModuleEncryption.h @@ -0,0 +1,18 @@ +#ifndef GCRYPTCLI_MODULE_ENCRYPTION_H +#define GCRYPTCLI_MODULE_ENCRYPTION_H + +namespace Module { + //! This module will encrypt supplied input + class Encryption { + public: + //! Will run the module + static void Run(); + + private: + // No instanciation! >:( + Encryption() {}; + }; +} + +#endif + diff --git a/GCryptCLI/include/ModuleGenerateKey.h b/GCryptCLI/include/ModuleGenerateKey.h new file mode 100644 index 0000000..19343c9 --- /dev/null +++ b/GCryptCLI/include/ModuleGenerateKey.h @@ -0,0 +1,19 @@ +#ifndef GCRYPTCLI_MODULE_GENERATEKEY_H +#define GCRYPTCLI_MODULE_GENERATEKEY_H + +namespace Module { + // This module just generates a key, and outputs it. + // Can be used to create a keyfiles. + class GenerateKey { + public: + //! Will write the key to a file + static void Run(); + + private: + // No instanciation! >:( + GenerateKey() {}; + }; +} + +#endif + diff --git a/GCryptCLI/include/ModuleHashing.h b/GCryptCLI/include/ModuleHashing.h new file mode 100644 index 0000000..24ef854 --- /dev/null +++ b/GCryptCLI/include/ModuleHashing.h @@ -0,0 +1,18 @@ +#ifndef GCRYPTCLI_MODULE_HASHING_H +#define GCRYPTCLI_MODULE_HASHING_H + +namespace Module { + //! This module will hash supplied input + class Hashing { + public: + //! Will run the module + static void Run(); + + private: + // No instanciation! >:( + Hashing() {}; + }; +} + +#endif + diff --git a/GCryptCLI/include/ProgressPrinter.h b/GCryptCLI/include/ProgressPrinter.h new file mode 100644 index 0000000..6b23c8b --- /dev/null +++ b/GCryptCLI/include/ProgressPrinter.h @@ -0,0 +1,23 @@ +#ifndef GCRYPTCLI_PROGRESSPRINTER_H +#define GCRYPTCLI_PROGRESSPRINTER_H + +#include +#include "CommandlineInterface.h" + +// This class has the task to output progress to stderr, if requested +class ProgressPrinter { + public: + //! Will print progress to stderr, if requested, and the interval matches + static void PrintIfAppropriate( + const std::string& message, + const std::size_t current, + const std::size_t target + ); + + private: + // No instanciation! >:( + ProgressPrinter() {}; +}; + +#endif + diff --git a/GCryptCLI/include/Version.h b/GCryptCLI/include/Version.h index d6b959c..5b9f81f 100644 --- a/GCryptCLI/include/Version.h +++ b/GCryptCLI/include/Version.h @@ -1,3 +1,7 @@ -#pragma once -#define GHETTOCRYPTCLI_VERSION 0.1241 +#ifndef GCRYPTCLI_VERSION_H +#define GCRYPTCLI_VERSION_H + +#define GCRYPTCLI_VERSION 0.1251 + +#endif diff --git a/GCryptCLI/readme.md b/GCryptCLI/readme.md index 4884e4c..f79a573 100644 --- a/GCryptCLI/readme.md +++ b/GCryptCLI/readme.md @@ -1,62 +1,88 @@ # GCrypt CLI -Easy text and file encryption via the command line using Ghetto Crypt. Now supporting [*esoteric data formats*](#esoteric-data-formats)... :) +Easy text and file encryption via the command line using GCrypt. Now supporting [*esoteric data formats*](#esoteric-data-formats)... :) Again, please only use this as an obfuscator or if the only other option would be no encryption at all. Do you want to quickly and securely encrypt stuff via the command line? Use openssl-cli with the aes cipher. It's a bit more wordy but much faster and more secure. -Still want to use Ghetto Crypt cli? Here ya go! -Just clone this repository, navigate into this directory and run `cmake . && make`. It shouuuld *\*crosses fingers\** just build. +Still want to use GCrypt cli? Here ya go! +Just clone this repository, navigate into this directory and compile it: + +## How do i use this? +### Prerequsities: +Have these depencies installed: +* git +* make +* cmake +* build-essentials (c++ >= 17) (might work with 11, if you're lucky) + +### How2compile +1) Clone this repository. +2) Download all submodules: `git submodule update --init --recursive`. +3) Cd into /GCryptCLI: `cd GCryptCLI`. +4) Create a build directory: `cmake -B build`. +5) Cd into the build directory: `cd build`. +6) Compile: `make`. +The executable `gcrypt` should now lie in `build/`. If you want to use this globally, you could move it to `/usr/bin/`, or some other location in your $PATH. -## How do i use this? +### Options and flags All arguments and flags: ``` -$ gecrypt --help -CLI for the ghettocrypt cipher/obfuscator +CLI for the GCrypt cipher/obfuscator Copyright (c) 2022 Leon Etienne -Ghettocrypt v0.21 -Ghettocrypt CLI v0.124 -THIS IS EXPERIMENTAL SOFTWARE AND MUST BE CONSIDERED INSECURE. DO NOT USE THIS TO ENCRYPT SENSITIVE DATA! READ THE README FILES ACCESSIBLE AT "https://github.com/Leonetienne/GhettoCrypt/blob/master/readme.md" AND "https://github.com/Leonetienne/GhettoCrypt/blob/master/GhettoCryptCLI/readme.md" +GCrypt v0.236 +GCrypt CLI v0.1241 +THIS IS EXPERIMENTAL SOFTWARE AND MUST BE CONSIDERED INSECURE. DO NOT USE THIS TO ENCRYPT SENSITIVE DATA! READ THE README FILES ACCESSIBLE AT "https://gitea.leonetienne.de/leonetienne/GCrypt" ==== AVAILABLE PARAMETERS ==== ---iobase-8 VOID incompatibilities=[--iobase-2, --iobase-10, --iobase-64, --iobase-uwu, --iobase-ugh] Interpret and format ciphertexts in base8 +--iobase-bytes VOID incompatibilities=[--iobase-2, --iobase-8, --iobase-10, --iobase-16, --iobase-64, --iobase-uwu, --iobase-ugh] Interpret and output ciphertexts as raw bytes. + +--iobase-16 VOID incompatibilities=[--iobase-bytes, --iobase-2, --iobase-8, --iobase-64, --iobase-uwu, --iobase-ugh] Interpret and format ciphertexts in base16 (hex) --progress -p VOID Print digestion progress to stdout. May be advisable for large files, as the cipher is rather slow. ---cli-version VOID Will supply the version of ghettocrypt-cli used. +--cli-version -v VOID Will supply the version of GCryptCLI used. --keyfile -kf STRING incompatibilities=[--key, --keyask, --hash] Read in the first {KEYSIZE}(=512) bits of this file and use that as an encryption key. WARNING: Arguments may be logged by the system! ---iobase-2 VOID incompatibilities=[--iobase-8, --iobase-10, --iobase-64, --iobase-uwu, --iobase-ugh] Interpret and format ciphertexts in base2 +--iobase-2 VOID incompatibilities=[--iobase-bytes, --iobase-8, --iobase-10, --iobase-16, --iobase-64, --iobase-uwu, --iobase-ugh] Interpret and format ciphertexts in base2 ---iobase-10 VOID incompatibilities=[--iobase-2, --iobase-8, --iobase-64, --iobase-uwu, --iobase-ugh] Interpret and format ciphertexts in base10 +--iobase-10 VOID incompatibilities=[--iobase-bytes, --iobase-2, --iobase-8, --iobase-16, --iobase-64, --iobase-uwu, --iobase-ugh] Interpret and format ciphertexts in base10 ---iobase-ugh VOID incompatibilities=[--iobase-2, --iobase-8, --iobase-10, --iobase-64, --iobase-uwu] Interpret and format ciphertexts in base ugh +--ofile -o STRING incompatibilities=[--ostdout, --hash] Write output in this file. ---iobase-uwu VOID incompatibilities=[--iobase-2, --iobase-8, --iobase-10, --iobase-64, --iobase-ugh] Interpret and format ciphertexts in base uwu - ---iobase-64 VOID incompatibilities=[--iobase-2, --iobase-8, --iobase-10, --iobase-uwu, --iobase-ugh] Interpret and format ciphertexts in base64 - ---ostdout VOID incompatibilities=[--ofile, --hash] Output of digested files will be dumped to stdout instead of a file. - ---encrypt -e VOID incompatibilities=[--decrypt, --hash] Use the encryption routine. - ---infile -if STRING incompatibilities=[--intext] Encrypt this file. Saves as {filename}.crypt, if not specified otherwise. - ---version -v VOID Will supply the version of ghettocrypt used. - ---decrypt -d VOID incompatibilities=[--encrypt, --hash] Use decryption routine. - ---hash -h VOID incompatibilities=[--encrypt, --decrypt] Use the ghetto cipher as a hash digest. - ---intext -it STRING incompatibilities=[--infile] Encrypt this string. Dumps to stdout. - ---ofile -o STRING incompatibilities=[--ostdout, --hash] Use this filename for output if --infile is specified. Gets ignored otherwise. +--key -k STRING incompatibilities=[--keyfile, --keyask, --hash] Use this value as a password to extrapolate the encryption key. WARNING: Arguments may be logged by the system! --keyask -ka VOID incompatibilities=[--key, --keyfile, --hash] Read the encryption key from stdin. ---key -k STRING incompatibilities=[--keyfile, --keyask, --hash] Use this value as a password to extrapolate the encryption key. WARNING: Arguments may be logged by the system! +--no-newline VOID Don't postfix stdout output with a newline + +--puffer-output VOID Will digest the entire data before initiating any output. + +--puffer-input VOID Will read the entire input before beginning any digestion. + +--decrypt -d VOID incompatibilities=[--encrypt, --hash, --generate-key] Use decryption module. + +--lib-version VOID Will supply the version of GCryptLib used. + +--iobase-ugh VOID incompatibilities=[--iobase-bytes, --iobase-2, --iobase-8, --iobase-10, --iobase-16, --iobase-64, --iobase-uwu] Interpret and format ciphertexts in base ugh + +--infile -if STRING incompatibilities=[--intext] Encrypt this file. + +--iobase-uwu VOID incompatibilities=[--iobase-bytes, --iobase-2, --iobase-8, --iobase-10, --iobase-16, --iobase-64, --iobase-ugh] Interpret and format ciphertexts in base uwu + +--iobase-64 VOID incompatibilities=[--iobase-bytes, --iobase-2, --iobase-8, --iobase-10, --iobase-16, --iobase-uwu, --iobase-ugh] Interpret and format ciphertexts in base64 + +--iobase-8 VOID incompatibilities=[--iobase-bytes, --iobase-2, --iobase-10, --iobase-16, --iobase-64, --iobase-uwu, --iobase-ugh] Interpret and format ciphertexts in base8 + +--encrypt -e VOID incompatibilities=[--decrypt, --hash] Use the encryption module. + +--generate-key VOID incompatibilities=[--encrypt, --decrypt, --hash] Use the key generation module. Will generate a random key based on hardware events, output it, and exit. + +--hash -h VOID incompatibilities=[--encrypt, --decrypt, --generate-key] Use the GHash hash module to calculate a hashsum. + +--intext -it STRING incompatibilities=[--infile] Encrypt this string. ``` ### Examples @@ -64,13 +90,13 @@ Please note that commonly used arguments are supplied in their short form (`-e` #### I want to encrypt text! ```sh -$ gecrypt -e --keyask --intext "hello, world!" +$ gcrypt -e --keyask --intext "hello, world!" efbebc429c8370bf84f00b0d8ccbaf7858b3b87d71ff58cb1cfefa8fb0c68094c0865565873aa8a5254ede59be46e81a4d4917e679b18cb290dbd6669cb6207a ``` #### Now decrypt it ```sh -$ gecrypt -d --keyask --intext "efbebc429c8370bf84f00b0d8ccbaf7858b3b87d71ff58cb1cfefa8fb0c68094c0865565873aa8a5254ede59be46e81a4d4917e679b18cb290dbd6669cb6207a" +$ gcrypt -d --keyask --intext "efbebc429c8370bf84f00b0d8ccbaf7858b3b87d71ff58cb1cfefa8fb0c68094c0865565873aa8a5254ede59be46e81a4d4917e679b18cb290dbd6669cb6207a" hello, world! ``` @@ -80,95 +106,109 @@ hello, world! > Outputting it in base-64 took just over ONE MINUTE. In base-2 over SEVEN MINUTES. The general trend seems to be, the larger the base, the better it performs. ```sh -$ gecrypt -e --keyask --intext "hello, world!" --iobase-2 +$ gcrypt -e --keyask --intext "hello, world!" --iobase-2 111001001011100000011111000100010100110011100110000100100101001001110001001000101011110000000011011100001010111010001000110111110110011011100000001100110001001000100111011000101010010001011011111011001000011111100100101001011110011101110001010011000101011001111010000001001100101110000101101101101001110100100001101010111101010000100111101110000110011101100101101011000011101001000011010010011001111010001001101000101001100101010000100010111101101100010000000001001110010001001011001001011011010111001101000100 -$ gecrypt -e --keyask --intext "hello, world!" --iobase-8 +$ gcrypt -e --keyask --intext "hello, world!" --iobase-8 71134037042463460445116110536003341272106766334014611047305221337310374451363561230531720114560555516441527520475606354553035103223172115051452042755420011621131133271504 -$ gecrypt -e --keyask --intext "hello, world!" --iobase-10 +$ gcrypt -e --keyask --intext "hello, world!" --iobase-10 2994749439449970047518881970731547473115480925772565399786126223459744370490386223437520234266936877059618216185983047971748564015130703048737306773910340 -$ gecrypt -e --keyask --intext "hello, world!" --iobase-64 +$ gcrypt -e --keyask --intext "hello, world!" --iobase-64 Co/WjpV5nPrCaz0QMdrXAXzzOH5HODRsBNL22KZowmGMcTLwfmsQpzt7Ik+ViR5vOhUXowFQeR5x2vbcj1X5ae ``` I won't be pasting in stdout for every example. It would become too cluttered. #### Passing the key as an argument ```sh -$ gecrypt -e --key "secretpassword" --intext "hello, world!" +$ gcrypt -e --key "secretpassword" --intext "hello, world!" ``` > :warning: Some operating systems will log cli arguments! THIS WOULD BE THE ENTIRE KEY! #### Using keyfiles ```sh -$ gecrypt -e --keyfile "dog.jpg" --intext "hello, world!" +$ gcrypt -e --keyfile "dog.jpg" --intext "hello, world!" ``` + +#### Creating keyfiles +```sh +$ gcrypt --generate-key --ofile "my-keyfile.bin" +``` +This will generate a random 512-bit keyfile from hardware events > :warning: Some operating systems will log cli arguments! One might find your keyfile! #### Encrypting files ```sh -$ gecrypt -e --keyask --infile "cat.jpg" +$ gcrypt -e --keyask --infile "cat.jpg" ``` File `cat.jpg.crypt` will be created. #### Encrypting files to a target file name ```sh -$ gecrypt -e --keyask --infile "cat.jpg" -o "encrypted_cat.jpg" +$ gcrypt -e --keyask --infile "cat.jpg" -o "encrypted_cat.jpg" ``` File `encrypted_cat.jpg` will be created. #### Decrypting files ```sh -$ gecrypt -d --keyask --infile "cat.jpg.crypt" +$ gcrypt -d --keyask --infile "cat.jpg.crypt" ``` File `cat.jpg.crypt.plain` will be created. Its contents match `cat.jpg` > :warning: Since this is a block cipher, decrypted files may be tailpadded with a few nullbytes. #### Decrypting files to a target file name ```sh -$ gecrypt -d --keyask --infile "cat.jpg.crypt" -o "decrypted_cat.jpg" +$ gcrypt -d --keyask --infile "cat.jpg.crypt" -o "decrypted_cat.jpg" ``` File `decrypted_cat.jpg` will be created. You can now open it again. #### Encrypting large files takes time. How's the progress? ```sh -$ gecrypt -e --keyask --infile "cat.jpg" --progress +$ gcrypt -e --keyask --infile "cat.jpg" --progress ``` Something along the lines of `Encrypting... (Block 200 / 1148 - 17.4216%)` will be regularly, but not too often, printed to stdout. #### Any cipher can also compute hashsums ```sh -$ gecrypt -h --intext "hello, world!" +$ gcrypt -h --intext "hello, world!" a96f42c9d97e46b9e1ed7de5182770170d4ef9b7b8264f3fbd89b38dc60c1fe06232653f5856013307fc020fb1d35f2bea26bc0f373c5ac35a722c6b03d8254d -$ gecrypt -h --infile "cat.jpg" +$ gcrypt -h --infile "cat.jpg" fe6bdfb6ec39771c4fdcdc40e52397bcd67fbfef0ad5a15ebbd8b9e4c2a815848b3984eda5ef6f727e9e420c23500c90c42ab80ac5659048be8969357741e3e5 - ``` -The hashsum will always be of size BLOCK_SIZE. That is 512 bits by default. -> :warning: Calculating a hashsum takes about twice as long as regular encryption. +The hashsum will always be of size BLOCK_SIZE. That is 512. #### What version am i running? -Depending on wether you want to know the GhettoCrypt version or the CLI's version, use either `--version` or `--cli-version`. It will print out a floating point number. +Depending on wether you want to know the GCrypt version or the CLI's version, +use either `--cli-version` or `--lib-version`. +It will print out a floating point number. #### I want to stream the output of file en/decryption. -You can dump the binary result to stdout. In this example, we'll stream it into a jpg file, -but you could stream it to whatever you'd like. +// Easily! If you do not supply any output or input, stdout and stdin will be used instead! ```sh -$ gecrypt -d --keyask --infile "cat.jpg.crypt" --ostdout > "decrypted_cat.jpg" +# mpv is a media player, as an example +$ gcrypt -d --key "123" --infile "music.mp3.crypt" | mpv - +``` + +#### Don't want to stream input and output? +By default, gcrypt will read a block, digest it, and output the result immediately. +Sometimes you don't want that. Use these flags, respectively: +``` +--puffer-input # Reads all input to memory before beginning to digest it +--puffer-output # Digests all input before beginning to write any ``` ## Esoteric data formats #### Base *UwU* ```sh -$ gecrypt -e --keyask --intext "hello, world!" --iobase-uwu +$ gcrypt -e --keyask --intext "hello, world!" --iobase-uwu :) sewnpaiii tastieee uhh?! nappies cutewr twe best cutieee :O tastieee senpaiiiw favowite toesy-woesies ^.^ :3 best chomp whiffle uwu Awww sewnpaiii comfy-womfy :p keewl Awww youuu nyeko :O tasties hiiiii heeeey (*^_^*) youuu toot uhh..? smush (*^_^*) *bites-lip* whiffle haaaay nyah! comfy-womfy :) cutsie Owww haaaay snaffle haaaai haaaai nyeko *sweats* :) uhh..? boop toot *bites-lip* <3 whiiiich whiffskaws ^.^ twe whiffskaws hiiiii *sweats* Owww dewicious i tasties :P awe hewwo boop rawr uwu dewicious eughh twe cutsie xD ``` #### Base **UGH!** ```sh -$ gecrypt -e --keyask --intext "hello, world!" --iobase-ugh +$ gcrypt -e --keyask --intext "hello, world!" --iobase-ugh Grr... Wha-? Aah! Aah! Uh-huh... Aah! Grr... Aah! Aah! Uh-huh... Ah... Ugh Grr... Ugh Pft! Nu-uh... Gah! Bah! Huh...? Ah... Uh-huh... Wha-? Pft! Nu-uh... Ugh Wha-? Psh! Agh! Ah... Aah! Nu-uh... Psh! Pft! Nu-uh... Psh! Shh! Gah! Ah... Pft! Gah! Shh! Bah! Gah! Uh-huh... Gah! Duh! Aah! Uh-huh... Er- Nu-uh... Gah! Wha-? Pft! Er- Shh! Ah... Huh...? Er- Wha-? Uh-huh... Ah... Shh! Ugh Bah! Wha-? Uaah! Ah... Nu-uh... Uh-huh... Ugh Pft! Pft! Gah! Shh! Shh! Wha-? Bah! Ugh Grr... Aah! Pft! Nu-uh... Ah... Aah! Agh! Er- Psh! Uaah! Nu-uh... Ugh Wha-? Uh-huh... Shh! Pft! Aah! Agh! Grr... Agh! Agh! Grr... Pft! Wha-? Wha-? Uh-huh... Aah! Ugh Aah! Pft! Gah! Bah! Huh...? Ugh Bah! Uaah! Gah! Bah! Duh! Duh! Uh-huh... Grr... Ah... Grr... Ugh Ah... Pft! ``` diff --git a/GCryptCLI/src/CommandlineInterface.cpp b/GCryptCLI/src/CommandlineInterface.cpp index ae2b8f9..a5e6a66 100644 --- a/GCryptCLI/src/CommandlineInterface.cpp +++ b/GCryptCLI/src/CommandlineInterface.cpp @@ -2,20 +2,19 @@ #include #include #include -#include +#include #include "Version.h" using namespace Hazelnp; -using namespace Leonetienne; +using namespace Leonetienne::GCrypt; -void CommandlineInterface::Init(int argc, const char* const* argv) -{ +void CommandlineInterface::Init(int argc, const char* const* argv) { /* General information */ std::stringstream ss; - ss << "CLI for the gcrypt cipher/obfuscator" << std::endl + ss << "CLI for the GCrypt cipher/obfuscator" << std::endl << "Copyright (c) 2022 Leon Etienne" << std::endl - << "GCrypt v" << GHETTOCRYPT_VERSION << std::endl - << "GCrypt CLI v" << GHETTOCRYPTCLI_VERSION << std::endl + << "GCrypt v" << GCRYPT_VERSION << std::endl + << "GCrypt CLI v" << GCRYPTCLI_VERSION << std::endl << "THIS IS EXPERIMENTAL SOFTWARE AND MUST BE CONSIDERED INSECURE. DO NOT USE THIS TO ENCRYPT SENSITIVE DATA! READ THE README FILES ACCESSIBLE AT \"https://gitea.leonetienne.de/leonetienne/GCrypt\""; nupp.SetBriefDescription(ss.str()); ss.str(""); @@ -23,39 +22,39 @@ void CommandlineInterface::Init(int argc, const char* const* argv) nupp.SetCrashOnFail("true"); /* Builtin documentation */ - nupp.RegisterDescription("--encrypt", "Use the encryption routine."); + nupp.RegisterDescription("--encrypt", "Use the encryption module."); nupp.RegisterConstraint("--encrypt", ParamConstraint(true, DATA_TYPE::VOID, {}, false, {"--decrypt", "--hash" })); nupp.RegisterAbbreviation("-e", "--encrypt"); - nupp.RegisterDescription("--decrypt", "Use decryption routine."); - nupp.RegisterConstraint("--decrypt", ParamConstraint(true, DATA_TYPE::VOID, {}, false, { "--encrypt", "--hash" })); + nupp.RegisterDescription("--decrypt", "Use decryption module."); + nupp.RegisterConstraint("--decrypt", ParamConstraint(true, DATA_TYPE::VOID, {}, false, { "--encrypt", "--hash", "--generate-key" })); nupp.RegisterAbbreviation("-d", "--decrypt"); - nupp.RegisterDescription("--hash", "Use the ghetto cipher as a hash digest."); - nupp.RegisterConstraint("--hash", ParamConstraint(true, DATA_TYPE::VOID, {}, false, { "--encrypt", "--decrypt" })); + nupp.RegisterDescription("--hash", "Use the GHash hash module to calculate a hashsum."); + nupp.RegisterConstraint("--hash", ParamConstraint(true, DATA_TYPE::VOID, {}, false, { "--encrypt", "--decrypt", "--generate-key" })); nupp.RegisterAbbreviation("-h", "--hash"); - nupp.RegisterDescription("--intext", "Encrypt this string. Dumps to stdout."); + nupp.RegisterDescription("--generate-key", "Use the key generation module. Will generate a random key based on hardware events, output it, and exit."); + nupp.RegisterConstraint("--generate-key", ParamConstraint(true, DATA_TYPE::VOID, {}, false, { "--encrypt", "--decrypt", "--hash" })); + + nupp.RegisterDescription("--intext", "Encrypt this string."); nupp.RegisterConstraint("--intext", ParamConstraint(true, DATA_TYPE::STRING, {}, false, { "--infile" })); nupp.RegisterAbbreviation("-it", "--intext"); - nupp.RegisterDescription("--infile", "Encrypt this file. Saves as {filename}.crypt, if not specified otherwise."); + nupp.RegisterDescription("--infile", "Encrypt this file."); nupp.RegisterConstraint("--infile", ParamConstraint(true, DATA_TYPE::STRING, {}, false, { "--intext" })); nupp.RegisterAbbreviation("-if", "--infile"); - nupp.RegisterDescription("--ofile", "Use this filename for output if --infile is specified. Gets ignored otherwise."); + nupp.RegisterDescription("--ofile", "Write output in this file."); nupp.RegisterConstraint("--ofile", ParamConstraint(true, DATA_TYPE::STRING, {}, false, { "--ostdout", "--hash" })); nupp.RegisterAbbreviation("-of", "--ofile"); nupp.RegisterAbbreviation("-o", "--ofile"); - nupp.RegisterDescription("--ostdout", "Output of digested files will be dumped to stdout instead of a file."); - nupp.RegisterConstraint("--ostdout", ParamConstraint(true, DATA_TYPE::VOID, {}, false, { "--ofile", "--hash" })); - nupp.RegisterDescription("--key", "Use this value as a password to extrapolate the encryption key. WARNING: Arguments may be logged by the system!"); nupp.RegisterConstraint("--key", ParamConstraint(true, DATA_TYPE::STRING, {}, false, { "--keyfile", "--keyask", "--hash" })); nupp.RegisterAbbreviation("-k", "--key"); - ss << "Read in the first {KEYSIZE}(=" << GCrypt::BLOCK_SIZE << ") bits of this file and use that as an encryption key. WARNING: Arguments may be logged by the system!"; + ss << "Read in the first {KEYSIZE}(=" << Block::BLOCK_SIZE_BITS << ") bits of this file and use that as an encryption key. WARNING: Arguments may be logged by the system!"; nupp.RegisterDescription("--keyfile", ss.str()); ss.str(""); nupp.RegisterConstraint("--keyfile", ParamConstraint(true, DATA_TYPE::STRING, {}, false, { "--key", "--keyask", "--hash" })); @@ -65,34 +64,52 @@ void CommandlineInterface::Init(int argc, const char* const* argv) nupp.RegisterConstraint("--keyask", ParamConstraint(true, DATA_TYPE::VOID, {}, false, { "--key", "--keyfile", "--hash" })); nupp.RegisterAbbreviation("-ka", "--keyask"); - nupp.RegisterDescription("--progress", "Print digestion progress to stdout. May be advisable for large files, as the cipher is rather slow."); + nupp.RegisterDescription("--progress", "Print digestion progress to stderr. May be advisable for large files, as the cipher is rather slow."); nupp.RegisterConstraint("--progress", ParamConstraint(true, DATA_TYPE::VOID, {}, false, {})); nupp.RegisterAbbreviation("-p", "--progress"); + nupp.RegisterDescription("--progress-interval", "Print digestion progress reports every these many data blocks."); + nupp.RegisterConstraint("--progress-interval", ParamConstraint(true, DATA_TYPE::INT, { "1000" }, true, {})); + + nupp.RegisterDescription("--iobase-bytes", "Interpret and output ciphertexts as raw bytes."); + nupp.RegisterConstraint("--iobase-bytes", ParamConstraint(true, DATA_TYPE::VOID, {}, false, { "--iobase-2", "--iobase-8", "--iobase-10", "--iobase-16", "--iobase-64", "--iobase-uwu", "--iobase-ugh" })); + nupp.RegisterDescription("--iobase-2", "Interpret and format ciphertexts in base2"); - nupp.RegisterConstraint("--iobase-2", ParamConstraint(true, DATA_TYPE::VOID, {}, false, { "--iobase-8", "--iobase-10", "--iobase-64", "--iobase-uwu", "--iobase-ugh" })); + nupp.RegisterConstraint("--iobase-2", ParamConstraint(true, DATA_TYPE::VOID, {}, false, { "--iobase-bytes", "--iobase-8", "--iobase-10", "--iobase-16", "--iobase-64", "--iobase-uwu", "--iobase-ugh" })); nupp.RegisterDescription("--iobase-8", "Interpret and format ciphertexts in base8"); - nupp.RegisterConstraint("--iobase-8", ParamConstraint(true, DATA_TYPE::VOID, {}, false, { "--iobase-2", "--iobase-10", "--iobase-64", "--iobase-uwu", "--iobase-ugh" })); + nupp.RegisterConstraint("--iobase-8", ParamConstraint(true, DATA_TYPE::VOID, {}, false, { "--iobase-bytes", "--iobase-2", "--iobase-10", "--iobase-16", "--iobase-64", "--iobase-uwu", "--iobase-ugh" })); nupp.RegisterDescription("--iobase-10", "Interpret and format ciphertexts in base10"); - nupp.RegisterConstraint("--iobase-10", ParamConstraint(true, DATA_TYPE::VOID, {}, false, { "--iobase-2", "--iobase-8", "--iobase-64", "--iobase-uwu", "--iobase-ugh" })); + nupp.RegisterConstraint("--iobase-10", ParamConstraint(true, DATA_TYPE::VOID, {}, false, { "--iobase-bytes", "--iobase-2", "--iobase-8", "--iobase-16", "--iobase-64", "--iobase-uwu", "--iobase-ugh" })); + + nupp.RegisterDescription("--iobase-16", "Interpret and format ciphertexts in base16 (hex)"); + nupp.RegisterConstraint("--iobase-16", ParamConstraint(true, DATA_TYPE::VOID, {}, false, { "--iobase-bytes", "--iobase-2", "--iobase-8", "--iobase-64", "--iobase-uwu", "--iobase-ugh" })); nupp.RegisterDescription("--iobase-64", "Interpret and format ciphertexts in base64"); - nupp.RegisterConstraint("--iobase-64", ParamConstraint(true, DATA_TYPE::VOID, {}, false, { "--iobase-2", "--iobase-8", "--iobase-10", "--iobase-uwu", "--iobase-ugh" })); + nupp.RegisterConstraint("--iobase-64", ParamConstraint(true, DATA_TYPE::VOID, {}, false, { "--iobase-bytes", "--iobase-2", "--iobase-8", "--iobase-10", "--iobase-16", "--iobase-uwu", "--iobase-ugh" })); nupp.RegisterDescription("--iobase-uwu", "Interpret and format ciphertexts in base uwu"); - nupp.RegisterConstraint("--iobase-uwu", ParamConstraint(true, DATA_TYPE::VOID, {}, false, { "--iobase-2", "--iobase-8", "--iobase-10", "--iobase-64", "--iobase-ugh" })); + nupp.RegisterConstraint("--iobase-uwu", ParamConstraint(true, DATA_TYPE::VOID, {}, false, { "--iobase-bytes", "--iobase-2", "--iobase-8", "--iobase-10", "--iobase-16", "--iobase-64", "--iobase-ugh" })); nupp.RegisterDescription("--iobase-ugh", "Interpret and format ciphertexts in base ugh"); - nupp.RegisterConstraint("--iobase-ugh", ParamConstraint(true, DATA_TYPE::VOID, {}, false, { "--iobase-2", "--iobase-8", "--iobase-10", "--iobase-64", "--iobase-uwu" })); + nupp.RegisterConstraint("--iobase-ugh", ParamConstraint(true, DATA_TYPE::VOID, {}, false, { "--iobase-bytes", "--iobase-2", "--iobase-8", "--iobase-10", "--iobase-16", "--iobase-64", "--iobase-uwu" })); - nupp.RegisterDescription("--version", "Will supply the version of ghettocrypt used."); - nupp.RegisterConstraint("--version", ParamConstraint(true, DATA_TYPE::VOID, {}, false, {})); - nupp.RegisterAbbreviation("-v", "--version"); + nupp.RegisterDescription("--lib-version", "Will supply the version of GCryptLib used."); + nupp.RegisterConstraint("--lib-version", ParamConstraint(true, DATA_TYPE::VOID, {}, false, {})); - nupp.RegisterDescription("--cli-version", "Will supply the version of ghettocrypt-cli used."); + nupp.RegisterDescription("--cli-version", "Will supply the version of GCryptCLI used."); nupp.RegisterConstraint("--cli-version", ParamConstraint(true, DATA_TYPE::VOID, {}, false, {})); + nupp.RegisterAbbreviation("-v", "--cli-version"); + + nupp.RegisterDescription("--puffer-input", "Will read the entire input before beginning any digestion."); + nupp.RegisterConstraint("--puffer-input", ParamConstraint(true, DATA_TYPE::VOID, {}, false, {})); + + nupp.RegisterDescription("--puffer-output", "Will digest the entire data before initiating any output."); + nupp.RegisterConstraint("--puffer-output", ParamConstraint(true, DATA_TYPE::VOID, {}, false, {})); + + nupp.RegisterDescription("--no-newline", "Don't postfix stdout output with a newline"); + nupp.RegisterConstraint("--no-newline", ParamConstraint(true, DATA_TYPE::VOID, {}, false, {})); /* Now parse */ nupp.Parse(argc, argv); @@ -103,45 +120,69 @@ void CommandlineInterface::Init(int argc, const char* const* argv) return; } -Hazelnp::CmdArgsInterface& CommandlineInterface::Get() -{ +Hazelnp::CmdArgsInterface& CommandlineInterface::Get() { return nupp; } -void CommandlineInterface::SpecialCompatibilityChecking() -{ - // Encryption key - // Do we have EITHER --hash (no key required), --key, --keyask or --keyfile given? - if ( - (!nupp.HasParam("--hash")) && - (!nupp.HasParam("--key")) && - (!nupp.HasParam("--keyfile")) && - (!nupp.HasParam("--keyask")) - ) - CrashWithMsg("No encryption key supplied! Please supply either --key, --keyfile, or --keyask!"); +void CommandlineInterface::SpecialCompatibilityChecking() { - // Encryption input - // Do we have EITHER --intext or --infile? - if ( - (!nupp.HasParam("--intext")) && - (!nupp.HasParam("--infile")) - ) - CrashWithMsg("No encryption input supplied! Please supply either --intext or --infile!"); - - // Encryption mode + // Active module // Do we have EITHER --encrypt or --decrypt or --hash? if ( + (!nupp.HasParam("--generate-key")) && (!nupp.HasParam("--hash")) && (!nupp.HasParam("--encrypt")) && (!nupp.HasParam("--decrypt")) - ) - CrashWithMsg("No encryption mode supplied! Please supply either --encrypt, --decrypt, or --hash!"); + ) { + CrashWithMsg("No module supplied! Please supply either --encrypt, --decrypt, --hash, or --generate-key!"); + } + + // Encryption key + // Do we have EITHER --hash (no key required), --generate-key (no key required), --key, --keyask or --keyfile given? + if ( + (!nupp.HasParam("--hash")) && + (!nupp.HasParam("--generate-key")) && + (!nupp.HasParam("--key")) && + (!nupp.HasParam("--keyfile")) && + (!nupp.HasParam("--keyask")) + ) { + CrashWithMsg("No encryption key supplied! Please supply either --key, --keyfile, or --keyask!"); + } + + // Check that, if supplied, filename strings are not empty. + if ( + (nupp.HasParam("--ofile")) && + (nupp["--ofile"].GetString().length() == 0) + ) { + CrashWithMsg("Length of --ofile is zero! That can't be a valid path!"); + } + + if ( + (nupp.HasParam("--ifile")) && + (nupp["--ifile"].GetString().length() == 0) + ) { + CrashWithMsg("Length of --ifile is zero! That can't be a valid path!"); + } + + if ( + (nupp.HasParam("--keyfile")) && + (nupp["--keyfile"].GetString().length() == 0) + ) { + CrashWithMsg("Length of --keyfile is zero! That can't be a valid path!"); + } + + if ( + (nupp.HasParam("--progress")) && + (!nupp.HasParam("--puffer-input")) + + ) { + CrashWithMsg("--progress requires --puffer-input to work!"); + } return; } -void CommandlineInterface::CrashWithMsg(const std::string& msg) -{ +void CommandlineInterface::CrashWithMsg(const std::string& msg) { std::cerr << nupp.GetBriefDescription() << std::endl @@ -151,16 +192,17 @@ void CommandlineInterface::CrashWithMsg(const std::string& msg) exit(-1); } -void CommandlineInterface::CatchVersionQueries() -{ - if (nupp.HasParam("--version")) - { - std::cout << GHETTOCRYPT_VERSION << std::endl; +void CommandlineInterface::CatchVersionQueries() { + if ( + (nupp.HasParam("--version")) || + (nupp.HasParam("--cli-version")) + ) { + std::cout << GCRYPTCLI_VERSION << std::endl; exit(0); } - else if (nupp.HasParam("--cli-version")) + else if (nupp.HasParam("--lib-version")) { - std::cout << GHETTOCRYPTCLI_VERSION << std::endl; + std::cout << GCRYPT_VERSION << std::endl; exit(0); } diff --git a/GCryptCLI/src/Configuration.cpp b/GCryptCLI/src/Configuration.cpp new file mode 100644 index 0000000..e56365c --- /dev/null +++ b/GCryptCLI/src/Configuration.cpp @@ -0,0 +1,186 @@ +#include "Configuration.h" +#include "CommandlineInterface.h" + +void Configuration::Parse() { + DecideModule(); + DecideInputFrom(); + DecideOutputTo(); + DecideCiphertextFormat(); + MapCiphertextFormatToIOBases(); + + return; +} + +void Configuration::DecideModule() { + if (CommandlineInterface::Get().HasParam("--encrypt")) { + activeModule = MODULE::ENCRYPTION; + return; + } + else if (CommandlineInterface::Get().HasParam("--decrypt")) { + activeModule = MODULE::DECRYPTION; + return; + } + else if (CommandlineInterface::Get().HasParam("--hash")) { + activeModule = MODULE::HASH; + return; + } + else if (CommandlineInterface::Get().HasParam("--generate-key")) { + activeModule = MODULE::GENERATE_KEY; + return; + } + + throw std::runtime_error("No module option found. Is the CLI parser configuration correct?."); + + return; +} + +void Configuration::DecideInputFrom() { + + if (CommandlineInterface::Get().HasParam("--intext")) { + inputFrom = INPUT_FROM::PARAMETER; + } + else if (CommandlineInterface::Get().HasParam("--infile")) { + inputFrom = INPUT_FROM::FILE; + inputFilename = CommandlineInterface::Get()["--infile"].GetString(); + } + else { + inputFrom = INPUT_FROM::STDIN; + } + + return; +} + +void Configuration::DecideOutputTo() { + + if (CommandlineInterface::Get().HasParam("--ofile")) { + outputTo = OUTPUT_TO::FILE; + outputFilename = CommandlineInterface::Get()["--ofile"].GetString(); + } + else { + outputTo = OUTPUT_TO::STDOUT; + } + + return; +} + +void Configuration::DecideCiphertextFormat() { + + // Do we have any iobase explicitly specified? + if (CommandlineInterface::Get().HasParam("--iobase-bytes")) { + ciphertextFormat = IOBASE_FORMAT::BASE_BYTES; + } + else if (CommandlineInterface::Get().HasParam("--iobase-2")) { + ciphertextFormat = IOBASE_FORMAT::BASE_2; + } + else if (CommandlineInterface::Get().HasParam("--iobase-8")) { + ciphertextFormat = IOBASE_FORMAT::BASE_8; + } + else if (CommandlineInterface::Get().HasParam("--iobase-10")) { + ciphertextFormat = IOBASE_FORMAT::BASE_10; + } + else if (CommandlineInterface::Get().HasParam("--iobase-16")) { + ciphertextFormat = IOBASE_FORMAT::BASE_16; + } + else if (CommandlineInterface::Get().HasParam("--iobase-64")) { + ciphertextFormat = IOBASE_FORMAT::BASE_64; + } + else if (CommandlineInterface::Get().HasParam("--iobase-uwu")) { + ciphertextFormat = IOBASE_FORMAT::BASE_UWU; + } + else if (CommandlineInterface::Get().HasParam("--iobase-ugh")) { + ciphertextFormat = IOBASE_FORMAT::BASE_UGH; + } + + // So we have no iobase explicitly specified.. Let's default.. + + // If we are encrypting, + else if (activeModule == MODULE::ENCRYPTION) { + // and input comes from a parameter, + // and output goes to stdout, + // let's assume base-16. + if ( + (inputFrom == INPUT_FROM::PARAMETER) && + (outputTo == OUTPUT_TO::STDOUT) + ) { + ciphertextFormat = IOBASE_FORMAT::BASE_16; + } + + // Any other case whilst encrypting, we'll assume base-bytes. + else { + ciphertextFormat = IOBASE_FORMAT::BASE_BYTES; + return; + } + + } + + // Else, if we are hashing, + else if (activeModule == MODULE::HASH) { + // output is always defaults to base 16 + ciphertextFormat = IOBASE_FORMAT::BASE_16; + } + + // Else, if we are decrypting, + else if (activeModule == MODULE::DECRYPTION) { + // and input comes from a parameter, we'll assume base-16. + if (inputFrom == INPUT_FROM::PARAMETER) { + ciphertextFormat = IOBASE_FORMAT::BASE_16; + } + // Any other case whilst decrypting, we'll assume base-bytes. + else { + ciphertextFormat = IOBASE_FORMAT::BASE_BYTES; + } + } + + // Else, if we are generating a key, + else if (activeModule == MODULE::GENERATE_KEY) { + // and we're outputting to stdout, we'll use base-16. + if (outputTo == OUTPUT_TO::STDOUT) { + ciphertextFormat = IOBASE_FORMAT::BASE_16; + } + // else, we're outputting to a file, use base-bytes. + else { + ciphertextFormat = IOBASE_FORMAT::BASE_BYTES; + } + } + + // Fallback: Bytes + else { + ciphertextFormat = IOBASE_FORMAT::BASE_BYTES; + } + + return; +} + +void Configuration::MapCiphertextFormatToIOBases() { + + // Now, map the ciphertextFormat to either formatIn or formatOut. + switch (activeModule) { + // For encryption, keygen, and hashing: + // input is bytes and output is ciphertext + case MODULE::ENCRYPTION: + case MODULE::HASH: + case MODULE::GENERATE_KEY: + formatIn = IOBASE_FORMAT::BASE_BYTES; + formatOut = ciphertextFormat; + break; + + // For decryption: + // input is ciphertext and output is bytes + case MODULE::DECRYPTION: + formatIn = ciphertextFormat; + formatOut = IOBASE_FORMAT::BASE_BYTES; + break; + } + + return; +} + +std::string Configuration::inputFilename; +std::string Configuration::outputFilename; +Configuration::MODULE Configuration::activeModule; +Configuration::IOBASE_FORMAT Configuration::formatIn; +Configuration::IOBASE_FORMAT Configuration::formatOut; +Configuration::IOBASE_FORMAT Configuration::ciphertextFormat; +Configuration::INPUT_FROM Configuration::inputFrom; +Configuration::OUTPUT_TO Configuration::outputTo; + diff --git a/GCryptCLI/src/DataFormatter.cpp b/GCryptCLI/src/DataFormatter.cpp new file mode 100644 index 0000000..345a47a --- /dev/null +++ b/GCryptCLI/src/DataFormatter.cpp @@ -0,0 +1,299 @@ +#include "DataFormatter.h" +#include "Bases.h" +#include +#include +#include + +using namespace Leonetienne::GCrypt; +using namespace Leonetienne::StringTools; +using namespace Leonetienne::GeneralUtility; + +std::string DataFormatter::FormatBlock( + const Block& block, + const Configuration::IOBASE_FORMAT base +) { + switch (base) { + case Configuration::IOBASE_FORMAT::BASE_BYTES: + return block.ToByteString(); + + case Configuration::IOBASE_FORMAT::BASE_2: + return block.ToBinaryString(); + + case Configuration::IOBASE_FORMAT::BASE_8: + return Bin2CustomBase( + block.ToBinaryString(), + BASE_8, + blockLengthByBase[base] + ); + + case Configuration::IOBASE_FORMAT::BASE_10: + return Bin2CustomBase( + block.ToBinaryString(), + BASE_10, + blockLengthByBase[base] + ); + + case Configuration::IOBASE_FORMAT::BASE_16: + return block.ToHexString(); + + case Configuration::IOBASE_FORMAT::BASE_64: + return Bin2CustomBase( + block.ToBinaryString(), + BASE_64, + blockLengthByBase[base] + ); + + case Configuration::IOBASE_FORMAT::BASE_UWU: + return Bin2CustomBase( + block.ToBinaryString(), + BASE_UWU, + blockLengthByBase[base], + " " + ); + + case Configuration::IOBASE_FORMAT::BASE_UGH: + return Bin2CustomBase( + block.ToBinaryString(), + BASE_UGH, + blockLengthByBase[base], + " " + ); + + default: + throw std::invalid_argument("FormatBlock(): Iobase not found! Oh no. Anyway."); + } +} + +std::string DataFormatter::FormatBlocks( + const std::vector& blocks, + const Configuration::IOBASE_FORMAT base +) { + std::stringstream ss; + + std::size_t i = 0; + for (const Block& block : blocks) { + ss << FormatBlock(block, base); + + // If we are dealing with a multichar base, we must append a + // seperator (space), but only if its not the last block. + if ( + (base == Configuration::IOBASE_FORMAT::BASE_UWU) || + (base == Configuration::IOBASE_FORMAT::BASE_UGH) + ) { + if (i++ != blocks.size()) { + ss << " "; + } + } + } + + return ss.str(); +} + +Block DataFormatter::DecodeFormat( + const std::string& str, + const Configuration::IOBASE_FORMAT base +) { + + Block b; + + switch (base) { + case Configuration::IOBASE_FORMAT::BASE_BYTES: + b.FromByteString(str); + break; + + case Configuration::IOBASE_FORMAT::BASE_2: + b.FromBinaryString(str); + break; + + case Configuration::IOBASE_FORMAT::BASE_8: + b.FromBinaryString( + CustomBase2Bin( + str, + BASE_8 + ) + ); + break; + + case Configuration::IOBASE_FORMAT::BASE_10: + b.FromBinaryString( + CustomBase2Bin( + str, + BASE_10 + ) + ); + break; + + case Configuration::IOBASE_FORMAT::BASE_16: + b.FromHexString(str); + break; + + case Configuration::IOBASE_FORMAT::BASE_64: + b.FromBinaryString( + CustomBase2Bin( + str, + BASE_64 + ) + ); + break; + + case Configuration::IOBASE_FORMAT::BASE_UWU: + b.FromBinaryString( + CustomBase2Bin( + str, + BASE_UWU, + " " + ) + ); + break; + + case Configuration::IOBASE_FORMAT::BASE_UGH: + b.FromBinaryString( + CustomBase2Bin( + str, + BASE_UGH, + " " + ) + ); + break; + + default: + throw std::invalid_argument("StringToBlock(): Iobase now found! Oh no. Anyway."); + } + + return b; +} + +std::vector DataFormatter::DecodeFormatMultiblock( + const std::string& str, + const Configuration::IOBASE_FORMAT base +) { + std::vector blocks; + + // A block is this many digits wide, in encoding + const std::size_t blockWidth = blockLengthByBase[base]; + + // How many blocks are we dealing with here? + const std::size_t n_blocks = (str.length() / blockWidth) + 1; + blocks.reserve(n_blocks); + + // Iterate over the string, and parse all blocks + // We now have to differentiate between single-char digit sets (like hex), + // and multi-char digit sets (like uwu): + switch (base) { + case Configuration::IOBASE_FORMAT::BASE_BYTES: + case Configuration::IOBASE_FORMAT::BASE_2: + case Configuration::IOBASE_FORMAT::BASE_8: + case Configuration::IOBASE_FORMAT::BASE_10: + case Configuration::IOBASE_FORMAT::BASE_16: + case Configuration::IOBASE_FORMAT::BASE_64: + // Easy case: Each digit is exactly one char in size. + // We can just calculate how many bytes we should read. + for (std::size_t i = 0; i < str.length(); i += blockWidth) { + + const std::string subs = str.substr(i, blockWidth); + + Block newBlock = DecodeFormat( + subs, + base + ); + + blocks.emplace_back(newBlock); + } + break; + + case Configuration::IOBASE_FORMAT::BASE_UWU: + case Configuration::IOBASE_FORMAT::BASE_UGH: { + // Hard case: Each digit is n digits long. Digits may vary in length. + // They are seperated by spaces. + // We have to parse them... + std::size_t digitsPassed = 0; + std::size_t blockStart = 0; + for (std::size_t i = 0; i < str.length(); i++) { + + if (str[i] == ' ') { + digitsPassed++; + + if (digitsPassed == blockWidth) { + const std::string subs = str.substr( + blockStart, + i - blockStart + ); + + Block newBlock = DecodeFormat( + subs, + base + ); + + blocks.emplace_back(newBlock); + + digitsPassed = 0; + blockStart = i+1; + } + } + } + // Here should never be any digits left. A formatted block should ALWAYS be full length. + break; + } + + default: + throw std::invalid_argument("DataFormatter::StringToBlocks() has been passed an unknown base! No switch-case matched!"); + break; + + } + + return blocks; +} + +std::string DataFormatter::Bin2CustomBase( + const std::string& bin, + const std::vector& customSet, + const std::size_t minLen, + const std::string& seperator +) { + std::stringstream ss; + + // Translate to custom set + const std::vector vec_base_custom = + Leonetienne::GeneralUtility::BaseConversion::BaseX_2_Y>( + bin, + "01", + customSet, + minLen + ); + + // Convert to string + for (std::size_t i = 0; i < vec_base_custom.size(); i++) { + ss << vec_base_custom[i]; + + if (i != vec_base_custom.size() - 1) { + ss << seperator; + } + } + + return ss.str(); +} + +std::string DataFormatter::CustomBase2Bin( + const std::string& in, + const std::vector& customSet, + const std::string& seperator +) { + // Split input into symbols + const std::vector in_symbols = StringTools::Split(in, seperator); + + // Translate to binary + std::string binary = + Leonetienne::GeneralUtility::BaseConversion::BaseX_2_Y, std::string>( + in_symbols, + customSet, + std::string("01"), + Block::BLOCK_SIZE_BITS + ); + + if (binary.length() != Block::BLOCK_SIZE_BITS) { + throw std::invalid_argument("DataFormatter::CustomBase2Bin() received input that doesn't translate to a bitstring of length 512!"); + } + + return binary; +} + diff --git a/GCryptCLI/src/DataIngestionLayer.cpp b/GCryptCLI/src/DataIngestionLayer.cpp new file mode 100644 index 0000000..96c1991 --- /dev/null +++ b/GCryptCLI/src/DataIngestionLayer.cpp @@ -0,0 +1,369 @@ +#include "DataIngestionLayer.h" +#include "CommandlineInterface.h" +#include "DataFormatter.h" +#include "Bases.h" +#include +#include +#include +#include +#include +#include + +using namespace IO; +using namespace Leonetienne::StringTools; + +void DataIngestionLayer::Init() { + + // Set our istream + switch (Configuration::inputFrom) { + + // Are we reading from stdin? + case Configuration::INPUT_FROM::STDIN: + + // Redirect our istream to stdin + in = &std::cin; + break; + + // Are we reading from a file? + case Configuration::INPUT_FROM::FILE: + + // Open the file + ifs.open( + Configuration::inputFilename, + std::ios::in | std::ios::binary + ); + + // A little bit of error handling + if (!ifs.good()) { + throw std::runtime_error("Unable to open infilestream!"); + } + + // Redirect our istream to this infilestream + in = &ifs; + break; + + // Are we reading from a parameter? + case Configuration::INPUT_FROM::PARAMETER: + + // Create an instringstream with our parameter + iss = std::istringstream( + CommandlineInterface::Get()["--intext"].GetString() + ); + + // Redirect our istream to this instringstream + in = &iss; + + break; + } + + // Derive from our the current module if we're reading ciphertext or not + if ( + (Configuration::activeModule == Configuration::MODULE::DECRYPTION) + ) { + isReadingCiphertext = true; + } + else { + isReadingCiphertext = false; + } + + initialized = true; + reachedEof = false; + nBlocksRead = 0; + + return; +} + +void DataIngestionLayer::Destruct() { + + if (Configuration::inputFrom == Configuration::INPUT_FROM::FILE) { + ifs.close(); + } + + return; +} + +void DataIngestionLayer::ReadBlock() { + if (!initialized) { + throw std::runtime_error("Attempted to read on uninitialized DataIngestionLayer!"); + } + + if (!reachedEof) { + // A block is this many digits wide, in encoding + const std::size_t blockWidth = blockLengthByBase[Configuration::formatIn]; + + // Iterate over the string, and parse all blocks + // We now have to differentiate between single-char digit sets (like hex), + // and multi-char digit sets (like uwu): + switch (Configuration::formatIn) { + case Configuration::IOBASE_FORMAT::BASE_BYTES: + case Configuration::IOBASE_FORMAT::BASE_2: + case Configuration::IOBASE_FORMAT::BASE_8: + case Configuration::IOBASE_FORMAT::BASE_10: + case Configuration::IOBASE_FORMAT::BASE_16: + case Configuration::IOBASE_FORMAT::BASE_64: { + // Easy case: Each digit is exactly one char in size. + // We can just calculate how many bytes we have to read. + + // bytesRead is always of the correct length, 0-padded. + std::size_t n_bytes_read; + const std::string dataRead = ReadBytes(blockWidth, n_bytes_read); + + // If we've read 0 bytes, this was the last block + // and it's completely empty. We can abort without doing anything. + // The ReadBytes function takes care of setting the reachedEof flag. + if (n_bytes_read == 0) { + return; + } + + // If we are reading ciphertext + // make sure we've read enough bytes to compose a block. + if ( + (isReadingCiphertext) && + (n_bytes_read < blockWidth) + ) { + throw std::runtime_error("DataIngestionLayer::ReadBlock() read an input-data fragment that is smaller than a data block should be. Is your cipher text incomplete?"); + } + + // This should decode to a block just like this. + Block newBlock; + + // Special-case: We are reading cleartext (no ciphertext) + // cleartext is always base_bytes + if (!isReadingCiphertext) { + // When just reading cleartext-bytes, we also allow shorter strings + // than BLOCK_SIZE. These will just get zero-padded. + newBlock.FromTextString(dataRead); + } + else { + // Else: recode to a block. + newBlock = DataFormatter::DecodeFormat( + dataRead, + Configuration::formatIn + ); + } + + blocks.emplace(newBlock); + nBlocksRead++; + break; + } + + case Configuration::IOBASE_FORMAT::BASE_UWU: + case Configuration::IOBASE_FORMAT::BASE_UGH: { + // The whole of Italy doesn't have as much spaghetti as this is... + + // Hard case: Each digit is n digits long. Digits may vary in length. + // They are seperated by spaces. + // We have to parse them... + std::string overshoot = ""; // this is how much we've read too much in the last iteration + + + // Gets terminated by a break statement + while (1) { + // We'll read chunks of 64 bytes... This should be a good + // median, to also support small multi-byte-digit sets + std::size_t n_bytes_read = 0; + int lastDigitPos = -1; // Should point the the space BEFORE it. Relative to chunk. + std::size_t digitsCollected = 0; + std::stringstream digits; + bool foundBlock = false; + + // Remember to prepend our overshoot from the previous iteration this chunk + std::string chunk = overshoot + ReadBytes(64, n_bytes_read); + + // We should also strip all linebreaks from the chunk, that may be a result of manual stdin input. + chunk = StringTools::Replace(chunk, '\n', ""); + + // We can't just check for completeness by n_bytes_read... + // It can be any number of bytes, since any digit is n bytes long... + + // Parse the 64-byte chunk string we've just fetched: + for (std::size_t i = 0; i < chunk.size(); i++) { + + // If we are near the end, and have still not found a complete block, let's load an additional chunk + if (i == chunk.size() - 2) { + const std::string nextChunk = ReadBytes(64, n_bytes_read); + if (n_bytes_read != 0) { + chunk += StringTools::Replace(nextChunk, '\n', ""); + } + } + + // If i is on a space, or at the end of the chunk, + // and, at least one of i, or lastDigitPos is on a space, + if ( + ( + (chunk[i] == ' ') || + (i == chunk.size() - 1) + ) && + ( + (chunk[i] == ' ') || + lastDigitPos >= 0 // This basically does the same as discribed, but safer, as its default value is -1. + ) + ){ + digitsCollected++; + + // We have found a digit. Let's store it away... + // We're putting them in a stringstream, to fit the format required by the data formatter... + + // We have a slight edgecase if we're currently on the last char. Then we do NOT want to read one short. + // This is because we ususally stand on a seperator char (' '), which we do not want to extract. But in that case, + // in which we're standing on the last char, it could be not a seperator char. + // note: chunk[i] != ' ' can only be true if we're on the last char. + if (chunk[i] == ' ') { + digits << + chunk.substr( + lastDigitPos + 1, + (int)i - lastDigitPos - 1 + ) + ; + } else { + digits << + chunk.substr( + lastDigitPos + 1, + (int)i - lastDigitPos + ) + ; + } + + // Add a seperator, if its not the last + if (digitsCollected != blockWidth) { + digits << ' '; + } + + lastDigitPos = i; + + // Do we have enough digits to form a block? + if (digitsCollected == blockWidth) { + // We've found a complete block! + + // Trim excess nullbytes off out digit string + const std::string digitString = std::string( + digits.str().data(), + strlen(digits.str().data()) + ); + + // Decode it to a block object + const Block newBlock = DataFormatter::DecodeFormat( + digitString, + Configuration::formatIn + ); + + // Enqueue it to be processed by some module + blocks.emplace(newBlock); + nBlocksRead++; + foundBlock = true; + + // Now we have to calculate how many bytes we've read TOO MANY. + // We have to trim this current chunk to be our new overshoot. + + // If we still have more than a byte left, leave out the current seperator char + if (i < chunk.size() - 1) { + overshoot = chunk.substr(i+1); // Take all bytes from the next iterator, to the end + } + // Else: we are on the last char: there is no overshoot + else { + overshoot = ""; + } + + // Stop the for loop + break; + } + } + } + + // Exit-condition: + // We have not found any block, not even any digit. + if ((!foundBlock) && (digitsCollected == 0)) { + break; + } + + // Hard-abort: We have not finished reading a block + if (!foundBlock) { + throw std::runtime_error("DataIngestionLayer reached EOF whilst parsing multi-byte-digit block..."); + } + } + + break; + } + + default: + throw std::invalid_argument("DataFormatter::StringToBlocks() has been passed an unknown base! No switch-case matched!"); + } + + } + + return; +} + +std::string DataIngestionLayer::ReadBytes(const std::size_t n, std::size_t& out_bytes_read) { + + // Prepare a buffer to read to + char* buf = new char[n+1]; + memset(buf, 0, (n+1) * sizeof(buf[0])); + + // Read + in->read(buf, n * sizeof(buf[0])); + + // Fetch how much we've read + out_bytes_read = in->gcount(); + + // Is this fewer bytes than got requested? + if (out_bytes_read < n) { + // Yes: EOF reached. + reachedEof = true; + } + + // Translate buffer to a standard string + const std::string sbuf(buf, n); + delete[] buf; + + // Return our buffer + return sbuf; +} + +bool DataIngestionLayer::ReachedEOF() { + return reachedEof; +} + +bool DataIngestionLayer::IsBlockReady() { + // We're not ready, if we haven't reached EOF, if we should puffer + // the input. + if ( + (CommandlineInterface::Get().HasParam("--puffer-input")) && + (!reachedEof) + ) { + return false; + } + + // If we're not puffering, just return whether or not + // we have any blocks... + return blocks.size() > 0; +} + +bool DataIngestionLayer::IsFinished() { + return (reachedEof) && (blocks.size() == 0); +} + +Block DataIngestionLayer::GetNextBlock() { + if (!IsBlockReady()) { + throw std::runtime_error("Attempted to get the next block, but there are none left!"); + } + + // Why... why not just return a T in pop()??? + const Block popped = blocks.front(); + blocks.pop(); + return popped; +} + +std::size_t DataIngestionLayer::NBlocksRead() { + return nBlocksRead; +} + +std::istream* DataIngestionLayer::in; +std::ifstream DataIngestionLayer::ifs; +std::istringstream DataIngestionLayer::iss; +bool DataIngestionLayer::reachedEof = false; +bool DataIngestionLayer::initialized = false; +bool DataIngestionLayer::isReadingCiphertext; +std::size_t DataIngestionLayer::nBlocksRead = 0; +std::queue DataIngestionLayer::blocks; + diff --git a/GCryptCLI/src/DataOutputLayer.cpp b/GCryptCLI/src/DataOutputLayer.cpp new file mode 100644 index 0000000..26fd4eb --- /dev/null +++ b/GCryptCLI/src/DataOutputLayer.cpp @@ -0,0 +1,149 @@ +#include "DataOutputLayer.h" +#include "DataFormatter.h" +#include "CommandlineInterface.h" +#include +#include +#include + +using namespace IO; + +void DataOutputLayer::Init() { + + // Set our ostream + switch (Configuration::outputTo) { + + // Are we writing to stdout? + case Configuration::OUTPUT_TO::STDOUT: + + // Redirect our ostream to stdout + out = &std::cout; + break; + + // Are we writing to a file? + case Configuration::OUTPUT_TO::FILE: + + // Open the file + ofs.open( + Configuration::outputFilename, + std::ios::out | std::ios::binary + ); + + // A little bit of error handling + if (!ofs.good()) { + throw std::runtime_error("Unable to open outfilestream!"); + } + + // Redirect our ostream to this outfilestream + out = &ofs; + break; + } + + initialized = true; + reachedEof = false; + + return; + +} + +void DataOutputLayer::Destruct() { + if (Configuration::outputTo == Configuration::OUTPUT_TO::FILE) { + ofs.close(); + } + + return; +} + +void DataOutputLayer::Enqueue(const Block& block) { + blocks.emplace(block); + return; +} + +void DataOutputLayer::WriteBlock() { + // Some error checking + if (!initialized) { + throw std::runtime_error("Attempted to write on uninitialized DataOutputLayer!"); + } + + // Check if we have any block to write + // and if we should (output-puffering) + // Basically: only output if we have anything to output, and + // if --puffer-output is given, only output once we have reachedEof. + if ( + (blocks.size() > 0) && + ( + (!CommandlineInterface::Get().HasParam("--puffer-output")) || + (reachedEof) + ) + ) { + // Fetch the block to write + const Block block = blocks.front(); + blocks.pop(); + + // Recode it to our output format + const std::string formattedBlock = + DataFormatter::FormatBlock( + block, + Configuration::formatOut + ); + + // Dump it + // This way we avoid zerobytes getting trimmed off... + out->write(formattedBlock.data(), formattedBlock.length()); + + // If this is not the last block, and the used iobase set + // requires it, append a seperator + if ( + (!IsFinished()) && + ( + (Configuration::formatOut == Configuration::IOBASE_FORMAT::BASE_UWU) || + (Configuration::formatOut == Configuration::IOBASE_FORMAT::BASE_UGH) + ) + ) { + *out << " "; + } + + AddTrailingLinebreakIfRequired(); + out->flush(); + + } + + return; +} + +void DataOutputLayer::ReachedEOF() { + reachedEof = true; + + // Add the trailing linebreak here aswell, as, if input is ciphertext, + // ReachedEOF() may only be called after all blocks are already written + AddTrailingLinebreakIfRequired(); + + return; +} + +bool DataOutputLayer::IsFinished() { + return (reachedEof) && (blocks.size() == 0); +} + +void DataOutputLayer::AddTrailingLinebreakIfRequired() { + // If we are finished, and are outputting to stdout, + // and input format is not bytes, + // and the user didn't specifically opt out, print a newline + if ( + (IsFinished()) && + (Configuration::outputTo == Configuration::OUTPUT_TO::STDOUT) && + (Configuration::formatOut != Configuration::IOBASE_FORMAT::BASE_BYTES) && + (!CommandlineInterface::Get().HasParam("--no-newline")) + ) { + *out << std::endl; + out->flush(); + } + + return; +} + +std::ostream* DataOutputLayer::out; +std::ofstream DataOutputLayer::ofs; +bool DataOutputLayer::reachedEof = false; +bool DataOutputLayer::initialized = false; +std::queue DataOutputLayer::blocks; + diff --git a/GCryptCLI/src/KeyManager.cpp b/GCryptCLI/src/KeyManager.cpp new file mode 100644 index 0000000..94b758a --- /dev/null +++ b/GCryptCLI/src/KeyManager.cpp @@ -0,0 +1,107 @@ +#include "KeyManager.h" +#include "CommandlineInterface.h" +#include "Configuration.h" +#include +#include + +// Required for hidden password input +#if defined _WIN32 || defined _WIN64 +#include +#elif defined __GNUG__ +#include +#include +#endif + +void KeyManager::PrepareKey() { + + // Special-case: We are hashing: + // no key needed. + if (Configuration::activeModule == Configuration::MODULE::HASH) { + return; + } + + // Special-case: We are generating a keyfile: + // generate a random key from hardware events. + else if (Configuration::activeModule == Configuration::MODULE::GENERATE_KEY) { + key = Key::Random(); + return; + } + + // Else: + // Is a password passed on the command line? + else if (CommandlineInterface::Get().HasParam("--key")) { + // Fetch it, and hash it to a key + std::string password = CommandlineInterface::Get()["--key"].GetString(); + + key = Key::FromPassword(password); + + // Don't forget to zero string memory. + memset(password.data(), 0, password.size()); + return; + } + + // Should we prompt for a password on stdin? + else if (CommandlineInterface::Get().HasParam("--keyask")) { + // Create a password prompt + std::string password = PasswordPrompt(); + + key = Key::FromPassword(password); + + // Don't forget to zero string memory. + memset(password.data(), 0, password.size()); + return; + } + + // Else: + // Should we read from a keyfile? + else if (CommandlineInterface::Get().HasParam("--keyfile")) { + // Fetch the filename, and read it + const std::string filepath = CommandlineInterface::Get()["--keyfile"].GetString(); + key = Key::LoadFromFile(filepath); + return; + } + + throw std::runtime_error("No key option found. Is the CLI parser configuration correct?."); + + return; +} + + +const Key& KeyManager::GetKey() { + return key; +} + +std::string KeyManager::PasswordPrompt() { +// Disable stdin-echo +#if defined _WIN32 || defined _WIN64 + HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE); + DWORD mode = 0; + GetConsoleMode(hStdin, &mode); + SetConsoleMode(hStdin, mode & (~ENABLE_ECHO_INPUT)); + +#elif defined __GNUG__ + termios oldt; + tcgetattr(STDIN_FILENO, &oldt); + termios newt = oldt; + newt.c_lflag &= ~ECHO; + tcsetattr(STDIN_FILENO, TCSANOW, &newt); + +#endif + + // Prompt stdin + std::string password; + std::cin >> password; + +// Restore previous config +#if defined _WIN32 || defined _WIN64 + SetConsoleMode(hStdin, mode); + +#elif defined __GNUG__ + tcsetattr(STDIN_FILENO, TCSANOW, &oldt); +#endif + + return password; +} + +Key KeyManager::key; + diff --git a/GCryptCLI/src/ModuleDecryption.cpp b/GCryptCLI/src/ModuleDecryption.cpp new file mode 100644 index 0000000..cf51487 --- /dev/null +++ b/GCryptCLI/src/ModuleDecryption.cpp @@ -0,0 +1,67 @@ +#include "ModuleDecryption.h" +#include "DataIngestionLayer.h" +#include "DataOutputLayer.h" +#include "ProgressPrinter.h" +#include "KeyManager.h" +#include + +using namespace Module; +using namespace Leonetienne::GCrypt; + +void Decryption::Run() { + + // Initialize the data ingestion layer + IO::DataIngestionLayer::Init(); + + // Initialize the data output layer + IO::DataOutputLayer::Init(); + + // Initialize a cipher + GCipher cipher( + KeyManager::GetKey(), + GCipher::DIRECTION::DECIPHER + ); + + std::size_t nBlocksDigested = 0; + while (!IO::DataOutputLayer::IsFinished()) { + // Read in new blocks, if not reached eof + if (!IO::DataIngestionLayer::ReachedEOF()) { + IO::DataIngestionLayer::ReadBlock(); + } + + // Process a block, if one is ready + if (IO::DataIngestionLayer::IsBlockReady()) { + + // Print progress, if appropriate + ProgressPrinter::PrintIfAppropriate( + "Decrypting", + nBlocksDigested, + IO::DataIngestionLayer::NBlocksRead() + ); + + const Block cleartext = IO::DataIngestionLayer::GetNextBlock(); + const Block ciphertext = cipher.Digest(cleartext); + nBlocksDigested++; + + // Enqueue the block for output + IO::DataOutputLayer::Enqueue(ciphertext); + } + + // Tell the data output layer that it received the + // last block, if it did + if (IO::DataIngestionLayer::IsFinished()) { + IO::DataOutputLayer::ReachedEOF(); + } + + // Attempt to write a block + IO::DataOutputLayer::WriteBlock(); + } + + // Destruct the data ingestion layer + IO::DataIngestionLayer::Destruct(); + + // Destruct the data output layer + IO::DataOutputLayer::Destruct(); + + return; +} diff --git a/GCryptCLI/src/ModuleEncryption.cpp b/GCryptCLI/src/ModuleEncryption.cpp new file mode 100644 index 0000000..b902f42 --- /dev/null +++ b/GCryptCLI/src/ModuleEncryption.cpp @@ -0,0 +1,68 @@ +#include "ModuleEncryption.h" +#include "DataIngestionLayer.h" +#include "DataOutputLayer.h" +#include "KeyManager.h" +#include "ProgressPrinter.h" +#include +#include + +using namespace Module; +using namespace Leonetienne::GCrypt; + +void Encryption::Run() { + + // Initialize the data ingestion layer + IO::DataIngestionLayer::Init(); + + // Initialize the data output layer + IO::DataOutputLayer::Init(); + + // Initialize a cipher + GCipher cipher( + KeyManager::GetKey(), + GCipher::DIRECTION::ENCIPHER + ); + + std::size_t nBlocksDigested = 0; + while (!IO::DataOutputLayer::IsFinished()) { + // Read in new blocks, if not reached eof + if (!IO::DataIngestionLayer::ReachedEOF()) { + IO::DataIngestionLayer::ReadBlock(); + } + + // Process a block, if one is ready + if (IO::DataIngestionLayer::IsBlockReady()) { + + // Print progress, if appropriate + ProgressPrinter::PrintIfAppropriate( + "Encrypting", + nBlocksDigested, + IO::DataIngestionLayer::NBlocksRead() + ); + + const Block cleartext = IO::DataIngestionLayer::GetNextBlock(); + const Block ciphertext = cipher.Digest(cleartext); + nBlocksDigested++; + + // Enqueue the block for output + IO::DataOutputLayer::Enqueue(ciphertext); + } + + // Tell the data output layer that it just received the + // last block, if it did + if (IO::DataIngestionLayer::IsFinished()) { + IO::DataOutputLayer::ReachedEOF(); + } + + // Attempt to write a block + IO::DataOutputLayer::WriteBlock(); + } + + // Destruct the data ingestion layer + IO::DataIngestionLayer::Destruct(); + + // Destruct the data output layer + IO::DataOutputLayer::Destruct(); + + return; +} diff --git a/GCryptCLI/src/ModuleGenerateKey.cpp b/GCryptCLI/src/ModuleGenerateKey.cpp new file mode 100644 index 0000000..3707829 --- /dev/null +++ b/GCryptCLI/src/ModuleGenerateKey.cpp @@ -0,0 +1,32 @@ +#include "ModuleGenerateKey.h" +#include "DataOutputLayer.h" +#include "KeyManager.h" +#include "CommandlineInterface.h" + +using namespace Leonetienne::GCrypt; +using namespace Module; + +void GenerateKey::Run() { + + // Initialize the data output layer + IO::DataOutputLayer::Init(); + + // Enqueue our single block of data + IO::DataOutputLayer::Enqueue(KeyManager::GetKey()); + + // Tell the data output layer, that is has received all blocks + IO::DataOutputLayer::ReachedEOF(); + + // Tell it to write all blocks + // (a single call should suffice, but a while-loop is the proper + // way to do it) + while (!IO::DataOutputLayer::IsFinished()) { + IO::DataOutputLayer::WriteBlock(); + } + + // Destruct the data output layer + IO::DataOutputLayer::Destruct(); + + return; +} + diff --git a/GCryptCLI/src/ModuleHashing.cpp b/GCryptCLI/src/ModuleHashing.cpp new file mode 100644 index 0000000..f1c613f --- /dev/null +++ b/GCryptCLI/src/ModuleHashing.cpp @@ -0,0 +1,64 @@ +#include "ModuleHashing.h" +#include "DataIngestionLayer.h" +#include "DataOutputLayer.h" +#include "ProgressPrinter.h" +#include "KeyManager.h" +#include + +using namespace Module; +using namespace Leonetienne::GCrypt; + +void Hashing::Run() { + + // Initialize the data ingestion layer + IO::DataIngestionLayer::Init(); + + // Initialize the data output layer + IO::DataOutputLayer::Init(); + + // Initialize a ghasher + GHash hasher; + + // Read in new blocks, if not reached eof + std::size_t nBlocksDigested = 0; + while (!IO::DataIngestionLayer::IsFinished()) { + if (!IO::DataIngestionLayer::ReachedEOF()) { + IO::DataIngestionLayer::ReadBlock(); + } + + // Process a block, if one is ready + if (IO::DataIngestionLayer::IsBlockReady()) { + + // Print progress, if appropriate + ProgressPrinter::PrintIfAppropriate( + "Hashing", + nBlocksDigested, + IO::DataIngestionLayer::NBlocksRead() + ); + + const Block cleartext = IO::DataIngestionLayer::GetNextBlock(); + hasher.Digest(cleartext); + nBlocksDigested++; + } + } + + // Wait until we've finished digesting all blocks + // Enqueue that single block (the hash result) to the output layer + IO::DataOutputLayer::Enqueue(hasher.GetHashsum()); + + // Tell the data output layer that that was the last block + IO::DataOutputLayer::ReachedEOF(); + + // Dump it + while (!IO::DataOutputLayer::IsFinished()) { + IO::DataOutputLayer::WriteBlock(); + } + + // Destruct the data ingestion layer + IO::DataIngestionLayer::Destruct(); + + // Destruct the data output layer + IO::DataOutputLayer::Destruct(); + + return; +} diff --git a/GCryptCLI/src/ProgressPrinter.cpp b/GCryptCLI/src/ProgressPrinter.cpp new file mode 100644 index 0000000..f6ea1ad --- /dev/null +++ b/GCryptCLI/src/ProgressPrinter.cpp @@ -0,0 +1,28 @@ +#include "ProgressPrinter.h" +#include "CommandlineInterface.h" +#include + +void ProgressPrinter::PrintIfAppropriate( + const std::string& message, + const std::size_t current, + const std::size_t target +) { + if ( + (CommandlineInterface::Get().HasParam("--progress")) && + (current % CommandlineInterface::Get()["--progress-interval"].GetInt32() == 0) + ) { + std::cerr + << message + << "... (Block " + << current + 1 + << " / " + << target + << " - " << ((float)(current+1)*100 / target) + << "%)" + << std::endl + ; + } + + return; +} + diff --git a/GCryptCLI/src/main.cpp b/GCryptCLI/src/main.cpp index 497f8c5..0240519 100644 --- a/GCryptCLI/src/main.cpp +++ b/GCryptCLI/src/main.cpp @@ -1,420 +1,41 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "CommandlineInterface.h" - -#include "Bases.h" -#include -#include - -// Required for hidden password input -#if defined _WIN32 || defined _WIN64 -#include -#elif defined __GNUG__ -#include -#include -#endif - -using namespace ::Leonetienne::GCrypt; -using namespace ::Leonetienne::StringTools; -using namespace ::Leonetienne::GeneralUtility; - -enum class OPERATION_MODE { - ENCRYPT, - DECRYPT, - HASH -}; - -namespace { - inline std::string Bin2CustomBase(const std::string& bin, const std::vector& customSet, const std::string& seperator = "") - { - std::stringstream ss; - - // Translate to custom set - const std::vector vec_base_custom = - Leonetienne::GeneralUtility::BaseConversion::BaseX_2_Y>(bin, "01", customSet); - - // Convert to string - for (std::size_t i = 0; i < vec_base_custom.size(); i++) - { - ss << vec_base_custom[i]; - - if (i != vec_base_custom.size() - 1) - ss << seperator; - } - - return ss.str(); - } - - inline std::string CustomBase2Bin(const std::string& in, const std::vector& customSet, const std::string& seperator = "") - { - // Split input into symbols - const std::vector in_symbols = StringTools::Split(in, seperator); - - // Translate to binary - std::string binary = - Leonetienne::GeneralUtility::BaseConversion::BaseX_2_Y, std::string>(in_symbols, customSet, std::string("01")); - - // Apply padding to be a multiple of BLOCK_SIZE - // Count how many bits we need - std::size_t required_size = 0; - while (required_size < binary.length()) - required_size += BLOCK_SIZE; - - // Create a string of that many zeroes - std::string padding = ""; - for (std::size_t i = binary.length(); i < required_size; i++) - padding += "0"; - - // Prepend it to our bitstring - binary = padding + binary; - - return binary; - } -} - -//! Will prompt a user password from stdin, hiding the input -std::string PasswordPrompt() -{ - // Disable stdin-echo - #if defined _WIN32 || defined _WIN64 - HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE); - DWORD mode = 0; - GetConsoleMode(hStdin, &mode); - SetConsoleMode(hStdin, mode & (~ENABLE_ECHO_INPUT)); - - #elif defined __GNUG__ - termios oldt; - tcgetattr(STDIN_FILENO, &oldt); - termios newt = oldt; - newt.c_lflag &= ~ECHO; - tcsetattr(STDIN_FILENO, TCSANOW, &newt); - - #endif - - // Prompt stdin - std::string key; - std::cin >> key; - - // Restore previous config - #if defined _WIN32 || defined _WIN64 - SetConsoleMode(hStdin, mode); - - #elif defined __GNUG__ - tcsetattr(STDIN_FILENO, TCSANOW, &oldt); - - #endif - - // Return input - return key; -} - -Block GetEncryptionKey() -{ - // Easy-case: key supplied as param - if (CommandlineInterface::Get().HasParam("--key")) - return PasswordToKey(CommandlineInterface::Get()["--key"].GetString()); - - // Case: Ask for key - else if (CommandlineInterface::Get().HasParam("--keyask")) - return PasswordToKey(PasswordPrompt()); - - // Case: Read key from file - else if (CommandlineInterface::Get().HasParam("--keyfile")) - { - const std::string keyfilepath = CommandlineInterface::Get()["--keyfile"].GetString(); - - // Read this many chars - const std::size_t maxChars = BLOCK_SIZE / 8; - - // Open ifilestream for keyfile - std::ifstream ifs(keyfilepath, std::ios::in | std::ios::binary); - - // Is the file open now? Or were there any issues... - if (!ifs.good()) - { - std::cerr << "Unable to open ifilestream for keyfile \"" << keyfilepath << "\"! Aborting..." << std::endl; - exit(-1); - } - - // Read these chars to buffer - char* ckeyfileContent = new char[maxChars]; - memset(ckeyfileContent, 0, maxChars * sizeof(char)); - ifs.read(ckeyfileContent, maxChars); - ifs.close(); - - // Convert the buffer to a bit block of key size - std::stringstream ss; - for (std::size_t i = 0; i < maxChars; i++) - ss << std::bitset<8>(ckeyfileContent[i]); - - Block key(ss.str()); - - // And delete the buffer - delete[] ckeyfileContent; - ckeyfileContent = nullptr; - - // Return it - return key; - } - - // Unreachable - throw std::runtime_error("This code should not have been reached. Most likely, the cli argument parser failed making sure at least one key method was supplied."); -} - -const Flexblock GetInputText(const OPERATION_MODE operationMode) -{ - // Easy-case: input text supplied as param - if (CommandlineInterface::Get().HasParam("--intext")) { - switch (operationMode) { - // Encryption, or hashing mode: We want to return the text as-is, as bits - case OPERATION_MODE::ENCRYPT: - case OPERATION_MODE::HASH: - return StringToBits(CommandlineInterface::Get()["--intext"].GetString()); - - // Decryption mode: We need to first convert our input to a bitstring - case OPERATION_MODE::DECRYPT: - const std::string userInput = CommandlineInterface::Get()["--intext"].GetString(); - - // Are we using iobase 2? - if (CommandlineInterface::Get().HasParam("--iobase-2")) - // Yes: convert to binary from base 2 - return CustomBase2Bin(userInput, BASE_2); - - // Are we using iobase 8? - else if (CommandlineInterface::Get().HasParam("--iobase-8")) - // Yes: convert to binary from base 8 - return CustomBase2Bin(userInput, BASE_8); - - // Are we using iobase 10? - else if (CommandlineInterface::Get().HasParam("--iobase-10")) - // Yes: convert to binary from base 10 - return CustomBase2Bin(userInput, BASE_10); - - // Are we using iobase 64? - else if (CommandlineInterface::Get().HasParam("--iobase-64")) - // Yes: convert to binary from base 64 - return CustomBase2Bin(userInput, BASE_64); - - // Are we using iobase uwu? - else if (CommandlineInterface::Get().HasParam("--iobase-uwu")) - // Yes: convert to binary from base uwu - return CustomBase2Bin(userInput, BASE_UWU, " "); - - // Are we using iobase emoji? - else if (CommandlineInterface::Get().HasParam("--iobase-ugh")) - // Yes: convert to binary from base ugh - return CustomBase2Bin(userInput, BASE_UGH, " "); - - // Else, no special output format specified, use hex - else - return HexstringToBits(userInput); - } - } - - - // Case: Read from file - else if (CommandlineInterface::Get().HasParam("--infile")) - return ReadFileToBits(CommandlineInterface::Get()["--infile"].GetString()); - - // Unreachable - throw std::runtime_error("This code should not have been reached. Most likely, the cli argument parser failed making sure at least one input method was supplied."); -} - -const std::string GetOutfileName(const OPERATION_MODE operationMode) -{ - // Do we have an output file name specified? - // Use it. - if (CommandlineInterface::Get().HasParam("--ofile")) - return CommandlineInterface::Get()["--ofile"].GetString(); - - // Else: append a custom postfix to the inputs filename - else - return CommandlineInterface::Get()["--infile"].GetString() + - (operationMode == OPERATION_MODE::ENCRYPT ? ".crypt" : ".plain"); -} - -int main(int argc, char** argv) -{ - // Init cmdargs - CommandlineInterface::Init(argc, argv); - - // Get operation modes - OPERATION_MODE opMode; - if (CommandlineInterface::Get().HasParam("--encrypt")) - opMode = OPERATION_MODE::ENCRYPT; - else if (CommandlineInterface::Get().HasParam("--hash")) - opMode = OPERATION_MODE::HASH; - else if (CommandlineInterface::Get().HasParam("--decrypt")) - opMode = OPERATION_MODE::DECRYPT; - else - // Unreachable - throw std::runtime_error("This code should not have been reached. Most likely, the cli argument parser failed making sure at least one mode of operation (-e, -d, -h) was supplied."); - - const bool isFileMode = CommandlineInterface::Get().HasParam("--infile"); - const bool printDigestionProgress = CommandlineInterface::Get().HasParam("--progress"); - - - // Digest - Flexblock output; - - switch (opMode) { - case OPERATION_MODE::ENCRYPT: { - // Gather input and key - const Block encryptionKey = GetEncryptionKey(); - const Flexblock input = GetInputText(opMode); - - // Create cipher - Cipher cipher(encryptionKey); - - // Run it - output = cipher.Encipher(input, printDigestionProgress); - break; - } - - case OPERATION_MODE::DECRYPT: { - // Gather input and key - const Block encryptionKey = GetEncryptionKey(); - const Flexblock input = GetInputText(opMode); - - // Create cipher - Cipher cipher(encryptionKey); - - // Run it - output = cipher.Decipher(input, printDigestionProgress); - break; - } - - case OPERATION_MODE::HASH: { - // Gather input - Flexblock input = GetInputText(opMode); - - // Also add an extra left-padded block with the length of the input, - // to make sure, H(n) != H(n + 0x0) - input += PadStringToLength(Block(input.size()).to_string(), BLOCK_SIZE, '0'); - - // Derive an encryption key - // This is quiet sensitive, because it absolutely MUST be impossible - // to acquire K without already knowing M. - // This alone is making this cipher a one-way function. - // Just reducing the output is not a solution, because: If len(input) <= BLOCK_SIZE: H(m) = E(m)_k ^ k, with k being 0 <= k <= BLOCK_SIZE. That's not good. - // - // So here's what we're doing: - // - We're going to take the first BLOCK_SIZE bits of the input (it's length is at least that because of the size embedding) - // - We're creating a sudo-random initialization vector, seeded with this block. - // - That's our encryption key. - // This way, the encryption key can only be obtained by already knowing the first BLOCK_SIZE bits of the cleartext. - // By not taking in ALL the bits for deriving a key, we're saving a LOT on performance, as otherwise we would basically be encrypting the input THREE times. - // Why create an initialization vector at all? To make sure, even an input of only zeroes would provide a strong key - const Block encryptionKey = InitializationVector( - Block(input.substr(0, BLOCK_SIZE)) - ); - - // Create cipher - Cipher cipher(encryptionKey); - - // Run it - output = cipher.Encipher(input, printDigestionProgress); - - if (printDigestionProgress) { - std::cout << "Done encrypting... Applying reduction function on ciphertext..." << std::endl; - std::cout << "This will take about just as long..." << std::endl; - } - - // Reduce output to a single block - output = ReductionFunction_Flexblock2Block(output).to_string(); - - break; - } - } - - - // Now output the darn thing. - // Outputting to a file (only if not hashing) - if ((isFileMode) && (opMode != OPERATION_MODE::HASH)) - { - // File mode is a bit different. - - // Dump to stdout? - if (CommandlineInterface::Get().HasParam("--ostdout")) - { - const std::string outstr = BitsToBytes(output); - - // We have to print char-by-char to prevent a nullbyte terminating output. - for (std::size_t i = 0; i < outstr.size(); i++) - std::cout << outstr[i]; - } - // Else: Dump to file - else - { - const std::string outfileName = GetOutfileName(opMode); - WriteBitsToFile(outfileName, output); - } - } - // Else: we are just dealing with a string, or want to print a hashsum - else - { - switch (opMode) { - case OPERATION_MODE::ENCRYPT: - case OPERATION_MODE::HASH: { - - // We are encrypting or hashing: - // Output to stdout as a formatted bytestring - std::string formattedCiphertext; - - // Are we using iobase 2? - if (CommandlineInterface::Get().HasParam("--iobase-2")) - // Yes: convert hex to base 2 - formattedCiphertext = Bin2CustomBase(output, BASE_2); - - // Are we using iobase 8? - else if (CommandlineInterface::Get().HasParam("--iobase-8")) - // Yes: convert binary to base 8 - formattedCiphertext = Bin2CustomBase(output, BASE_8); - - // Are we using iobase 10? - else if (CommandlineInterface::Get().HasParam("--iobase-10")) - // Yes: convert binary to base 10 - formattedCiphertext = Bin2CustomBase(output, BASE_10); - - // Are we using iobase 64? - else if (CommandlineInterface::Get().HasParam("--iobase-64")) - // Yes: convert binary to base 64 - formattedCiphertext = Bin2CustomBase(output, BASE_64); - - // Are we using iobase uwu? - else if (CommandlineInterface::Get().HasParam("--iobase-uwu")) - // Yes: convert binary to base 64 - formattedCiphertext = Bin2CustomBase(output, BASE_UWU, " "); - - // Are we using iobase ugh? - else if (CommandlineInterface::Get().HasParam("--iobase-ugh")) - // Yes: convert binary to base 64 - formattedCiphertext = Bin2CustomBase(output, BASE_UGH, " "); - - // Else, no special output format specified, use hex - else - formattedCiphertext = BitsToHexstring(output); - - std::cout << formattedCiphertext << std::endl; - break; - } - - case OPERATION_MODE::DECRYPT: - // We are just decrypting: - // Just print it string-formatted - std::cout << BitsToString(output) << std::endl; - break; - } - } - - return 0; -} - +#include "CommandlineInterface.h" +#include "Configuration.h" +#include "KeyManager.h" +#include "ModuleGenerateKey.h" +#include "ModuleEncryption.h" +#include "ModuleDecryption.h" +#include "ModuleHashing.h" + +int main(int argc, char* const* argv) { + + // Init cli args + CommandlineInterface::Init(argc, argv); + + // Parse configuration + Configuration::Parse(); + + // Prepare the key + KeyManager::PrepareKey(); + + // Launch our module + switch (Configuration::activeModule) { + case Configuration::MODULE::ENCRYPTION: + Module::Encryption::Run(); + break; + + case Configuration::MODULE::DECRYPTION: + Module::Decryption::Run(); + break; + + case Configuration::MODULE::GENERATE_KEY: + Module::GenerateKey::Run(); + break; + + case Configuration::MODULE::HASH: + Module::Hashing::Run(); + break; + } + + return 0; +} + diff --git a/GCryptCLI/test/Catch2.h b/GCryptCLI/test/Catch2.h new file mode 100644 index 0000000..356946a --- /dev/null +++ b/GCryptCLI/test/Catch2.h @@ -0,0 +1,17965 @@ +/* + * Catch v2.13.8 + * Generated: 2022-01-03 21:20:09.589503 + * ---------------------------------------------------------- + * This file has been merged from multiple headers. Please don't edit it directly + * Copyright (c) 2022 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED +#define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED +// start catch.hpp + + +#define CATCH_VERSION_MAJOR 2 +#define CATCH_VERSION_MINOR 13 +#define CATCH_VERSION_PATCH 8 + +#ifdef __clang__ +# pragma clang system_header +#elif defined __GNUC__ +# pragma GCC system_header +#endif + +// start catch_suppress_warnings.h + +#ifdef __clang__ +# ifdef __ICC // icpc defines the __clang__ macro +# pragma warning(push) +# pragma warning(disable: 161 1682) +# else // __ICC +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wpadded" +# pragma clang diagnostic ignored "-Wswitch-enum" +# pragma clang diagnostic ignored "-Wcovered-switch-default" +# endif +#elif defined __GNUC__ +// Because REQUIREs trigger GCC's -Wparentheses, and because still +// supported version of g++ have only buggy support for _Pragmas, +// Wparentheses have to be suppressed globally. +# pragma GCC diagnostic ignored "-Wparentheses" // See #674 for details + +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wunused-variable" +# pragma GCC diagnostic ignored "-Wpadded" +#endif +// end catch_suppress_warnings.h +#if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER) +# define CATCH_IMPL +# define CATCH_CONFIG_ALL_PARTS +#endif + +// In the impl file, we want to have access to all parts of the headers +// Can also be used to sanely support PCHs +#if defined(CATCH_CONFIG_ALL_PARTS) +# define CATCH_CONFIG_EXTERNAL_INTERFACES +# if defined(CATCH_CONFIG_DISABLE_MATCHERS) +# undef CATCH_CONFIG_DISABLE_MATCHERS +# endif +# if !defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) +# define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER +# endif +#endif + +#if !defined(CATCH_CONFIG_IMPL_ONLY) +// start catch_platform.h + +// See e.g.: +// https://opensource.apple.com/source/CarbonHeaders/CarbonHeaders-18.1/TargetConditionals.h.auto.html +#ifdef __APPLE__ +# include +# if (defined(TARGET_OS_OSX) && TARGET_OS_OSX == 1) || \ + (defined(TARGET_OS_MAC) && TARGET_OS_MAC == 1) +# define CATCH_PLATFORM_MAC +# elif (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1) +# define CATCH_PLATFORM_IPHONE +# endif + +#elif defined(linux) || defined(__linux) || defined(__linux__) +# define CATCH_PLATFORM_LINUX + +#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) || defined(__MINGW32__) +# define CATCH_PLATFORM_WINDOWS +#endif + +// end catch_platform.h + +#ifdef CATCH_IMPL +# ifndef CLARA_CONFIG_MAIN +# define CLARA_CONFIG_MAIN_NOT_DEFINED +# define CLARA_CONFIG_MAIN +# endif +#endif + +// start catch_user_interfaces.h + +namespace Catch { +unsigned int rngSeed(); +} + +// end catch_user_interfaces.h +// start catch_tag_alias_autoregistrar.h + +// start catch_common.h + +// start catch_compiler_capabilities.h + +// Detect a number of compiler features - by compiler +// The following features are defined: +// +// CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported? +// CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported? +// CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported? +// CATCH_CONFIG_DISABLE_EXCEPTIONS : Are exceptions enabled? +// **************** +// Note to maintainers: if new toggles are added please document them +// in configuration.md, too +// **************** + +// In general each macro has a _NO_ form +// (e.g. CATCH_CONFIG_NO_POSIX_SIGNALS) which disables the feature. +// Many features, at point of detection, define an _INTERNAL_ macro, so they +// can be combined, en-mass, with the _NO_ forms later. + +#ifdef __cplusplus + +# if (__cplusplus >= 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L) +# define CATCH_CPP14_OR_GREATER +# endif + +# if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +# define CATCH_CPP17_OR_GREATER +# endif + +#endif + +// Only GCC compiler should be used in this block, so other compilers trying to +// mask themselves as GCC should be ignored. +#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) && !defined(__LCC__) +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" ) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" ) + +# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) + +#endif + +#if defined(__clang__) + +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic push" ) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic pop" ) + +// As of this writing, IBM XL's implementation of __builtin_constant_p has a bug +// which results in calls to destructors being emitted for each temporary, +// without a matching initialization. In practice, this can result in something +// like `std::string::~string` being called on an uninitialized value. +// +// For example, this code will likely segfault under IBM XL: +// ``` +// REQUIRE(std::string("12") + "34" == "1234") +// ``` +// +// Therefore, `CATCH_INTERNAL_IGNORE_BUT_WARN` is not implemented. +# if !defined(__ibmxl__) && !defined(__CUDACC__) +# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) /* NOLINT(cppcoreguidelines-pro-type-vararg, hicpp-vararg) */ +# endif + +# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \ + _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"") + +# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) + +# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" ) + +# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" ) + +# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wunused-template\"" ) + +#endif // __clang__ + +//////////////////////////////////////////////////////////////////////////////// +// Assume that non-Windows platforms support posix signals by default +#if !defined(CATCH_PLATFORM_WINDOWS) +#define CATCH_INTERNAL_CONFIG_POSIX_SIGNALS +#endif + +//////////////////////////////////////////////////////////////////////////////// +// We know some environments not to support full POSIX signals +#if defined(__CYGWIN__) || defined(__QNX__) || defined(__EMSCRIPTEN__) || defined(__DJGPP__) +#define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS +#endif + +#ifdef __OS400__ +# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS +# define CATCH_CONFIG_COLOUR_NONE +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Android somehow still does not support std::to_string +#if defined(__ANDROID__) +# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING +# define CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Not all Windows environments support SEH properly +#if defined(__MINGW32__) +# define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH +#endif + +//////////////////////////////////////////////////////////////////////////////// +// PS4 +#if defined(__ORBIS__) +# define CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Cygwin +#ifdef __CYGWIN__ + +// Required for some versions of Cygwin to declare gettimeofday +// see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin +# define _BSD_SOURCE +// some versions of cygwin (most) do not support std::to_string. Use the libstd check. +// https://gcc.gnu.org/onlinedocs/gcc-4.8.2/libstdc++/api/a01053_source.html line 2812-2813 +# if !((__cplusplus >= 201103L) && defined(_GLIBCXX_USE_C99) \ + && !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF)) + +# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING + +# endif +#endif // __CYGWIN__ + +//////////////////////////////////////////////////////////////////////////////// +// Visual C++ +#if defined(_MSC_VER) + +// Universal Windows platform does not support SEH +// Or console colours (or console at all...) +# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) +# define CATCH_CONFIG_COLOUR_NONE +# else +# define CATCH_INTERNAL_CONFIG_WINDOWS_SEH +# endif + +# if !defined(__clang__) // Handle Clang masquerading for msvc + +// MSVC traditional preprocessor needs some workaround for __VA_ARGS__ +// _MSVC_TRADITIONAL == 0 means new conformant preprocessor +// _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor +# if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL) +# define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +# endif // MSVC_TRADITIONAL + +// Only do this if we're not using clang on Windows, which uses `diagnostic push` & `diagnostic pop` +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION __pragma( warning(push) ) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION __pragma( warning(pop) ) +# endif // __clang__ + +#endif // _MSC_VER + +#if defined(_REENTRANT) || defined(_MSC_VER) +// Enable async processing, as -pthread is specified or no additional linking is required +# define CATCH_INTERNAL_CONFIG_USE_ASYNC +#endif // _MSC_VER + +//////////////////////////////////////////////////////////////////////////////// +// Check if we are compiled with -fno-exceptions or equivalent +#if defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND) +# define CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED +#endif + +//////////////////////////////////////////////////////////////////////////////// +// DJGPP +#ifdef __DJGPP__ +# define CATCH_INTERNAL_CONFIG_NO_WCHAR +#endif // __DJGPP__ + +//////////////////////////////////////////////////////////////////////////////// +// Embarcadero C++Build +#if defined(__BORLANDC__) +#define CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN +#endif + +//////////////////////////////////////////////////////////////////////////////// + +// Use of __COUNTER__ is suppressed during code analysis in +// CLion/AppCode 2017.2.x and former, because __COUNTER__ is not properly +// handled by it. +// Otherwise all supported compilers support COUNTER macro, +// but user still might want to turn it off +#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L ) +#define CATCH_INTERNAL_CONFIG_COUNTER +#endif + +//////////////////////////////////////////////////////////////////////////////// + +// RTX is a special version of Windows that is real time. +// This means that it is detected as Windows, but does not provide +// the same set of capabilities as real Windows does. +#if defined(UNDER_RTSS) || defined(RTX64_BUILD) +#define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH +#define CATCH_INTERNAL_CONFIG_NO_ASYNC +#define CATCH_CONFIG_COLOUR_NONE +#endif + +#if !defined(_GLIBCXX_USE_C99_MATH_TR1) +#define CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER +#endif + +// Various stdlib support checks that require __has_include +#if defined(__has_include) +// Check if string_view is available and usable +#if __has_include() && defined(CATCH_CPP17_OR_GREATER) +# define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW +#endif + +// Check if optional is available and usable +# if __has_include() && defined(CATCH_CPP17_OR_GREATER) +# define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL +# endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) + +// Check if byte is available and usable +# if __has_include() && defined(CATCH_CPP17_OR_GREATER) +# include +# if defined(__cpp_lib_byte) && (__cpp_lib_byte > 0) +# define CATCH_INTERNAL_CONFIG_CPP17_BYTE +# endif +# endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) + +// Check if variant is available and usable +# if __has_include() && defined(CATCH_CPP17_OR_GREATER) +# if defined(__clang__) && (__clang_major__ < 8) +// work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852 +// fix should be in clang 8, workaround in libstdc++ 8.2 +# include +# if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) +# define CATCH_CONFIG_NO_CPP17_VARIANT +# else +# define CATCH_INTERNAL_CONFIG_CPP17_VARIANT +# endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) +# else +# define CATCH_INTERNAL_CONFIG_CPP17_VARIANT +# endif // defined(__clang__) && (__clang_major__ < 8) +# endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) +#endif // defined(__has_include) + +#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) +# define CATCH_CONFIG_COUNTER +#endif +#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) && !defined(CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH) +# define CATCH_CONFIG_WINDOWS_SEH +#endif +// This is set by default, because we assume that unix compilers are posix-signal-compatible by default. +#if defined(CATCH_INTERNAL_CONFIG_POSIX_SIGNALS) && !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS) +# define CATCH_CONFIG_POSIX_SIGNALS +#endif +// This is set by default, because we assume that compilers with no wchar_t support are just rare exceptions. +#if !defined(CATCH_INTERNAL_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_WCHAR) +# define CATCH_CONFIG_WCHAR +#endif + +#if !defined(CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_CPP11_TO_STRING) +# define CATCH_CONFIG_CPP11_TO_STRING +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_NO_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_CPP17_OPTIONAL) +# define CATCH_CONFIG_CPP17_OPTIONAL +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_NO_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_CPP17_STRING_VIEW) +# define CATCH_CONFIG_CPP17_STRING_VIEW +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_VARIANT) && !defined(CATCH_CONFIG_NO_CPP17_VARIANT) && !defined(CATCH_CONFIG_CPP17_VARIANT) +# define CATCH_CONFIG_CPP17_VARIANT +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_BYTE) && !defined(CATCH_CONFIG_NO_CPP17_BYTE) && !defined(CATCH_CONFIG_CPP17_BYTE) +# define CATCH_CONFIG_CPP17_BYTE +#endif + +#if defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) +# define CATCH_INTERNAL_CONFIG_NEW_CAPTURE +#endif + +#if defined(CATCH_INTERNAL_CONFIG_NEW_CAPTURE) && !defined(CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NEW_CAPTURE) +# define CATCH_CONFIG_NEW_CAPTURE +#endif + +#if !defined(CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) +# define CATCH_CONFIG_DISABLE_EXCEPTIONS +#endif + +#if defined(CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_NO_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_POLYFILL_ISNAN) +# define CATCH_CONFIG_POLYFILL_ISNAN +#endif + +#if defined(CATCH_INTERNAL_CONFIG_USE_ASYNC) && !defined(CATCH_INTERNAL_CONFIG_NO_ASYNC) && !defined(CATCH_CONFIG_NO_USE_ASYNC) && !defined(CATCH_CONFIG_USE_ASYNC) +# define CATCH_CONFIG_USE_ASYNC +#endif + +#if defined(CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_NO_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_ANDROID_LOGWRITE) +# define CATCH_CONFIG_ANDROID_LOGWRITE +#endif + +#if defined(CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_NO_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_GLOBAL_NEXTAFTER) +# define CATCH_CONFIG_GLOBAL_NEXTAFTER +#endif + +// Even if we do not think the compiler has that warning, we still have +// to provide a macro that can be used by the code. +#if !defined(CATCH_INTERNAL_START_WARNINGS_SUPPRESSION) +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION +#endif +#if !defined(CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS +#endif + +// The goal of this macro is to avoid evaluation of the arguments, but +// still have the compiler warn on problems inside... +#if !defined(CATCH_INTERNAL_IGNORE_BUT_WARN) +# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) +#endif + +#if defined(__APPLE__) && defined(__apple_build_version__) && (__clang_major__ < 10) +# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#elif defined(__clang__) && (__clang_major__ < 5) +# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#endif + +#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#endif + +#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) +#define CATCH_TRY if ((true)) +#define CATCH_CATCH_ALL if ((false)) +#define CATCH_CATCH_ANON(type) if ((false)) +#else +#define CATCH_TRY try +#define CATCH_CATCH_ALL catch (...) +#define CATCH_CATCH_ANON(type) catch (type) +#endif + +#if defined(CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_NO_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) +#define CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#endif + +// end catch_compiler_capabilities.h +#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line +#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) +#ifdef CATCH_CONFIG_COUNTER +# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ ) +#else +# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) +#endif + +#include +#include +#include + +// We need a dummy global operator<< so we can bring it into Catch namespace later +struct Catch_global_namespace_dummy {}; +std::ostream& operator<<(std::ostream&, Catch_global_namespace_dummy); + +namespace Catch { + +struct CaseSensitive { enum Choice { +Yes, +No +}; }; + +class NonCopyable { +NonCopyable( NonCopyable const& ) = delete; +NonCopyable( NonCopyable && ) = delete; +NonCopyable& operator = ( NonCopyable const& ) = delete; +NonCopyable& operator = ( NonCopyable && ) = delete; + +protected: +NonCopyable(); +virtual ~NonCopyable(); +}; + +struct SourceLineInfo { + +SourceLineInfo() = delete; +SourceLineInfo( char const* _file, std::size_t _line ) noexcept +: file( _file ), +line( _line ) +{} + +SourceLineInfo( SourceLineInfo const& other ) = default; +SourceLineInfo& operator = ( SourceLineInfo const& ) = default; +SourceLineInfo( SourceLineInfo&& ) noexcept = default; +SourceLineInfo& operator = ( SourceLineInfo&& ) noexcept = default; + +bool empty() const noexcept { return file[0] == '\0'; } +bool operator == ( SourceLineInfo const& other ) const noexcept; +bool operator < ( SourceLineInfo const& other ) const noexcept; + +char const* file; +std::size_t line; +}; + +std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ); + +// Bring in operator<< from global namespace into Catch namespace +// This is necessary because the overload of operator<< above makes +// lookup stop at namespace Catch +using ::operator<<; + +// Use this in variadic streaming macros to allow +// >> +StreamEndStop +// as well as +// >> stuff +StreamEndStop +struct StreamEndStop { +std::string operator+() const; +}; +template +T const& operator + ( T const& value, StreamEndStop ) { +return value; +} +} + +#define CATCH_INTERNAL_LINEINFO \ + ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) + +// end catch_common.h +namespace Catch { + +struct RegistrarForTagAliases { +RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); +}; + +} // end namespace Catch + +#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION + +// end catch_tag_alias_autoregistrar.h +// start catch_test_registry.h + +// start catch_interfaces_testcase.h + +#include + +namespace Catch { + +class TestSpec; + +struct ITestInvoker { +virtual void invoke () const = 0; +virtual ~ITestInvoker(); +}; + +class TestCase; +struct IConfig; + +struct ITestCaseRegistry { +virtual ~ITestCaseRegistry(); +virtual std::vector const& getAllTests() const = 0; +virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0; +}; + +bool isThrowSafe( TestCase const& testCase, IConfig const& config ); +bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); +std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); +std::vector const& getAllTestCasesSorted( IConfig const& config ); + +} + +// end catch_interfaces_testcase.h +// start catch_stringref.h + +#include +#include +#include +#include + +namespace Catch { + +/// A non-owning string class (similar to the forthcoming std::string_view) +/// Note that, because a StringRef may be a substring of another string, +/// it may not be null terminated. +class StringRef { +public: +using size_type = std::size_t; +using const_iterator = const char*; + +private: +static constexpr char const* const s_empty = ""; + +char const* m_start = s_empty; +size_type m_size = 0; + +public: // construction +constexpr StringRef() noexcept = default; + +StringRef( char const* rawChars ) noexcept; + +constexpr StringRef( char const* rawChars, size_type size ) noexcept +: m_start( rawChars ), +m_size( size ) +{} + +StringRef( std::string const& stdString ) noexcept +: m_start( stdString.c_str() ), +m_size( stdString.size() ) +{} + +explicit operator std::string() const { +return std::string(m_start, m_size); +} + +public: // operators +auto operator == ( StringRef const& other ) const noexcept -> bool; +auto operator != (StringRef const& other) const noexcept -> bool { +return !(*this == other); +} + +auto operator[] ( size_type index ) const noexcept -> char { +assert(index < m_size); +return m_start[index]; +} + +public: // named queries +constexpr auto empty() const noexcept -> bool { +return m_size == 0; +} +constexpr auto size() const noexcept -> size_type { +return m_size; +} + +// Returns the current start pointer. If the StringRef is not +// null-terminated, throws std::domain_exception +auto c_str() const -> char const*; + +public: // substrings and searches +// Returns a substring of [start, start + length). +// If start + length > size(), then the substring is [start, size()). +// If start > size(), then the substring is empty. +auto substr( size_type start, size_type length ) const noexcept -> StringRef; + +// Returns the current start pointer. May not be null-terminated. +auto data() const noexcept -> char const*; + +constexpr auto isNullTerminated() const noexcept -> bool { +return m_start[m_size] == '\0'; +} + +public: // iterators +constexpr const_iterator begin() const { return m_start; } +constexpr const_iterator end() const { return m_start + m_size; } +}; + +auto operator += ( std::string& lhs, StringRef const& sr ) -> std::string&; +auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&; + +constexpr auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef { +return StringRef( rawChars, size ); +} +} // namespace Catch + +constexpr auto operator "" _catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef { +return Catch::StringRef( rawChars, size ); +} + +// end catch_stringref.h +// start catch_preprocessor.hpp + + +#define CATCH_RECURSION_LEVEL0(...) __VA_ARGS__ +#define CATCH_RECURSION_LEVEL1(...) CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL2(...) CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL3(...) CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL4(...) CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL5(...) CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(__VA_ARGS__))) + +#ifdef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_EXPAND_VARGS(...) __VA_ARGS__ +// MSVC needs more evaluations +#define CATCH_RECURSION_LEVEL6(...) CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(__VA_ARGS__))) +#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL6(CATCH_RECURSION_LEVEL6(__VA_ARGS__)) +#else +#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL5(__VA_ARGS__) +#endif + +#define CATCH_REC_END(...) +#define CATCH_REC_OUT + +#define CATCH_EMPTY() +#define CATCH_DEFER(id) id CATCH_EMPTY() + +#define CATCH_REC_GET_END2() 0, CATCH_REC_END +#define CATCH_REC_GET_END1(...) CATCH_REC_GET_END2 +#define CATCH_REC_GET_END(...) CATCH_REC_GET_END1 +#define CATCH_REC_NEXT0(test, next, ...) next CATCH_REC_OUT +#define CATCH_REC_NEXT1(test, next) CATCH_DEFER ( CATCH_REC_NEXT0 ) ( test, next, 0) +#define CATCH_REC_NEXT(test, next) CATCH_REC_NEXT1(CATCH_REC_GET_END test, next) + +#define CATCH_REC_LIST0(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST1(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0) ) ( f, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST2(f, x, peek, ...) f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) + +#define CATCH_REC_LIST0_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST1_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0_UD) ) ( f, userdata, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST2_UD(f, userdata, x, peek, ...) f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) + +// Applies the function macro `f` to each of the remaining parameters, inserts commas between the results, +// and passes userdata as the first parameter to each invocation, +// e.g. CATCH_REC_LIST_UD(f, x, a, b, c) evaluates to f(x, a), f(x, b), f(x, c) +#define CATCH_REC_LIST_UD(f, userdata, ...) CATCH_RECURSE(CATCH_REC_LIST2_UD(f, userdata, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) + +#define CATCH_REC_LIST(f, ...) CATCH_RECURSE(CATCH_REC_LIST2(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) + +#define INTERNAL_CATCH_EXPAND1(param) INTERNAL_CATCH_EXPAND2(param) +#define INTERNAL_CATCH_EXPAND2(...) INTERNAL_CATCH_NO## __VA_ARGS__ +#define INTERNAL_CATCH_DEF(...) INTERNAL_CATCH_DEF __VA_ARGS__ +#define INTERNAL_CATCH_NOINTERNAL_CATCH_DEF +#define INTERNAL_CATCH_STRINGIZE(...) INTERNAL_CATCH_STRINGIZE2(__VA_ARGS__) +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_STRINGIZE2(...) #__VA_ARGS__ +#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) +#else +// MSVC is adding extra space and needs another indirection to expand INTERNAL_CATCH_NOINTERNAL_CATCH_DEF +#define INTERNAL_CATCH_STRINGIZE2(...) INTERNAL_CATCH_STRINGIZE3(__VA_ARGS__) +#define INTERNAL_CATCH_STRINGIZE3(...) #__VA_ARGS__ +#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) (INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) + 1) +#endif + +#define INTERNAL_CATCH_MAKE_NAMESPACE2(...) ns_##__VA_ARGS__ +#define INTERNAL_CATCH_MAKE_NAMESPACE(name) INTERNAL_CATCH_MAKE_NAMESPACE2(name) + +#define INTERNAL_CATCH_REMOVE_PARENS(...) INTERNAL_CATCH_EXPAND1(INTERNAL_CATCH_DEF __VA_ARGS__) + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) decltype(get_wrapper()) +#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)) +#else +#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) INTERNAL_CATCH_EXPAND_VARGS(decltype(get_wrapper())) +#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))) +#endif + +#define INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(...)\ + CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST,__VA_ARGS__) + +#define INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_0) INTERNAL_CATCH_REMOVE_PARENS(_0) +#define INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_0, _1) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_1) +#define INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_0, _1, _2) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_1, _2) +#define INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_0, _1, _2, _3) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_1, _2, _3) +#define INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_0, _1, _2, _3, _4) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_1, _2, _3, _4) +#define INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_0, _1, _2, _3, _4, _5) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_1, _2, _3, _4, _5) +#define INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_0, _1, _2, _3, _4, _5, _6) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_1, _2, _3, _4, _5, _6) +#define INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_0, _1, _2, _3, _4, _5, _6, _7) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_1, _2, _3, _4, _5, _6, _7) +#define INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_1, _2, _3, _4, _5, _6, _7, _8) +#define INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9) +#define INTERNAL_CATCH_REMOVE_PARENS_11_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10) + +#define INTERNAL_CATCH_VA_NARGS_IMPL(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N + +#define INTERNAL_CATCH_TYPE_GEN\ + template struct TypeList {};\ + template\ + constexpr auto get_wrapper() noexcept -> TypeList { return {}; }\ + template class...> struct TemplateTypeList{};\ + template class...Cs>\ + constexpr auto get_wrapper() noexcept -> TemplateTypeList { return {}; }\ + template\ + struct append;\ + template\ + struct rewrap;\ + template class, typename...>\ + struct create;\ + template class, typename>\ + struct convert;\ + \ + template \ + struct append { using type = T; };\ + template< template class L1, typename...E1, template class L2, typename...E2, typename...Rest>\ + struct append, L2, Rest...> { using type = typename append, Rest...>::type; };\ + template< template class L1, typename...E1, typename...Rest>\ + struct append, TypeList, Rest...> { using type = L1; };\ + \ + template< template class Container, template class List, typename...elems>\ + struct rewrap, List> { using type = TypeList>; };\ + template< template class Container, template class List, class...Elems, typename...Elements>\ + struct rewrap, List, Elements...> { using type = typename append>, typename rewrap, Elements...>::type>::type; };\ + \ + template