From 117798d0fb76fb23721528921bdb69b3dc010a26 Mon Sep 17 00:00:00 2001 From: Leonetienne Date: Wed, 16 Mar 2022 01:31:28 +0100 Subject: [PATCH] Implemented hashsum mode --- GhettoCrypt/Util.h | 35 +++++- GhettoCryptCLI/CommandlineInterface.cpp | 26 +++-- GhettoCryptCLI/Version.h | 2 +- GhettoCryptCLI/main.cpp | 146 +++++++++++++++++++----- 4 files changed, 164 insertions(+), 45 deletions(-) diff --git a/GhettoCrypt/Util.h b/GhettoCrypt/Util.h index 7db3e7c..e8458f8 100644 --- a/GhettoCrypt/Util.h +++ b/GhettoCrypt/Util.h @@ -6,6 +6,8 @@ #include "SecureBitset.h" #include "Block.h" #include "Flexblock.h" +#include "Config.h" +#include "Cipher.h" #include "InitializationVector.h" namespace GhettoCipher @@ -236,15 +238,42 @@ namespace GhettoCipher //! : return b ^ iv(b) inline Block PasswordToKey(const std::string& in) { - Block b; + // Let's provide a nice initial value to be sure even a password of length 0 results in a proper key + Block b = InitializationVector(StringToBitblock("3J7IipfQTDJbO8jtasz9PgWui6faPaEMOuVuAqyhB1S2CRcLw5caawewgDUEG1WN")); // Segment the password in segments of key-size, and xor them together. for (std::size_t i = 0; i < in.size(); i += BLOCK_SIZE / 8) - b ^= StringToBitblock( + { + const Block fragment = StringToBitblock( PadStringToLength(in.substr(i, BLOCK_SIZE / 8), BLOCK_SIZE / 8, 0, false) ); - return b ^ InitializationVector(b); + // To provide confusion, xor the blocks together + // To provide diffusion, hash fragment to fragment' first + b ^= Block(Cipher(fragment).Encipher(fragment.to_string())); + } + + + return b; + } + + //! Will reduce a flexblock (they are of arbitrary length) to a single block. + //! This single block should change completely, if a single bit in the input flexblock changes anywhere. + inline Block ReductionFunction_Flexblock2Block(const Flexblock& in) + { + Block b; // No initialization vector needed here + + // Segment the input in segments of BLOCK_SIZE, and xor them together. + for (std::size_t i = 0; i < in.size(); i += BLOCK_SIZE) + { + const Block fragment = Block(PadStringToLength(in.substr(i, BLOCK_SIZE), BLOCK_SIZE, 0, false)); + + // To provide confusion, xor the blocks together + // To provide diffusion, hash fragment to fragment' first + b ^= Block(Cipher(fragment).Encipher(fragment.to_string())); + } + + return b; } //! Will read a file into a flexblock diff --git a/GhettoCryptCLI/CommandlineInterface.cpp b/GhettoCryptCLI/CommandlineInterface.cpp index c3a261b..177cf4a 100644 --- a/GhettoCryptCLI/CommandlineInterface.cpp +++ b/GhettoCryptCLI/CommandlineInterface.cpp @@ -22,13 +22,17 @@ void CommandlineInterface::Init(int argc, const char* const* argv) /* Builtin documentation */ nupp.RegisterDescription("--encrypt", "Use the encryption routine."); - nupp.RegisterConstraint("--encrypt", ParamConstraint(true, DATA_TYPE::VOID, {}, false, {"--decrypt"})); + 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" })); + nupp.RegisterConstraint("--decrypt", ParamConstraint(true, DATA_TYPE::VOID, {}, false, { "--encrypt", "--hash" })); 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.RegisterAbbreviation("-h", "--hash"); + nupp.RegisterDescription("--intext", "Encrypt this string. Dumps to stdout."); nupp.RegisterConstraint("--intext", ParamConstraint(true, DATA_TYPE::STRING, {}, false, { "--infile" })); nupp.RegisterAbbreviation("-it", "--intext"); @@ -38,25 +42,25 @@ void CommandlineInterface::Init(int argc, const char* const* argv) nupp.RegisterAbbreviation("-if", "--infile"); nupp.RegisterDescription("--ofile", "Use this filename for output if --infile is specified. Gets ignored otherwise."); - nupp.RegisterConstraint("--ofile", ParamConstraint(true, DATA_TYPE::STRING, {}, false, { "--ostdout" })); + 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" })); + 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" })); + nupp.RegisterConstraint("--key", ParamConstraint(true, DATA_TYPE::STRING, {}, false, { "--keyfile", "--keyask", "--hash" })); nupp.RegisterAbbreviation("-k", "--key"); ss << "Read in the first {KEYSIZE}(=" << GhettoCipher::BLOCK_SIZE << ") 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" })); + nupp.RegisterConstraint("--keyfile", ParamConstraint(true, DATA_TYPE::STRING, {}, false, { "--key", "--keyask", "--hash" })); nupp.RegisterAbbreviation("-kf", "--keyfile"); nupp.RegisterDescription("--keyask", "Read the encryption key from stdin."); - nupp.RegisterConstraint("--keyask", ParamConstraint(true, DATA_TYPE::VOID, {}, false, { "--key", "--keyfile" })); + 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."); @@ -105,8 +109,9 @@ Hazelnp::CmdArgsInterface& CommandlineInterface::Get() void CommandlineInterface::SpecialCompatibilityChecking() { // Encryption key - // Do we have EITHER --key, --keyask or --keyfile given? + // 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")) @@ -122,12 +127,13 @@ void CommandlineInterface::SpecialCompatibilityChecking() CrashWithMsg("No encryption input supplied! Please supply either --intext or --infile!"); // Encryption mode - // Do we have EITHER --encrypt or --decrypt? + // Do we have EITHER --encrypt or --decrypt or --hash? if ( + (!nupp.HasParam("--hash")) && (!nupp.HasParam("--encrypt")) && (!nupp.HasParam("--decrypt")) ) - CrashWithMsg("No encryption mode supplied! Please supply either --encrypt or --decrypt!"); + CrashWithMsg("No encryption mode supplied! Please supply either --encrypt, --decrypt, or --hash!"); return; } diff --git a/GhettoCryptCLI/Version.h b/GhettoCryptCLI/Version.h index c86d6e3..b10cdd5 100644 --- a/GhettoCryptCLI/Version.h +++ b/GhettoCryptCLI/Version.h @@ -1,2 +1,2 @@ #pragma once -#define GHETTOCRYPTCLI_VERSION 0.123 +#define GHETTOCRYPTCLI_VERSION 0.124 diff --git a/GhettoCryptCLI/main.cpp b/GhettoCryptCLI/main.cpp index 67760b6..29dd180 100644 --- a/GhettoCryptCLI/main.cpp +++ b/GhettoCryptCLI/main.cpp @@ -24,6 +24,12 @@ using namespace GhettoCipher; +enum class OPERATION_MODE { + ENCRYPT, + DECRYPT, + HASH +}; + namespace { inline std::string Bin2CustomBase(const std::string& bin, const std::vector& customSet, const std::string& seperator = "") { @@ -112,11 +118,11 @@ Block GetEncryptionKey() { // Easy-case: key supplied as param if (CommandlineInterface::Get().HasParam("--key")) - return StringToBitblock(CommandlineInterface::Get()["--key"].GetString()); + return PasswordToKey(CommandlineInterface::Get()["--key"].GetString()); // Case: Ask for key else if (CommandlineInterface::Get().HasParam("--keyask")) - return StringToBitblock(PasswordPrompt()); + return PasswordToKey(PasswordPrompt()); // Case: Read key from file else if (CommandlineInterface::Get().HasParam("--keyfile")) @@ -161,17 +167,18 @@ Block GetEncryptionKey() 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(bool encryptionMode) +const Flexblock GetInputText(const OPERATION_MODE operationMode) { // Easy-case: input text supplied as param - if (CommandlineInterface::Get().HasParam("--intext")) - // Encryption mode: We want to return the text as-is, as bits - if (encryptionMode) + 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 - else - { + case OPERATION_MODE::DECRYPT: const std::string userInput = CommandlineInterface::Get()["--intext"].GetString(); // Are we using iobase 2? @@ -208,6 +215,7 @@ const Flexblock GetInputText(bool encryptionMode) else return HexstringToBits(userInput); } + } // Case: Read from file @@ -218,7 +226,7 @@ const Flexblock GetInputText(bool encryptionMode) 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 bool isEncryptionMode) +const std::string GetOutfileName(const OPERATION_MODE operationMode) { // Do we have an output file name specified? // Use it. @@ -227,7 +235,8 @@ const std::string GetOutfileName(const bool isEncryptionMode) // Else: append a custom postfix to the inputs filename else - return CommandlineInterface::Get()["--infile"].GetString() + (isEncryptionMode ? ".crypt" : ".plain"); + return CommandlineInterface::Get()["--infile"].GetString() + + (operationMode == OPERATION_MODE::ENCRYPT ? ".crypt" : ".plain"); } int main(int argc, char** argv) @@ -235,30 +244,99 @@ int main(int argc, char** argv) // Init cmdargs CommandlineInterface::Init(argc, argv); - // Get encryption key - const Block encryptionKey = GetEncryptionKey(); - // Get operation modes - const bool shouldEncrypt = CommandlineInterface::Get().HasParam("--encrypt"); - const bool isFileMode = CommandlineInterface::Get().HasParam("--infile"); + 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"); - // Get the input text - const Flexblock input = GetInputText(shouldEncrypt); // Digest Flexblock output; - Cipher cipher(encryptionKey); - const bool printDigestionProgress = CommandlineInterface::Get().HasParam("--progress"); + 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; + } + } - if (shouldEncrypt) - output = cipher.Encipher(input, printDigestionProgress); - else - output = cipher.Decipher(input, printDigestionProgress); // Now output the darn thing. - // Outputting a file - if (isFileMode) + // Outputting to a file (only if not hashing) + if ((isFileMode) && (opMode != OPERATION_MODE::HASH)) { // File mode is a bit different. @@ -274,15 +352,18 @@ int main(int argc, char** argv) // Else: Dump to file else { - const std::string outfileName = GetOutfileName(shouldEncrypt); + const std::string outfileName = GetOutfileName(opMode); WriteBitsToFile(outfileName, output); } } - // Else: we are just dealing with a string + // Else: we are just dealing with a string, or want to print a hashsum else { - if (shouldEncrypt) { - // We are decrypting: + 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; @@ -311,7 +392,7 @@ int main(int argc, char** argv) // Yes: convert binary to base 64 formattedCiphertext = Bin2CustomBase(output, BASE_UWU, " "); - // Are we using iobase ugh? + // Are we using iobase ugh? else if (CommandlineInterface::Get().HasParam("--iobase-ugh")) // Yes: convert binary to base 64 formattedCiphertext = Bin2CustomBase(output, BASE_UGH, " "); @@ -321,11 +402,14 @@ int main(int argc, char** argv) formattedCiphertext = BitsToHexstring(output); std::cout << formattedCiphertext << std::endl; + break; } - else { + + case OPERATION_MODE::DECRYPT: // We are just decrypting: // Just print it string-formatted std::cout << BitsToString(output) << std::endl; + break; } }