Implemented hashsum mode

This commit is contained in:
Leonetienne 2022-03-16 01:31:28 +01:00
parent cdad33c738
commit 117798d0fb
4 changed files with 164 additions and 45 deletions

View File

@ -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

View File

@ -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;
}

View File

@ -1,2 +1,2 @@
#pragma once
#define GHETTOCRYPTCLI_VERSION 0.123
#define GHETTOCRYPTCLI_VERSION 0.124

View File

@ -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<std::string>& 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;
switch (opMode) {
case OPERATION_MODE::ENCRYPT: {
// Gather input and key
const Block encryptionKey = GetEncryptionKey();
const Flexblock input = GetInputText(opMode);
// Create cipher
Cipher cipher(encryptionKey);
const bool printDigestionProgress = CommandlineInterface::Get().HasParam("--progress");
if (shouldEncrypt)
// Run it
output = cipher.Encipher(input, printDigestionProgress);
else
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 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;
@ -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;
}
}