Merge branch 'feature/relaunch' into master
1
.gitignore
vendored
@ -1,3 +1,4 @@
|
||||
build/
|
||||
*.swp
|
||||
*_/
|
||||
|
||||
|
6
.gitmodules
vendored
@ -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
|
||||
|
@ -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
|
||||
)
|
||||
|
||||
|
@ -1,6 +1,28 @@
|
||||
#pragma once
|
||||
#ifndef GCRYPTCLI_BASES_H
|
||||
#define GCRYPTCLI_BASES_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
// 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<Configuration::IOBASE_FORMAT, std::size_t>({
|
||||
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<std::string> BASE_2 = { "0","1" };
|
||||
@ -116,3 +138,6 @@ static const std::vector<std::string> BASE_UGH = {
|
||||
"Bah!",
|
||||
"Wha-?"
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -1,4 +1,6 @@
|
||||
#pragma once
|
||||
#ifndef GCRYPTCLI_CLIINTERFACE_H
|
||||
#define GCRYPTCLI_CLIINTERFACE_H
|
||||
|
||||
#include <Hazelnupp/CmdArgsInterface.h>
|
||||
|
||||
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
|
||||
|
||||
|
61
GCryptCLI/include/Configuration.h
Normal file
@ -0,0 +1,61 @@
|
||||
#ifndef GCRYPTCLI_CONFIGURATION_H
|
||||
#define GCRYPTCLI_CONFIGURATION_H
|
||||
|
||||
#include <string>
|
||||
|
||||
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
|
||||
|
58
GCryptCLI/include/DataFormatter.h
Normal file
@ -0,0 +1,58 @@
|
||||
#ifndef GCRYPTCLI_DATAFORMATTER_H
|
||||
#define GCRYPTCLI_DATAFORMATTER_H
|
||||
|
||||
#include <GCrypt/Block.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#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<Block>& 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<Block> DecodeFormatMultiblock(
|
||||
const std::string& str,
|
||||
const Configuration::IOBASE_FORMAT base
|
||||
);
|
||||
|
||||
|
||||
private:
|
||||
static std::string Bin2CustomBase(
|
||||
const std::string& bin,
|
||||
const std::vector<std::string>& customSet,
|
||||
const std::size_t minLen,
|
||||
const std::string& seperator = ""
|
||||
);
|
||||
|
||||
static std::string CustomBase2Bin(
|
||||
const std::string& in,
|
||||
const std::vector<std::string>& customSet,
|
||||
const std::string& seperator = ""
|
||||
);
|
||||
|
||||
// No instanciation! >:(
|
||||
DataFormatter() {};
|
||||
};
|
||||
|
||||
#endif
|
||||
|
75
GCryptCLI/include/DataIngestionLayer.h
Normal file
@ -0,0 +1,75 @@
|
||||
#ifndef GCRYPTCLI_DATAINGESTIONLAYER_H
|
||||
#define GCRYPTCLI_DATAINGESTIONLAYER_H
|
||||
|
||||
#include <iosfwd>
|
||||
#include <queue>
|
||||
#include <GCrypt/Block.h>
|
||||
#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<Block> blocks;
|
||||
|
||||
// No instanciation! >:(
|
||||
DataIngestionLayer();
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
61
GCryptCLI/include/DataOutputLayer.h
Normal file
@ -0,0 +1,61 @@
|
||||
#ifndef GCRYPTCLI_DATAOUTPUTLAYER_H
|
||||
#define GCRYPTCLI_DATAOUTPUTLAYER_H
|
||||
|
||||
#include <iosfwd>
|
||||
#include <queue>
|
||||
#include <GCrypt/Block.h>
|
||||
#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<Block> blocks;
|
||||
|
||||
//! No instanciation >:(
|
||||
DataOutputLayer() {};
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
30
GCryptCLI/include/KeyManager.h
Normal file
@ -0,0 +1,30 @@
|
||||
#ifndef GCRYPTCLI_KEYMANAGER_H
|
||||
#define GCRYPTCLI_KEYMANAGER_H
|
||||
|
||||
#include <GCrypt/Key.h>
|
||||
|
||||
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
|
||||
|
18
GCryptCLI/include/ModuleDecryption.h
Normal file
@ -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
|
||||
|
18
GCryptCLI/include/ModuleEncryption.h
Normal file
@ -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
|
||||
|
19
GCryptCLI/include/ModuleGenerateKey.h
Normal file
@ -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
|
||||
|
18
GCryptCLI/include/ModuleHashing.h
Normal file
@ -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
|
||||
|
23
GCryptCLI/include/ProgressPrinter.h
Normal file
@ -0,0 +1,23 @@
|
||||
#ifndef GCRYPTCLI_PROGRESSPRINTER_H
|
||||
#define GCRYPTCLI_PROGRESSPRINTER_H
|
||||
|
||||
#include <iostream>
|
||||
#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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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!
|
||||
```
|
||||
|
||||
|
@ -2,20 +2,19 @@
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <GCrypt/Version.h>
|
||||
#include <GCrypt/Config.h>
|
||||
#include <GCrypt/Block.h>
|
||||
#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);
|
||||
}
|
||||
|
||||
|
186
GCryptCLI/src/Configuration.cpp
Normal file
@ -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;
|
||||
|
299
GCryptCLI/src/DataFormatter.cpp
Normal file
@ -0,0 +1,299 @@
|
||||
#include "DataFormatter.h"
|
||||
#include "Bases.h"
|
||||
#include <GeneralUtility/BaseConversion.h>
|
||||
#include <StringTools/StringTools.h>
|
||||
#include <GCrypt/Util.h>
|
||||
|
||||
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<Block>& 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<Block> DataFormatter::DecodeFormatMultiblock(
|
||||
const std::string& str,
|
||||
const Configuration::IOBASE_FORMAT base
|
||||
) {
|
||||
std::vector<Block> 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<std::string>& customSet,
|
||||
const std::size_t minLen,
|
||||
const std::string& seperator
|
||||
) {
|
||||
std::stringstream ss;
|
||||
|
||||
// Translate to custom set
|
||||
const std::vector<std::string> vec_base_custom =
|
||||
Leonetienne::GeneralUtility::BaseConversion::BaseX_2_Y<std::string, std::vector<std::string>>(
|
||||
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<std::string>& customSet,
|
||||
const std::string& seperator
|
||||
) {
|
||||
// Split input into symbols
|
||||
const std::vector<std::string> in_symbols = StringTools::Split(in, seperator);
|
||||
|
||||
// Translate to binary
|
||||
std::string binary =
|
||||
Leonetienne::GeneralUtility::BaseConversion::BaseX_2_Y<std::vector<std::string>, 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;
|
||||
}
|
||||
|
369
GCryptCLI/src/DataIngestionLayer.cpp
Normal file
@ -0,0 +1,369 @@
|
||||
#include "DataIngestionLayer.h"
|
||||
#include "CommandlineInterface.h"
|
||||
#include "DataFormatter.h"
|
||||
#include "Bases.h"
|
||||
#include <StringTools/StringTools.h>
|
||||
#include <iostream>
|
||||
#include <istream>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <cstring>
|
||||
|
||||
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<Block> DataIngestionLayer::blocks;
|
||||
|
149
GCryptCLI/src/DataOutputLayer.cpp
Normal file
@ -0,0 +1,149 @@
|
||||
#include "DataOutputLayer.h"
|
||||
#include "DataFormatter.h"
|
||||
#include "CommandlineInterface.h"
|
||||
#include <ostream>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
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<Block> DataOutputLayer::blocks;
|
||||
|
107
GCryptCLI/src/KeyManager.cpp
Normal file
@ -0,0 +1,107 @@
|
||||
#include "KeyManager.h"
|
||||
#include "CommandlineInterface.h"
|
||||
#include "Configuration.h"
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
|
||||
// Required for hidden password input
|
||||
#if defined _WIN32 || defined _WIN64
|
||||
#include <Windows.h>
|
||||
#elif defined __GNUG__
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
#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;
|
||||
|
67
GCryptCLI/src/ModuleDecryption.cpp
Normal file
@ -0,0 +1,67 @@
|
||||
#include "ModuleDecryption.h"
|
||||
#include "DataIngestionLayer.h"
|
||||
#include "DataOutputLayer.h"
|
||||
#include "ProgressPrinter.h"
|
||||
#include "KeyManager.h"
|
||||
#include <GCrypt/GCipher.h>
|
||||
|
||||
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;
|
||||
}
|
68
GCryptCLI/src/ModuleEncryption.cpp
Normal file
@ -0,0 +1,68 @@
|
||||
#include "ModuleEncryption.h"
|
||||
#include "DataIngestionLayer.h"
|
||||
#include "DataOutputLayer.h"
|
||||
#include "KeyManager.h"
|
||||
#include "ProgressPrinter.h"
|
||||
#include <GCrypt/GCipher.h>
|
||||
#include <iostream>
|
||||
|
||||
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;
|
||||
}
|
32
GCryptCLI/src/ModuleGenerateKey.cpp
Normal file
@ -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;
|
||||
}
|
||||
|
64
GCryptCLI/src/ModuleHashing.cpp
Normal file
@ -0,0 +1,64 @@
|
||||
#include "ModuleHashing.h"
|
||||
#include "DataIngestionLayer.h"
|
||||
#include "DataOutputLayer.h"
|
||||
#include "ProgressPrinter.h"
|
||||
#include "KeyManager.h"
|
||||
#include <GCrypt/GHash.h>
|
||||
|
||||
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;
|
||||
}
|
28
GCryptCLI/src/ProgressPrinter.cpp
Normal file
@ -0,0 +1,28 @@
|
||||
#include "ProgressPrinter.h"
|
||||
#include "CommandlineInterface.h"
|
||||
#include <iostream>
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -1,420 +1,41 @@
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <GCrypt/Util.h>
|
||||
#include <GCrypt/Config.h>
|
||||
#include <GCrypt/Cipher.h>
|
||||
#include <GCrypt/Flexblock.h>
|
||||
#include <GCrypt/Block.h>
|
||||
#include "CommandlineInterface.h"
|
||||
|
||||
#include "Bases.h"
|
||||
#include <GeneralUtility/BaseConversion.h>
|
||||
#include <StringTools/StringTools.h>
|
||||
|
||||
// Required for hidden password input
|
||||
#if defined _WIN32 || defined _WIN64
|
||||
#include <Windows.h>
|
||||
#elif defined __GNUG__
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
#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<std::string>& customSet, const std::string& seperator = "")
|
||||
{
|
||||
std::stringstream ss;
|
||||
|
||||
// Translate to custom set
|
||||
const std::vector<std::string> vec_base_custom =
|
||||
Leonetienne::GeneralUtility::BaseConversion::BaseX_2_Y<std::string, std::vector<std::string>>(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<std::string>& customSet, const std::string& seperator = "")
|
||||
{
|
||||
// Split input into symbols
|
||||
const std::vector<std::string> in_symbols = StringTools::Split(in, seperator);
|
||||
|
||||
// Translate to binary
|
||||
std::string binary =
|
||||
Leonetienne::GeneralUtility::BaseConversion::BaseX_2_Y<std::vector<std::string>, 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;
|
||||
}
|
||||
|
||||
|
17965
GCryptCLI/test/Catch2.h
Normal file
90
GCryptCLI/test/DataFormatter.cpp
Normal file
@ -0,0 +1,90 @@
|
||||
#include <DataFormatter.h>
|
||||
#include <GCrypt/Key.h>
|
||||
#include <GCrypt/GPrng.h>
|
||||
#include "Catch2.h"
|
||||
|
||||
using namespace Leonetienne::GCrypt;
|
||||
|
||||
// Tests that recoding iobase formats works for individual blocks, with random data
|
||||
TEMPLATE_TEST_CASE_SIG(
|
||||
__FILE__"Data formats can be converted, without changing in value, with indivudual blocks, with random data",
|
||||
"[DataFormatter]",
|
||||
((Configuration::IOBASE_FORMAT formatUnderTest), formatUnderTest),
|
||||
Configuration::IOBASE_FORMAT::BASE_BYTES,
|
||||
Configuration::IOBASE_FORMAT::BASE_2,
|
||||
Configuration::IOBASE_FORMAT::BASE_8,
|
||||
Configuration::IOBASE_FORMAT::BASE_10,
|
||||
Configuration::IOBASE_FORMAT::BASE_16,
|
||||
Configuration::IOBASE_FORMAT::BASE_64,
|
||||
Configuration::IOBASE_FORMAT::BASE_UWU,
|
||||
Configuration::IOBASE_FORMAT::BASE_UGH
|
||||
) {
|
||||
|
||||
// Let's use a GPrng instead of Key::Random,
|
||||
// because Key::Random is rather slow (because it's using hardware events).
|
||||
// We just want randomized data for tests...
|
||||
|
||||
GPrng prng(Key::Random());
|
||||
|
||||
// Let's try 10 different random blocks per test
|
||||
for (std::size_t i = 0; i < 10; i++) {
|
||||
|
||||
// Setup
|
||||
const Block b_initial = prng.GetBlock();
|
||||
|
||||
// Exercise
|
||||
// Convert to a custom base
|
||||
const std::string b_format = DataFormatter::FormatBlock(
|
||||
b_initial,
|
||||
formatUnderTest
|
||||
);
|
||||
|
||||
// Convert back to a block
|
||||
const Block b_retrieved = DataFormatter::DecodeFormat(
|
||||
b_format,
|
||||
formatUnderTest
|
||||
);
|
||||
|
||||
// Verify
|
||||
// Do b_initial and b_retrieved match?
|
||||
REQUIRE(b_initial == b_retrieved);
|
||||
}
|
||||
}
|
||||
|
||||
// Tests that recoding iobase format works
|
||||
TEMPLATE_TEST_CASE_SIG(
|
||||
__FILE__"Data formats can be converted, without changing in value, with indivudual blocks, with very little data (lots of nullbytes)",
|
||||
"[DataFormatter]",
|
||||
((Configuration::IOBASE_FORMAT formatUnderTest), formatUnderTest),
|
||||
Configuration::IOBASE_FORMAT::BASE_BYTES,
|
||||
Configuration::IOBASE_FORMAT::BASE_2,
|
||||
Configuration::IOBASE_FORMAT::BASE_8,
|
||||
Configuration::IOBASE_FORMAT::BASE_10,
|
||||
Configuration::IOBASE_FORMAT::BASE_16,
|
||||
Configuration::IOBASE_FORMAT::BASE_64,
|
||||
Configuration::IOBASE_FORMAT::BASE_UWU,
|
||||
Configuration::IOBASE_FORMAT::BASE_UGH
|
||||
) {
|
||||
|
||||
// Setup
|
||||
Block b_initial;
|
||||
b_initial.FromTextString("Heyu");
|
||||
|
||||
// Exercise
|
||||
// Convert to a custom base
|
||||
const std::string b_format = DataFormatter::FormatBlock(
|
||||
b_initial,
|
||||
formatUnderTest
|
||||
);
|
||||
|
||||
// Convert back to a block
|
||||
const Block b_retrieved = DataFormatter::DecodeFormat(
|
||||
b_format,
|
||||
formatUnderTest
|
||||
);
|
||||
|
||||
// Verify
|
||||
// Do b_initial and b_retrieved match?
|
||||
REQUIRE(b_initial == b_retrieved);
|
||||
}
|
||||
|
2
GCryptCLI/test/main.cpp
Normal file
@ -0,0 +1,2 @@
|
||||
#define CATCH_CONFIG_MAIN
|
||||
#include "Catch2.h"
|
@ -15,6 +15,11 @@ target_include_directories(${PROJECT_NAME} PRIVATE
|
||||
include
|
||||
)
|
||||
|
||||
target_compile_options(${PROJECT_NAME} PRIVATE
|
||||
-Werror
|
||||
-fdiagnostics-color=always
|
||||
)
|
||||
|
||||
#########
|
||||
# Tests #
|
||||
#########
|
||||
@ -29,7 +34,13 @@ target_include_directories(test PRIVATE
|
||||
include
|
||||
)
|
||||
|
||||
## Move test images to build dir
|
||||
target_compile_options(test PRIVATE
|
||||
-Werror
|
||||
-fdiagnostics-color=always
|
||||
)
|
||||
|
||||
|
||||
## Move test assest to build dir
|
||||
ADD_CUSTOM_COMMAND(
|
||||
TARGET ${PROJECT_NAME} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
@ -37,16 +48,39 @@ ADD_CUSTOM_COMMAND(
|
||||
)
|
||||
|
||||
|
||||
##############
|
||||
# Executable #
|
||||
##############
|
||||
FILE(GLOB exec_src exec/*.cpp)
|
||||
add_executable(exec
|
||||
${exec_src}
|
||||
)
|
||||
target_link_libraries(exec ${PROJECT_NAME})
|
||||
|
||||
target_include_directories(exec PRIVATE
|
||||
###############
|
||||
# Executables #
|
||||
###############
|
||||
FILE(GLOB bmpp_src exec/BmpPP/BmpPP/src/*.cpp)
|
||||
FILE(GLOB eule_src exec/Eule/Eule/src/*.cpp)
|
||||
add_compile_definitions(_EULE_NO_INTRINSICS_)
|
||||
include_directories(
|
||||
include
|
||||
exec/BmpPP/BmpPP/include
|
||||
exec/Eule/Eule/include
|
||||
)
|
||||
|
||||
## Move exec assets to build dir
|
||||
ADD_CUSTOM_COMMAND(
|
||||
TARGET ${PROJECT_NAME} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
${CMAKE_SOURCE_DIR}/exec/execAssets/ $<TARGET_FILE_DIR:${PROJECT_NAME}>/execAssets/
|
||||
)
|
||||
|
||||
function(DECLARE_EXEC_EXAMPLE name)
|
||||
add_executable(example-${name} exec/${name}.cpp ${bmpp_src} ${eule_src})
|
||||
target_link_libraries(example-${name} ${PROJECT_NAME})
|
||||
target_compile_options(example-${name} PRIVATE -Werror -fdiagnostics-color=always)
|
||||
endfunction()
|
||||
|
||||
# These are the names of the cpp files in /exec/, without the ".cpp".
|
||||
DECLARE_EXEC_EXAMPLE(encrypt-decrypt-files)
|
||||
DECLARE_EXEC_EXAMPLE(encrypt-decrypt-strings)
|
||||
DECLARE_EXEC_EXAMPLE(benchmark-encryption)
|
||||
DECLARE_EXEC_EXAMPLE(benchmark-prng)
|
||||
DECLARE_EXEC_EXAMPLE(visualize-singleblock-diffusion)
|
||||
DECLARE_EXEC_EXAMPLE(visualize-multiblock-diffusion)
|
||||
DECLARE_EXEC_EXAMPLE(visualize-extreme-input-diffusion)
|
||||
DECLARE_EXEC_EXAMPLE(visualize-prng-distribution)
|
||||
DECLARE_EXEC_EXAMPLE(visualize-hashing-distribution)
|
||||
|
||||
|
21
GCryptLib/artifacts/optimization-performance-log.md
Normal file
@ -0,0 +1,21 @@
|
||||
Old way (400 rounds, no matrix mult): 38.01s
|
||||
- 400 was the minimum for good diffusion
|
||||
|
||||
New way (10 rounds, with matrix mult): 1.16s
|
||||
- 10 rounds now give sufficient diffusion
|
||||
- still using bitsets and strings
|
||||
|
||||
With new block class (instead of bitsets) (10 rounds): 0.35s
|
||||
- still partially using bitsets and strings
|
||||
|
||||
With new block class (6 rounds, still good diffusion): 0.21s
|
||||
|
||||
With new templated block class (full block and half block): 0.14s
|
||||
- finally no more bitsets
|
||||
|
||||
With new sbox, reduction, and expansion function: 0.03s
|
||||
|
||||
With additional jumbling in feistel rounds: 0.03s
|
||||
|
||||
With direct file reading/writing from data blocks: 0.02s
|
||||
|
10
GCryptLib/doxygen/Makefile
Normal file
@ -0,0 +1,10 @@
|
||||
all:
|
||||
# Copy all but the header of the readme here
|
||||
tail ../../readme.md -n +2 > index.md
|
||||
#
|
||||
# Run doxygen
|
||||
doxygen doxyfig
|
||||
#
|
||||
# Cleanup index.md
|
||||
rm index.md
|
||||
|
@ -1,11 +0,0 @@
|
||||
#!zsh
|
||||
|
||||
# Copy repository readme here to be used as a cover page
|
||||
tail ../../readme.md -n +2 > index.md
|
||||
|
||||
# Run doxygen
|
||||
doxygen doxyfig
|
||||
|
||||
# Cleanup index.md
|
||||
rm -f index.md
|
||||
|
23
GCryptLib/exec/Benchmark.h
Normal file
@ -0,0 +1,23 @@
|
||||
#ifndef GCRYPTEXAMPLE_BENCHMARK_H
|
||||
#define GCRYPTEXAMPLE_BENCHMARK_H
|
||||
|
||||
#include <functional>
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
|
||||
void Benchmark(const std::string& brief, std::function<void()> toBenchmark) {
|
||||
std::cout << "Benchmarking " << brief << "..." << std::endl;
|
||||
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
|
||||
toBenchmark();
|
||||
|
||||
auto end = std::chrono::steady_clock::now();
|
||||
double seconds = (double)std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() / 1000.0;
|
||||
std::cout << seconds << " seconds." << std::endl << std::endl;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
1
GCryptLib/exec/BmpPP
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 522f9551ae31215cf63fcad88cacbfc8c818ccaf
|
1
GCryptLib/exec/Eule
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 1bfd756aa8bd1d484857735d63b4fdf8a15bbed5
|
58
GCryptLib/exec/Visualize.h
Normal file
@ -0,0 +1,58 @@
|
||||
#ifndef GCRYPTEXAMPLE_VISUALIZE_H
|
||||
#define GCRYPTEXAMPLE_VISUALIZE_H
|
||||
|
||||
#include <GCrypt/Block.h>
|
||||
#include <BmpPP/Bmp.h>
|
||||
#include <string>
|
||||
|
||||
using namespace Leonetienne::GCrypt;
|
||||
using namespace Leonetienne::Eule;
|
||||
using namespace Leonetienne::BmpPP;
|
||||
|
||||
void VisualizeBlock(const Block& block, const std::string& name) {
|
||||
BMP bmp(Vector2i(32, 16), Colormode::RGB);
|
||||
|
||||
std::size_t i = 0;
|
||||
for (std::size_t x = 0; x < bmp.GetDimensions().x; x++)
|
||||
for (std::size_t y = 0; y < bmp.GetDimensions().y; y++) {
|
||||
const std::uint8_t pixel = block.GetBit(i) == false ? 255 : 0;
|
||||
bmp.SetPixel(Vector2i(x, y), pixel);
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
bmp.Write(name);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// size.x*size.y MUST equal blocks.size() * Block::BLOCK_SIZE_BITS. That should be, by defalt blocks.size * 512.
|
||||
void VisualizeBlocks(const std::vector<Block>& blocks, const Vector2i& size, const std::string& name) {
|
||||
|
||||
//! A bit of error checking...
|
||||
if (size.x*size.y != blocks.size() * Block::BLOCK_SIZE_BITS) {
|
||||
throw std::invalid_argument("Supplied unfitting widht/height for visualization. Does not fit bits!");
|
||||
}
|
||||
|
||||
BMP bmp(size, Colormode::RGB);
|
||||
|
||||
std::size_t i = 0;
|
||||
for (std::size_t x = 0; x < bmp.GetDimensions().x; x++)
|
||||
for (std::size_t y = 0; y < bmp.GetDimensions().y; y++) {
|
||||
|
||||
const std::size_t blockIndex = i / Block::BLOCK_SIZE_BITS;
|
||||
const std::size_t relBitIndex = i - blockIndex * Block::BLOCK_SIZE_BITS;
|
||||
|
||||
const std::uint8_t pixel = blocks[blockIndex].GetBit(relBitIndex) == false ? 255 : 0;
|
||||
bmp.SetPixel(Vector2i(x, y), pixel);
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
bmp.Write(name);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
21
GCryptLib/exec/benchmark-encryption.cpp
Normal file
@ -0,0 +1,21 @@
|
||||
#include <GCrypt/GWrapper.h>
|
||||
#include "Benchmark.h"
|
||||
|
||||
using namespace Leonetienne::GCrypt;
|
||||
|
||||
int main() {
|
||||
|
||||
Benchmark(
|
||||
"file encryption",
|
||||
[]() {
|
||||
GWrapper::EncryptFile(
|
||||
"./execAssets/big-testfile.bmp",
|
||||
"./execAssets/testimage.bmp.crypt",
|
||||
Key::FromPassword("password1")
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
42
GCryptLib/exec/benchmark-prng.cpp
Normal file
@ -0,0 +1,42 @@
|
||||
#include <GCrypt/GPrng.h>
|
||||
#include <iostream>
|
||||
#include "Benchmark.h"
|
||||
|
||||
using namespace Leonetienne::GCrypt;
|
||||
|
||||
int main() {
|
||||
|
||||
Benchmark(
|
||||
"generating 1.000.000 32-bit uints using prng.GetRandom<uint32_t>()",
|
||||
[]() {
|
||||
GPrng prng(Key::Random());
|
||||
for (int i = 0; i < 1000000; i++) {
|
||||
prng.GetRandom<std::uint32_t>();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
Benchmark(
|
||||
"generating 1.000.000 uint32_t using prng()",
|
||||
[]() {
|
||||
GPrng prng(Key::Random());
|
||||
for (int i = 0; i < 1000000; i++) {
|
||||
prng();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
Benchmark(
|
||||
"generating 100.000 data blocks using prng.GetBlock()",
|
||||
[]() {
|
||||
GPrng prng(Key::Random());
|
||||
for (int i = 0; i < 100000; i++) {
|
||||
prng.GetBlock();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
25
GCryptLib/exec/encrypt-decrypt-files.cpp
Normal file
@ -0,0 +1,25 @@
|
||||
#include <GCrypt/GWrapper.h>
|
||||
#include <iostream>
|
||||
|
||||
using namespace Leonetienne::GCrypt;
|
||||
|
||||
int main() {
|
||||
std::cout << "Example on how to encrypt & decrypt any file:" << std::endl;
|
||||
|
||||
// Encrypt
|
||||
GWrapper::EncryptFile(
|
||||
"./execAssets/testimage.bmp",
|
||||
"./execAssets/testimage.bmp.crypt",
|
||||
Key::FromPassword("password1")
|
||||
);
|
||||
|
||||
// Decrypt
|
||||
GWrapper::DecryptFile(
|
||||
"./execAssets/testimage.bmp.crypt",
|
||||
"./execAssets/testimage.bmp.clear.bmp",
|
||||
Key::FromPassword("password1"
|
||||
));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
30
GCryptLib/exec/encrypt-decrypt-strings.cpp
Normal file
@ -0,0 +1,30 @@
|
||||
#include <GCrypt/GWrapper.h>
|
||||
#include <iostream>
|
||||
|
||||
using namespace Leonetienne::GCrypt;
|
||||
|
||||
int main() {
|
||||
std::cout << "Example on how to encrypt & decrypt strings:" << std::endl;
|
||||
|
||||
const std::string cleartext = "Hello, World :3";
|
||||
std::cout << "Cleartext: " << cleartext << std::endl;
|
||||
|
||||
// Encrypt
|
||||
const std::string ciphertext = GWrapper::EncryptString(
|
||||
cleartext,
|
||||
Key::FromPassword("password1")
|
||||
);
|
||||
|
||||
std::cout << "Ciphertext: " << ciphertext << std::endl;
|
||||
|
||||
// Decrypt
|
||||
const std::string decrypted = GWrapper::DecryptString(
|
||||
ciphertext,
|
||||
Key::FromPassword("password1")
|
||||
);
|
||||
|
||||
std::cout << "Decrypted: " << decrypted << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
BIN
GCryptLib/exec/execAssets/big-testfile.bmp
Normal file
After Width: | Height: | Size: 10 MiB |
BIN
GCryptLib/exec/execAssets/testimage.bmp
Normal file
After Width: | Height: | Size: 108 KiB |
@ -1,45 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <GCrypt/GCryptWrapper.h>
|
||||
#include <GCrypt/SecureBitset.h>
|
||||
#include <GCrypt/Util.h>
|
||||
#include <GCrypt/InitializationVector.h>
|
||||
|
||||
using namespace Leonetienne::GCrypt;
|
||||
|
||||
void ExampleString() {
|
||||
std::cout << "Example on how to encrypt & decrypt a string:" << std::endl;
|
||||
|
||||
// Get some string
|
||||
const std::string input = "I am a super secret message!";
|
||||
std::cout << input << std::endl;
|
||||
|
||||
// Encrypt
|
||||
const std::string encrypted = GCryptWrapper::EncryptString(input, "password1");
|
||||
std::cout << encrypted << std::endl;
|
||||
|
||||
// Decrypt
|
||||
const std::string decrypted = GCryptWrapper::DecryptString(encrypted, "password1");
|
||||
std::cout << decrypted << std::endl;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void ExampleFiles() {
|
||||
std::cout << "Example on how to encrypt & decrypt any file:" << std::endl;
|
||||
|
||||
// Encrypt
|
||||
GCryptWrapper::EncryptFile("main.cpp", "main.cpp.crypt", "password1");
|
||||
|
||||
// Decrypt
|
||||
GCryptWrapper::DecryptFile("main.cpp.crypt", "main.cpp.clear", "password1");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int main() {
|
||||
ExampleString();
|
||||
//ExampleFiles();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
54
GCryptLib/exec/visualize-extreme-input-diffusion.cpp
Normal file
@ -0,0 +1,54 @@
|
||||
#include <iostream>
|
||||
#include <GCrypt/GWrapper.h>
|
||||
#include <GCrypt/Key.h>
|
||||
#include <GCrypt/Util.h>
|
||||
#include <BmpPP/Bmp.h>
|
||||
#include "Visualize.h"
|
||||
|
||||
const std::string execName = "visualize-extreme-input-diffusion";
|
||||
|
||||
using namespace Leonetienne::GCrypt;
|
||||
using namespace Leonetienne::BmpPP;
|
||||
using namespace Leonetienne::Eule;
|
||||
|
||||
int main() {
|
||||
// These are magic values, which work for this specific input string.
|
||||
// If you want to try another string, get the size of all blocks in bits (it gets printed),
|
||||
// and find two integer factors which factor up to it. These are your images width, and height.
|
||||
const Vector2i visualizationDimension = Vector2i(56, 64);
|
||||
|
||||
|
||||
// Create a key
|
||||
// The key is almost just zeores (it doesn't have an IV, so what you see is what goes in the cipher.)
|
||||
// In case you're wondering, I can tell you right now that an all-zero input with an all-zero key will result in an all-zero ciphertext.
|
||||
Key key;
|
||||
key.Reset();
|
||||
key.SetBit(199, true);
|
||||
VisualizeBlock(key, execName+"-key.bmp");
|
||||
|
||||
// Create our input blocks
|
||||
// All the input blocks are just zeroes!
|
||||
std::vector<Block> input_blocks;
|
||||
input_blocks.resize(7);
|
||||
for (Block& block : input_blocks) {
|
||||
block.Reset();
|
||||
}
|
||||
|
||||
std::cout << "Input size is " << input_blocks.size() * Block::BLOCK_SIZE_BITS << " bits long." << std::endl;
|
||||
VisualizeBlocks(input_blocks, visualizationDimension, execName+"-input.bmp");
|
||||
|
||||
// Encrypt it
|
||||
std::vector<Block> ciphertext_blocks = GWrapper::CipherBlocks(input_blocks, key, GCipher::DIRECTION::ENCIPHER);
|
||||
VisualizeBlocks(ciphertext_blocks, visualizationDimension, execName+"-output.bmp");
|
||||
|
||||
// Now flip a single bit in the input
|
||||
input_blocks[3].FlipBit(156);
|
||||
VisualizeBlocks(input_blocks, visualizationDimension, execName+"-input-flip.bmp");
|
||||
|
||||
// Encrypt it again
|
||||
ciphertext_blocks = GWrapper::CipherBlocks(input_blocks, key, GCipher::DIRECTION::ENCIPHER);
|
||||
VisualizeBlocks(ciphertext_blocks, visualizationDimension, execName+"-output-flip.bmp");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
34
GCryptLib/exec/visualize-hashing-distribution.cpp
Normal file
@ -0,0 +1,34 @@
|
||||
#include <iostream>
|
||||
#include <GCrypt/GHash.h>
|
||||
#include <BmpPP/Bmp.h>
|
||||
#include "Visualize.h"
|
||||
|
||||
const std::string execName = "visualize-hashing-distribution";
|
||||
|
||||
using namespace Leonetienne::GCrypt;
|
||||
using namespace Leonetienne::BmpPP;
|
||||
using namespace Leonetienne::Eule;
|
||||
|
||||
void HashAndVisualize(const Block& b, const std::string filename) {
|
||||
GHash hasher;
|
||||
hasher.Digest(b);
|
||||
VisualizeBlock(hasher.GetHashsum(), filename);
|
||||
}
|
||||
|
||||
int main() {
|
||||
|
||||
// Get some random input
|
||||
Block a;
|
||||
a.FromTextString("Hello, World :3");
|
||||
VisualizeBlock(a, execName+"-input-a.bmp");
|
||||
HashAndVisualize(a, execName+"-output-a.bmp");
|
||||
|
||||
// Now flip a bit
|
||||
Block b = a;
|
||||
b.FlipBit(4);
|
||||
VisualizeBlock(b, execName+"-input-b.bmp");
|
||||
HashAndVisualize(b, execName+"-output-b.bmp");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
45
GCryptLib/exec/visualize-multiblock-diffusion.cpp
Normal file
@ -0,0 +1,45 @@
|
||||
#include <iostream>
|
||||
#include <GCrypt/GWrapper.h>
|
||||
#include <GCrypt/Key.h>
|
||||
#include <GCrypt/Util.h>
|
||||
#include <BmpPP/Bmp.h>
|
||||
#include "Visualize.h"
|
||||
|
||||
const std::string execName = "visualize-multiblock-diffusion";
|
||||
|
||||
using namespace Leonetienne::GCrypt;
|
||||
using namespace Leonetienne::BmpPP;
|
||||
using namespace Leonetienne::Eule;
|
||||
|
||||
int main() {
|
||||
// These are magic values, which work for this specific input string.
|
||||
// If you want to try another string, get the size of all blocks in bits (it gets printed),
|
||||
// and find two integer factors which factor up to it. These are your images width, and height.
|
||||
const Vector2i visualizationDimension = Vector2i(56, 64);
|
||||
|
||||
|
||||
// Create a key
|
||||
const Key key = Key::Random();
|
||||
VisualizeBlock(key, execName+"-key.bmp");
|
||||
|
||||
// Create our input blocks
|
||||
const std::string input = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
|
||||
std::vector<Block> input_blocks = StringToBitblocks(input);
|
||||
std::cout << "Input size is " << input_blocks.size() * Block::BLOCK_SIZE_BITS << " bits long." << std::endl;
|
||||
VisualizeBlocks(input_blocks, visualizationDimension, execName+"-input.bmp");
|
||||
|
||||
// Encrypt it
|
||||
std::vector<Block> ciphertext_blocks = GWrapper::CipherBlocks(input_blocks, key, GCipher::DIRECTION::ENCIPHER);
|
||||
VisualizeBlocks(ciphertext_blocks, visualizationDimension, execName+"-output.bmp");
|
||||
|
||||
// Now flip a single bit in the input
|
||||
input_blocks[3].FlipBit(156);
|
||||
VisualizeBlocks(input_blocks, visualizationDimension, execName+"-input-flip.bmp");
|
||||
|
||||
// Encrypt it again
|
||||
ciphertext_blocks = GWrapper::CipherBlocks(input_blocks, key, GCipher::DIRECTION::ENCIPHER);
|
||||
VisualizeBlocks(ciphertext_blocks, visualizationDimension, execName+"-output-flip.bmp");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
146
GCryptLib/exec/visualize-prng-distribution.cpp
Normal file
@ -0,0 +1,146 @@
|
||||
#include <iostream>
|
||||
#include <GCrypt/Key.h>
|
||||
#include <GCrypt/GPrng.h>
|
||||
#include <BmpPP/Bmp.h>
|
||||
#include "Benchmark.h"
|
||||
|
||||
const std::string execName = "visualize-prng-distribution";
|
||||
|
||||
using namespace Leonetienne::GCrypt;
|
||||
using namespace Leonetienne::BmpPP;
|
||||
using namespace Leonetienne::Eule;
|
||||
|
||||
int main() {
|
||||
|
||||
// Black/white
|
||||
Benchmark(
|
||||
"black/white - GetBit()",
|
||||
[]() {
|
||||
Key seed = Key::Random();
|
||||
BMP bmp(Vector2i(800, 800), Colormode::RGB);
|
||||
GPrng prng(seed);
|
||||
|
||||
for (std::size_t x = 0; x < bmp.GetDimensions().x; x++)
|
||||
for (std::size_t y = 0; y < bmp.GetDimensions().y; y++) {
|
||||
const std::uint8_t pixel = prng.GetBit() ? 255 : 0;
|
||||
bmp.SetPixel(Vector2i(x, y), pixel);
|
||||
}
|
||||
|
||||
bmp.Write(execName+"-blackwhite.bmp");
|
||||
}
|
||||
);
|
||||
|
||||
// Grayscale (using GetRandom<>)
|
||||
Benchmark(
|
||||
"grayscale - GetRandom<std::uint8_t>()",
|
||||
[]() {
|
||||
Key seed = Key::Random();
|
||||
BMP bmp(Vector2i(800, 800), Colormode::RGB);
|
||||
GPrng prng(seed);
|
||||
|
||||
for (std::size_t x = 0; x < bmp.GetDimensions().x; x++)
|
||||
for (std::size_t y = 0; y < bmp.GetDimensions().y; y++) {
|
||||
const std::uint8_t pixel = prng.GetRandom<std::uint8_t>();
|
||||
bmp.SetPixel(Vector2i(x, y), pixel);
|
||||
}
|
||||
|
||||
bmp.Write(execName+"-getrandom-grayscale.bmp");
|
||||
}
|
||||
);
|
||||
|
||||
// Grayscale (using operator()) (this one returns a whole uint32)
|
||||
Benchmark(
|
||||
"grayscale - operator()",
|
||||
[]() {
|
||||
Key seed = Key::Random();
|
||||
BMP bmp(Vector2i(800, 800), Colormode::RGB);
|
||||
GPrng prng(seed);
|
||||
|
||||
for (std::size_t x = 0; x < bmp.GetDimensions().x; x++)
|
||||
for (std::size_t y = 0; y < bmp.GetDimensions().y; y++) {
|
||||
const std::uint8_t pixel = prng() % 256;
|
||||
bmp.SetPixel(Vector2i(x, y), pixel);
|
||||
}
|
||||
|
||||
bmp.Write(execName+"-operator-grayscale.bmp");
|
||||
}
|
||||
);
|
||||
|
||||
// Color (using GetRandom<>)
|
||||
Benchmark(
|
||||
"color - GetRandom<uint8_t>()",
|
||||
[]() {
|
||||
Key seed = Key::Random();
|
||||
BMP bmp(Vector2i(800, 800), Colormode::RGB);
|
||||
GPrng prng(seed);
|
||||
|
||||
for (std::size_t x = 0; x < bmp.GetDimensions().x; x++)
|
||||
for (std::size_t y = 0; y < bmp.GetDimensions().y; y++) {
|
||||
const std::uint8_t r = prng.GetRandom<std::uint8_t>();
|
||||
const std::uint8_t g = prng.GetRandom<std::uint8_t>();
|
||||
const std::uint8_t b = prng.GetRandom<std::uint8_t>();
|
||||
bmp.SetPixel(Vector2i(x, y), r, g, b);
|
||||
}
|
||||
|
||||
bmp.Write(execName+"-getrandom-color.bmp");
|
||||
}
|
||||
);
|
||||
|
||||
// Color (using operator()) (this one returns a whole uint32)
|
||||
Benchmark(
|
||||
"color - operator()",
|
||||
[]() {
|
||||
Key seed = Key::Random();
|
||||
BMP bmp(Vector2i(800, 800), Colormode::RGB);
|
||||
GPrng prng(seed);
|
||||
|
||||
for (std::size_t x = 0; x < bmp.GetDimensions().x; x++)
|
||||
for (std::size_t y = 0; y < bmp.GetDimensions().y; y++) {
|
||||
const std::uint8_t r = prng() % 256;
|
||||
const std::uint8_t g = prng() % 256;
|
||||
const std::uint8_t b = prng() % 256;
|
||||
bmp.SetPixel(Vector2i(x, y), r, g, b);
|
||||
}
|
||||
|
||||
bmp.Write(execName+"-operator-color.bmp");
|
||||
}
|
||||
);
|
||||
|
||||
// Color (using GetBlock())
|
||||
//(this one returns a derivation of the current hashsum, without using up randomness)
|
||||
Benchmark(
|
||||
"color - GetBlock()",
|
||||
[]() {
|
||||
Key seed = Key::Random();
|
||||
BMP bmp(Vector2i(800, 800), Colormode::RGB);
|
||||
GPrng prng(seed);
|
||||
|
||||
std::size_t bytes_written = 0;
|
||||
while (bytes_written < bmp.GetPixelbufferSize()) {
|
||||
const Block block = prng.GetBlock();
|
||||
|
||||
std::size_t bytesToCopy = 0;
|
||||
if (bmp.GetPixelbufferSize() - bytes_written < Block::BLOCK_SIZE) {
|
||||
bytesToCopy = bmp.GetPixelbufferSize() - bytes_written;
|
||||
}
|
||||
else {
|
||||
bytesToCopy = Block::BLOCK_SIZE;
|
||||
}
|
||||
|
||||
memcpy(
|
||||
(char*)bmp.GetPixelbuffer().data() + bytes_written,
|
||||
(char*)block.Data(),
|
||||
bytesToCopy
|
||||
);
|
||||
|
||||
bytes_written += bytesToCopy;
|
||||
|
||||
}
|
||||
|
||||
bmp.Write(execName+"-getblock-color.bmp");
|
||||
}
|
||||
);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
45
GCryptLib/exec/visualize-singleblock-diffusion.cpp
Normal file
@ -0,0 +1,45 @@
|
||||
#include <iostream>
|
||||
#include <GCrypt/GWrapper.h>
|
||||
#include <GCrypt/Key.h>
|
||||
#include <BmpPP/Bmp.h>
|
||||
#include "Visualize.h"
|
||||
|
||||
const std::string execName = "visualize-singleblock-diffusion";
|
||||
|
||||
using namespace Leonetienne::GCrypt;
|
||||
using namespace Leonetienne::BmpPP;
|
||||
using namespace Leonetienne::Eule;
|
||||
|
||||
int main() {
|
||||
|
||||
// Create input block
|
||||
const std::string input_str = "Hello :3";
|
||||
Block input;
|
||||
input.FromTextString(input_str);
|
||||
VisualizeBlock(input, execName+"-input.bmp");
|
||||
|
||||
// Create a key
|
||||
const Key key = Key::Random();
|
||||
VisualizeBlock(key, execName+"-key.bmp");
|
||||
|
||||
// Create a cipher
|
||||
GCipher cipher(key, GCipher::DIRECTION::ENCIPHER);
|
||||
|
||||
// Encipher our block
|
||||
Block cipherblock = cipher.Digest(input);
|
||||
VisualizeBlock(cipherblock, execName+"-output.bmp");
|
||||
|
||||
// Now flip a bit in the input
|
||||
input.FlipBit(35);
|
||||
VisualizeBlock(input, execName+"-input-flip.bmp");
|
||||
|
||||
// Reset our cipher, and encipher the input with a flipped bit
|
||||
cipher.Initialize(key, GCipher::DIRECTION::ENCIPHER);
|
||||
|
||||
// Encipher our block
|
||||
cipherblock = cipher.Digest(input);
|
||||
VisualizeBlock(cipherblock, execName+"-output-flip.bmp");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,8 +1,216 @@
|
||||
#pragma once
|
||||
#include "GCrypt/SecureBitset.h"
|
||||
#include "GCrypt/Config.h"
|
||||
#ifndef GCRYPT_BLOCK_H
|
||||
#define GCRYPT_BLOCK_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <ostream>
|
||||
#include <bitset>
|
||||
|
||||
namespace Leonetienne::GCrypt {
|
||||
typedef SecureBitset<BLOCK_SIZE> Block;
|
||||
|
||||
/** This class represents a block of data,
|
||||
* and provides functions to manipulate it
|
||||
*/
|
||||
template <typename T>
|
||||
class Basic_Block {
|
||||
public:
|
||||
//! Will constuct an uninitialized data block
|
||||
Basic_Block();
|
||||
|
||||
//! Will construct this block from a string like "101010".. Length MUST be 512.
|
||||
Basic_Block(const std::string& other);
|
||||
|
||||
//! Copy-ctor
|
||||
Basic_Block(const Basic_Block<T>& other);
|
||||
|
||||
~Basic_Block();
|
||||
|
||||
//! Will construct this block from a string like "011101..".
|
||||
void FromBinaryString(const std::string& str);
|
||||
|
||||
//! Will construct this block from a hexstring
|
||||
void FromHexString(const std::string& str);
|
||||
|
||||
//! Will construct this block from a bytestring (any characters)
|
||||
void FromByteString(const std::string& str);
|
||||
|
||||
//! Will construct this block from a textstring (any length)
|
||||
void FromTextString(const std::string& str);
|
||||
|
||||
//! Will create a bitset-compatible string ("0101110..") representation
|
||||
//! of this block. Length will always be 512.
|
||||
std::string ToBinaryString() const;
|
||||
|
||||
//! Will create a hexstring representation of this block.
|
||||
std::string ToHexString() const;
|
||||
|
||||
//! Will create a bytestring representation of this block.
|
||||
std::string ToByteString() const;
|
||||
|
||||
//! Will create a textstring representation of this block.
|
||||
//! The difference to a bytestring is that it gets trimmed after a nullterminator.
|
||||
std::string ToTextString() const;
|
||||
|
||||
//! Will matrix-multiply two blocks together.
|
||||
//! Since the matrices values are pretty much sudo-random,
|
||||
//! they will most likely integer-overflow.
|
||||
//! So see this as a one-way function.
|
||||
[[nodiscard]] Basic_Block<T> MMul(const Basic_Block<T>& other) const;
|
||||
[[nodiscard]] Basic_Block<T> operator*(const Basic_Block<T>& other) const;
|
||||
|
||||
//! Will matrix-multiply two blocks together,
|
||||
//! and directly write into this same block.
|
||||
//! Since the matrices values are pretty much sudo-random,
|
||||
//! they will most likely integer-overflow.
|
||||
//! So see this as a one-way function.
|
||||
void MMulInplace(const Basic_Block<T>& other);
|
||||
Basic_Block<T>& operator*=(const Basic_Block<T>& other);
|
||||
|
||||
//! Will xor two blocks together
|
||||
[[nodiscard]] Basic_Block<T> Xor(const Basic_Block<T>& other) const;
|
||||
//! Will xor two blocks together
|
||||
[[nodiscard]] Basic_Block<T> operator^(const Basic_Block<T>& other) const;
|
||||
|
||||
//! Will xor two blocks together, inplace
|
||||
void XorInplace(const Basic_Block<T>& other);
|
||||
//! Will xor two blocks together, inplace
|
||||
Basic_Block<T>& operator^=(const Basic_Block<T>& other);
|
||||
|
||||
//! Will add all the integer making up this block, one by one
|
||||
[[nodiscard]] Basic_Block<T> Add(const Basic_Block<T>& other) const;
|
||||
//! Will add all the integer making up this block, one by one
|
||||
[[nodiscard]] Basic_Block<T> operator+(const Basic_Block<T>& other) const;
|
||||
|
||||
//! Will add all the integer making up this block, one by one, inplace
|
||||
void AddInplace(const Basic_Block<T>& other);
|
||||
//! Will add all the integer making up this block, one by one, inplace
|
||||
Basic_Block<T>& operator+=(const Basic_Block<T>& other);
|
||||
|
||||
//! Will subtract all the integer making up this block, one by one
|
||||
[[nodiscard]] Basic_Block<T> Sub(const Basic_Block<T>& other) const;
|
||||
//! Will subtract all the integer making up this block, one by one
|
||||
[[nodiscard]] Basic_Block<T> operator-(const Basic_Block<T>& other) const;
|
||||
|
||||
//! Will subtract all the integer making up this block, one by one, inplace
|
||||
void SubInplace(const Basic_Block<T>& other);
|
||||
//! Will subtract all the integer making up this block, one by one, inplace
|
||||
Basic_Block<T>& operator-=(const Basic_Block<T>& other);
|
||||
|
||||
//! Will shift rows upwards by 1
|
||||
[[nodiscard]] Basic_Block<T> ShiftRowsUp() const;
|
||||
|
||||
//! Will shift rows upwards by 1
|
||||
void ShiftRowsUpInplace();
|
||||
|
||||
//! Will shift matrix rows downwards by 1
|
||||
[[nodiscard]] Basic_Block<T> ShiftRowsDown() const;
|
||||
|
||||
//! Will shift matrix rows downwards by 1
|
||||
void ShiftRowsDownInplace();
|
||||
|
||||
//! Will shift matrix columns to the left by 1
|
||||
[[nodiscard]] Basic_Block<T> ShiftColumnsLeft() const;
|
||||
|
||||
//! Will shift matrix columns to the left by 1
|
||||
void ShiftColumnsLeftInplace();
|
||||
|
||||
//! Will shift matrix columns to the right by 1
|
||||
[[nodiscard]] Basic_Block<T> ShiftColumnsRight() const;
|
||||
|
||||
//! Will shift matrix columns to the right by 1
|
||||
void ShiftColumnsRightInplace();
|
||||
|
||||
//! Will shift array cells to the left by 1
|
||||
[[nodiscard]] Basic_Block<T> ShiftCellsLeft() const;
|
||||
|
||||
//! Will shift array cells to the left by 1
|
||||
void ShiftCellsLeftInplace();
|
||||
|
||||
//! Will shift array cells to the right by 1
|
||||
[[nodiscard]] Basic_Block<T> ShiftCellsRight() const;
|
||||
|
||||
//! Will shift array cells to the right by 1
|
||||
void ShiftCellsRightInplace();
|
||||
|
||||
//! Will copy a block
|
||||
Basic_Block<T>& operator=(const Basic_Block<T>& other);
|
||||
|
||||
//! Will compare whether or not two blocks are equal
|
||||
[[nodiscard]] bool operator==(const Basic_Block<T>& other) const;
|
||||
//! Will compare whether or not two blocks are unequal
|
||||
[[nodiscard]] bool operator!=(const Basic_Block<T>& other) const;
|
||||
|
||||
//! Will zero all data
|
||||
void Reset();
|
||||
|
||||
//! Will return the state of any given bit
|
||||
[[nodiscard]] bool GetBit(const std::size_t index) const;
|
||||
|
||||
//! Will set the state of any given bit
|
||||
void SetBit(const std::size_t index, const bool state);
|
||||
|
||||
//! Will flip the state of any given bit
|
||||
void FlipBit(const std::size_t index);
|
||||
|
||||
//! Will shift all bits to the left by 1
|
||||
[[nodiscard]] Basic_Block<T> ShiftBitsLeft() const;
|
||||
|
||||
//! Will shift all bits to the left by 1, inplace
|
||||
void ShiftBitsLeftInplace();
|
||||
|
||||
//! Will shift all bits to the right by 1
|
||||
[[nodiscard]] Basic_Block<T> ShiftBitsRight() const;
|
||||
|
||||
//! Will shift all bits to the right by 1, inplace
|
||||
void ShiftBitsRightInplace();
|
||||
|
||||
//! Returns 32-bit chunks of data, indexed by matrix coordinates (0-3)
|
||||
[[nodiscard]] T& Get(const std::uint8_t row, const std::uint8_t column);
|
||||
//! Returns 32-bit chunks of data, indexed by matrix coordinates (0-3)
|
||||
[[nodiscard]] const T& Get(const std::uint8_t row, const std::uint8_t column) const;
|
||||
|
||||
//! Returns 32-bit chunks of data, indexed by a 1d-index (0-16)
|
||||
[[nodiscard]] T& Get(const std::uint8_t index);
|
||||
|
||||
//! Returns 32-bit chunks of data, indexed by a 1d-index (0-16)
|
||||
[[nodiscard]] const T& Get(const std::uint8_t index) const;
|
||||
|
||||
//! Returns 32-bit chunks of data, indexed by a 1d-index (0-16)
|
||||
[[nodiscard]] T& operator[](const std::uint8_t index);
|
||||
|
||||
//! Returns 32-bit chunks of data, indexed by a 1d-index (0-16)
|
||||
[[nodiscard]] const T& operator[](const std::uint8_t index) const;
|
||||
|
||||
//! Will return a reference to the data array
|
||||
T* Data() noexcept;
|
||||
|
||||
//! Will return a reference to the data array
|
||||
const T* Data() const noexcept;
|
||||
|
||||
static constexpr std::size_t CHUNK_SIZE = sizeof(T);
|
||||
static constexpr std::size_t CHUNK_SIZE_BITS = CHUNK_SIZE * 8;
|
||||
static constexpr std::size_t BLOCK_SIZE = CHUNK_SIZE * 16;
|
||||
static constexpr std::size_t BLOCK_SIZE_BITS = CHUNK_SIZE_BITS * 16;
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os, const Basic_Block<T>& b) {
|
||||
for (std::size_t i = 0; i < b.data.size(); i++) {
|
||||
os << std::bitset<Basic_Block<T>::CHUNK_SIZE_BITS>(b.data[i]).to_string();
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
std::array<T, 16> data;
|
||||
};
|
||||
|
||||
//! This a full-sized 512-bit block
|
||||
typedef Basic_Block<std::uint32_t> Block;
|
||||
|
||||
//! This is a half-block used within the feistel class
|
||||
typedef Basic_Block<std::uint16_t> Halfblock;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -1,39 +0,0 @@
|
||||
#pragma once
|
||||
#include "GCrypt/Feistel.h"
|
||||
#include "GCrypt/Flexblock.h"
|
||||
|
||||
namespace Leonetienne::GCrypt {
|
||||
/** Class to apply a block cipher to messages of arbitrary length in a distributed manner
|
||||
*/
|
||||
class Cipher {
|
||||
public:
|
||||
explicit Cipher(const Block& key);
|
||||
explicit Cipher(const std::string& password);
|
||||
|
||||
Cipher(const Cipher& other) = delete;
|
||||
Cipher(Cipher&& other) noexcept = delete;
|
||||
|
||||
~Cipher();
|
||||
|
||||
//! Will set the key
|
||||
void SetKey(const Block& key);
|
||||
|
||||
//! Will set the key from a password
|
||||
void SetPassword(const std::string& password);
|
||||
|
||||
//! Will encipher a flexblock of data
|
||||
Flexblock Encipher(const Flexblock& data, bool printProgress = false) const;
|
||||
|
||||
//! Will decipher a flexblock of data
|
||||
Flexblock Decipher(const Flexblock& data, bool printProgress = false) const;
|
||||
|
||||
private:
|
||||
Block key;
|
||||
|
||||
//! Will zero the memory used by the key
|
||||
void ZeroKeyMemory();
|
||||
|
||||
// Initial value for cipher block chaining
|
||||
Block initializationVector;
|
||||
};
|
||||
}
|
@ -1,10 +1,12 @@
|
||||
#pragma once
|
||||
#ifndef GCRYPT_CONFIG_H
|
||||
#define GCRYPT_CONFIG_H
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
namespace Leonetienne::GCrypt {
|
||||
// MUST BE A POWER OF 2 > 4
|
||||
constexpr std::size_t BLOCK_SIZE = 512;
|
||||
|
||||
// MUST BE > 2
|
||||
constexpr std::size_t N_ROUNDS = 64;
|
||||
constexpr std::size_t N_ROUNDS = 6;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -1,14 +1,20 @@
|
||||
#pragma once
|
||||
#ifndef GCRYPT_FEISTEL_H
|
||||
#define GCRYPT_FEISTEL_H
|
||||
|
||||
#include "GCrypt/Keyset.h"
|
||||
#include "GCrypt/Block.h"
|
||||
#include "GCrypt/Halfblock.h"
|
||||
#include "GCrypt/Key.h"
|
||||
|
||||
namespace Leonetienne::GCrypt {
|
||||
/** Class to perform a feistel block chipher
|
||||
*/
|
||||
class Feistel {
|
||||
public:
|
||||
explicit Feistel(const Block& key);
|
||||
//! Empty initializer. If you use this, you must call SetKey()!
|
||||
Feistel();
|
||||
|
||||
//! Will initialize the feistel cipher with a key
|
||||
explicit Feistel(const Key& key);
|
||||
|
||||
Feistel(const Feistel& other) = delete;
|
||||
Feistel(Feistel&& other) noexcept = delete;
|
||||
@ -17,7 +23,7 @@ namespace Leonetienne::GCrypt {
|
||||
|
||||
//! Will set the seed-key for this feistel network.
|
||||
//! Roundkeys will be derived from this.
|
||||
void SetKey(const Block& key);
|
||||
void SetKey(const Key& key);
|
||||
|
||||
//! Will encipher a data block via the set seed-key
|
||||
Block Encipher(const Block& data);
|
||||
@ -25,13 +31,15 @@ namespace Leonetienne::GCrypt {
|
||||
//! Will decipher a data block via the set seed-key
|
||||
Block Decipher(const Block& data);
|
||||
|
||||
void operator=(const Feistel& other);
|
||||
|
||||
private:
|
||||
//! Will run the feistel rounds, with either regular key
|
||||
//! order or reversed key order
|
||||
Block Run(const Block& data, bool reverseKeys);
|
||||
Block Run(const Block& data, bool modeEncrypt);
|
||||
|
||||
//! Arbitrary cipher function
|
||||
static Halfblock F(Halfblock m, const Block& key);
|
||||
static Halfblock F(Halfblock m, const Key& key);
|
||||
|
||||
//! Split a data block into two half blocks (into L and R)
|
||||
static std::pair<Halfblock, Halfblock> FeistelSplit(const Block& block);
|
||||
@ -42,18 +50,23 @@ namespace Leonetienne::GCrypt {
|
||||
//! Will expand a halfblock to a fullblock
|
||||
static Block ExpansionFunction(const Halfblock& block);
|
||||
|
||||
//! Will compress a fullblock to a halfblock
|
||||
static Halfblock CompressionFunction(const Block& block);
|
||||
//! Will reduce a fullblock to a halfblock
|
||||
static Halfblock ReductionFunction(const Block& block);
|
||||
|
||||
//! Substitutes four bits by static random others
|
||||
static std::string SBox(const std::string& in);
|
||||
//! Substitutes eight bits by static random others, inplace
|
||||
static void SBox(Block& block);
|
||||
|
||||
//! Will generate a the round keys
|
||||
void GenerateRoundKeys(const Block& seedKey);
|
||||
void GenerateRoundKeys(const Key& seedKey);
|
||||
|
||||
//! Will zero the memory used by the keyset
|
||||
void ZeroKeyMemory();
|
||||
|
||||
Keyset roundKeys;
|
||||
|
||||
bool isInitialized = false;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -1,7 +0,0 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
|
||||
namespace Leonetienne::GCrypt {
|
||||
//! A "bitset" of variable length
|
||||
typedef std::string Flexblock;
|
||||
}
|
54
GCryptLib/include/GCrypt/GCipher.h
Normal file
@ -0,0 +1,54 @@
|
||||
#ifndef GCRYPT_GCIPHER_H
|
||||
#define GCRYPT_GCIPHER_H
|
||||
|
||||
#include "GCrypt/Feistel.h"
|
||||
|
||||
namespace Leonetienne::GCrypt {
|
||||
/** Class to apply a block/-stream cipher to messages of arbitrary length in a distributed manner
|
||||
*/
|
||||
class GCipher {
|
||||
public:
|
||||
//! Describes the direction the cipher runs in
|
||||
enum class DIRECTION {
|
||||
ENCIPHER,
|
||||
DECIPHER
|
||||
};
|
||||
|
||||
//! Empty initializer. If you use this, you must call Initialize()!
|
||||
GCipher();
|
||||
|
||||
//! Will initialize this cipher with a key
|
||||
explicit GCipher(const Key& key, const DIRECTION direction);
|
||||
|
||||
// Disable copying
|
||||
GCipher(const GCipher& other) = delete;
|
||||
GCipher(GCipher&& other) noexcept = delete;
|
||||
|
||||
//! Will digest a data block, and return it
|
||||
Block Digest(const Block& input);
|
||||
|
||||
//! Will update the base key used
|
||||
void SetKey(const Key& key);
|
||||
|
||||
void operator=(const GCipher& other);
|
||||
|
||||
|
||||
//! Will initialize the cipher with a key, and a mode.
|
||||
//! If called on an existing object, it will reset its state.
|
||||
void Initialize(const Key& key, const DIRECTION direction);
|
||||
|
||||
private:
|
||||
DIRECTION direction;
|
||||
|
||||
//! The feistel instance to be used
|
||||
Feistel feistel;
|
||||
|
||||
//! The last block, required for CBC.
|
||||
Block lastBlock;
|
||||
|
||||
bool isInitialized = false;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
45
GCryptLib/include/GCrypt/GHash.h
Normal file
@ -0,0 +1,45 @@
|
||||
#ifndef GCRYPT_GHASH_H
|
||||
#define GCRYPT_GHASH_H
|
||||
|
||||
#include "GCrypt/Block.h"
|
||||
#include "GCrypt/GCipher.h"
|
||||
#include <vector>
|
||||
|
||||
namespace Leonetienne::GCrypt {
|
||||
/** This class implements a hash function, based on the GCrypt cipher
|
||||
*/
|
||||
class GHash {
|
||||
public:
|
||||
GHash();
|
||||
|
||||
//! Will add the hash value of the block `data` to the hashsum.
|
||||
//! WARNING: If you compute hashes using this digestive method,
|
||||
//! you REALLY REALLY should add a trailing block just containing the cleartext size!
|
||||
//! You MOST LIKELY just want to use the wrapper function GHash::CalculateHashsum(Flexblock const&) instead!
|
||||
void Digest(const Block& data);
|
||||
|
||||
//! Will return the current hashsum
|
||||
const Block& GetHashsum() const;
|
||||
|
||||
//! Will calculate a hashsum for `blocks`.
|
||||
//! Whilst n_bytes is optional, it is HIGHLY recommended to supply.
|
||||
//! Without specifying the size of the input (doesn't always have to be 512*n bits)
|
||||
//! b'293eff' would hash to the exact same values as b'293eff0000'
|
||||
static Block CalculateHashsum(const std::vector<Block>& blocks, std::size_t n_bytes = std::string::npos);
|
||||
|
||||
//! Will calculate a hashsum for a string
|
||||
static Block HashString(const std::string& str);
|
||||
|
||||
void operator=(const GHash& other);
|
||||
|
||||
private:
|
||||
//! The cipher to use
|
||||
GCipher cipher;
|
||||
|
||||
//! The current state of the hashsum
|
||||
Block block;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
71
GCryptLib/include/GCrypt/GPrng.h
Normal file
@ -0,0 +1,71 @@
|
||||
#ifndef GCRYPT_GPRNG_H
|
||||
#define GCRYPT_GPRNG_H
|
||||
|
||||
#include "GCrypt/GHash.h"
|
||||
#include "GCrypt/Util.h"
|
||||
#include <string.h>
|
||||
#include <sstream>
|
||||
#include <type_traits>
|
||||
|
||||
namespace Leonetienne::GCrypt {
|
||||
/** This class implements a pseudo random number generator, based on the GCrypt hash function
|
||||
*/
|
||||
class GPrng {
|
||||
public:
|
||||
//! Will instanciate the prng with a seed. Seed could also be a GCrypt::Key.
|
||||
GPrng(const Block& seed);
|
||||
|
||||
//! Will instanciate the GPrng with no seed. You should really seed it later.
|
||||
GPrng();
|
||||
|
||||
//! Will reset and seed the prng. Seed could also be a GCrypt::Key.
|
||||
void Seed(const Block& seed);
|
||||
|
||||
//! Will return a random bit.
|
||||
bool GetBit();
|
||||
|
||||
//! Will return a randomized instance of any primitive.
|
||||
template <typename T>
|
||||
T GetRandom() {
|
||||
static_assert(std::is_fundamental<T>::value, "Leonetienne::GCrypt::GPrng::GetRandom() may only be used with primitive types!");
|
||||
|
||||
// Pull the required amount of bits
|
||||
std::stringstream ss;
|
||||
for (std::size_t i = 0; i < sizeof(T)*8; i++) {
|
||||
ss << (GetBit() ? '1' : '0');
|
||||
}
|
||||
|
||||
// Transform to bytes
|
||||
const std::string bits = ss.str();
|
||||
ss.str("");
|
||||
for (std::size_t i = 0; i < bits.size(); i += 8) {
|
||||
ss << (char)std::bitset<8>(bits.substr(i, 8)).to_ulong();
|
||||
}
|
||||
const std::string bytes = ss.str();
|
||||
|
||||
// Cram bytes into type
|
||||
T t;
|
||||
memcpy(&t, bytes.data(), sizeof(T));
|
||||
|
||||
// Return our randomized primitive
|
||||
return t;
|
||||
}
|
||||
|
||||
//! Will return a random unsigned 32-bit integer
|
||||
std::uint32_t operator()();
|
||||
|
||||
//! Will return a random block
|
||||
Block GetBlock();
|
||||
|
||||
private:
|
||||
//! Will generate the next block of random bits
|
||||
void AdvanceBlock();
|
||||
|
||||
GHash hasher;
|
||||
Block seed;
|
||||
std::size_t nextBit = 0;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -1,33 +1,45 @@
|
||||
#pragma once
|
||||
#ifndef GCRYPT_GWRAPPER_H
|
||||
#define GCRYPT_GWRAPPER_H
|
||||
|
||||
#include "GCrypt/Block.h"
|
||||
#include "GCrypt/GCipher.h"
|
||||
#include "GCrypt/Key.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace Leonetienne::GCrypt {
|
||||
/** This class is a wrapper to make working with the GhettoCipher
|
||||
/** This class is a wrapper to make working with the GCipher
|
||||
* super easy with a python-like syntax
|
||||
*/
|
||||
class GCryptWrapper {
|
||||
class GWrapper {
|
||||
public:
|
||||
//! Will encrypt a string and return it hexadecimally encoded.
|
||||
static std::string EncryptString(const std::string& cleartext, const std::string& password);
|
||||
static std::string EncryptString(const std::string& cleartext, const Key& key);
|
||||
|
||||
//! Will decrypt a hexadecimally encoded string.
|
||||
static std::string DecryptString(const std::string& ciphertext, const std::string& password);
|
||||
static std::string DecryptString(const std::string& ciphertext, const Key& key);
|
||||
|
||||
//! Will encrypt a file.
|
||||
//! Returns false if anything goes wrong (like, file-access).
|
||||
//! @filename_in The file to be read.
|
||||
//! @filename_out The file the encrypted version should be saved in.
|
||||
static bool EncryptFile(const std::string& filename_in, const std::string& filename_out, const std::string& password, bool printProgressReport = false);
|
||||
static bool EncryptFile(const std::string& filename_in, const std::string& filename_out, const Key& key, bool printProgressReport = false);
|
||||
|
||||
//! Will decrypt a file.
|
||||
//! Returns false if anything goes wrong (like, file-access).
|
||||
//! @filename_in The file to be read.
|
||||
//! @filename_out The file the decrypted version should be saved in.
|
||||
static bool DecryptFile(const std::string& filename_in, const std::string& filename_out, const std::string& password, bool printProgressReport = false);
|
||||
static bool DecryptFile(const std::string& filename_in, const std::string& filename_out, const Key& key, bool printProgressReport = false);
|
||||
|
||||
//! Will enncrypt or decrypt an entire flexblock of binary data, given a key.
|
||||
static std::vector<Block> CipherBlocks(const std::vector<Block>& data, const Key& key, const GCipher::DIRECTION direction);
|
||||
|
||||
private:
|
||||
|
||||
// No instanciation! >:(
|
||||
GCryptWrapper();
|
||||
GWrapper();
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -1,9 +0,0 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include "GCrypt/SecureBitset.h"
|
||||
#include "GCrypt/Config.h"
|
||||
|
||||
namespace Leonetienne::GCrypt {
|
||||
constexpr std::size_t HALFBLOCK_SIZE = (BLOCK_SIZE / 2);
|
||||
typedef SecureBitset<HALFBLOCK_SIZE> Halfblock;
|
||||
}
|
37
GCryptLib/include/GCrypt/Key.h
Normal file
@ -0,0 +1,37 @@
|
||||
#ifndef GCRYPT_KEY_H
|
||||
#define GCRYPT_KEY_H
|
||||
#include "GCrypt/Block.h"
|
||||
#include <string>
|
||||
|
||||
namespace Leonetienne::GCrypt {
|
||||
|
||||
/** This class represents encryption keys.
|
||||
* You can copy them, create them from data blocks,
|
||||
* or even read from files.
|
||||
*/
|
||||
class Key : public Block {
|
||||
public:
|
||||
//! Will generate a key from a password
|
||||
static Key FromPassword(const std::string& password);
|
||||
|
||||
//! Will generate a random key from actual randomness (std::random_device)
|
||||
static Key Random();
|
||||
|
||||
//! Loads a keyfile
|
||||
static Key LoadFromFile(const std::string& path);
|
||||
|
||||
//! Will save a keyfile
|
||||
void WriteToFile(const std::string& path) const;
|
||||
|
||||
Key();
|
||||
Key(const Key& k);
|
||||
Key(const Block& b);
|
||||
|
||||
private:
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -1,8 +1,13 @@
|
||||
#pragma once
|
||||
#ifndef GCRYPT_KEYSET_H
|
||||
#define GCRYPT_KEYSET_H
|
||||
|
||||
#include <array>
|
||||
#include "GCrypt/Block.h"
|
||||
#include "GCrypt/Key.h"
|
||||
#include "GCrypt/Config.h"
|
||||
|
||||
namespace Leonetienne::GCrypt {
|
||||
typedef std::array<Block, N_ROUNDS> Keyset;
|
||||
typedef std::array<Key, N_ROUNDS> Keyset;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
46
GCryptLib/include/GCrypt/SBoxLookup.h
Normal file
@ -0,0 +1,46 @@
|
||||
#ifndef GCRYPT_SBOX_LOOKUP
|
||||
#define GCRYPT_SBOX_LOOKUP
|
||||
|
||||
#include <array>
|
||||
|
||||
namespace Leonetienne::GCrypt {
|
||||
|
||||
static const std::array<std::uint8_t, 256> sboxLookup {
|
||||
0x23,0xF7,0xA2,0x31,0x5B,0x0C,0x13,0xEE,
|
||||
0xF8,0x1B,0x0B,0xA9,0x37,0x9F,0x55,0xF3,
|
||||
0x7D,0x71,0x9E,0x89,0x38,0x53,0x3D,0xE9,
|
||||
0x47,0xA4,0x30,0xBF,0x82,0xE3,0x69,0x5C,
|
||||
0x3C,0x88,0xDE,0x7F,0x29,0xEB,0x5F,0x02,
|
||||
0x63,0x42,0xDC,0x36,0x20,0x6B,0x9C,0x4A,
|
||||
0xA8,0x11,0x6D,0x67,0x3A,0xF4,0x33,0x61,
|
||||
0x1C,0x4C,0x22,0x07,0x6F,0x80,0xA5,0x96,
|
||||
0x77,0x62,0xDD,0x6A,0x60,0x05,0x08,0x72,
|
||||
0xE5,0x1D,0x39,0xA7,0x58,0x3F,0x57,0xAD,
|
||||
0xFF,0x09,0xC5,0xB2,0xE7,0x94,0xEA,0x34,
|
||||
0x9A,0x5D,0x52,0xE2,0xAE,0x99,0xBB,0x44,
|
||||
0xFD,0x73,0xEC,0x87,0xED,0xBA,0x7C,0xF5,
|
||||
0xDA,0x01,0xC7,0xC1,0xB6,0x81,0x12,0xD5,
|
||||
0x7A,0x16,0x18,0x97,0x74,0x32,0xBD,0x56,
|
||||
0xFE,0x79,0x3E,0x95,0x93,0xAB,0x6C,0xC6,
|
||||
0x2A,0x27,0x19,0x26,0x2E,0x41,0x0F,0x85,
|
||||
0x66,0x59,0xEF,0x98,0x10,0xE8,0xE6,0x49,
|
||||
0xAC,0x0D,0x2D,0xD3,0xF0,0x92,0x8F,0xD9,
|
||||
0xAF,0xC8,0xB5,0xBC,0x6E,0xD6,0x78,0x8E,
|
||||
0x17,0xCB,0x75,0x50,0x51,0x84,0xB7,0x4D,
|
||||
0x70,0x9D,0x8A,0x2B,0x0E,0x35,0xB9,0x40,
|
||||
0x00,0x21,0xC3,0xB3,0x43,0xCD,0x65,0xC0,
|
||||
0x4E,0xCF,0xCA,0x28,0x45,0x46,0x54,0x8B,
|
||||
0x0A,0x5A,0x1F,0x24,0xFA,0xA6,0xB4,0xD7,
|
||||
0x3B,0xFC,0x5E,0xA3,0xB8,0x04,0xCE,0xF2,
|
||||
0xFB,0xD4,0x8C,0xE4,0x90,0xB1,0x06,0x8D,
|
||||
0x86,0xA1,0xE0,0x68,0xD1,0x2C,0x03,0x64,
|
||||
0x9B,0x4F,0x14,0x1E,0x7B,0x76,0x48,0xCC,
|
||||
0xC4,0x15,0xAA,0xC9,0xE1,0x91,0xF6,0xD0,
|
||||
0x25,0xA0,0x7E,0xB0,0x1A,0xBE,0xC2,0x4B,
|
||||
0xF1,0xD2,0xDF,0x2F,0xF9,0xDB,0xD8,0x83
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -1,286 +0,0 @@
|
||||
#pragma once
|
||||
#include <bitset>
|
||||
#include <ostream>
|
||||
#include <istream>
|
||||
|
||||
namespace Leonetienne::GCrypt {
|
||||
/** Wrapper for std::bitset<T> that zeroes memory upon deletion.
|
||||
* This does not include ALL methods, but the ones needed.
|
||||
*
|
||||
* Just creating a specialization of std::bitset<T> does not work.
|
||||
*/
|
||||
template <std::size_t T>
|
||||
class SecureBitset {
|
||||
public:
|
||||
explicit SecureBitset();
|
||||
explicit SecureBitset(const std::string& str);
|
||||
explicit SecureBitset(const long long int i);
|
||||
|
||||
~SecureBitset();
|
||||
|
||||
bool operator==(const SecureBitset<T>& other) const;
|
||||
bool operator!=(const SecureBitset<T>& other) const;
|
||||
bool operator[](const std::size_t) const;
|
||||
bool test(const std::size_t index) const;
|
||||
bool all() const;
|
||||
bool any() const;
|
||||
bool none() const;
|
||||
std::size_t count() const;
|
||||
std::size_t size() const;
|
||||
SecureBitset<T>& operator&=(const SecureBitset<T>& other);
|
||||
SecureBitset<T>& operator|=(const SecureBitset<T>& other);
|
||||
SecureBitset<T>& operator^=(const SecureBitset<T>& other);
|
||||
SecureBitset<T> operator&(const SecureBitset<T>& other);
|
||||
SecureBitset<T> operator|(const SecureBitset<T>& other);
|
||||
SecureBitset<T> operator^(const SecureBitset<T>& other);
|
||||
SecureBitset<T> operator~() const;
|
||||
SecureBitset<T>& operator<<=(const std::size_t offset);
|
||||
SecureBitset<T>& operator>>=(const std::size_t offset);
|
||||
SecureBitset<T> operator<<(const std::size_t offset) const;
|
||||
SecureBitset<T> operator>>(const std::size_t offset) const;
|
||||
SecureBitset<T>& set();
|
||||
SecureBitset<T>& set(const std::size_t index, bool value = true);
|
||||
SecureBitset<T>& reset();
|
||||
SecureBitset<T>& reset(const std::size_t index);
|
||||
SecureBitset<T>& flip();
|
||||
SecureBitset<T>& flip(const std::size_t index);
|
||||
std::string to_string() const;
|
||||
unsigned long to_ulong() const;
|
||||
unsigned long long to_ullong() const;
|
||||
|
||||
std::bitset<T>& Get();
|
||||
const std::bitset<T>& Get() const;
|
||||
|
||||
private:
|
||||
std::bitset<T> bitset;
|
||||
};
|
||||
|
||||
template<std::size_t T>
|
||||
inline SecureBitset<T>::SecureBitset()
|
||||
:
|
||||
bitset() {
|
||||
return;
|
||||
}
|
||||
|
||||
template<std::size_t T>
|
||||
inline SecureBitset<T>::SecureBitset(const std::string& str)
|
||||
:
|
||||
bitset(str) {
|
||||
return;
|
||||
}
|
||||
|
||||
template<std::size_t T>
|
||||
inline SecureBitset<T>::SecureBitset(const long long int i)
|
||||
:
|
||||
bitset(i) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Don't optimize the destructor out!!!
|
||||
// These pragmas only work for MSVC and g++, as far as i know. Beware!!!
|
||||
#if defined _WIN32 || defined _WIN64
|
||||
#pragma optimize("", off )
|
||||
#elif defined __GNUG__
|
||||
#pragma GCC push_options
|
||||
#pragma GCC optimize ("O0")
|
||||
#endif
|
||||
template<std::size_t T>
|
||||
inline SecureBitset<T>::~SecureBitset() {
|
||||
bitset.reset();
|
||||
return;
|
||||
}
|
||||
#if defined _WIN32 || defined _WIN64
|
||||
#pragma optimize("", on )
|
||||
#elif defined __GNUG__
|
||||
#pragma GCC pop_options
|
||||
#endif
|
||||
|
||||
template<std::size_t T>
|
||||
inline bool SecureBitset<T>::operator==(const SecureBitset<T>& other) const {
|
||||
return bitset == other.bitset;
|
||||
}
|
||||
|
||||
template<std::size_t T>
|
||||
inline bool SecureBitset<T>::operator!=(const SecureBitset<T>& other) const {
|
||||
return bitset != other.bitset;
|
||||
}
|
||||
|
||||
template<std::size_t T>
|
||||
inline bool SecureBitset<T>::operator[](const std::size_t index) const {
|
||||
return bitset[index];
|
||||
}
|
||||
|
||||
template<std::size_t T>
|
||||
inline bool SecureBitset<T>::test(const std::size_t index) const {
|
||||
return bitset.test(index);
|
||||
}
|
||||
|
||||
template<std::size_t T>
|
||||
inline bool SecureBitset<T>::all() const {
|
||||
return bitset.all();
|
||||
}
|
||||
|
||||
template<std::size_t T>
|
||||
inline bool SecureBitset<T>::any() const {
|
||||
return bitset.any();
|
||||
}
|
||||
|
||||
template<std::size_t T>
|
||||
inline bool SecureBitset<T>::none() const {
|
||||
return bitset.none();
|
||||
}
|
||||
|
||||
template<std::size_t T>
|
||||
inline std::size_t SecureBitset<T>::count() const {
|
||||
return bitset.count();
|
||||
}
|
||||
|
||||
template<std::size_t T>
|
||||
inline std::size_t SecureBitset<T>::size() const {
|
||||
return bitset.count();
|
||||
}
|
||||
|
||||
template<std::size_t T>
|
||||
inline SecureBitset<T>& SecureBitset<T>::operator&=(const SecureBitset<T>& other) {
|
||||
bitset &= other.bitset;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<std::size_t T>
|
||||
inline SecureBitset<T>& SecureBitset<T>::operator|=(const SecureBitset<T>& other) {
|
||||
bitset |= other.bitset;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<std::size_t T>
|
||||
inline SecureBitset<T>& SecureBitset<T>::operator^=(const SecureBitset<T>& other) {
|
||||
bitset ^= other.bitset;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<std::size_t T>
|
||||
inline SecureBitset<T> SecureBitset<T>::operator&(const SecureBitset<T>& other) {
|
||||
SecureBitset bs;
|
||||
bs.bitset = bitset & other.bitset;
|
||||
return bs;
|
||||
}
|
||||
|
||||
template<std::size_t T>
|
||||
inline SecureBitset<T> SecureBitset<T>::operator|(const SecureBitset<T>& other) {
|
||||
SecureBitset bs;
|
||||
bs.bitset = bitset | other.bitset;
|
||||
return bs;
|
||||
}
|
||||
|
||||
template<std::size_t T>
|
||||
inline SecureBitset<T> SecureBitset<T>::operator^(const SecureBitset<T>& other) {
|
||||
SecureBitset bs;
|
||||
bs.bitset = bitset ^ other.bitset;
|
||||
return bs;
|
||||
}
|
||||
|
||||
template<std::size_t T>
|
||||
inline SecureBitset<T> SecureBitset<T>::operator~() const {
|
||||
SecureBitset bs;
|
||||
bs.bitset = ~bitset;
|
||||
return bs;
|
||||
}
|
||||
|
||||
template<std::size_t T>
|
||||
inline SecureBitset<T>& SecureBitset<T>::operator<<=(const std::size_t offset) {
|
||||
bitset <<= offset;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<std::size_t T>
|
||||
inline SecureBitset<T>& SecureBitset<T>::operator>>=(const std::size_t offset) {
|
||||
bitset >>= offset;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<std::size_t T>
|
||||
inline SecureBitset<T> SecureBitset<T>::operator<<(const std::size_t offset) const {
|
||||
SecureBitset bs;
|
||||
bs.bitset = bitset << offset;
|
||||
return bs;
|
||||
}
|
||||
|
||||
template<std::size_t T>
|
||||
inline SecureBitset<T> SecureBitset<T>::operator>>(const std::size_t offset) const {
|
||||
SecureBitset bs;
|
||||
bs.bitset = bitset >> offset;
|
||||
return bs;
|
||||
}
|
||||
|
||||
template<std::size_t T>
|
||||
inline SecureBitset<T>& SecureBitset<T>::set() {
|
||||
bitset.set();
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<std::size_t T>
|
||||
inline SecureBitset<T>& SecureBitset<T>::set(const std::size_t index, bool value) {
|
||||
bitset.set(index, value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<std::size_t T>
|
||||
inline SecureBitset<T>& SecureBitset<T>::reset() {
|
||||
bitset.reset();
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<std::size_t T>
|
||||
inline SecureBitset<T>& SecureBitset<T>::reset(const std::size_t index) {
|
||||
bitset.reset(index);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<std::size_t T>
|
||||
inline SecureBitset<T>& SecureBitset<T>::flip() {
|
||||
bitset.flip();
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<std::size_t T>
|
||||
inline SecureBitset<T>& SecureBitset<T>::flip(const std::size_t index) {
|
||||
bitset.flip(index);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<std::size_t T>
|
||||
inline std::string SecureBitset<T>::to_string() const {
|
||||
return bitset.to_string();
|
||||
}
|
||||
|
||||
template<std::size_t T>
|
||||
inline unsigned long SecureBitset<T>::to_ulong() const {
|
||||
return bitset.to_ulong();
|
||||
}
|
||||
|
||||
template<std::size_t T>
|
||||
inline unsigned long long SecureBitset<T>::to_ullong() const {
|
||||
return bitset.to_ullong();
|
||||
}
|
||||
|
||||
template<std::size_t T>
|
||||
inline std::bitset<T>& SecureBitset<T>::Get() {
|
||||
return bitset;
|
||||
}
|
||||
|
||||
template<std::size_t T>
|
||||
inline const std::bitset<T>& SecureBitset<T>::Get() const {
|
||||
return bitset;
|
||||
}
|
||||
|
||||
template <std::size_t T>
|
||||
inline std::ostream& operator<<(std::ostream& ofs, const SecureBitset<T>& bs) {
|
||||
return ofs << bs.Get();
|
||||
}
|
||||
|
||||
template <std::size_t T>
|
||||
inline std::istream& operator>>(std::istream& ifs, const SecureBitset<T>& bs) {
|
||||
return ifs >> bs.Get();
|
||||
}
|
||||
}
|
@ -1,309 +1,44 @@
|
||||
#pragma once
|
||||
#ifndef GCRYPT_UTIL_H
|
||||
#define GCRYPT_UTIL_H
|
||||
|
||||
#include <bitset>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include <cstring>
|
||||
#include "GCrypt/SecureBitset.h"
|
||||
#include <vector>
|
||||
#include "GCrypt/Block.h"
|
||||
#include "GCrypt/Flexblock.h"
|
||||
#include "GCrypt/Config.h"
|
||||
#include "GCrypt/Cipher.h"
|
||||
#include "GCrypt/GCipher.h"
|
||||
#include "GCrypt/InitializationVector.h"
|
||||
|
||||
namespace Leonetienne::GCrypt {
|
||||
//! Mod-operator that works with negative values
|
||||
inline int Mod(const int numerator, const int denominator) {
|
||||
return (denominator + (numerator % denominator)) % denominator;
|
||||
}
|
||||
//! Mod-operator that works with negative values
|
||||
inline int Mod(const int numerator, const int denominator) {
|
||||
return (denominator + (numerator % denominator)) % denominator;
|
||||
}
|
||||
|
||||
//! Will perform a wrapping left-bitshift on a bitset
|
||||
template <std::size_t T>
|
||||
inline SecureBitset<T> Shiftl(const SecureBitset<T>& bits, const std::size_t amount) {
|
||||
std::stringstream ss;
|
||||
const std::string bitss = bits.to_string();
|
||||
//! Will pad a string to a set length with a certain character
|
||||
std::string PadStringToLength(const std::string& str, const std::size_t len, const char pad, const bool padLeft = true);
|
||||
|
||||
for (std::size_t i = 0; i < bitss.size(); i++) {
|
||||
ss << bitss[Mod((int)(i + amount), (int)bitss.size())];
|
||||
}
|
||||
//! Will convert a string to a vector of blocks
|
||||
std::vector<Block> StringToBitblocks(const std::string& str);
|
||||
|
||||
return SecureBitset<T>(ss.str());
|
||||
}
|
||||
//! Will convert an array of data blocks to a bytestring
|
||||
std::string BitblocksToBytes(const std::vector<Block>& bits);
|
||||
|
||||
//! Will perform a wrapping right-bitshift on a bitset
|
||||
template <std::size_t T>
|
||||
inline SecureBitset<T> Shiftr(const SecureBitset<T>& bits, const std::size_t amount) {
|
||||
std::stringstream ss;
|
||||
const std::string bitss = bits.to_string();
|
||||
//! Will convert an array of blocks to a character-string
|
||||
//! The difference to BitblocksToBytes() is, that it strips excess nullbytes
|
||||
std::string BitblocksToString(const std::vector<Block>& blocks);
|
||||
|
||||
for (std::size_t i = 0; i < bitss.size(); i++) {
|
||||
ss << bitss[Mod((i - amount), bitss.size())];
|
||||
}
|
||||
//! Will read a file directly to data blocks, and yield the amount of bytes read
|
||||
std::vector<Block> ReadFileToBlocks(const std::string& filepath, std::size_t& bytes_read);
|
||||
|
||||
return SecureBitset<T>(ss.str());
|
||||
}
|
||||
//! Will read a file directly to data blocks
|
||||
std::vector<Block> ReadFileToBlocks(const std::string& filepath);
|
||||
|
||||
//! Will pad a string to a set length with a certain character
|
||||
inline std::string PadStringToLength(const std::string& str, const std::size_t len, const char pad, const bool padLeft = true) {
|
||||
// Fast-reject: Already above padded length
|
||||
if (str.length() >= len) {
|
||||
return str;
|
||||
}
|
||||
|
||||
std::stringstream ss;
|
||||
|
||||
// Pad left:
|
||||
if (padLeft) {
|
||||
for (std::size_t i = 0; i < len - str.size(); i++) {
|
||||
ss << pad;
|
||||
}
|
||||
ss << str;
|
||||
}
|
||||
// Pad right:
|
||||
else {
|
||||
ss << str;
|
||||
for (std::size_t i = 0; i < len - str.size(); i++) {
|
||||
ss << pad;
|
||||
}
|
||||
}
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
//! Will convert a string to a fixed-size data block
|
||||
inline Block StringToBitblock(const std::string& s) {
|
||||
std::stringstream ss;
|
||||
|
||||
for (std::size_t i = 0; i < s.size(); i++) {
|
||||
ss << std::bitset<8>(s[i]);
|
||||
}
|
||||
|
||||
// Pad rest with zeores
|
||||
return Block(PadStringToLength(ss.str(), 128, '0', false));
|
||||
}
|
||||
|
||||
//! Will convert a string to a flexible data block
|
||||
inline Flexblock StringToBits(const std::string& s) {
|
||||
std::stringstream ss;
|
||||
|
||||
for (std::size_t i = 0; i < s.size(); i++) {
|
||||
ss << std::bitset<8>(s[i]);
|
||||
}
|
||||
|
||||
return Flexblock(ss.str());
|
||||
}
|
||||
|
||||
//! Will convert a fixed-size data block to a bytestring
|
||||
inline std::string BitblockToBytes(const Block& bits) {
|
||||
std::stringstream ss;
|
||||
|
||||
const std::string bitstring = bits.to_string();
|
||||
|
||||
for (std::size_t i = 0; i < BLOCK_SIZE; i += 8) {
|
||||
ss << (char)std::bitset<8>(bitstring.substr(i, 8)).to_ulong();
|
||||
}
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
//! Will convert a fixed-size data block to a string
|
||||
//! The difference to BitblockToBytes() is, that it strips excess nullbytes
|
||||
inline std::string BitblockToString(const Block& bits) {
|
||||
// Decode to bytes
|
||||
std::string text = BitblockToBytes(bits);
|
||||
|
||||
// Dümp excess nullbytes
|
||||
text.resize(strlen(text.data()));
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
//! Will convert a flexible data block to a bytestring
|
||||
inline std::string BitsToBytes(const Flexblock& bits) {
|
||||
std::stringstream ss;
|
||||
|
||||
const std::string bitstring = bits;
|
||||
|
||||
for (std::size_t i = 0; i < bits.size(); i += 8) {
|
||||
ss << (char)std::bitset<8>(bitstring.substr(i, 8)).to_ulong();
|
||||
}
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
//! Will convert a flexible data block to a string
|
||||
//! The difference to BitsToBytes() is, that it strips excess nullbytes
|
||||
inline std::string BitsToString(const Flexblock& bits) {
|
||||
// Decode to bytes
|
||||
std::string text = BitsToBytes(bits);
|
||||
|
||||
// Dümp excess nullbytes
|
||||
text.resize(strlen(text.data()));
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
//! Turns a fixed-size data block into a hex-string
|
||||
inline std::string BitblockToHexstring(const Block& b) {
|
||||
std::stringstream ss;
|
||||
const std::string charset = "0123456789abcdef";
|
||||
const std::string bstr = b.to_string();
|
||||
|
||||
for (std::size_t i = 0; i < bstr.size(); i += 4) {
|
||||
ss << charset[std::bitset<4>(bstr.substr(i, 4)).to_ulong()];
|
||||
}
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
//! Turns a flexible data block into a hex-string
|
||||
inline std::string BitsToHexstring(const Flexblock& b) {
|
||||
std::stringstream ss;
|
||||
const std::string charset = "0123456789abcdef";
|
||||
const std::string bstr = b;
|
||||
|
||||
for (std::size_t i = 0; i < bstr.size(); i += 4) {
|
||||
ss << charset[std::bitset<4>(bstr.substr(i, 4)).to_ulong()];
|
||||
}
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
|
||||
//! Turns a hex string into a fixed-size data block
|
||||
inline Block HexstringToBitblock(const std::string& hexstring) {
|
||||
std::stringstream ss;
|
||||
|
||||
for (std::size_t i = 0; i < hexstring.size(); i++) {
|
||||
const char c = hexstring[i];
|
||||
|
||||
// Get value
|
||||
std::size_t value;
|
||||
if ((c >= '0') && (c <= '9')) {
|
||||
// Is it a number?
|
||||
value = ((std::size_t)c - '0') + 0;
|
||||
}
|
||||
else if ((c >= 'a') && (c <= 'f')) {
|
||||
// Else, it is a lowercase letter
|
||||
value = ((std::size_t)c - 'a') + 10;
|
||||
}
|
||||
else {
|
||||
throw std::logic_error("non-hex string detected in HexstringToBits()");
|
||||
}
|
||||
|
||||
// Append to our bits
|
||||
ss << std::bitset<4>(value);
|
||||
}
|
||||
|
||||
return Block(ss.str());
|
||||
}
|
||||
|
||||
//! Turns a hex string into a flexible data block
|
||||
inline Flexblock HexstringToBits(const std::string& hexstring) {
|
||||
std::stringstream ss;
|
||||
|
||||
for (std::size_t i = 0; i < hexstring.size(); i++) {
|
||||
const char c = hexstring[i];
|
||||
|
||||
// Get value
|
||||
std::size_t value;
|
||||
if ((c >= '0') && (c <= '9')) {
|
||||
// Is it a number?
|
||||
value = ((std::size_t)c - '0') + 0;
|
||||
}
|
||||
else if ((c >= 'a') && (c <= 'f')) {
|
||||
// Else, it is a lowercase letter
|
||||
value = ((std::size_t)c - 'a') + 10;
|
||||
}
|
||||
else {
|
||||
throw std::logic_error("non-hex string detected in HexstringToBits()");
|
||||
}
|
||||
|
||||
// Append to our bits
|
||||
ss << std::bitset<4>(value);
|
||||
}
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
//! Creates a key of size BLOCK_SIZE from a password of arbitrary length.
|
||||
//! Note that if your password is shorter (in bits) than BLOCK_SIZE, the rest of the key will be padded with 0 (see next line!).
|
||||
//! To provide a better initial key, (and to get rid of padding zeroes), the raw result (b) will be xor'd with an initialization vector based on b.
|
||||
//! : return b ^ iv(b)
|
||||
inline Block PasswordToKey(const std::string& in) {
|
||||
// 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) {
|
||||
const Block fragment = StringToBitblock(
|
||||
PadStringToLength(in.substr(i, BLOCK_SIZE / 8), BLOCK_SIZE / 8, 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 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
|
||||
inline Flexblock ReadFileToBits(const std::string& filepath) {
|
||||
// Read file
|
||||
std::ifstream ifs(filepath, std::ios::binary);
|
||||
|
||||
if (!ifs.good()) {
|
||||
throw std::runtime_error("Unable to open ifilestream!");
|
||||
}
|
||||
|
||||
std::stringstream ss;
|
||||
std::copy(
|
||||
std::istreambuf_iterator<char>(ifs),
|
||||
std::istreambuf_iterator<char>(),
|
||||
std::ostreambuf_iterator<char>(ss)
|
||||
);
|
||||
|
||||
ifs.close();
|
||||
|
||||
const std::string bytes = ss.str();
|
||||
|
||||
// Convert bytes to bits
|
||||
return StringToBits(bytes);
|
||||
}
|
||||
|
||||
//! Will save bits to a binary file
|
||||
inline void WriteBitsToFile(const std::string& filepath, const Flexblock& bits) {
|
||||
// Convert bits to bytes
|
||||
const std::string bytes = BitsToBytes(bits);
|
||||
|
||||
// Write bits to file
|
||||
std::ofstream ofs(filepath, std::ios::binary);
|
||||
|
||||
if (!ofs.good()) {
|
||||
throw std::runtime_error("Unable to open ofilestream!");
|
||||
}
|
||||
|
||||
ofs.write(bytes.data(), bytes.length());
|
||||
ofs.close();
|
||||
|
||||
return;
|
||||
}
|
||||
//! Will write data blocks directly to a file
|
||||
void WriteBlocksToFile(const std::string& filepath, const std::vector<Block>& blocks);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -1,3 +1,7 @@
|
||||
#pragma once
|
||||
#define GHETTOCRYPT_VERSION 0.22
|
||||
#ifndef GCRYPT_VERSION_H
|
||||
#define GCRYPT_VERSION_H
|
||||
|
||||
#define GCRYPT_VERSION 0.236
|
||||
|
||||
#endif
|
||||
|
||||
|
838
GCryptLib/src/Block.cpp
Normal file
@ -0,0 +1,838 @@
|
||||
#include "GCrypt/Block.h"
|
||||
#include "GCrypt/Config.h"
|
||||
#include "GCrypt/Util.h"
|
||||
#include <sstream>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <iomanip>
|
||||
#include <ios>
|
||||
|
||||
// Just to be sure, the compiler will optimize this
|
||||
// little formula out, let's do it in the preprocessor
|
||||
#define MAT_INDEX(row, column) (column*4 + row)
|
||||
|
||||
namespace Leonetienne::GCrypt {
|
||||
|
||||
template <typename T>
|
||||
Basic_Block<T>::Basic_Block() {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Basic_Block<T>::Basic_Block(const std::string& str) {
|
||||
FromBinaryString(str);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Basic_Block<T>::Basic_Block(const Basic_Block<T>& other) {
|
||||
data = other.data;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Basic_Block<T>::FromBinaryString(const std::string& str) {
|
||||
|
||||
if (str.length() != BLOCK_SIZE_BITS) {
|
||||
throw std::invalid_argument(
|
||||
std::string("Unable to read binary block: \"") + str + "\": Length is not BLOCK_SIZE_BITS."
|
||||
);
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < data.size(); i++) {
|
||||
data[i] = std::bitset<CHUNK_SIZE_BITS>(
|
||||
str.substr(i*CHUNK_SIZE_BITS, CHUNK_SIZE_BITS)
|
||||
).to_ulong();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Basic_Block<T>::FromHexString(const std::string& str) {
|
||||
|
||||
if (str.length() != BLOCK_SIZE*2) {
|
||||
throw std::invalid_argument(
|
||||
std::string("Unable to read hex block: \"") + str + "\": Length is not BLOCK_SIZE*2."
|
||||
);
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < str.length(); i += CHUNK_SIZE*2) {
|
||||
const std::string hexChunk = str.substr(i, CHUNK_SIZE*2);
|
||||
try {
|
||||
data[i / (CHUNK_SIZE*2)] = std::stoul(hexChunk, NULL, 16);
|
||||
}
|
||||
catch (std::invalid_argument&) {
|
||||
throw std::invalid_argument(
|
||||
std::string("Unable to read hex block: \"") + hexChunk + "\"."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Basic_Block<T>::FromByteString(const std::string& str) {
|
||||
|
||||
if (str.length() != BLOCK_SIZE) {
|
||||
throw std::invalid_argument(
|
||||
std::string("Unable to read byte block: \"") + str + "\": Length is not BLOCK_SIZE."
|
||||
);
|
||||
}
|
||||
|
||||
// Iterate over all bytes in the block
|
||||
std::uint8_t* curByte = (std::uint8_t*)(void*)Data();
|
||||
const char* strIt = 0;
|
||||
for (std::size_t i = 0; i < BLOCK_SIZE; i++) {
|
||||
*curByte++ = str[i];
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Basic_Block<T>::FromTextString(const std::string& str) {
|
||||
// Just pad the input string to lenght, and treat it as a byte string
|
||||
FromByteString(
|
||||
PadStringToLength(str, BLOCK_SIZE, '\0', false)
|
||||
);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::string Basic_Block<T>::ToBinaryString() const {
|
||||
|
||||
std::stringstream ss;
|
||||
for (std::size_t i = 0; i < data.size(); i++) {
|
||||
ss << std::bitset<CHUNK_SIZE_BITS>(data[i]).to_string();
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::string Basic_Block<T>::ToHexString() const {
|
||||
|
||||
std::stringstream ss;
|
||||
for (std::size_t i = 0; i < data.size(); i++) {
|
||||
ss
|
||||
<< std::setfill('0')
|
||||
<< std::setw(CHUNK_SIZE*2)
|
||||
<< std::hex
|
||||
<< data[i]
|
||||
;
|
||||
}
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::string Basic_Block<T>::ToByteString() const {
|
||||
|
||||
std::stringstream ss;
|
||||
ss.write((const char*)(void*)Data(), BLOCK_SIZE);
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::string Basic_Block<T>::ToTextString() const {
|
||||
|
||||
std::string bytes = ToByteString();
|
||||
|
||||
// Trim extra nullterminators
|
||||
bytes.resize(strlen(bytes.data()));
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Basic_Block<T> Basic_Block<T>::MMul(const Basic_Block<T>& o) const {
|
||||
|
||||
Basic_Block<T> m;
|
||||
|
||||
// Maybe pre-calculate the 1d-index...?
|
||||
|
||||
m.Get(0, 0) = (this->Get(0, 0) * o.Get(0, 0)) + (this->Get(0, 1) * o.Get(1, 0)) + (this->Get(0, 2) * o.Get(2, 0)) + (this->Get(0, 3) * o.Get(3, 0));
|
||||
m.Get(0, 1) = (this->Get(0, 0) * o.Get(0, 1)) + (this->Get(0, 1) * o.Get(1, 1)) + (this->Get(0, 2) * o.Get(2, 1)) + (this->Get(0, 3) * o.Get(3, 1));
|
||||
m.Get(0, 2) = (this->Get(0, 0) * o.Get(0, 2)) + (this->Get(0, 1) * o.Get(1, 2)) + (this->Get(0, 2) * o.Get(2, 2)) + (this->Get(0, 3) * o.Get(3, 2));
|
||||
m.Get(0, 3) = (this->Get(0, 0) * o.Get(0, 3)) + (this->Get(0, 1) * o.Get(1, 3)) + (this->Get(0, 2) * o.Get(2, 3)) + (this->Get(0, 3) * o.Get(3, 3));
|
||||
|
||||
m.Get(1, 0) = (this->Get(1, 0) * o.Get(0, 0)) + (this->Get(1, 1) * o.Get(1, 0)) + (this->Get(1, 2) * o.Get(2, 0)) + (this->Get(1, 3) * o.Get(3, 0));
|
||||
m.Get(1, 1) = (this->Get(1, 0) * o.Get(0, 1)) + (this->Get(1, 1) * o.Get(1, 1)) + (this->Get(1, 2) * o.Get(2, 1)) + (this->Get(1, 3) * o.Get(3, 1));
|
||||
m.Get(1, 2) = (this->Get(1, 0) * o.Get(0, 2)) + (this->Get(1, 1) * o.Get(1, 2)) + (this->Get(1, 2) * o.Get(2, 2)) + (this->Get(1, 3) * o.Get(3, 2));
|
||||
m.Get(1, 3) = (this->Get(1, 0) * o.Get(0, 3)) + (this->Get(1, 1) * o.Get(1, 3)) + (this->Get(1, 2) * o.Get(2, 3)) + (this->Get(1, 3) * o.Get(3, 3));
|
||||
|
||||
m.Get(2, 0) = (this->Get(2, 0) * o.Get(0, 0)) + (this->Get(2, 1) * o.Get(1, 0)) + (this->Get(2, 2) * o.Get(2, 0)) + (this->Get(2, 3) * o.Get(3, 0));
|
||||
m.Get(2, 1) = (this->Get(2, 0) * o.Get(0, 1)) + (this->Get(2, 1) * o.Get(1, 1)) + (this->Get(2, 2) * o.Get(2, 1)) + (this->Get(2, 3) * o.Get(3, 1));
|
||||
m.Get(2, 2) = (this->Get(2, 0) * o.Get(0, 2)) + (this->Get(2, 1) * o.Get(1, 2)) + (this->Get(2, 2) * o.Get(2, 2)) + (this->Get(2, 3) * o.Get(3, 2));
|
||||
m.Get(2, 3) = (this->Get(2, 0) * o.Get(0, 3)) + (this->Get(2, 1) * o.Get(1, 3)) + (this->Get(2, 2) * o.Get(2, 3)) + (this->Get(2, 3) * o.Get(3, 3));
|
||||
|
||||
m.Get(3, 0) = (this->Get(3, 0) * o.Get(0, 0)) + (this->Get(3, 1) * o.Get(1, 0)) + (this->Get(3, 2) * o.Get(2, 0)) + (this->Get(3, 3) * o.Get(3, 0));
|
||||
m.Get(3, 1) = (this->Get(3, 0) * o.Get(0, 1)) + (this->Get(3, 1) * o.Get(1, 1)) + (this->Get(3, 2) * o.Get(2, 1)) + (this->Get(3, 3) * o.Get(3, 1));
|
||||
m.Get(3, 2) = (this->Get(3, 0) * o.Get(0, 2)) + (this->Get(3, 1) * o.Get(1, 2)) + (this->Get(3, 2) * o.Get(2, 2)) + (this->Get(3, 3) * o.Get(3, 2));
|
||||
m.Get(3, 3) = (this->Get(3, 0) * o.Get(0, 3)) + (this->Get(3, 1) * o.Get(1, 3)) + (this->Get(3, 2) * o.Get(2, 3)) + (this->Get(3, 3) * o.Get(3, 3));
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Basic_Block<T> Basic_Block<T>::operator*(const Basic_Block<T>& other) const {
|
||||
return this->MMul(other);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Basic_Block<T>::MMulInplace(const Basic_Block<T>& o) {
|
||||
|
||||
Basic_Block<T> m = *this;
|
||||
|
||||
// Maybe pre-calculate the 1d-index...?
|
||||
|
||||
this->Get(0, 0) = (m.Get(0, 0) * o.Get(0, 0)) + (m.Get(0, 1) * o.Get(1, 0)) + (m.Get(0, 2) * o.Get(2, 0)) + (m.Get(0, 3) * o.Get(3, 0));
|
||||
this->Get(0, 1) = (m.Get(0, 0) * o.Get(0, 1)) + (m.Get(0, 1) * o.Get(1, 1)) + (m.Get(0, 2) * o.Get(2, 1)) + (m.Get(0, 3) * o.Get(3, 1));
|
||||
this->Get(0, 2) = (m.Get(0, 0) * o.Get(0, 2)) + (m.Get(0, 1) * o.Get(1, 2)) + (m.Get(0, 2) * o.Get(2, 2)) + (m.Get(0, 3) * o.Get(3, 2));
|
||||
this->Get(0, 3) = (m.Get(0, 0) * o.Get(0, 3)) + (m.Get(0, 1) * o.Get(1, 3)) + (m.Get(0, 2) * o.Get(2, 3)) + (m.Get(0, 3) * o.Get(3, 3));
|
||||
|
||||
this->Get(1, 0) = (m.Get(1, 0) * o.Get(0, 0)) + (m.Get(1, 1) * o.Get(1, 0)) + (m.Get(1, 2) * o.Get(2, 0)) + (m.Get(1, 3) * o.Get(3, 0));
|
||||
this->Get(1, 1) = (m.Get(1, 0) * o.Get(0, 1)) + (m.Get(1, 1) * o.Get(1, 1)) + (m.Get(1, 2) * o.Get(2, 1)) + (m.Get(1, 3) * o.Get(3, 1));
|
||||
this->Get(1, 2) = (m.Get(1, 0) * o.Get(0, 2)) + (m.Get(1, 1) * o.Get(1, 2)) + (m.Get(1, 2) * o.Get(2, 2)) + (m.Get(1, 3) * o.Get(3, 2));
|
||||
this->Get(1, 3) = (m.Get(1, 0) * o.Get(0, 3)) + (m.Get(1, 1) * o.Get(1, 3)) + (m.Get(1, 2) * o.Get(2, 3)) + (m.Get(1, 3) * o.Get(3, 3));
|
||||
|
||||
this->Get(2, 0) = (m.Get(2, 0) * o.Get(0, 0)) + (m.Get(2, 1) * o.Get(1, 0)) + (m.Get(2, 2) * o.Get(2, 0)) + (m.Get(2, 3) * o.Get(3, 0));
|
||||
this->Get(2, 1) = (m.Get(2, 0) * o.Get(0, 1)) + (m.Get(2, 1) * o.Get(1, 1)) + (m.Get(2, 2) * o.Get(2, 1)) + (m.Get(2, 3) * o.Get(3, 1));
|
||||
this->Get(2, 2) = (m.Get(2, 0) * o.Get(0, 2)) + (m.Get(2, 1) * o.Get(1, 2)) + (m.Get(2, 2) * o.Get(2, 2)) + (m.Get(2, 3) * o.Get(3, 2));
|
||||
this->Get(2, 3) = (m.Get(2, 0) * o.Get(0, 3)) + (m.Get(2, 1) * o.Get(1, 3)) + (m.Get(2, 2) * o.Get(2, 3)) + (m.Get(2, 3) * o.Get(3, 3));
|
||||
|
||||
this->Get(3, 0) = (m.Get(3, 0) * o.Get(0, 0)) + (m.Get(3, 1) * o.Get(1, 0)) + (m.Get(3, 2) * o.Get(2, 0)) + (m.Get(3, 3) * o.Get(3, 0));
|
||||
this->Get(3, 1) = (m.Get(3, 0) * o.Get(0, 1)) + (m.Get(3, 1) * o.Get(1, 1)) + (m.Get(3, 2) * o.Get(2, 1)) + (m.Get(3, 3) * o.Get(3, 1));
|
||||
this->Get(3, 2) = (m.Get(3, 0) * o.Get(0, 2)) + (m.Get(3, 1) * o.Get(1, 2)) + (m.Get(3, 2) * o.Get(2, 2)) + (m.Get(3, 3) * o.Get(3, 2));
|
||||
this->Get(3, 3) = (m.Get(3, 0) * o.Get(0, 3)) + (m.Get(3, 1) * o.Get(1, 3)) + (m.Get(3, 2) * o.Get(2, 3)) + (m.Get(3, 3) * o.Get(3, 3));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Basic_Block<T>& Basic_Block<T>::operator*=(const Basic_Block<T>& other) {
|
||||
MMulInplace(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Basic_Block<T> Basic_Block<T>::Xor(const Basic_Block<T>& other) const {
|
||||
|
||||
Basic_Block<T> m;
|
||||
for (std::size_t i = 0; i < data.size(); i++) {
|
||||
m.Get(i) = this->Get(i) ^ other.Get(i);
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Basic_Block<T> Basic_Block<T>::operator^(const Basic_Block<T>& other) const {
|
||||
return Xor(other);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Basic_Block<T>::XorInplace(const Basic_Block<T>& other) {
|
||||
for (std::size_t i = 0; i < data.size(); i++) {
|
||||
this->Get(i) ^= other.Get(i);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Basic_Block<T>& Basic_Block<T>::operator^=(const Basic_Block<T>& other) {
|
||||
XorInplace(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Basic_Block<T> Basic_Block<T>::Add(const Basic_Block<T>& other) const {
|
||||
|
||||
Basic_Block<T> m;
|
||||
for (std::size_t i = 0; i < data.size(); i++) {
|
||||
m.Get(i) = this->Get(i) + other.Get(i);
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Basic_Block<T> Basic_Block<T>::operator+(const Basic_Block<T>& other) const {
|
||||
return Add(other);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Basic_Block<T>::AddInplace(const Basic_Block<T>& other) {
|
||||
for (std::size_t i = 0; i < data.size(); i++) {
|
||||
this->Get(i) += other.Get(i);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Basic_Block<T>& Basic_Block<T>::operator+=(const Basic_Block<T>& other) {
|
||||
AddInplace(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Basic_Block<T> Basic_Block<T>::Sub(const Basic_Block<T>& other) const {
|
||||
|
||||
Basic_Block<T> m;
|
||||
for (std::size_t i = 0; i < data.size(); i++) {
|
||||
m.Get(i) = this->Get(i) - other.Get(i);
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Basic_Block<T> Basic_Block<T>::operator-(const Basic_Block<T>& other) const {
|
||||
return Sub(other);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Basic_Block<T>::SubInplace(const Basic_Block<T>& other) {
|
||||
for (std::size_t i = 0; i < data.size(); i++) {
|
||||
this->Get(i) -= other.Get(i);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Basic_Block<T>& Basic_Block<T>::operator-=(const Basic_Block<T>& other) {
|
||||
SubInplace(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Basic_Block<T>::ShiftRowsUpInplace() {
|
||||
Basic_Block<T> tmp = *this;
|
||||
|
||||
Get(MAT_INDEX(0, 0)) = tmp.Get(MAT_INDEX(1, 0));
|
||||
Get(MAT_INDEX(0, 1)) = tmp.Get(MAT_INDEX(1, 1));
|
||||
Get(MAT_INDEX(0, 2)) = tmp.Get(MAT_INDEX(1, 2));
|
||||
Get(MAT_INDEX(0, 3)) = tmp.Get(MAT_INDEX(1, 3));
|
||||
|
||||
Get(MAT_INDEX(1, 0)) = tmp.Get(MAT_INDEX(2, 0));
|
||||
Get(MAT_INDEX(1, 1)) = tmp.Get(MAT_INDEX(2, 1));
|
||||
Get(MAT_INDEX(1, 2)) = tmp.Get(MAT_INDEX(2, 2));
|
||||
Get(MAT_INDEX(1, 3)) = tmp.Get(MAT_INDEX(2, 3));
|
||||
|
||||
Get(MAT_INDEX(2, 0)) = tmp.Get(MAT_INDEX(3, 0));
|
||||
Get(MAT_INDEX(2, 1)) = tmp.Get(MAT_INDEX(3, 1));
|
||||
Get(MAT_INDEX(2, 2)) = tmp.Get(MAT_INDEX(3, 2));
|
||||
Get(MAT_INDEX(2, 3)) = tmp.Get(MAT_INDEX(3, 3));
|
||||
|
||||
Get(MAT_INDEX(3, 0)) = tmp.Get(MAT_INDEX(0, 0));
|
||||
Get(MAT_INDEX(3, 1)) = tmp.Get(MAT_INDEX(0, 1));
|
||||
Get(MAT_INDEX(3, 2)) = tmp.Get(MAT_INDEX(0, 2));
|
||||
Get(MAT_INDEX(3, 3)) = tmp.Get(MAT_INDEX(0, 3));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Basic_Block<T> Basic_Block<T>::ShiftRowsUp() const {
|
||||
Basic_Block<T> b;
|
||||
|
||||
b.Get(MAT_INDEX(0, 0)) = Get(MAT_INDEX(1, 0));
|
||||
b.Get(MAT_INDEX(0, 1)) = Get(MAT_INDEX(1, 1));
|
||||
b.Get(MAT_INDEX(0, 2)) = Get(MAT_INDEX(1, 2));
|
||||
b.Get(MAT_INDEX(0, 3)) = Get(MAT_INDEX(1, 3));
|
||||
|
||||
b.Get(MAT_INDEX(1, 0)) = Get(MAT_INDEX(2, 0));
|
||||
b.Get(MAT_INDEX(1, 1)) = Get(MAT_INDEX(2, 1));
|
||||
b.Get(MAT_INDEX(1, 2)) = Get(MAT_INDEX(2, 2));
|
||||
b.Get(MAT_INDEX(1, 3)) = Get(MAT_INDEX(2, 3));
|
||||
|
||||
b.Get(MAT_INDEX(2, 0)) = Get(MAT_INDEX(3, 0));
|
||||
b.Get(MAT_INDEX(2, 1)) = Get(MAT_INDEX(3, 1));
|
||||
b.Get(MAT_INDEX(2, 2)) = Get(MAT_INDEX(3, 2));
|
||||
b.Get(MAT_INDEX(2, 3)) = Get(MAT_INDEX(3, 3));
|
||||
|
||||
b.Get(MAT_INDEX(3, 0)) = Get(MAT_INDEX(0, 0));
|
||||
b.Get(MAT_INDEX(3, 1)) = Get(MAT_INDEX(0, 1));
|
||||
b.Get(MAT_INDEX(3, 2)) = Get(MAT_INDEX(0, 2));
|
||||
b.Get(MAT_INDEX(3, 3)) = Get(MAT_INDEX(0, 3));
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Basic_Block<T>::ShiftRowsDownInplace() {
|
||||
Basic_Block<T> tmp = *this;
|
||||
|
||||
Get(MAT_INDEX(0, 0)) = tmp.Get(MAT_INDEX(3, 0));
|
||||
Get(MAT_INDEX(0, 1)) = tmp.Get(MAT_INDEX(3, 1));
|
||||
Get(MAT_INDEX(0, 2)) = tmp.Get(MAT_INDEX(3, 2));
|
||||
Get(MAT_INDEX(0, 3)) = tmp.Get(MAT_INDEX(3, 3));
|
||||
|
||||
Get(MAT_INDEX(1, 0)) = tmp.Get(MAT_INDEX(0, 0));
|
||||
Get(MAT_INDEX(1, 1)) = tmp.Get(MAT_INDEX(0, 1));
|
||||
Get(MAT_INDEX(1, 2)) = tmp.Get(MAT_INDEX(0, 2));
|
||||
Get(MAT_INDEX(1, 3)) = tmp.Get(MAT_INDEX(0, 3));
|
||||
|
||||
Get(MAT_INDEX(2, 0)) = tmp.Get(MAT_INDEX(1, 0));
|
||||
Get(MAT_INDEX(2, 1)) = tmp.Get(MAT_INDEX(1, 1));
|
||||
Get(MAT_INDEX(2, 2)) = tmp.Get(MAT_INDEX(1, 2));
|
||||
Get(MAT_INDEX(2, 3)) = tmp.Get(MAT_INDEX(1, 3));
|
||||
|
||||
Get(MAT_INDEX(3, 0)) = tmp.Get(MAT_INDEX(2, 0));
|
||||
Get(MAT_INDEX(3, 1)) = tmp.Get(MAT_INDEX(2, 1));
|
||||
Get(MAT_INDEX(3, 2)) = tmp.Get(MAT_INDEX(2, 2));
|
||||
Get(MAT_INDEX(3, 3)) = tmp.Get(MAT_INDEX(2, 3));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Basic_Block<T> Basic_Block<T>::ShiftRowsDown() const {
|
||||
Basic_Block<T> b;
|
||||
|
||||
b.Get(MAT_INDEX(0, 0)) = Get(MAT_INDEX(3, 0));
|
||||
b.Get(MAT_INDEX(0, 1)) = Get(MAT_INDEX(3, 1));
|
||||
b.Get(MAT_INDEX(0, 2)) = Get(MAT_INDEX(3, 2));
|
||||
b.Get(MAT_INDEX(0, 3)) = Get(MAT_INDEX(3, 3));
|
||||
|
||||
b.Get(MAT_INDEX(1, 0)) = Get(MAT_INDEX(0, 0));
|
||||
b.Get(MAT_INDEX(1, 1)) = Get(MAT_INDEX(0, 1));
|
||||
b.Get(MAT_INDEX(1, 2)) = Get(MAT_INDEX(0, 2));
|
||||
b.Get(MAT_INDEX(1, 3)) = Get(MAT_INDEX(0, 3));
|
||||
|
||||
b.Get(MAT_INDEX(2, 0)) = Get(MAT_INDEX(1, 0));
|
||||
b.Get(MAT_INDEX(2, 1)) = Get(MAT_INDEX(1, 1));
|
||||
b.Get(MAT_INDEX(2, 2)) = Get(MAT_INDEX(1, 2));
|
||||
b.Get(MAT_INDEX(2, 3)) = Get(MAT_INDEX(1, 3));
|
||||
|
||||
b.Get(MAT_INDEX(3, 0)) = Get(MAT_INDEX(2, 0));
|
||||
b.Get(MAT_INDEX(3, 1)) = Get(MAT_INDEX(2, 1));
|
||||
b.Get(MAT_INDEX(3, 2)) = Get(MAT_INDEX(2, 2));
|
||||
b.Get(MAT_INDEX(3, 3)) = Get(MAT_INDEX(2, 3));
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Basic_Block<T>::ShiftColumnsLeftInplace() {
|
||||
Basic_Block<T> tmp = *this;
|
||||
|
||||
Get(MAT_INDEX(0, 0)) = tmp.Get(MAT_INDEX(0, 1));
|
||||
Get(MAT_INDEX(1, 0)) = tmp.Get(MAT_INDEX(1, 1));
|
||||
Get(MAT_INDEX(2, 0)) = tmp.Get(MAT_INDEX(2, 1));
|
||||
Get(MAT_INDEX(3, 0)) = tmp.Get(MAT_INDEX(3, 1));
|
||||
|
||||
Get(MAT_INDEX(0, 1)) = tmp.Get(MAT_INDEX(0, 2));
|
||||
Get(MAT_INDEX(1, 1)) = tmp.Get(MAT_INDEX(1, 2));
|
||||
Get(MAT_INDEX(2, 1)) = tmp.Get(MAT_INDEX(2, 2));
|
||||
Get(MAT_INDEX(3, 1)) = tmp.Get(MAT_INDEX(3, 2));
|
||||
|
||||
Get(MAT_INDEX(0, 2)) = tmp.Get(MAT_INDEX(0, 3));
|
||||
Get(MAT_INDEX(1, 2)) = tmp.Get(MAT_INDEX(1, 3));
|
||||
Get(MAT_INDEX(2, 2)) = tmp.Get(MAT_INDEX(2, 3));
|
||||
Get(MAT_INDEX(3, 2)) = tmp.Get(MAT_INDEX(3, 3));
|
||||
|
||||
Get(MAT_INDEX(0, 3)) = tmp.Get(MAT_INDEX(0, 0));
|
||||
Get(MAT_INDEX(1, 3)) = tmp.Get(MAT_INDEX(1, 0));
|
||||
Get(MAT_INDEX(2, 3)) = tmp.Get(MAT_INDEX(2, 0));
|
||||
Get(MAT_INDEX(3, 3)) = tmp.Get(MAT_INDEX(3, 0));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Basic_Block<T> Basic_Block<T>::ShiftColumnsLeft() const {
|
||||
Basic_Block<T> b;
|
||||
|
||||
b.Get(MAT_INDEX(0, 0)) = Get(MAT_INDEX(0, 1));
|
||||
b.Get(MAT_INDEX(1, 0)) = Get(MAT_INDEX(1, 1));
|
||||
b.Get(MAT_INDEX(2, 0)) = Get(MAT_INDEX(2, 1));
|
||||
b.Get(MAT_INDEX(3, 0)) = Get(MAT_INDEX(3, 1));
|
||||
|
||||
b.Get(MAT_INDEX(0, 1)) = Get(MAT_INDEX(0, 2));
|
||||
b.Get(MAT_INDEX(1, 1)) = Get(MAT_INDEX(1, 2));
|
||||
b.Get(MAT_INDEX(2, 1)) = Get(MAT_INDEX(2, 2));
|
||||
b.Get(MAT_INDEX(3, 1)) = Get(MAT_INDEX(3, 2));
|
||||
|
||||
b.Get(MAT_INDEX(0, 2)) = Get(MAT_INDEX(0, 3));
|
||||
b.Get(MAT_INDEX(1, 2)) = Get(MAT_INDEX(1, 3));
|
||||
b.Get(MAT_INDEX(2, 2)) = Get(MAT_INDEX(2, 3));
|
||||
b.Get(MAT_INDEX(3, 2)) = Get(MAT_INDEX(3, 3));
|
||||
|
||||
b.Get(MAT_INDEX(0, 3)) = Get(MAT_INDEX(0, 0));
|
||||
b.Get(MAT_INDEX(1, 3)) = Get(MAT_INDEX(1, 0));
|
||||
b.Get(MAT_INDEX(2, 3)) = Get(MAT_INDEX(2, 0));
|
||||
b.Get(MAT_INDEX(3, 3)) = Get(MAT_INDEX(3, 0));
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Basic_Block<T>::ShiftColumnsRightInplace() {
|
||||
Basic_Block<T> tmp = *this;
|
||||
|
||||
Get(MAT_INDEX(0, 1)) = tmp.Get(MAT_INDEX(0, 0));
|
||||
Get(MAT_INDEX(1, 1)) = tmp.Get(MAT_INDEX(1, 0));
|
||||
Get(MAT_INDEX(2, 1)) = tmp.Get(MAT_INDEX(2, 0));
|
||||
Get(MAT_INDEX(3, 1)) = tmp.Get(MAT_INDEX(3, 0));
|
||||
|
||||
Get(MAT_INDEX(0, 2)) = tmp.Get(MAT_INDEX(0, 1));
|
||||
Get(MAT_INDEX(1, 2)) = tmp.Get(MAT_INDEX(1, 1));
|
||||
Get(MAT_INDEX(2, 2)) = tmp.Get(MAT_INDEX(2, 1));
|
||||
Get(MAT_INDEX(3, 2)) = tmp.Get(MAT_INDEX(3, 1));
|
||||
|
||||
Get(MAT_INDEX(0, 3)) = tmp.Get(MAT_INDEX(0, 2));
|
||||
Get(MAT_INDEX(1, 3)) = tmp.Get(MAT_INDEX(1, 2));
|
||||
Get(MAT_INDEX(2, 3)) = tmp.Get(MAT_INDEX(2, 2));
|
||||
Get(MAT_INDEX(3, 3)) = tmp.Get(MAT_INDEX(3, 2));
|
||||
|
||||
Get(MAT_INDEX(0, 0)) = tmp.Get(MAT_INDEX(0, 3));
|
||||
Get(MAT_INDEX(1, 0)) = tmp.Get(MAT_INDEX(1, 3));
|
||||
Get(MAT_INDEX(2, 0)) = tmp.Get(MAT_INDEX(2, 3));
|
||||
Get(MAT_INDEX(3, 0)) = tmp.Get(MAT_INDEX(3, 3));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Basic_Block<T> Basic_Block<T>::ShiftColumnsRight() const {
|
||||
Basic_Block<T> b;
|
||||
|
||||
b.Get(MAT_INDEX(0, 1)) = Get(MAT_INDEX(0, 0));
|
||||
b.Get(MAT_INDEX(1, 1)) = Get(MAT_INDEX(1, 0));
|
||||
b.Get(MAT_INDEX(2, 1)) = Get(MAT_INDEX(2, 0));
|
||||
b.Get(MAT_INDEX(3, 1)) = Get(MAT_INDEX(3, 0));
|
||||
|
||||
b.Get(MAT_INDEX(0, 2)) = Get(MAT_INDEX(0, 1));
|
||||
b.Get(MAT_INDEX(1, 2)) = Get(MAT_INDEX(1, 1));
|
||||
b.Get(MAT_INDEX(2, 2)) = Get(MAT_INDEX(2, 1));
|
||||
b.Get(MAT_INDEX(3, 2)) = Get(MAT_INDEX(3, 1));
|
||||
|
||||
b.Get(MAT_INDEX(0, 3)) = Get(MAT_INDEX(0, 2));
|
||||
b.Get(MAT_INDEX(1, 3)) = Get(MAT_INDEX(1, 2));
|
||||
b.Get(MAT_INDEX(2, 3)) = Get(MAT_INDEX(2, 2));
|
||||
b.Get(MAT_INDEX(3, 3)) = Get(MAT_INDEX(3, 2));
|
||||
|
||||
b.Get(MAT_INDEX(0, 0)) = Get(MAT_INDEX(0, 3));
|
||||
b.Get(MAT_INDEX(1, 0)) = Get(MAT_INDEX(1, 3));
|
||||
b.Get(MAT_INDEX(2, 0)) = Get(MAT_INDEX(2, 3));
|
||||
b.Get(MAT_INDEX(3, 0)) = Get(MAT_INDEX(3, 3));
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Basic_Block<T>::ShiftCellsLeftInplace() {
|
||||
Basic_Block<T> tmp = *this;
|
||||
|
||||
Get(15) = tmp.Get(0);
|
||||
|
||||
for (std::size_t i = 0; i < 15; i++) {
|
||||
Get(i) = tmp.Get(i+1);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Basic_Block<T> Basic_Block<T>::ShiftCellsLeft() const {
|
||||
Basic_Block<T> b;
|
||||
|
||||
b.Get(15) = Get(0);
|
||||
|
||||
for (std::size_t i = 0; i < 15; i++) {
|
||||
b.Get(i) = Get(i+1);
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Basic_Block<T>::ShiftCellsRightInplace() {
|
||||
Basic_Block<T> tmp = *this;
|
||||
|
||||
Get(0) = tmp.Get(15);
|
||||
|
||||
for (std::size_t i = 1; i < 16; i++) {
|
||||
Get(i) = tmp.Get(i-1);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Basic_Block<T> Basic_Block<T>::ShiftCellsRight() const {
|
||||
Basic_Block<T> b;
|
||||
|
||||
b.Get(0) = Get(15);
|
||||
|
||||
for (std::size_t i = 1; i < 16; i++) {
|
||||
b.Get(i) = Get(i-1);
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Basic_Block<T>& Basic_Block<T>::operator=(const Basic_Block<T>& other) {
|
||||
data = other.data;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool Basic_Block<T>::GetBit(const std::size_t index) const {
|
||||
// Fetch index of integer the bit is located in
|
||||
const std::size_t intIndex = index / CHUNK_SIZE_BITS;
|
||||
|
||||
// Fetch bit index relative to that int
|
||||
const std::size_t relBitIndex = index - (intIndex * CHUNK_SIZE_BITS);
|
||||
|
||||
// Pre-calculate the bitmask to use
|
||||
const std::size_t bitmask = 1 << (CHUNK_SIZE_BITS - relBitIndex - 1);
|
||||
|
||||
return data[intIndex] & bitmask;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Basic_Block<T>::SetBit(const std::size_t index, const bool state) {
|
||||
// Fetch index of integer the bit is located in
|
||||
const std::size_t intIndex = index / CHUNK_SIZE_BITS;
|
||||
|
||||
// Fetch bit index relative to that int
|
||||
const std::size_t relBitIndex = index - (intIndex * CHUNK_SIZE_BITS);
|
||||
|
||||
// Pre-calculate the bitmask to use
|
||||
const std::size_t bitmask = 1 << (CHUNK_SIZE_BITS - relBitIndex - 1);
|
||||
|
||||
// Set the bit
|
||||
if (state) {
|
||||
data[intIndex] |= bitmask;
|
||||
}
|
||||
// Clear the bit
|
||||
else {
|
||||
data[intIndex] &= ~bitmask;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Basic_Block<T>::FlipBit(const std::size_t index) {
|
||||
// Fetch index of integer the bit is located in
|
||||
const std::size_t intIndex = index / CHUNK_SIZE_BITS;
|
||||
|
||||
// Fetch bit index relative to that int
|
||||
const std::size_t relBitIndex = index - (intIndex * CHUNK_SIZE_BITS);
|
||||
|
||||
// Pre-calculate the bitmask to use
|
||||
const std::size_t bitmask = 1 << (CHUNK_SIZE_BITS - relBitIndex - 1);
|
||||
|
||||
data[intIndex] ^= bitmask;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Basic_Block<T> Basic_Block<T>::ShiftBitsLeft() const {
|
||||
Basic_Block<T> b;
|
||||
|
||||
// First, copy this block over
|
||||
b = *this;
|
||||
|
||||
// Then, shift all integers individually
|
||||
for (std::size_t i = 0; i < data.size(); i++) {
|
||||
b.data[i] <<= 1;
|
||||
}
|
||||
|
||||
// Current state: the LSB is zero everywhere. We have to carry
|
||||
// it over manually from the previous state.
|
||||
|
||||
// Carry over the MSB of data[i] to LSB of data[i-1]
|
||||
constexpr std::size_t bitmaskMsb = 1 << (CHUNK_SIZE_BITS - 1);
|
||||
constexpr std::size_t bitmaskLsb = 1;
|
||||
for (int i = 0; i < data.size(); i++) {
|
||||
const bool msb = data[i] & bitmaskMsb;
|
||||
|
||||
// Set the lsb
|
||||
if (msb) {
|
||||
b.data[Mod(i-1, data.size())] |= bitmaskLsb;
|
||||
}
|
||||
// Clear the lsb
|
||||
else {
|
||||
b.data[Mod(i-1, data.size())] &= ~bitmaskLsb;
|
||||
}
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Basic_Block<T>::ShiftBitsLeftInplace() {
|
||||
Basic_Block<T> tmp = *this;
|
||||
|
||||
// Then, shift all integers individually
|
||||
for (std::size_t i = 0; i < data.size(); i++) {
|
||||
data[i] <<= 1;
|
||||
}
|
||||
|
||||
// Current state: the LSB is zero everywhere. We have to carry
|
||||
// it over manually from the previous state.
|
||||
|
||||
// Carry over the MSB of data[i] to LSB of data[i-1]
|
||||
constexpr std::size_t bitmaskMsb = 1 << (CHUNK_SIZE_BITS - 1);
|
||||
constexpr std::size_t bitmaskLsb = 1;
|
||||
for (int i = 0; i < data.size(); i++) {
|
||||
const bool msb = tmp.data[i] & bitmaskMsb;
|
||||
|
||||
// Set the lsb
|
||||
if (msb) {
|
||||
data[Mod(i-1, data.size())] |= bitmaskLsb;
|
||||
}
|
||||
// Clear the lsb
|
||||
else {
|
||||
data[Mod(i-1, data.size())] &= ~bitmaskLsb;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Basic_Block<T> Basic_Block<T>::ShiftBitsRight() const {
|
||||
Basic_Block<T> b;
|
||||
|
||||
// First, copy this block over
|
||||
b = *this;
|
||||
|
||||
// Then, shift all integers individually
|
||||
for (std::size_t i = 0; i < data.size(); i++) {
|
||||
b.data[i] >>= 1;
|
||||
}
|
||||
|
||||
// Current state: the LSB is zero everywhere. We have to carry
|
||||
// it over manually from the previous state.
|
||||
|
||||
// Carry over the LSB of data[i] to MSB of data[i+1]
|
||||
constexpr std::size_t bitmaskMsb = 1 << (CHUNK_SIZE_BITS - 1);
|
||||
constexpr std::size_t bitmaskLsb = 1;
|
||||
for (int i = 0; i < data.size(); i++) {
|
||||
const bool lsb = data[i] & bitmaskLsb;
|
||||
|
||||
// Set the msb
|
||||
if (lsb) {
|
||||
b.data[Mod(i+1, data.size())] |= bitmaskMsb;
|
||||
}
|
||||
// Clear the msb
|
||||
else {
|
||||
b.data[Mod(i+1, data.size())] &= ~bitmaskMsb;
|
||||
}
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Basic_Block<T>::ShiftBitsRightInplace() {
|
||||
Basic_Block<T> tmp = *this;
|
||||
|
||||
// Then, shift all integers individually
|
||||
for (std::size_t i = 0; i < data.size(); i++) {
|
||||
data[i] >>= 1;
|
||||
}
|
||||
|
||||
// Current state: the LSB is zero everywhere. We have to carry
|
||||
// it over manually from the previous state.
|
||||
|
||||
// Carry over the LSB of data[i] to MSB of data[i+1]
|
||||
constexpr std::size_t bitmaskMsb = 1 << (CHUNK_SIZE_BITS - 1);
|
||||
constexpr std::size_t bitmaskLsb = 1;
|
||||
for (int i = 0; i < data.size(); i++) {
|
||||
const bool lsb = tmp.data[i] & bitmaskLsb;
|
||||
|
||||
// Set the msb
|
||||
if (lsb) {
|
||||
data[Mod(i+1, data.size())] |= bitmaskMsb;
|
||||
}
|
||||
// Clear the msb
|
||||
else {
|
||||
data[Mod(i+1, data.size())] &= ~bitmaskMsb;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T& Basic_Block<T>::Get(const std::uint8_t row, const std::uint8_t column){
|
||||
return data[MAT_INDEX(row, column)];
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
const T& Basic_Block<T>::Get(const std::uint8_t row, const std::uint8_t column) const {
|
||||
return data[MAT_INDEX(row, column)];
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T& Basic_Block<T>::Get(const std::uint8_t index) {
|
||||
return data[index];
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
const T& Basic_Block<T>::Get(const std::uint8_t index) const {
|
||||
return data[index];
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T& Basic_Block<T>::operator[](const std::uint8_t index) {
|
||||
return data[index];
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
const T& Basic_Block<T>::operator[](const std::uint8_t index) const {
|
||||
return data[index];
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T* Basic_Block<T>::Data() noexcept {
|
||||
return data.data();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
const T* Basic_Block<T>::Data() const noexcept {
|
||||
return data.data();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool Basic_Block<T>::operator==(const Basic_Block<T>& other) const {
|
||||
return data == other.data;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool Basic_Block<T>::operator!=(const Basic_Block<T>& other) const {
|
||||
return data != other.data;
|
||||
}
|
||||
|
||||
#if defined _WIN32 || defined _WIN64
|
||||
#pragma optimize("", off )
|
||||
#elif defined __GNUG__
|
||||
#pragma GCC push_options
|
||||
#pragma GCC optimize ("O0")
|
||||
#endif
|
||||
template <typename T>
|
||||
Basic_Block<T>::~Basic_Block() {
|
||||
Reset();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Basic_Block<T>::Reset() {
|
||||
memset(data.data(), 0, CHUNK_SIZE*data.size());
|
||||
return;
|
||||
}
|
||||
#if defined _WIN32 || defined _WIN64
|
||||
#pragma optimize("", on )
|
||||
#elif defined __GNUG__
|
||||
#pragma GCC pop_options
|
||||
#endif
|
||||
|
||||
// Instantiate templates
|
||||
template class Basic_Block<std::uint32_t>;
|
||||
template class Basic_Block<std::uint16_t>;
|
||||
}
|
||||
|
||||
|
||||
#undef MAT_INDEX
|
||||
|
@ -1,135 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include "GCrypt/Cipher.h"
|
||||
#include "GCrypt/Util.h"
|
||||
#include "GCrypt/InitializationVector.h"
|
||||
|
||||
namespace Leonetienne::GCrypt {
|
||||
|
||||
Cipher::Cipher(const Block& key)
|
||||
:
|
||||
key { key },
|
||||
initializationVector(InitializationVector(key)) {
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Cipher::Cipher(const std::string& password)
|
||||
:
|
||||
key { PasswordToKey(password) },
|
||||
initializationVector(InitializationVector(key)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Cipher::~Cipher() {
|
||||
// Clear key memory
|
||||
ZeroKeyMemory();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void Cipher::SetKey(const Block& key) {
|
||||
ZeroKeyMemory();
|
||||
|
||||
this->key = key;
|
||||
return;
|
||||
}
|
||||
|
||||
void Cipher::SetPassword(const std::string& password) {
|
||||
ZeroKeyMemory();
|
||||
|
||||
key = PasswordToKey(password);
|
||||
return;
|
||||
}
|
||||
|
||||
Flexblock Cipher::Encipher(const Flexblock& data, bool printProgress) const {
|
||||
// Split cleartext into blocks
|
||||
std::vector<Block> blocks;
|
||||
|
||||
for (std::size_t i = 0; i < data.size(); i += BLOCK_SIZE) {
|
||||
blocks.push_back(Block(
|
||||
PadStringToLength(data.substr(i, BLOCK_SIZE), BLOCK_SIZE, '0', false))
|
||||
);
|
||||
}
|
||||
|
||||
// Encrypt individual blocks using cipher block chaining
|
||||
Feistel feistel(key);
|
||||
|
||||
for (std::size_t i = 0; i < blocks.size(); i++) {
|
||||
// Print reports if desired. If we have > 1000 blocks, print one report every 100 blocks. Otherwise for every 10th block.
|
||||
if ((i % ((blocks.size() > 1000)? 100 : 10) == 0) && (printProgress)) {
|
||||
std::cout << "Encrypting... (Block " << i << " / " << blocks.size() << " - " << ((float)i*100 / blocks.size()) << "%)" << std::endl;
|
||||
}
|
||||
|
||||
const Block& lastBlock = (i>0) ? blocks[i-1] : initializationVector;
|
||||
blocks[i] = feistel.Encipher(blocks[i] ^ lastBlock); // Xor last cipher block with new clear text block before E()
|
||||
}
|
||||
|
||||
// Concatenate ciphertext blocks back into a flexblock
|
||||
std::stringstream ss;
|
||||
for (Block& b : blocks) {
|
||||
ss << b;
|
||||
}
|
||||
|
||||
// Return it
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
Flexblock Cipher::Decipher(const Flexblock& data, bool printProgress) const {
|
||||
// Split ciphertext into blocks
|
||||
std::vector<Block> blocks;
|
||||
|
||||
for (std::size_t i = 0; i < data.size(); i += BLOCK_SIZE) {
|
||||
blocks.push_back(Block(
|
||||
PadStringToLength(data.substr(i, BLOCK_SIZE), BLOCK_SIZE, '0', false))
|
||||
);
|
||||
}
|
||||
|
||||
// Decrypt individual blocks
|
||||
Feistel feistel(key);
|
||||
|
||||
// We can't do this in-loop for decryption, because we are decrypting the blocks in-place.
|
||||
Block lastBlock = initializationVector;
|
||||
|
||||
for (std::size_t i = 0; i < blocks.size(); i++) {
|
||||
// Print reports if desired. If we have > 1000 blocks, print one report every 100 blocks. Otherwise for every 10th block.
|
||||
if ((i % ((blocks.size() > 1000) ? 100 : 10) == 0) && (printProgress)) {
|
||||
std::cout << "Decrypting... (Block " << i << " / " << blocks.size() << " - " << ((float)i*100/ blocks.size()) << "%)" << std::endl;
|
||||
}
|
||||
|
||||
Block tmpCopy = blocks[i];
|
||||
|
||||
blocks[i] = feistel.Decipher(blocks[i]) ^ lastBlock; // Decipher cipher block [i] and then xor it with the last cipher block [i-1] we've had
|
||||
|
||||
lastBlock = std::move(tmpCopy);
|
||||
}
|
||||
|
||||
// Concatenate ciphertext blocks back into a flexblock
|
||||
std::stringstream ss;
|
||||
for (Block& b : blocks) {
|
||||
ss << b;
|
||||
}
|
||||
|
||||
// Return it
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
// These pragmas only work for MSVC and g++, as far as i know. Beware!!!
|
||||
#if defined _WIN32 || defined _WIN64
|
||||
#pragma optimize("", off )
|
||||
#elif defined __GNUG__
|
||||
#pragma GCC push_options
|
||||
#pragma GCC optimize ("O0")
|
||||
#endif
|
||||
void Cipher::ZeroKeyMemory() {
|
||||
key.reset();
|
||||
return;
|
||||
}
|
||||
#if defined _WIN32 || defined _WIN64
|
||||
#pragma optimize("", on )
|
||||
#elif defined __GNUG__
|
||||
#pragma GCC pop_options
|
||||
#endif
|
||||
|
||||
}
|
||||
|
@ -2,242 +2,256 @@
|
||||
#include "GCrypt/Feistel.h"
|
||||
#include "GCrypt/Util.h"
|
||||
#include "GCrypt/Config.h"
|
||||
#include "GCrypt/SBoxLookup.h"
|
||||
|
||||
namespace Leonetienne::GCrypt {
|
||||
|
||||
Feistel::Feistel(const Block& key) {
|
||||
SetKey(key);
|
||||
return;
|
||||
Feistel::Feistel() {
|
||||
}
|
||||
|
||||
Feistel::Feistel(const Key& key) {
|
||||
SetKey(key);
|
||||
}
|
||||
|
||||
Feistel::~Feistel() {
|
||||
ZeroKeyMemory();
|
||||
|
||||
return;
|
||||
ZeroKeyMemory();
|
||||
}
|
||||
|
||||
void Feistel::SetKey(const Block& key) {
|
||||
GenerateRoundKeys(key);
|
||||
return;
|
||||
void Feistel::SetKey(const Key& key) {
|
||||
GenerateRoundKeys(key);
|
||||
isInitialized = true;
|
||||
}
|
||||
|
||||
Block Feistel::Encipher(const Block& data) {
|
||||
return Run(data, false);
|
||||
return Run(data, false);
|
||||
}
|
||||
|
||||
Block Feistel::Decipher(const Block& data) {
|
||||
return Run(data, true);
|
||||
return Run(data, true);
|
||||
}
|
||||
|
||||
Block Feistel::Run(const Block& data, bool reverseKeys) {
|
||||
const auto splitData = FeistelSplit(data);
|
||||
Halfblock l = splitData.first;
|
||||
Halfblock r = splitData.second;
|
||||
Block Feistel::Run(const Block& data, bool modeEncrypt) {
|
||||
if (!isInitialized) {
|
||||
throw std::runtime_error("Attempted to digest data on uninitialized GCipher!");
|
||||
}
|
||||
|
||||
Halfblock tmp;
|
||||
const auto splitData = FeistelSplit(data);
|
||||
Halfblock l = splitData.first;
|
||||
Halfblock r = splitData.second;
|
||||
|
||||
for (std::size_t i = 0; i < N_ROUNDS; i++) {
|
||||
// Calculate key index
|
||||
std::size_t keyIndex;
|
||||
if (reverseKeys) {
|
||||
keyIndex = N_ROUNDS - i - 1;
|
||||
}
|
||||
else {
|
||||
keyIndex = i;
|
||||
}
|
||||
Halfblock tmp;
|
||||
|
||||
// Do a feistel round
|
||||
tmp = r;
|
||||
r = l ^ F(r, roundKeys[keyIndex]);
|
||||
l = tmp;
|
||||
for (std::size_t i = 0; i < N_ROUNDS; i++) {
|
||||
|
||||
// Encryption
|
||||
if (modeEncrypt) {
|
||||
const std::size_t keyIndex = i;
|
||||
|
||||
// Do a feistel round
|
||||
tmp = r;
|
||||
r = l ^ F(r, roundKeys[keyIndex]);
|
||||
l = tmp;
|
||||
|
||||
// Jumble it up a bit more
|
||||
l.ShiftRowsUpInplace();
|
||||
l.ShiftCellsRightInplace();
|
||||
l.ShiftBitsLeftInplace();
|
||||
l.ShiftColumnsLeftInplace();
|
||||
// Seal all these operations with a key
|
||||
l += ReductionFunction(roundKeys[keyIndex]);
|
||||
}
|
||||
|
||||
// Block has finished de*ciphering.
|
||||
// Let's generate a new set of round keys.
|
||||
GenerateRoundKeys((Block)roundKeys.back());
|
||||
// Decryption
|
||||
else {
|
||||
// Decryption needs keys in reverse order
|
||||
const std::size_t keyIndex = N_ROUNDS - i - 1;
|
||||
|
||||
return FeistelCombine(r, l);
|
||||
// Unjumble the jumble
|
||||
r -= ReductionFunction(roundKeys[keyIndex]);
|
||||
r.ShiftColumnsRightInplace();
|
||||
r.ShiftBitsRightInplace();
|
||||
r.ShiftCellsLeftInplace();
|
||||
r.ShiftRowsDownInplace();
|
||||
|
||||
// Do a feistel round
|
||||
tmp = r;
|
||||
r = l ^ F(r, roundKeys[keyIndex]);
|
||||
l = tmp;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Block has finished de*ciphering.
|
||||
// Let's generate a new set of round keys.
|
||||
GenerateRoundKeys(roundKeys.back());
|
||||
|
||||
return FeistelCombine(r, l);
|
||||
}
|
||||
|
||||
Halfblock Feistel::F(Halfblock m, const Block& key) {
|
||||
// Made-up F function
|
||||
Halfblock Feistel::F(Halfblock m, const Key& key) {
|
||||
|
||||
// Expand to full bitwidth
|
||||
Block m_expanded = ExpansionFunction(m);
|
||||
// Made-up F function:
|
||||
// Expand to full bitwidth
|
||||
Block m_expanded = ExpansionFunction(m);
|
||||
|
||||
// Shift to left by 1
|
||||
m_expanded = Shiftl(m_expanded, 1);
|
||||
// Mix up the block a bit
|
||||
m_expanded.ShiftCellsRightInplace();
|
||||
m_expanded.ShiftRowsUpInplace();
|
||||
|
||||
// Xor with key
|
||||
m_expanded ^= key;
|
||||
// Matrix-mult with key (this is irreversible)
|
||||
m_expanded *= key;
|
||||
|
||||
// Non-linearly apply subsitution boxes
|
||||
std::stringstream ss;
|
||||
const std::string m_str = m_expanded.to_string();
|
||||
// Now do a bitshift
|
||||
m_expanded.ShiftBitsLeftInplace();
|
||||
|
||||
for (std::size_t i = 0; i < BLOCK_SIZE; i += 4) {
|
||||
ss << SBox(m_str.substr(i, 4));
|
||||
}
|
||||
// Apply the sbox
|
||||
SBox(m_expanded);
|
||||
|
||||
m_expanded = Block(ss.str());
|
||||
// Reduce back to a halfblock
|
||||
Halfblock hb = ReductionFunction(m_expanded);
|
||||
|
||||
// Return the compressed version
|
||||
return CompressionFunction(m_expanded);
|
||||
// To jumble it up a last time,
|
||||
// matrix-multiply it with the input halfblock
|
||||
hb *= m;
|
||||
|
||||
return hb;
|
||||
}
|
||||
|
||||
std::pair<Halfblock, Halfblock> Feistel::FeistelSplit(const Block& block) {
|
||||
const std::string bits = block.to_string();
|
||||
Halfblock l;
|
||||
Halfblock r;
|
||||
|
||||
Halfblock l(bits.substr(0, bits.size() / 2));
|
||||
Halfblock r(bits.substr(bits.size() / 2));
|
||||
memcpy(l.Data(), block.Data(), Halfblock::BLOCK_SIZE);
|
||||
memcpy(r.Data(), block.Data() + 8, Halfblock::BLOCK_SIZE);
|
||||
// +8, because 8 is HALF the number of elements in the array. We only want to copy HALF a full-sized block.
|
||||
|
||||
return std::make_pair(l, r);
|
||||
return std::make_pair(l, r);
|
||||
}
|
||||
|
||||
Block Feistel::FeistelCombine(const Halfblock& l, const Halfblock& r) {
|
||||
return Block(l.to_string() + r.to_string());
|
||||
Block b;
|
||||
|
||||
memcpy(b.Data(), l.Data(), Halfblock::BLOCK_SIZE);
|
||||
memcpy(b.Data() + 8, r.Data(), Halfblock::BLOCK_SIZE);
|
||||
// +8, because 8 is HALF the number of elements in the array. We only want to copy HALF a full-sized block.
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
Block Feistel::ExpansionFunction(const Halfblock& block) {
|
||||
std::stringstream ss;
|
||||
const std::string bits = block.to_string();
|
||||
Block Feistel::ExpansionFunction(const Halfblock& hb) {
|
||||
Block b;
|
||||
|
||||
std::unordered_map<std::string, std::string> expansionMap;
|
||||
expansionMap["00"] = "1101";
|
||||
expansionMap["01"] = "1000";
|
||||
expansionMap["10"] = "0010";
|
||||
expansionMap["11"] = "0111";
|
||||
// Copy the bits over
|
||||
for (std::size_t i = 0; i < 16; i++) {
|
||||
b[i] = hb[i];
|
||||
}
|
||||
|
||||
// We have to double the bits!
|
||||
for (std::size_t i = 0; i < HALFBLOCK_SIZE; i += 2) {
|
||||
const std::string sub = bits.substr(i, 2);
|
||||
ss << expansionMap[sub];
|
||||
}
|
||||
// Multiply the block a few tims with a bitshifted version
|
||||
// This is irriversible, too
|
||||
for (std::size_t i = 0; i < 3; i++) {
|
||||
b *= b.ShiftBitsRight();
|
||||
}
|
||||
|
||||
return Block(ss.str());
|
||||
return b;
|
||||
}
|
||||
|
||||
Halfblock Feistel::CompressionFunction(const Block& block) {
|
||||
std::stringstream ss;
|
||||
const std::string bits = block.to_string();
|
||||
Halfblock Feistel::ReductionFunction(const Block& block) {
|
||||
|
||||
std::unordered_map<std::string, std::string> compressionMap;
|
||||
compressionMap["0000"] = "10";
|
||||
compressionMap["0001"] = "01";
|
||||
compressionMap["0010"] = "10";
|
||||
compressionMap["0011"] = "10";
|
||||
compressionMap["0100"] = "11";
|
||||
compressionMap["0101"] = "01";
|
||||
compressionMap["0110"] = "00";
|
||||
compressionMap["0111"] = "11";
|
||||
compressionMap["1000"] = "01";
|
||||
compressionMap["1001"] = "00";
|
||||
compressionMap["1010"] = "11";
|
||||
compressionMap["1011"] = "00";
|
||||
compressionMap["1100"] = "11";
|
||||
compressionMap["1101"] = "10";
|
||||
compressionMap["1110"] = "00";
|
||||
compressionMap["1111"] = "01";
|
||||
// Just apply a modulo operation, remapping a 32bit integer
|
||||
// onto 16bit space (default configuration).
|
||||
// Without saying, modulo is irreversible.
|
||||
Halfblock hb;
|
||||
for (std::size_t i = 0; i < 16; i++) {
|
||||
hb[i] = block[i] % (1 << (Halfblock::CHUNK_SIZE_BITS - 1));
|
||||
}
|
||||
|
||||
// We have to half the bits!
|
||||
for (std::size_t i = 0; i < BLOCK_SIZE; i += 4) {
|
||||
const std::string sub = bits.substr(i, 4);
|
||||
ss << compressionMap[sub];
|
||||
}
|
||||
|
||||
return Halfblock(ss.str());
|
||||
return hb;
|
||||
}
|
||||
|
||||
void Feistel::SBox(Block& block) {
|
||||
|
||||
std::uint8_t* curByte = (std::uint8_t*)(void*)block.Data();
|
||||
|
||||
// Iterate over all bytes in the block
|
||||
for (std::size_t i = 0; i < Block::BLOCK_SIZE; i++) {
|
||||
curByte++;
|
||||
|
||||
// Subsitute byte
|
||||
*curByte = sboxLookup[*curByte];
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
std::string Feistel::SBox(const std::string& in) {
|
||||
static std::unordered_map<std::string, std::string> subMap;
|
||||
static bool mapInitialized = false;
|
||||
if (!mapInitialized) {
|
||||
subMap["0000"] = "1100";
|
||||
subMap["0001"] = "1000";
|
||||
subMap["0010"] = "0001";
|
||||
subMap["0011"] = "0111";
|
||||
subMap["0100"] = "1011";
|
||||
subMap["0101"] = "0011";
|
||||
subMap["0110"] = "1101";
|
||||
subMap["0111"] = "1111";
|
||||
subMap["1000"] = "0000";
|
||||
subMap["1001"] = "1010";
|
||||
subMap["1010"] = "0100";
|
||||
subMap["1011"] = "1001";
|
||||
subMap["1100"] = "0010";
|
||||
subMap["1101"] = "1110";
|
||||
subMap["1110"] = "0101";
|
||||
subMap["1111"] = "0110";
|
||||
mapInitialized = true;
|
||||
}
|
||||
static std::unordered_map<std::string, std::string> subMap;
|
||||
static bool mapInitialized = false;
|
||||
if (!mapInitialized) {
|
||||
subMap["0000"] = "1100";
|
||||
subMap["0001"] = "1000";
|
||||
subMap["0010"] = "0001";
|
||||
subMap["0011"] = "0111";
|
||||
subMap["0100"] = "1011";
|
||||
subMap["0101"] = "0011";
|
||||
subMap["0110"] = "1101";
|
||||
subMap["0111"] = "1111";
|
||||
subMap["1000"] = "0000";
|
||||
subMap["1001"] = "1010";
|
||||
subMap["1010"] = "0100";
|
||||
subMap["1011"] = "1001";
|
||||
subMap["1100"] = "0010";
|
||||
subMap["1101"] = "1110";
|
||||
subMap["1110"] = "0101";
|
||||
subMap["1111"] = "0110";
|
||||
mapInitialized = true;
|
||||
}
|
||||
|
||||
return subMap[in];
|
||||
return subMap[in];
|
||||
}
|
||||
*/
|
||||
|
||||
void Feistel::GenerateRoundKeys(const Key& seedKey) {
|
||||
// Clear initial key memory
|
||||
ZeroKeyMemory();
|
||||
roundKeys = Keyset();
|
||||
|
||||
// Derive all round keys with simple matrix operations
|
||||
roundKeys[0] = seedKey;
|
||||
|
||||
for (std::size_t i = 1; i < roundKeys.size(); i++) {
|
||||
// Initialize new round key with last round key
|
||||
const Key& lastKey = roundKeys[i - 1];
|
||||
roundKeys[i] = lastKey;
|
||||
|
||||
// Stir it good
|
||||
roundKeys[i].ShiftRowsUpInplace();
|
||||
|
||||
// Bitshift and matrix-mult 3 times
|
||||
// (each time jumbles it up pretty good)
|
||||
// This is irreversible
|
||||
roundKeys[i].ShiftBitsRightInplace();
|
||||
roundKeys[i] *= lastKey;
|
||||
roundKeys[i].ShiftBitsRightInplace();
|
||||
roundKeys[i] *= lastKey;
|
||||
roundKeys[i].ShiftBitsRightInplace();
|
||||
roundKeys[i] *= lastKey;
|
||||
|
||||
// Lastly, do apply some cell shifting, and other mutations
|
||||
roundKeys[i].ShiftCellsRightInplace();
|
||||
roundKeys[i] += lastKey;
|
||||
roundKeys[i].ShiftColumnsRightInplace();
|
||||
roundKeys[i] ^= lastKey;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void Feistel::GenerateRoundKeys(const Block& seedKey) {
|
||||
// Clear initial key memory
|
||||
ZeroKeyMemory();
|
||||
roundKeys = Keyset();
|
||||
void Feistel::operator=(const Feistel& other) {
|
||||
roundKeys = other.roundKeys;
|
||||
isInitialized = other.isInitialized;
|
||||
|
||||
// Derive the initial two round keys
|
||||
|
||||
// Compress- substitute, and expand the seed key to form the initial and the second-initial round key
|
||||
// This action is non-linear and irreversible, and thus strenghtens security.
|
||||
Halfblock compressedSeed1 = CompressionFunction(seedKey);
|
||||
Halfblock compressedSeed2 = CompressionFunction(Shiftl(seedKey, 1)); // Shifting one key by 1 will result in a completely different compression
|
||||
|
||||
// To add further confusion, let's shift seed1 by 1 aswell (after compression, but before substitution)
|
||||
// but only if the total number of bits set are a multiple of 3
|
||||
// if it is a multiple of 4, we'll shift it by 1 into the opposite direction
|
||||
const std::size_t setBits1 = compressedSeed1.count();
|
||||
|
||||
if (setBits1 % 4 == 0) {
|
||||
compressedSeed1 = Shiftr(compressedSeed1, 1);
|
||||
}
|
||||
else if (setBits1 % 3 == 0) {
|
||||
compressedSeed1 = Shiftl(compressedSeed1, 1);
|
||||
}
|
||||
|
||||
// Now apply substitution
|
||||
std::stringstream ssKey1;
|
||||
std::stringstream ssKey2;
|
||||
const std::string bitsKey1 = compressedSeed1.to_string();
|
||||
const std::string bitsKey2 = compressedSeed2.to_string();
|
||||
|
||||
for (std::size_t i = 0; i < HALFBLOCK_SIZE; i += 4) {
|
||||
ssKey1 << SBox(bitsKey1.substr(i, 4));
|
||||
ssKey2 << SBox(bitsKey2.substr(i, 4));
|
||||
}
|
||||
|
||||
compressedSeed1 = Halfblock(ssKey1.str());
|
||||
compressedSeed2 = Halfblock(ssKey2.str());
|
||||
|
||||
// Now extrapolate them to BLOCK_SIZE (key size) again
|
||||
// Xor with the original seed key to get rid of the repititions caused by the expansion
|
||||
roundKeys[0] = ExpansionFunction(compressedSeed1) ^ seedKey;
|
||||
roundKeys[1] = ExpansionFunction(compressedSeed2) ^ seedKey;
|
||||
|
||||
// Now derive all other round keys
|
||||
|
||||
for (std::size_t i = 2; i < roundKeys.size(); i++) {
|
||||
// Initialize new round key with last round key
|
||||
Block newKey = roundKeys[i - 1];
|
||||
|
||||
// Shift to left by how many bits are set, modulo 8
|
||||
newKey = Shiftl(newKey, newKey.count() % 8); // This action is irreversible
|
||||
|
||||
// Split into two halfblocks,
|
||||
// apply F() to one halfblock with rk[i-2],
|
||||
// xor the other one with it
|
||||
// and put them back together
|
||||
auto halfkeys = FeistelSplit(newKey);
|
||||
Halfblock halfkey1 = F(halfkeys.first, roundKeys[i - 2]);
|
||||
Halfblock halfkey2 = halfkeys.second ^ halfkey1; // I know this is reversible, but it helps to diffuse future round keys.
|
||||
|
||||
roundKeys[i] = FeistelCombine(halfkey1, halfkey2);
|
||||
}
|
||||
|
||||
return;
|
||||
return;
|
||||
}
|
||||
|
||||
// These pragmas only work for MSVC and g++, as far as i know. Beware!!!
|
||||
@ -248,11 +262,11 @@ namespace Leonetienne::GCrypt {
|
||||
#pragma GCC optimize ("O0")
|
||||
#endif
|
||||
void Feistel::ZeroKeyMemory() {
|
||||
for (Block& key : roundKeys) {
|
||||
key.reset();
|
||||
}
|
||||
for (Key& key : roundKeys) {
|
||||
key.Reset();
|
||||
}
|
||||
|
||||
return;
|
||||
return;
|
||||
}
|
||||
#if defined _WIN32 || defined _WIN64
|
||||
#pragma optimize("", on )
|
||||
|
91
GCryptLib/src/GCipher.cpp
Normal file
@ -0,0 +1,91 @@
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
#include "GCrypt/GCipher.h"
|
||||
#include "GCrypt/Util.h"
|
||||
#include "GCrypt/InitializationVector.h"
|
||||
|
||||
namespace Leonetienne::GCrypt {
|
||||
|
||||
GCipher::GCipher() {
|
||||
}
|
||||
|
||||
GCipher::GCipher(const Key& key, const DIRECTION direction) :
|
||||
direction { direction },
|
||||
lastBlock(InitializationVector(key)), // Initialize our lastBlock with some deterministic initial value, based on the key
|
||||
feistel(key)
|
||||
{
|
||||
isInitialized = true;
|
||||
return;
|
||||
}
|
||||
|
||||
void GCipher::Initialize(const Key& key, const DIRECTION direction) {
|
||||
feistel = Feistel(key);
|
||||
lastBlock = InitializationVector(key);
|
||||
this->direction = direction;
|
||||
isInitialized = true;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Block GCipher::Digest(const Block& input) {
|
||||
|
||||
if (!isInitialized) {
|
||||
throw std::runtime_error("Attempted to digest data on uninitialized GCipher!");
|
||||
}
|
||||
|
||||
switch (direction) {
|
||||
case DIRECTION::ENCIPHER: {
|
||||
// Rename our input to cleartext
|
||||
const Block& cleartext = input;
|
||||
|
||||
// First, xor our cleartext with the last block, and then encipher it
|
||||
Block ciphertext = feistel.Encipher(cleartext ^ lastBlock);
|
||||
|
||||
// Now set our lastBlock to the ciphertext of this block
|
||||
lastBlock = ciphertext;
|
||||
|
||||
// Now return the ciphertext
|
||||
return ciphertext;
|
||||
}
|
||||
|
||||
case DIRECTION::DECIPHER: {
|
||||
// Rename our input into ciphertext
|
||||
const Block& ciphertext = input;
|
||||
|
||||
// First, decipher our ciphertext, and then xor it with our last block
|
||||
Block cleartext = feistel.Decipher(ciphertext) ^ lastBlock;
|
||||
|
||||
// Now set our lastBLock to the ciphertext of this block
|
||||
lastBlock = ciphertext;
|
||||
|
||||
// Now return the cleartext
|
||||
return cleartext;
|
||||
}
|
||||
}
|
||||
|
||||
throw std::runtime_error("Unreachable branch reached.");
|
||||
}
|
||||
|
||||
void GCipher::SetKey(const Key& key) {
|
||||
|
||||
if (!isInitialized) {
|
||||
throw std::runtime_error("Attempted to set key on uninitialized GCipher!");
|
||||
}
|
||||
|
||||
feistel.SetKey(key);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void GCipher::operator=(const GCipher& other) {
|
||||
direction = other.direction;
|
||||
feistel = other.feistel;
|
||||
lastBlock = other.lastBlock;
|
||||
isInitialized = other.isInitialized;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,88 +0,0 @@
|
||||
#include "GCrypt/GCryptWrapper.h"
|
||||
#include "GCrypt/Cipher.h"
|
||||
#include "GCrypt/Util.h"
|
||||
|
||||
namespace Leonetienne::GCrypt {
|
||||
|
||||
std::string GCryptWrapper::EncryptString(const std::string& cleartext, const std::string& password) {
|
||||
// Instanciate our cipher and supply a key
|
||||
const Block key = PasswordToKey(password);
|
||||
Cipher cipher(key);
|
||||
|
||||
// Recode the ascii-string to bits
|
||||
const Flexblock cleartext_bits = StringToBits(cleartext);
|
||||
|
||||
// Encrypt our cleartext bits
|
||||
const Flexblock ciphertext_bits = cipher.Encipher(cleartext_bits);
|
||||
|
||||
// Recode the ciphertext bits to a hex-string
|
||||
const std::string ciphertext = BitsToHexstring(ciphertext_bits);
|
||||
|
||||
// Return it
|
||||
return ciphertext;
|
||||
}
|
||||
|
||||
std::string GCryptWrapper::DecryptString(const std::string& ciphertext, const std::string& password) {
|
||||
// Instanciate our cipher and supply a key
|
||||
const Block key = PasswordToKey(password);
|
||||
Cipher cipher(key);
|
||||
|
||||
// Recode the hex-string to bits
|
||||
const Flexblock ciphertext_bits = HexstringToBits(ciphertext);
|
||||
|
||||
// Decrypt the ciphertext bits
|
||||
const std::string cleartext_bits = cipher.Decipher(ciphertext_bits);
|
||||
|
||||
// Recode the cleartext bits to an ascii-string
|
||||
const std::string cleartext = BitsToString(cleartext_bits);
|
||||
|
||||
// Return it
|
||||
return cleartext;
|
||||
}
|
||||
|
||||
bool GCryptWrapper::EncryptFile(const std::string& filename_in, const std::string& filename_out, const std::string& password, bool printProgressReport) {
|
||||
try {
|
||||
// Read the file to bits
|
||||
const Flexblock cleartext_bits = ReadFileToBits(filename_in);
|
||||
|
||||
// Instanciate our cipher and supply a key
|
||||
const Block key = PasswordToKey(password);
|
||||
Cipher cipher(key);
|
||||
|
||||
// Encrypt our cleartext bits
|
||||
const Flexblock ciphertext_bits = cipher.Encipher(cleartext_bits, printProgressReport);
|
||||
|
||||
// Write our ciphertext bits to file
|
||||
WriteBitsToFile(filename_out, ciphertext_bits);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (std::runtime_error&) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool GCryptWrapper::DecryptFile(const std::string& filename_in, const std::string& filename_out, const std::string& password, bool printProgressReport) {
|
||||
try {
|
||||
// Read the file to bits
|
||||
const Flexblock ciphertext_bits = ReadFileToBits(filename_in);
|
||||
|
||||
// Instanciate our cipher and supply a key
|
||||
const Block key = PasswordToKey(password);
|
||||
Cipher cipher(key);
|
||||
|
||||
// Decrypt the ciphertext bits
|
||||
const Flexblock cleartext_bits = cipher.Decipher(ciphertext_bits, printProgressReport);
|
||||
|
||||
// Write our cleartext bits to file
|
||||
WriteBitsToFile(filename_out, cleartext_bits);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (std::runtime_error&) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
97
GCryptLib/src/GHash.cpp
Normal file
@ -0,0 +1,97 @@
|
||||
#include "GCrypt/GHash.h"
|
||||
#include "GCrypt/Util.h"
|
||||
#include "GCrypt/InitializationVector.h"
|
||||
#include <vector>
|
||||
|
||||
namespace Leonetienne::GCrypt {
|
||||
|
||||
GHash::GHash() {
|
||||
// Initialize our cipher with a static, but randomly distributed key.
|
||||
Block ivSeed;
|
||||
ivSeed.FromByteString("3J7IipfQTDJbO8jtasz9PgWui6faPaEMOuVuAqyhB1S2CRcLw5caawewgDUEG1WN");
|
||||
block = InitializationVector(ivSeed);
|
||||
|
||||
Key key;
|
||||
key.FromByteString("nsoCZfvdqpRkeVTt9wzvPR3TT26peOW9E2kTHh3pdPCq2M7BpskvUljJHSrobUTI");
|
||||
|
||||
cipher = GCipher(
|
||||
// The key really does not matter, as it gets changed
|
||||
// each time before digesting anything.
|
||||
key,
|
||||
GCipher::DIRECTION::ENCIPHER
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void GHash::Digest(const Block& data) {
|
||||
// Set the cipher key to the current data to be hashed
|
||||
cipher.SetKey(data);
|
||||
|
||||
// Encipher the current block, and matrix-mult it with the current hashsum
|
||||
block ^= cipher.Digest(data);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const Block& GHash::GetHashsum() const {
|
||||
return block;
|
||||
}
|
||||
|
||||
Block GHash::CalculateHashsum(const std::vector<Block>& data, std::size_t n_bytes) {
|
||||
|
||||
// If we have no supplied n_bytes, let's just assume sizeof(data).
|
||||
if (n_bytes == std::string::npos) {
|
||||
n_bytes = data.size() * Block::BLOCK_SIZE;
|
||||
}
|
||||
|
||||
// Create hasher instance
|
||||
GHash hasher;
|
||||
|
||||
// Digest all blocks
|
||||
for (const Block& block : data) {
|
||||
hasher.Digest(block);
|
||||
}
|
||||
|
||||
// Add an additional block, containing the length of the input
|
||||
|
||||
// Here it is actually good to use a binary string ("10011"),
|
||||
// because std::size_t is not fixed to 32-bits. It may aswell
|
||||
// be 64 bits, depending on the platform.
|
||||
// Then it would be BAD to just cram it into a 32-bit uint32.
|
||||
// This way, in case of 64-bits, it would just occupy 2 uint32's.
|
||||
|
||||
// Also, this operation gets done ONCE per n blocks. This won't
|
||||
// hurt performance.
|
||||
|
||||
// I know that we are first converting n_bytes to str(n_bytes),
|
||||
// and then converting this to a binstring, making it unnecessarily large,
|
||||
// but who cares. It has a whole 512 bit block to itself.
|
||||
// The max size (2^64) would occupy 155 bits at max. (log10(2^64)*8 = 155)
|
||||
|
||||
std::stringstream ss;
|
||||
ss << n_bytes;
|
||||
Block lengthBlock;
|
||||
lengthBlock.FromTextString(ss.str());
|
||||
|
||||
// Digest the length block
|
||||
hasher.Digest(lengthBlock);
|
||||
|
||||
// Return the total hashsum
|
||||
return hasher.GetHashsum();
|
||||
}
|
||||
|
||||
Block GHash::HashString(const std::string& str) {
|
||||
const std::vector<Block> blocks = StringToBitblocks(str);
|
||||
const std::size_t n_bytes = str.length();
|
||||
|
||||
return CalculateHashsum(blocks, n_bytes);
|
||||
}
|
||||
|
||||
void GHash::operator=(const GHash& other) {
|
||||
cipher = other.cipher;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
116
GCryptLib/src/GPrng.cpp
Normal file
@ -0,0 +1,116 @@
|
||||
#include "GCrypt/GPrng.h"
|
||||
#include <cassert>
|
||||
|
||||
namespace Leonetienne::GCrypt {
|
||||
|
||||
GPrng::GPrng(const Block& seed) {
|
||||
this->seed = seed;
|
||||
hasher.Digest(seed);
|
||||
}
|
||||
|
||||
GPrng::GPrng() {
|
||||
}
|
||||
|
||||
void GPrng::Seed(const Block& seed) {
|
||||
hasher = GHash();
|
||||
this->seed = seed;
|
||||
hasher.Digest(seed);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
bool GPrng::GetBit() {
|
||||
// If we have no more bits to go, create new ones
|
||||
if (nextBit >= Block::BLOCK_SIZE_BITS) {
|
||||
AdvanceBlock();
|
||||
}
|
||||
|
||||
// Return the next bit.
|
||||
return hasher.GetHashsum().GetBit(nextBit++);
|
||||
}
|
||||
|
||||
void GPrng::AdvanceBlock() {
|
||||
// To prevent an attacker from being able
|
||||
// to predict block n, by knowing block n-1, and block n-2,
|
||||
// we will advance the hash function by block n-1 XOR seed.
|
||||
// This way it is impossible for an attacker to know the
|
||||
// state of the hash function, unless the seed is known.
|
||||
|
||||
// Advance the block (Add the current hashsum XOR seed to the hasher)
|
||||
hasher.Digest(Block(hasher.GetHashsum() ^ seed));
|
||||
|
||||
// Reset the pointer
|
||||
nextBit = 0;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Block GPrng::GetBlock() {
|
||||
// Tactic on efficiently generating a new block:
|
||||
// 1) Fetch complete current hashsum (it might have been partially given out already)
|
||||
// 2) Bitshift it, and matrix-mult it with the seed (that is irreversible)
|
||||
// That should be a one-way function, and create a new unique block.
|
||||
|
||||
// Performance improvement over the previous method:
|
||||
// (generating 100.000 blocks):
|
||||
// 12 seconds -> 0.12 seconds
|
||||
|
||||
// Fetch our current block
|
||||
Block hashsum = hasher.GetHashsum();
|
||||
|
||||
// Derive/'hash' it to hashsum'
|
||||
hashsum *= seed;
|
||||
hashsum.ShiftBitsLeftInplace();
|
||||
hashsum *= seed;
|
||||
|
||||
// Advance the block, so that the following block will be a new block
|
||||
AdvanceBlock();
|
||||
|
||||
// Return our hashsum
|
||||
return hashsum;
|
||||
}
|
||||
|
||||
std::uint32_t GPrng::operator()() {
|
||||
// Tactic:
|
||||
// A block intrinsically consists of 16 32-bit uints.
|
||||
// We'll just skip all the bits until the next whole integer,
|
||||
// fetch this complete int, and then advance our pointer
|
||||
// by 32 bits.
|
||||
|
||||
// Performance improvement over the previous method:
|
||||
// (generating 1.000.000 integers):
|
||||
// 5.26 seconds -> 3.75 seconds
|
||||
|
||||
|
||||
// Advance our pointer to the next whole uint32
|
||||
|
||||
// Do we even have >= 32 bits left in our block?
|
||||
if (nextBit > Block::BLOCK_SIZE_BITS - Block::CHUNK_SIZE_BITS) {
|
||||
// No: Create a new block
|
||||
AdvanceBlock();
|
||||
}
|
||||
|
||||
// We don't have to do this, if our pointer is already at
|
||||
// the beginning of a whole uint32
|
||||
if (nextBit % Block::CHUNK_SIZE_BITS != 0) {
|
||||
nextBit = ((nextBit / Block::CHUNK_SIZE_BITS) + 1) * Block::CHUNK_SIZE_BITS;
|
||||
}
|
||||
|
||||
// Fetch our integer from the block
|
||||
const std::uint32_t randint =
|
||||
hasher.GetHashsum().Get(nextBit / Block::CHUNK_SIZE_BITS);
|
||||
|
||||
// Advance our pointer
|
||||
nextBit += Block::CHUNK_SIZE_BITS;
|
||||
|
||||
// New state of the pointer:
|
||||
// It ow may be more now than the size of a block, but that
|
||||
// gets checked at the begin of a function.
|
||||
// Not at the end, like here.
|
||||
|
||||
// Return our integer
|
||||
return randint;
|
||||
}
|
||||
|
||||
}
|
||||
|
158
GCryptLib/src/GWrapper.cpp
Normal file
@ -0,0 +1,158 @@
|
||||
#include "GCrypt/GWrapper.h"
|
||||
#include "GCrypt/GCipher.h"
|
||||
#include "GCrypt/Util.h"
|
||||
#include <vector>
|
||||
|
||||
namespace Leonetienne::GCrypt {
|
||||
|
||||
std::string GWrapper::EncryptString(
|
||||
const std::string& cleartext,
|
||||
const Key& key)
|
||||
{
|
||||
// Recode the ascii-string to bits
|
||||
const std::vector<Block> cleartext_blocks = StringToBitblocks(cleartext);
|
||||
|
||||
// Create cipher instance
|
||||
GCipher cipher(key, GCipher::DIRECTION::ENCIPHER);
|
||||
|
||||
// Encrypt all blocks
|
||||
std::vector<Block> ciphertext_blocks;
|
||||
|
||||
for (const Block& clearBlock : cleartext_blocks) {
|
||||
ciphertext_blocks.emplace_back(cipher.Digest(clearBlock));
|
||||
}
|
||||
|
||||
// Recode the ciphertext blocks to a hex-string
|
||||
std::stringstream ss;
|
||||
for (const Block& block : ciphertext_blocks) {
|
||||
ss << block.ToHexString();
|
||||
}
|
||||
|
||||
// Return it
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string GWrapper::DecryptString(
|
||||
const std::string& ciphertext,
|
||||
const Key& key)
|
||||
{
|
||||
// Make sure our ciphertext is a multiple of block size
|
||||
if (ciphertext.length() % Block::BLOCK_SIZE*2 != 0) { // Two chars per byte
|
||||
throw std::runtime_error("Leonetienne::GCrypt::GWrapper::DecryptString() received ciphertext of length not a multiple of block size.");
|
||||
}
|
||||
|
||||
// Recode the hex-string to blocks
|
||||
std::vector<Block> ciphertext_blocks;
|
||||
ciphertext_blocks.reserve(ciphertext.length() / (Block::BLOCK_SIZE*2));
|
||||
for (std::size_t i = 0; i < ciphertext.length(); i += Block::BLOCK_SIZE*2) {
|
||||
Block block;
|
||||
block.FromHexString(ciphertext.substr(i, Block::BLOCK_SIZE*2));
|
||||
|
||||
ciphertext_blocks.emplace_back(block);
|
||||
}
|
||||
|
||||
// Create cipher instance
|
||||
GCipher cipher(key, GCipher::DIRECTION::DECIPHER);
|
||||
|
||||
// Decrypt all blocks
|
||||
std::vector<Block> cleartext_blocks;
|
||||
for (const Block& cipherBlock : ciphertext_blocks) {
|
||||
cleartext_blocks.emplace_back(cipher.Digest(cipherBlock));
|
||||
}
|
||||
|
||||
// Recode the cleartext blocks to bytes
|
||||
std::stringstream ss;
|
||||
for (const Block& block : cleartext_blocks) {
|
||||
ss << block.ToTextString();
|
||||
}
|
||||
|
||||
// Return it
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
bool GWrapper::EncryptFile(
|
||||
const std::string& filename_in,
|
||||
const std::string& filename_out,
|
||||
const Key& key,
|
||||
bool printProgressReport)
|
||||
{
|
||||
try {
|
||||
// Read the file to blocks
|
||||
const std::vector<Block> cleartext_blocks = ReadFileToBlocks(filename_in);
|
||||
|
||||
// Encrypt our cleartext blocks
|
||||
std::vector<Block> ciphertext_blocks;
|
||||
ciphertext_blocks.reserve(cleartext_blocks.size());
|
||||
|
||||
// Create cipher instance
|
||||
GCipher cipher(key, GCipher::DIRECTION::ENCIPHER);
|
||||
|
||||
// Encrypt all blocks
|
||||
for (const Block& clearBlock : cleartext_blocks) {
|
||||
ciphertext_blocks.emplace_back(cipher.Digest(clearBlock));
|
||||
}
|
||||
|
||||
// Write our ciphertext blocks to file
|
||||
WriteBlocksToFile(filename_out, ciphertext_blocks);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (std::runtime_error&) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool GWrapper::DecryptFile(
|
||||
const std::string& filename_in,
|
||||
const std::string& filename_out,
|
||||
const Key& key,
|
||||
bool printProgressReport)
|
||||
{
|
||||
try {
|
||||
// Read the file to blocks
|
||||
const std::vector<Block> ciphertext_blocks = ReadFileToBlocks(filename_in);
|
||||
|
||||
// Decrypt our cleartext blocks
|
||||
std::vector<Block> cleartext_blocks;
|
||||
cleartext_blocks.reserve(ciphertext_blocks.size());
|
||||
|
||||
// Create cipher instance
|
||||
GCipher cipher(key, GCipher::DIRECTION::DECIPHER);
|
||||
|
||||
// Decrypt all blocks
|
||||
for (const Block& cipherBlock : ciphertext_blocks) {
|
||||
cleartext_blocks.emplace_back(cipher.Digest(cipherBlock));
|
||||
}
|
||||
|
||||
// Write our cleartext blocks to file
|
||||
WriteBlocksToFile(filename_out, cleartext_blocks);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (std::runtime_error&) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Block> GWrapper::CipherBlocks(
|
||||
const std::vector<Block>& data,
|
||||
const Key& key,
|
||||
const GCipher::DIRECTION direction)
|
||||
{
|
||||
// Create cipher instance
|
||||
GCipher cipher(key, direction);
|
||||
|
||||
std::vector<Block> digested;
|
||||
digested.reserve(data.size());
|
||||
|
||||
// Digest all our blocks
|
||||
for (const Block& block : data) {
|
||||
Block digestedBlock = cipher.Digest(block);
|
||||
digested.emplace_back(digestedBlock);
|
||||
}
|
||||
|
||||
// Return it
|
||||
return digested;
|
||||
}
|
||||
}
|
||||
|
75
GCryptLib/src/Key.cpp
Normal file
@ -0,0 +1,75 @@
|
||||
#include "GCrypt/Key.h"
|
||||
#include "GCrypt/GHash.h"
|
||||
#include "GCrypt/Util.h"
|
||||
#include <random>
|
||||
#include <cassert>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
|
||||
namespace Leonetienne::GCrypt {
|
||||
|
||||
Key Key::FromPassword(const std::string& password) {
|
||||
return GHash::HashString(password);
|
||||
}
|
||||
|
||||
Key Key::Random() {
|
||||
// Create our truly-random rng
|
||||
std::random_device rng;
|
||||
constexpr std::size_t bitsPerCall = sizeof(std::random_device::result_type) * 8;
|
||||
|
||||
// Create a new key, and assign 16 random values
|
||||
Key key;
|
||||
for (std::size_t i = 0; i < 16; i++) {
|
||||
key[i] = rng();
|
||||
}
|
||||
|
||||
// Return it
|
||||
return key;
|
||||
}
|
||||
|
||||
Key Key::LoadFromFile(const std::string& path) {
|
||||
// Read this many chars
|
||||
const std::size_t maxChars = Key::BLOCK_SIZE;
|
||||
|
||||
// Open ifilestream for keyfile
|
||||
std::ifstream ifs(path, std::ios::in | std::ios::binary);
|
||||
|
||||
// Is the file open now? Or were there any issues...
|
||||
if (!ifs.good()) {
|
||||
throw std::runtime_error(std::string("Unable to open ifilestream for keyfile \"") + path + "\"! Aborting...");
|
||||
}
|
||||
|
||||
// Create a new key, and zero it
|
||||
Key key;
|
||||
key.Reset();
|
||||
|
||||
// Read into it
|
||||
ifs.read((char*)(void*)key.Data(), Key::BLOCK_SIZE);
|
||||
|
||||
// Return it
|
||||
return key;
|
||||
}
|
||||
|
||||
void Key::WriteToFile(const std::string& path) const {
|
||||
// Create an ofilestream
|
||||
std::ofstream ofs(path, std::ios::out | std::ios::binary);
|
||||
|
||||
// Write the key
|
||||
ofs.write((char*)(void*)Data(), Key::BLOCK_SIZE);
|
||||
|
||||
// Close the file handle
|
||||
ofs.close();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Key::Key() : Block() {
|
||||
}
|
||||
|
||||
Key::Key(const Block& b) : Block(b) {
|
||||
}
|
||||
|
||||
Key::Key(const Key& k) : Block(k) {
|
||||
}
|
||||
}
|
||||
|
142
GCryptLib/src/Util.cpp
Normal file
@ -0,0 +1,142 @@
|
||||
#include "GCrypt/Util.h"
|
||||
#include "GCrypt/GHash.h"
|
||||
#include <vector>
|
||||
|
||||
namespace Leonetienne::GCrypt {
|
||||
|
||||
std::string PadStringToLength(const std::string& str, const std::size_t len, const char pad, const bool padLeft) {
|
||||
// Fast-reject: Already above padded length
|
||||
if (str.length() >= len) {
|
||||
return str;
|
||||
}
|
||||
|
||||
std::stringstream ss;
|
||||
|
||||
// Pad left:
|
||||
if (padLeft) {
|
||||
for (std::size_t i = 0; i < len - str.size(); i++) {
|
||||
ss << pad;
|
||||
}
|
||||
ss << str;
|
||||
}
|
||||
// Pad right:
|
||||
else {
|
||||
ss << str;
|
||||
for (std::size_t i = 0; i < len - str.size(); i++) {
|
||||
ss << pad;
|
||||
}
|
||||
}
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string BitblocksToBytes(const std::vector<Block>& blocks) {
|
||||
std::stringstream ss;
|
||||
|
||||
for (const Block& block : blocks) {
|
||||
ss << block.ToByteString();
|
||||
}
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string BitblocksToString(const std::vector<Block>& blocks) {
|
||||
// Decode to bytes
|
||||
std::string text = BitblocksToBytes(blocks);
|
||||
|
||||
// Dümp excess nullbytes
|
||||
text.resize(strlen(text.data()));
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
std::vector<Block> ReadFileToBlocks(const std::string& filepath, std::size_t& bytes_read) {
|
||||
// Read file
|
||||
bytes_read = 0;
|
||||
|
||||
// "ate" specifies that the read-pointer is already at the end of the file
|
||||
// this allows to estimate the file size
|
||||
std::ifstream ifs(filepath, std::ios::binary | std::ios::ate);
|
||||
|
||||
if (!ifs.good()) {
|
||||
throw std::runtime_error("Unable to open ifilestream!");
|
||||
}
|
||||
|
||||
// Create our vector of blocks, and resorve a good guess
|
||||
// of memory
|
||||
std::vector<Block> blocks;
|
||||
blocks.reserve((ifs.tellg() / Block::BLOCK_SIZE) + 1);
|
||||
|
||||
// Move read head to the file beginning
|
||||
ifs.seekg(std::ios_base::beg);
|
||||
|
||||
// Whilst not reached eof, read into blocks
|
||||
while (!ifs.eof()) {
|
||||
// Create a new block, and zero it
|
||||
Block block;
|
||||
block.Reset();
|
||||
|
||||
// Read data into the block
|
||||
ifs.read((char*)(void*)block.Data(), Block::BLOCK_SIZE);
|
||||
const std::size_t n_bytes_read_block = ifs.gcount();
|
||||
bytes_read += n_bytes_read_block;
|
||||
|
||||
if (n_bytes_read_block > 0) {
|
||||
// Append the block to our vector
|
||||
blocks.emplace_back(block);
|
||||
}
|
||||
}
|
||||
|
||||
// Close the filehandle
|
||||
ifs.close();
|
||||
|
||||
return blocks;
|
||||
}
|
||||
|
||||
std::vector<Block> ReadFileToBlocks(const std::string& filepath) {
|
||||
std::size_t bytes_read_dummy; // Create a dumme for the parameter
|
||||
return ReadFileToBlocks(filepath, bytes_read_dummy);
|
||||
}
|
||||
|
||||
void WriteBlocksToFile(
|
||||
const std::string& filepath,
|
||||
const std::vector<Block>& blocks
|
||||
){
|
||||
|
||||
// Create outfile file handle
|
||||
std::ofstream ofs(filepath, std::ios::binary);
|
||||
|
||||
if (!ofs.good()) {
|
||||
throw std::runtime_error("Unable to open ofilestream!");
|
||||
}
|
||||
|
||||
// Write all the blocks
|
||||
for (const Block& block : blocks) {
|
||||
ofs.write((char*)(void*)block.Data(), Block::BLOCK_SIZE);
|
||||
}
|
||||
|
||||
// Close the filehandle
|
||||
ofs.close();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<Block> StringToBitblocks(const std::string& str) {
|
||||
|
||||
// Create our block vector, and reserve exactly
|
||||
// how many blocks are required to store this string
|
||||
const std::size_t num_blocks = (str.length() / Block::BLOCK_SIZE) + 1;
|
||||
std::vector<Block> blocks;
|
||||
blocks.reserve(num_blocks);
|
||||
|
||||
for (std::size_t i = 0; i < str.length(); i += Block::BLOCK_SIZE) {
|
||||
Block block;
|
||||
block.FromTextString(str.substr(i, Block::BLOCK_SIZE));
|
||||
|
||||
blocks.emplace_back(block);
|
||||
}
|
||||
|
||||
return blocks;
|
||||
}
|
||||
}
|
||||
|
883
GCryptLib/test/Block.cpp
Normal file
@ -0,0 +1,883 @@
|
||||
#include <GCrypt/Block.h>
|
||||
#include <GCrypt/Key.h>
|
||||
#include "Catch2.h"
|
||||
#include <cstdlib>
|
||||
#include <time.h>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace Leonetienne::GCrypt;
|
||||
|
||||
// Tests that writing and retrieving data from a block works
|
||||
TEST_CASE(__FILE__"/Write-read", "[Block]") {
|
||||
|
||||
// Setup
|
||||
Block block;
|
||||
|
||||
// Exercise
|
||||
for (std::size_t i = 0; i < 16; i++) {
|
||||
block.Get(i) = i * 1024;
|
||||
}
|
||||
|
||||
// Verify
|
||||
for (std::size_t i = 0; i < 16; i++) {
|
||||
REQUIRE(block.Get(i) == i * 1024);
|
||||
}
|
||||
}
|
||||
|
||||
// Tests that the copy constructor works
|
||||
TEST_CASE(__FILE__"/CCtor", "[Block]") {
|
||||
|
||||
// Setup
|
||||
Block block;
|
||||
for (std::size_t i = 0; i < 16; i++) {
|
||||
block.Get(i) = i * 1024;
|
||||
}
|
||||
|
||||
// Exercise
|
||||
Block block2(block);
|
||||
|
||||
// Verify
|
||||
for (std::size_t i = 0; i < 16; i++) {
|
||||
REQUIRE(block2.Get(i) == i * 1024);
|
||||
}
|
||||
}
|
||||
|
||||
// Tests that operator= works
|
||||
TEST_CASE(__FILE__"/operator=", "[Block]") {
|
||||
|
||||
// Setup
|
||||
Block block;
|
||||
for (std::size_t i = 0; i < 16; i++) {
|
||||
block.Get(i) = i * 1024;
|
||||
}
|
||||
|
||||
// Exercise
|
||||
Block block2;
|
||||
block2 = block;
|
||||
|
||||
// Verify
|
||||
for (std::size_t i = 0; i < 16; i++) {
|
||||
REQUIRE(block2.Get(i) == i * 1024);
|
||||
}
|
||||
}
|
||||
|
||||
// Tests that converting to, and from, binary strings works
|
||||
TEST_CASE(__FILE__"/BinaryStringConversion", "[Block]") {
|
||||
|
||||
// Setup
|
||||
srand(time(0));
|
||||
std::stringstream ss;
|
||||
|
||||
for (std::size_t i = 0; i < 512; i++) {
|
||||
ss << (rand()%2 == 0 ? '1' : '0');
|
||||
}
|
||||
|
||||
// Exercise
|
||||
Block block(ss.str());
|
||||
|
||||
// Verify
|
||||
REQUIRE(block.ToBinaryString() == ss.str());
|
||||
}
|
||||
|
||||
// Tests that converting to, and from, hexstrings works
|
||||
TEST_CASE(__FILE__"/HexStringConversion", "[Block]") {
|
||||
|
||||
// Setup
|
||||
srand(time(0));
|
||||
std::stringstream ss;
|
||||
|
||||
const std::string charset = "0123456789abcdef";
|
||||
for (std::size_t i = 0; i < 128; i++) {
|
||||
ss << charset[rand() % charset.length()];
|
||||
}
|
||||
|
||||
// Exercise
|
||||
Block block;
|
||||
block.FromHexString(ss.str());
|
||||
|
||||
// Verify
|
||||
REQUIRE(block.ToHexString() == ss.str());
|
||||
}
|
||||
|
||||
// Tests that converting to, and from, bytestrings works
|
||||
TEST_CASE(__FILE__"/ByteStringConversion", "[Block]") {
|
||||
|
||||
// Setup
|
||||
srand(time(0));
|
||||
std::stringstream ss;
|
||||
|
||||
for (std::size_t i = 0; i < 64; i++) {
|
||||
ss << (char)(rand() % 256);
|
||||
}
|
||||
|
||||
// Exercise
|
||||
Block block;
|
||||
block.FromByteString(ss.str());
|
||||
|
||||
// Verify
|
||||
REQUIRE(block.ToByteString() == ss.str());
|
||||
}
|
||||
|
||||
// Tests that converting to, and from, textstrings works
|
||||
TEST_CASE(__FILE__"/TextStringConversion", "[Block]") {
|
||||
|
||||
// Setup
|
||||
const std::string textstr = "Hello, World :3";
|
||||
|
||||
// Exercise
|
||||
Block block;
|
||||
block.FromTextString(textstr);
|
||||
|
||||
// Verify
|
||||
REQUIRE(block.ToTextString() == textstr);
|
||||
}
|
||||
|
||||
// Tests that operator* is the same as *=
|
||||
TEST_CASE(__FILE__"/operator*&=", "[Block]") {
|
||||
|
||||
// Setup
|
||||
Block block1;
|
||||
for (std::size_t i = 0; i < 16; i++) {
|
||||
block1.Get(i) = i * 1024;
|
||||
}
|
||||
|
||||
Block block2;
|
||||
for (std::size_t i = 0; i < 16; i++) {
|
||||
block2.Get(i) = i * 1024 * 2;
|
||||
}
|
||||
|
||||
// Exercise
|
||||
Block block3 = block1 * block2;
|
||||
block1 *= block2;
|
||||
|
||||
// Verify
|
||||
REQUIRE(block1 == block3);
|
||||
}
|
||||
|
||||
// Tests that operator^ (xor) works
|
||||
TEST_CASE(__FILE__"/xor", "[Block]") {
|
||||
|
||||
// Setup
|
||||
Block block;
|
||||
for (std::size_t i = 0; i < 16; i++) {
|
||||
block.Get(i) = i * 1024;
|
||||
}
|
||||
|
||||
Block xorRH;
|
||||
for (std::size_t i = 0; i < 16; i++) {
|
||||
xorRH.Get(i) = i * 5099;
|
||||
}
|
||||
|
||||
// Exercise
|
||||
Block result = block ^ xorRH;
|
||||
|
||||
Block manualResult;
|
||||
for (std::size_t i = 0; i < 16; i++) {
|
||||
manualResult.Get(i) = (i * 1024) ^ (i * 5099);
|
||||
}
|
||||
|
||||
// Verify
|
||||
REQUIRE(result == manualResult);
|
||||
}
|
||||
|
||||
// Tests that operator^ is the same as ^=
|
||||
TEST_CASE(__FILE__"/operator^&=", "[Block]") {
|
||||
|
||||
// Setup
|
||||
Block block1;
|
||||
for (std::size_t i = 0; i < 16; i++) {
|
||||
block1.Get(i) = i * 1024;
|
||||
}
|
||||
|
||||
Block block2;
|
||||
for (std::size_t i = 0; i < 16; i++) {
|
||||
block2.Get(i) = i * 5099 * 2;
|
||||
}
|
||||
|
||||
// Exercise
|
||||
Block block3 = block1 ^ block2;
|
||||
block1 ^= block2;
|
||||
|
||||
// Verify
|
||||
REQUIRE(block1 == block3);
|
||||
}
|
||||
|
||||
// Tests that operator+ (add) works
|
||||
TEST_CASE(__FILE__"/add", "[Block]") {
|
||||
|
||||
// Setup
|
||||
Block block;
|
||||
for (std::size_t i = 0; i < 16; i++) {
|
||||
block.Get(i) = i * 1024;
|
||||
}
|
||||
|
||||
Block addRH;
|
||||
for (std::size_t i = 0; i < 16; i++) {
|
||||
addRH.Get(i) = i * 5099;
|
||||
}
|
||||
|
||||
// Exercise
|
||||
Block result = block + addRH;
|
||||
|
||||
Block manualResult;
|
||||
for (std::size_t i = 0; i < 16; i++) {
|
||||
manualResult.Get(i) = (i * 1024) + (i * 5099);
|
||||
}
|
||||
|
||||
// Verify
|
||||
REQUIRE(result == manualResult);
|
||||
}
|
||||
|
||||
// Tests that operator+ is the same as +=
|
||||
TEST_CASE(__FILE__"/operator+&=", "[Block]") {
|
||||
|
||||
// Setup
|
||||
Block block1;
|
||||
for (std::size_t i = 0; i < 16; i++) {
|
||||
block1.Get(i) = i * 1024;
|
||||
}
|
||||
|
||||
Block block2;
|
||||
for (std::size_t i = 0; i < 16; i++) {
|
||||
block2.Get(i) = i * 5099 * 2;
|
||||
}
|
||||
|
||||
// Exercise
|
||||
Block block3 = block1 + block2;
|
||||
block1 += block2;
|
||||
|
||||
// Verify
|
||||
REQUIRE(block1 == block3);
|
||||
}
|
||||
|
||||
// Tests that operator- (subtract) works
|
||||
TEST_CASE(__FILE__"/subtract", "[Block]") {
|
||||
|
||||
// Setup
|
||||
Block block;
|
||||
for (std::size_t i = 0; i < 16; i++) {
|
||||
block.Get(i) = i * 1024;
|
||||
}
|
||||
|
||||
Block subRH;
|
||||
for (std::size_t i = 0; i < 16; i++) {
|
||||
subRH.Get(i) = i * 5099;
|
||||
}
|
||||
|
||||
// Exercise
|
||||
Block result = block - subRH;
|
||||
|
||||
Block manualResult;
|
||||
for (std::size_t i = 0; i < 16; i++) {
|
||||
manualResult.Get(i) = (i * 1024) - (i * 5099);
|
||||
}
|
||||
|
||||
// Verify
|
||||
REQUIRE(result == manualResult);
|
||||
}
|
||||
|
||||
// Tests that operator- is the same as -=
|
||||
TEST_CASE(__FILE__"/operator-&=", "[Block]") {
|
||||
|
||||
// Setup
|
||||
Block block1;
|
||||
for (std::size_t i = 0; i < 16; i++) {
|
||||
block1.Get(i) = i * 1024;
|
||||
}
|
||||
|
||||
Block block2;
|
||||
for (std::size_t i = 0; i < 16; i++) {
|
||||
block2.Get(i) = i * 5099 * 2;
|
||||
}
|
||||
|
||||
// Exercise
|
||||
Block block3 = block1 - block2;
|
||||
block1 -= block2;
|
||||
|
||||
// Verify
|
||||
REQUIRE(block1 == block3);
|
||||
}
|
||||
|
||||
// Tests that subtraction undoes addition, and vica versa
|
||||
TEST_CASE(__FILE__"/subtraction-undoes-addition", "[Block]") {
|
||||
// Setup
|
||||
const Block a = Key::FromPassword("Halleluja");
|
||||
const Block b = Key::FromPassword("Ananas");
|
||||
|
||||
// Exercise
|
||||
const Block a_plus_b = a + b;
|
||||
const Block a_plus_b_minus_b = a_plus_b - b;
|
||||
|
||||
// Verify
|
||||
REQUIRE(a == a_plus_b_minus_b);
|
||||
}
|
||||
|
||||
// Tests that operator== works correctly
|
||||
TEST_CASE(__FILE__"/operator==", "[Block]") {
|
||||
|
||||
// Setup
|
||||
Block block;
|
||||
for (std::size_t i = 0; i < 16; i++) {
|
||||
block.Get(i) = i * 1024;
|
||||
}
|
||||
|
||||
SECTION("Expected true") {
|
||||
Block sameBlock;
|
||||
for (std::size_t i = 0; i < 16; i++) {
|
||||
sameBlock.Get(i) = i * 1024;
|
||||
}
|
||||
|
||||
REQUIRE(block == sameBlock);
|
||||
}
|
||||
|
||||
SECTION("Expected false") {
|
||||
Block otherBlock;
|
||||
for (std::size_t i = 0; i < 16; i++) {
|
||||
otherBlock.Get(i) = i * 1024 + 1;
|
||||
}
|
||||
|
||||
REQUIRE_FALSE(block == otherBlock);
|
||||
}
|
||||
}
|
||||
|
||||
// Tests that operator!= works correctly
|
||||
TEST_CASE(__FILE__"/operator!=", "[Block]") {
|
||||
|
||||
// Setup
|
||||
Block block;
|
||||
for (std::size_t i = 0; i < 16; i++) {
|
||||
block.Get(i) = i * 1024;
|
||||
}
|
||||
|
||||
SECTION("Expected false") {
|
||||
Block sameBlock;
|
||||
for (std::size_t i = 0; i < 16; i++) {
|
||||
sameBlock.Get(i) = i * 1024;
|
||||
}
|
||||
|
||||
REQUIRE_FALSE(block != sameBlock);
|
||||
}
|
||||
|
||||
SECTION("Expected true") {
|
||||
Block otherBlock;
|
||||
for (std::size_t i = 0; i < 16; i++) {
|
||||
otherBlock.Get(i) = i * 1024 + 1;
|
||||
}
|
||||
|
||||
REQUIRE(block != otherBlock);
|
||||
}
|
||||
}
|
||||
|
||||
// Tests that getting the data via the matrix accessor works
|
||||
TEST_CASE(__FILE__"/matrix-accessor", "[Block]") {
|
||||
|
||||
// Setup
|
||||
Block block;
|
||||
for (std::size_t i = 0; i < 16; i++) {
|
||||
block.Get(i) = i;
|
||||
}
|
||||
|
||||
// Exercise
|
||||
REQUIRE(block.Get(0,0) == 0);
|
||||
REQUIRE(block.Get(1,0) == 1);
|
||||
REQUIRE(block.Get(2,0) == 2);
|
||||
REQUIRE(block.Get(3,0) == 3);
|
||||
REQUIRE(block.Get(0,1) == 4);
|
||||
REQUIRE(block.Get(1,1) == 5);
|
||||
REQUIRE(block.Get(2,1) == 6);
|
||||
REQUIRE(block.Get(3,1) == 7);
|
||||
REQUIRE(block.Get(0,2) == 8);
|
||||
REQUIRE(block.Get(1,2) == 9);
|
||||
REQUIRE(block.Get(2,2) == 10);
|
||||
REQUIRE(block.Get(3,2) == 11);
|
||||
REQUIRE(block.Get(0,3) == 12);
|
||||
REQUIRE(block.Get(1,3) == 13);
|
||||
REQUIRE(block.Get(2,3) == 14);
|
||||
REQUIRE(block.Get(3,3) == 15);
|
||||
}
|
||||
|
||||
// Tests that the reset method works
|
||||
TEST_CASE(__FILE__"/reset", "[Block]") {
|
||||
|
||||
// Setup
|
||||
Block block;
|
||||
for (std::size_t i = 0; i < 16; i++) {
|
||||
block.Get(i) = i + 33;
|
||||
}
|
||||
|
||||
// Exercise
|
||||
block.Reset();
|
||||
|
||||
// Verify
|
||||
for (std::size_t i = 0; i < 16; i++) {
|
||||
REQUIRE(block[i] == 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Tests that shift rows up works
|
||||
TEST_CASE(__FILE__"/shift-rows-up", "[Block]") {
|
||||
|
||||
// Setup
|
||||
Block a;
|
||||
a.Get(0,0) = 10; a.Get(0,1) = 11; a.Get(0,2) = 12; a.Get(0,3) = 13;
|
||||
a.Get(1,0) = 20; a.Get(1,1) = 21; a.Get(1,2) = 22; a.Get(1,3) = 23;
|
||||
a.Get(2,0) = 30; a.Get(2,1) = 31; a.Get(2,2) = 32; a.Get(2,3) = 33;
|
||||
a.Get(3,0) = 40; a.Get(3,1) = 41; a.Get(3,2) = 42; a.Get(3,3) = 43;
|
||||
|
||||
Block e; /* expected */
|
||||
e.Get(0,0) = 20; e.Get(0,1) = 21; e.Get(0,2) = 22; e.Get(0,3) = 23;
|
||||
e.Get(1,0) = 30; e.Get(1,1) = 31; e.Get(1,2) = 32; e.Get(1,3) = 33;
|
||||
e.Get(2,0) = 40; e.Get(2,1) = 41; e.Get(2,2) = 42; e.Get(2,3) = 43;
|
||||
e.Get(3,0) = 10; e.Get(3,1) = 11; e.Get(3,2) = 12; e.Get(3,3) = 13;
|
||||
|
||||
// Exercise
|
||||
a.ShiftRowsUpInplace();
|
||||
|
||||
// Verify
|
||||
REQUIRE(a == e);
|
||||
}
|
||||
|
||||
// Tests that ShiftRowsUpInplace() does the exact same thing as ShiftRowsUp()
|
||||
TEST_CASE(__FILE__"/shift-rows-up-same-as-inplace", "[Block]") {
|
||||
// Setup
|
||||
Block a = Key::FromPassword("Halleluja");
|
||||
const Block initial_a = a;
|
||||
|
||||
// Exercise and verify
|
||||
a.ShiftRowsUpInplace();
|
||||
REQUIRE(a == initial_a.ShiftRowsUp());
|
||||
}
|
||||
|
||||
// Tests that shift rows down works
|
||||
TEST_CASE(__FILE__"/shift-rows-down", "[Block]") {
|
||||
|
||||
// Setup
|
||||
Block a;
|
||||
a.Get(0,0) = 10; a.Get(0,1) = 11; a.Get(0,2) = 12; a.Get(0,3) = 13;
|
||||
a.Get(1,0) = 20; a.Get(1,1) = 21; a.Get(1,2) = 22; a.Get(1,3) = 23;
|
||||
a.Get(2,0) = 30; a.Get(2,1) = 31; a.Get(2,2) = 32; a.Get(2,3) = 33;
|
||||
a.Get(3,0) = 40; a.Get(3,1) = 41; a.Get(3,2) = 42; a.Get(3,3) = 43;
|
||||
|
||||
Block e; /* expected */
|
||||
e.Get(0,0) = 40; e.Get(0,1) = 41; e.Get(0,2) = 42; e.Get(0,3) = 43;
|
||||
e.Get(1,0) = 10; e.Get(1,1) = 11; e.Get(1,2) = 12; e.Get(1,3) = 13;
|
||||
e.Get(2,0) = 20; e.Get(2,1) = 21; e.Get(2,2) = 22; e.Get(2,3) = 23;
|
||||
e.Get(3,0) = 30; e.Get(3,1) = 31; e.Get(3,2) = 32; e.Get(3,3) = 33;
|
||||
|
||||
// Exercise
|
||||
a.ShiftRowsDownInplace();
|
||||
|
||||
// Verify
|
||||
REQUIRE(a == e);
|
||||
}
|
||||
|
||||
// Tests that ShiftRowsDownInplace() does the exact same thing as ShiftRowsDown()
|
||||
TEST_CASE(__FILE__"/shift-rows-down-same-as-inplace", "[Block]") {
|
||||
// Setup
|
||||
Block a = Key::FromPassword("Halleluja");
|
||||
const Block initial_a = a;
|
||||
|
||||
// Exercise and verify
|
||||
a.ShiftRowsDownInplace();
|
||||
REQUIRE(a == initial_a.ShiftRowsDown());
|
||||
}
|
||||
|
||||
// Tests that shift columns left works
|
||||
TEST_CASE(__FILE__"/shift-columns-left", "[Block]") {
|
||||
|
||||
// Setup
|
||||
Block a;
|
||||
a.Get(0,0) = 10; a.Get(0,1) = 11; a.Get(0,2) = 12; a.Get(0,3) = 13;
|
||||
a.Get(1,0) = 20; a.Get(1,1) = 21; a.Get(1,2) = 22; a.Get(1,3) = 23;
|
||||
a.Get(2,0) = 30; a.Get(2,1) = 31; a.Get(2,2) = 32; a.Get(2,3) = 33;
|
||||
a.Get(3,0) = 40; a.Get(3,1) = 41; a.Get(3,2) = 42; a.Get(3,3) = 43;
|
||||
|
||||
Block e; /* expected */
|
||||
e.Get(0,0) = 11; e.Get(0,1) = 12; e.Get(0,2) = 13; e.Get(0,3) = 10;
|
||||
e.Get(1,0) = 21; e.Get(1,1) = 22; e.Get(1,2) = 23; e.Get(1,3) = 20;
|
||||
e.Get(2,0) = 31; e.Get(2,1) = 32; e.Get(2,2) = 33; e.Get(2,3) = 30;
|
||||
e.Get(3,0) = 41; e.Get(3,1) = 42; e.Get(3,2) = 43; e.Get(3,3) = 40;
|
||||
|
||||
// Exercise
|
||||
a.ShiftColumnsLeftInplace();
|
||||
|
||||
// Verify
|
||||
REQUIRE(a == e);
|
||||
}
|
||||
|
||||
// Tests that ShiftColumnsLeftInplace()() does the exact same thing as ShiftColumnsLeft()
|
||||
TEST_CASE(__FILE__"/shift-columns-left-same-as-inplace", "[Block]") {
|
||||
// Setup
|
||||
Block a = Key::FromPassword("Halleluja");
|
||||
const Block initial_a = a;
|
||||
|
||||
// Exercise and verify
|
||||
a.ShiftColumnsLeftInplace();
|
||||
REQUIRE(a == initial_a.ShiftColumnsLeft());
|
||||
}
|
||||
|
||||
// Tests that shift columns right works
|
||||
TEST_CASE(__FILE__"/shift-columns-right", "[Block]") {
|
||||
|
||||
// Setup
|
||||
Block a;
|
||||
a.Get(0,0) = 10; a.Get(0,1) = 11; a.Get(0,2) = 12; a.Get(0,3) = 13;
|
||||
a.Get(1,0) = 20; a.Get(1,1) = 21; a.Get(1,2) = 22; a.Get(1,3) = 23;
|
||||
a.Get(2,0) = 30; a.Get(2,1) = 31; a.Get(2,2) = 32; a.Get(2,3) = 33;
|
||||
a.Get(3,0) = 40; a.Get(3,1) = 41; a.Get(3,2) = 42; a.Get(3,3) = 43;
|
||||
|
||||
Block e; /* expected */
|
||||
e.Get(0,0) = 13; e.Get(0,1) = 10; e.Get(0,2) = 11; e.Get(0,3) = 12;
|
||||
e.Get(1,0) = 23; e.Get(1,1) = 20; e.Get(1,2) = 21; e.Get(1,3) = 22;
|
||||
e.Get(2,0) = 33; e.Get(2,1) = 30; e.Get(2,2) = 31; e.Get(2,3) = 32;
|
||||
e.Get(3,0) = 43; e.Get(3,1) = 40; e.Get(3,2) = 41; e.Get(3,3) = 42;
|
||||
|
||||
// Exercise
|
||||
a.ShiftColumnsRightInplace();
|
||||
|
||||
// Verify
|
||||
REQUIRE(a == e);
|
||||
}
|
||||
|
||||
// Tests that ShiftColumnsRightInplace()() does the exact same thing as ShiftColumnsRight()
|
||||
TEST_CASE(__FILE__"/shift-columns-right-same-as-inplace", "[Block]") {
|
||||
// Setup
|
||||
Block a = Key::FromPassword("Halleluja");
|
||||
const Block initial_a = a;
|
||||
|
||||
// Exercise and verify
|
||||
a.ShiftColumnsRightInplace();
|
||||
REQUIRE(a == initial_a.ShiftColumnsRight());
|
||||
}
|
||||
|
||||
// Tests that shift cells left works
|
||||
TEST_CASE(__FILE__"/shift-cells-left", "[Block]") {
|
||||
|
||||
// Setup
|
||||
Block a;
|
||||
for (std::size_t i = 0; i < 16; i++) {
|
||||
a.Get(i) = i;
|
||||
}
|
||||
|
||||
Block expected;
|
||||
for (std::size_t i = 0; i < 15; i++) {
|
||||
expected.Get(i) = i+1;
|
||||
}
|
||||
expected.Get(15) = 0;
|
||||
|
||||
// Exercise
|
||||
a.ShiftCellsLeftInplace();
|
||||
|
||||
// Verify
|
||||
REQUIRE(a == expected);
|
||||
}
|
||||
|
||||
// Tests that ShiftCellsLeftInplace()() does the exact same thing as ShiftCellsLeft()
|
||||
TEST_CASE(__FILE__"/shift-cells-left-same-as-inplace", "[Block]") {
|
||||
// Setup
|
||||
Block a = Key::FromPassword("Halleluja");
|
||||
const Block initial_a = a;
|
||||
|
||||
// Exercise and verify
|
||||
a.ShiftCellsLeftInplace();
|
||||
REQUIRE(a == initial_a.ShiftCellsLeft());
|
||||
}
|
||||
|
||||
// Tests that shift cells right works
|
||||
TEST_CASE(__FILE__"/shift-cells-right", "[Block]") {
|
||||
|
||||
// Setup
|
||||
Block a;
|
||||
for (std::size_t i = 0; i < 16; i++) {
|
||||
a.Get(i) = i;
|
||||
}
|
||||
|
||||
Block expected;
|
||||
for (std::size_t i = 1; i < 16; i++) {
|
||||
expected.Get(i) = i-1;
|
||||
}
|
||||
expected.Get(0) = 15;
|
||||
|
||||
// Exercise
|
||||
a.ShiftCellsRightInplace();
|
||||
|
||||
// Verify
|
||||
REQUIRE(a == expected);
|
||||
}
|
||||
|
||||
// Tests that ShiftCellsRightInplace()() does the exact same thing as ShiftCellsRight()
|
||||
TEST_CASE(__FILE__"/shift-cells-right-same-as-inplace", "[Block]") {
|
||||
// Setup
|
||||
Block a = Key::FromPassword("Halleluja");
|
||||
const Block initial_a = a;
|
||||
|
||||
// Exercise and verify
|
||||
a.ShiftCellsRightInplace();
|
||||
REQUIRE(a == initial_a.ShiftCellsRight());
|
||||
}
|
||||
|
||||
// Tests that shifting down undoes shifting up, and vica versa
|
||||
TEST_CASE(__FILE__"/shift-down-undoes-shift-up", "[Block]") {
|
||||
// Setup
|
||||
Block a = Key::FromPassword("Halleluja");
|
||||
const Block initial_a = a;
|
||||
|
||||
// Exercise
|
||||
a.ShiftRowsUpInplace();
|
||||
a.ShiftRowsDownInplace();
|
||||
|
||||
// Verify
|
||||
REQUIRE(a == initial_a);
|
||||
}
|
||||
|
||||
// Tests that shifting left undoes shifting right, and vica versa
|
||||
TEST_CASE(__FILE__"/shift-left-undoes-shift-right", "[Block]") {
|
||||
// Setup
|
||||
Block a = Key::FromPassword("Halleluja");
|
||||
const Block initial_a = a;
|
||||
|
||||
// Exercise
|
||||
a.ShiftColumnsRightInplace();
|
||||
a.ShiftColumnsLeftInplace();
|
||||
|
||||
// Verify
|
||||
REQUIRE(a == initial_a);
|
||||
}
|
||||
|
||||
// Tests that shifting cells left undoes shifting cells right, and vica versa
|
||||
TEST_CASE(__FILE__"/cellshift-left-undoes-cellshift-right", "[Block]") {
|
||||
// Setup
|
||||
Block a = Key::FromPassword("Halleluja");
|
||||
const Block initial_a = a;
|
||||
|
||||
// Exercise
|
||||
a.ShiftCellsRightInplace();
|
||||
a.ShiftCellsLeftInplace();
|
||||
|
||||
// Verify
|
||||
REQUIRE(a == initial_a);
|
||||
}
|
||||
|
||||
// Tests that multiple, combined shifts and additions can be undone
|
||||
TEST_CASE(__FILE__"/multiple-combined-shifts-and-additions-can-be-undone", "[Block]") {
|
||||
// Setup
|
||||
Block a = Key::FromPassword("Halleluja");
|
||||
Block key = Key::FromPassword("Papaya");
|
||||
|
||||
const Block initial_a = a;
|
||||
|
||||
// Exercise (mix-up)
|
||||
for (std::size_t i = 0; i < 64; i++) {
|
||||
a.ShiftRowsUpInplace();
|
||||
a.ShiftColumnsLeftInplace();
|
||||
a += key;
|
||||
a.ShiftCellsRightInplace();
|
||||
}
|
||||
|
||||
// Exercise (un-mix)
|
||||
for (std::size_t i = 0; i < 64; i++) {
|
||||
a.ShiftCellsLeftInplace();
|
||||
a -= key;
|
||||
a.ShiftColumnsRightInplace();
|
||||
a.ShiftRowsDownInplace();
|
||||
}
|
||||
|
||||
// Verify
|
||||
REQUIRE(a == initial_a);
|
||||
}
|
||||
|
||||
// Tests that the get-bit method works
|
||||
TEST_CASE(__FILE__"/get-bit", "[Block]") {
|
||||
// Setup
|
||||
Block a = Key::FromPassword("Halleluja");
|
||||
|
||||
// Exercise
|
||||
std::stringstream ss;
|
||||
for (std::size_t i = 0; i < 512; i++) {
|
||||
ss << a.GetBit(i);
|
||||
}
|
||||
|
||||
// Verify
|
||||
REQUIRE(ss.str() == a.ToBinaryString());
|
||||
}
|
||||
|
||||
// Tests that the set-bit to-false method works
|
||||
TEST_CASE(__FILE__"/set-bit-to-false", "[Block]") {
|
||||
// Setup
|
||||
Block a = Key::FromPassword("Halleluja");
|
||||
|
||||
// Exercise
|
||||
a.SetBit(5, 0);
|
||||
a.SetBit(15, 0);
|
||||
a.SetBit(105, 0);
|
||||
a.SetBit(205, 0);
|
||||
|
||||
// Verify
|
||||
REQUIRE(a.GetBit(5) == false);
|
||||
REQUIRE(a.GetBit(15) == false);
|
||||
REQUIRE(a.GetBit(105) == false);
|
||||
REQUIRE(a.GetBit(205) == false);
|
||||
}
|
||||
|
||||
// Tests that the set-bit to-true method works
|
||||
TEST_CASE(__FILE__"/set-bit-to-true", "[Block]") {
|
||||
// Setup
|
||||
Block a = Key::FromPassword("Halleluja");
|
||||
|
||||
// Exercise
|
||||
a.SetBit(5, 1);
|
||||
a.SetBit(15, 1);
|
||||
a.SetBit(105, 1);
|
||||
a.SetBit(205, 1);
|
||||
|
||||
// Verify
|
||||
REQUIRE(a.GetBit(5) == true);
|
||||
REQUIRE(a.GetBit(15) == true);
|
||||
REQUIRE(a.GetBit(105) == true);
|
||||
REQUIRE(a.GetBit(205) == true);
|
||||
}
|
||||
|
||||
// Tests that the flip-bit method works
|
||||
TEST_CASE(__FILE__"/flip-bit", "[Block]") {
|
||||
// Setup
|
||||
Block a = Key::FromPassword("Halleluja");
|
||||
|
||||
std::string compare = a.ToBinaryString();
|
||||
compare[5] = compare[5] == '1' ? '0' : '1';
|
||||
compare[15] = compare[15] == '1' ? '0' : '1';
|
||||
compare[105] = compare[105] == '1' ? '0' : '1';
|
||||
compare[205] = compare[205] == '1' ? '0' : '1';
|
||||
|
||||
// Exercise
|
||||
a.FlipBit(5);
|
||||
a.FlipBit(15);
|
||||
a.FlipBit(105);
|
||||
a.FlipBit(205);
|
||||
|
||||
// Verify
|
||||
REQUIRE(a.ToBinaryString() == compare);
|
||||
}
|
||||
|
||||
// Tests that bitshifts (to the left) work
|
||||
TEST_CASE(__FILE__"/bitshift-left", "[Block]") {
|
||||
|
||||
// Setup
|
||||
srand(time(0));
|
||||
std::stringstream ss;
|
||||
|
||||
for (std::size_t i = 0; i < 512; i++) {
|
||||
ss << (rand()%2 == 0 ? '1' : '0');
|
||||
}
|
||||
const std::string originalBits = ss.str();
|
||||
ss.str("");
|
||||
|
||||
// Shift string manually
|
||||
std::string shiftedBits = originalBits;
|
||||
shiftedBits.erase(0, 1);
|
||||
ss << shiftedBits << originalBits[0];
|
||||
shiftedBits = ss.str();
|
||||
|
||||
// Create block of original bits
|
||||
Block block(originalBits);
|
||||
|
||||
// Exercise
|
||||
block = block.ShiftBitsLeft();
|
||||
|
||||
// Verify
|
||||
REQUIRE(block.ToBinaryString().length() == shiftedBits.length());
|
||||
REQUIRE(block.ToBinaryString() == shiftedBits);
|
||||
}
|
||||
|
||||
// Tests that inplace-bitshifts to the left do the exact same as copy bitshifts
|
||||
TEST_CASE(__FILE__"/bitshift-left-inplace", "[Block]") {
|
||||
|
||||
// Setup
|
||||
srand(time(0));
|
||||
std::stringstream ss;
|
||||
|
||||
for (std::size_t i = 0; i < 512; i++) {
|
||||
ss << (rand()%2 == 0 ? '1' : '0');
|
||||
}
|
||||
Block a(ss.str());
|
||||
|
||||
// Exercise
|
||||
Block b = a.ShiftBitsLeft();
|
||||
a.ShiftBitsLeftInplace();
|
||||
|
||||
// Verify
|
||||
REQUIRE(a == b);
|
||||
}
|
||||
|
||||
// Tests that bitshifts (to the right) work
|
||||
TEST_CASE(__FILE__"/bitshift-right", "[Block]") {
|
||||
|
||||
// Setup
|
||||
srand(time(0));
|
||||
std::stringstream ss;
|
||||
|
||||
for (std::size_t i = 0; i < 512; i++) {
|
||||
ss << (rand()%2 == 0 ? '1' : '0');
|
||||
}
|
||||
const std::string originalBits = ss.str();
|
||||
ss.str("");
|
||||
|
||||
// Shift string manually
|
||||
std::string shiftedBits = originalBits;
|
||||
shiftedBits.erase(shiftedBits.length() - 1);
|
||||
ss << originalBits[originalBits.length()-1] << shiftedBits;
|
||||
shiftedBits = ss.str();
|
||||
|
||||
// Create block of original bits
|
||||
Block block(originalBits);
|
||||
|
||||
// Exercise
|
||||
block = block.ShiftBitsRight();
|
||||
|
||||
// Verify
|
||||
REQUIRE(block.ToBinaryString().length() == shiftedBits.length());
|
||||
REQUIRE(block.ToBinaryString() == shiftedBits);
|
||||
}
|
||||
|
||||
// Tests that inplace-bitshifts to the right do the exact same as copy bitshifts
|
||||
TEST_CASE(__FILE__"/bitshift-right-inplace", "[Block]") {
|
||||
|
||||
// Setup
|
||||
srand(time(0));
|
||||
std::stringstream ss;
|
||||
|
||||
for (std::size_t i = 0; i < 512; i++) {
|
||||
ss << (rand()%2 == 0 ? '1' : '0');
|
||||
}
|
||||
Block a(ss.str());
|
||||
|
||||
// Exercise
|
||||
Block b = a.ShiftBitsRight();
|
||||
a.ShiftBitsRightInplace();
|
||||
|
||||
// Verify
|
||||
REQUIRE(a == b);
|
||||
}
|
||||
|
||||
// Tests that bitshifting undoes itself
|
||||
TEST_CASE(__FILE__"/bitshifting-undoes-itself", "[Block]") {
|
||||
|
||||
// Setup
|
||||
Block a = Key::FromPassword("Halleluja");
|
||||
|
||||
const Block initial_a = a;
|
||||
|
||||
// Exercise (mix-up)
|
||||
for (std::size_t i = 0; i < 100; i++) {
|
||||
a.ShiftBitsLeftInplace();
|
||||
}
|
||||
|
||||
// Exercise (un-mix)
|
||||
for (std::size_t i = 0; i < 100; i++) {
|
||||
a.ShiftBitsRightInplace();
|
||||
}
|
||||
|
||||
// Verify
|
||||
REQUIRE(a == initial_a);
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include <GCrypt/Cipher.h>
|
||||
#include <GCrypt/GWrapper.h>
|
||||
#include <GCrypt/Util.h>
|
||||
#include "Catch2.h"
|
||||
|
||||
@ -7,14 +7,13 @@ using namespace Leonetienne::GCrypt;
|
||||
// THESE TESTS ASSUME BLOCK_SIZE == 512
|
||||
|
||||
// Tests that encrypting a message of exactly BLOCK_SIZE yields the exact message back
|
||||
TEST_CASE(__FILE__"/SingleBlock_NoPadding", "[Encryption/Decryption consistency]") {
|
||||
TEST_CASE(__FILE__"/SingleBlock", "[Encryption/Decryption consistency]") {
|
||||
|
||||
// Instanciate our cipher and supply a key
|
||||
const Block key = PasswordToKey("1234");
|
||||
const Cipher cipher(key);
|
||||
const Key key = Key::FromPassword("1234");
|
||||
|
||||
// Recode the ascii-string to bits
|
||||
const Flexblock cleartext_bits =
|
||||
const std::string cleartext_bits =
|
||||
"1011110011010110000010110001111000111010111101001010100100011101"
|
||||
"0101110101010010100000110100001000011000111010001001110101111111"
|
||||
"1110110101100101110001010101011110001010000010111110011011010111"
|
||||
@ -24,199 +23,65 @@ TEST_CASE(__FILE__"/SingleBlock_NoPadding", "[Encryption/Decryption consistency]
|
||||
"1101100100000100010000001011100010010001101111100100101100010001"
|
||||
"0000011110010110111010110110111110011110011010001100100111110101";
|
||||
|
||||
std::vector<Block> cleartext_blocks({Block(cleartext_bits)});
|
||||
|
||||
// Encrypt our cleartext bits
|
||||
const Flexblock ciphertext_bits = cipher.Encipher(cleartext_bits);
|
||||
const std::vector<Block> ciphertext_blocks = GWrapper::CipherBlocks(cleartext_blocks, key, GCipher::DIRECTION::ENCIPHER);
|
||||
|
||||
// Decipher it again
|
||||
const Flexblock decryptedBits = cipher.Decipher(ciphertext_bits);
|
||||
const std::vector<Block> decrypted_blocks = GWrapper::CipherBlocks(ciphertext_blocks, key, GCipher::DIRECTION::DECIPHER);
|
||||
|
||||
// Assert that the decrypted text equals the plaintext
|
||||
REQUIRE(
|
||||
cleartext_bits ==
|
||||
decryptedBits
|
||||
cleartext_blocks ==
|
||||
decrypted_blocks
|
||||
);
|
||||
}
|
||||
|
||||
// Tests that encrypting a message of less than BLOCK_SIZE yields the exact message plus zero-padding back
|
||||
TEST_CASE(__FILE__"/SingleBlock_Padding", "[Encryption/Decryption consistency]") {
|
||||
|
||||
// Instanciate our cipher and supply a key
|
||||
const Block key = PasswordToKey("1234");
|
||||
const Cipher cipher(key);
|
||||
|
||||
// Recode the ascii-string to bits
|
||||
const Flexblock cleartext_bits =
|
||||
"1011110011010110000010110001111000111010111101001010100100011101"
|
||||
"0101110101010010100000110100001000011000111010001001110101111111"
|
||||
"1110110101100101110001010101011110001010000010111110011011010111"
|
||||
"1100110100111000000011100101010100110010001110010011000010111001"
|
||||
"0000010000010000011001111010011110111001000000000110101000110001"
|
||||
"0110111110110110100000010100000011010001000011100100111001001011"
|
||||
"1101100100000100";
|
||||
|
||||
const Flexblock cleartext_bits_EXPECTED_RESULT =
|
||||
"1011110011010110000010110001111000111010111101001010100100011101"
|
||||
"0101110101010010100000110100001000011000111010001001110101111111"
|
||||
"1110110101100101110001010101011110001010000010111110011011010111"
|
||||
"1100110100111000000011100101010100110010001110010011000010111001"
|
||||
"0000010000010000011001111010011110111001000000000110101000110001"
|
||||
"0110111110110110100000010100000011010001000011100100111001001011"
|
||||
"1101100100000100000000000000000000000000000000000000000000000000"
|
||||
"0000000000000000000000000000000000000000000000000000000000000000";
|
||||
|
||||
// Encrypt our cleartext bits
|
||||
const Flexblock ciphertext_bits = cipher.Encipher(cleartext_bits);
|
||||
|
||||
// Decipher it again
|
||||
const Flexblock decryptedBits = cipher.Decipher(ciphertext_bits);
|
||||
|
||||
// Assert that the decrypted text equals the plaintext
|
||||
REQUIRE(
|
||||
cleartext_bits_EXPECTED_RESULT ==
|
||||
decryptedBits
|
||||
);
|
||||
}
|
||||
|
||||
// Tests that a decrypted ciphertext equals its plaintrext version, using a cleartext that requires A LOT of blocks
|
||||
// Tests that a decrypted ciphertext equals its plaintext version, using a cleartext that requires A LOT of blocks
|
||||
TEST_CASE(__FILE__"MultiBlock_NoPadding/", "[Encryption/Decryption consistency]") {
|
||||
|
||||
// Instanciate our cipher and supply a key
|
||||
const Block key = PasswordToKey("1234");
|
||||
const Cipher cipher(key);
|
||||
const Key key = Key::FromPassword("1234");
|
||||
|
||||
// Recode the ascii-string to bits
|
||||
const Flexblock cleartext_bits =
|
||||
"1011110011010110000010110001111000111010111101001010100100011101"
|
||||
"0101110101010010100000110100001000011000111010001001110101111111"
|
||||
"1110110101100101110001010101011110001010000010111110011011010111"
|
||||
"1100110100111000000011100101010100110010001110010011000010111001"
|
||||
"0000010000010000011001111010011110111001000000000110101000110001"
|
||||
"0110111110110110100000010100000011010001000011100100111001001011"
|
||||
"1101100100000100010000001011100010010001101111100100101100010001"
|
||||
"0000011110010110111010110110111110011110011010001100100111110101"
|
||||
"1000010010000000000100101011110001000101101101100000010011111011"
|
||||
"1011111010110100100111100111110011100001111101111110000110001100"
|
||||
"0001000111000111101110000111011011101010100010100101100111010100"
|
||||
"0101111110110010110000111111011001101110101101100100100011000100"
|
||||
"1000110010101001000100001001101000011111101011111100100000100101"
|
||||
"1100001100111001011111001101000111011101011101000110010110110110"
|
||||
"0111001010011010010000010110000110010101101100101110111100100011"
|
||||
"0010111110011100010100000101100101110101101011110100100000110110"
|
||||
"1001101110101001001111111000010100011100100000101000111101101111"
|
||||
"0101111011110001101010111010001000111010101111001101100100100100"
|
||||
"1110110111001100011010110000101000011001011100101100111101110000"
|
||||
"1010101111011110000111011011011110000111010110110111111010101010"
|
||||
"0111100101111001010111101000001010100000111010111100111011111001"
|
||||
"0110111000000110100011011100101101010101101000010010011111100100"
|
||||
"0010111000001011101110000110010011101001111010100111110111110101"
|
||||
"1110111000000000101011000100101010000110110111101010011001111010"
|
||||
"1101011110001110000011010111001100001100101000000101000101000010"
|
||||
"0101000011011111010010110010000010101100001110011000110111110111"
|
||||
"1110010101011110111001100010110101101011100111100011101010001011"
|
||||
"0101110010100110101100111100010000111101111100000111000110110110"
|
||||
"1001100111000000011010100000011101011000010010011010001011110000"
|
||||
"1100100111111001001000011100110000011110001100000000010000001001"
|
||||
"1110000000110010000010011010100011011011000000000111110000110111"
|
||||
"0101110011001101010110010100011001110110000110010001100110011111";
|
||||
std::stringstream ss;
|
||||
const std::string charset = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
|
||||
// Read 10 blocks worth of characters
|
||||
srand(time(0));
|
||||
for (std::size_t i = 0; i < 512*10; i++) {
|
||||
ss << charset[rand() % charset.length()];
|
||||
}
|
||||
const std::string cleartext_str = ss.str();
|
||||
|
||||
std::vector<Block> cleartext_blocks = StringToBitblocks(cleartext_str);
|
||||
|
||||
// Encrypt our cleartext bits
|
||||
const Flexblock ciphertext_bits = cipher.Encipher(cleartext_bits);
|
||||
std::vector<Block> ciphertext_blocks;
|
||||
ciphertext_blocks.reserve(cleartext_blocks.size());
|
||||
{
|
||||
GCipher cipher(key, GCipher::DIRECTION::ENCIPHER);
|
||||
|
||||
for (const Block& clearBlock : cleartext_blocks) {
|
||||
ciphertext_blocks.emplace_back(cipher.Digest(clearBlock));
|
||||
}
|
||||
}
|
||||
|
||||
// Decipher it again
|
||||
const Flexblock decryptedBits = cipher.Decipher(ciphertext_bits);
|
||||
std::vector<Block> deciphered_blocks;
|
||||
deciphered_blocks.reserve(ciphertext_blocks.size());
|
||||
{
|
||||
GCipher cipher(key, GCipher::DIRECTION::DECIPHER);
|
||||
|
||||
for (const Block& ciphBlock : ciphertext_blocks) {
|
||||
deciphered_blocks.emplace_back(cipher.Digest(ciphBlock));
|
||||
}
|
||||
}
|
||||
|
||||
// Assert that the decrypted text equals the plaintext
|
||||
REQUIRE(
|
||||
cleartext_bits ==
|
||||
decryptedBits
|
||||
);
|
||||
}
|
||||
|
||||
// Tests that a decrypted ciphertext equals its plaintrext version, using a cleartext that requires A LOT of blocks
|
||||
TEST_CASE(__FILE__"MultiBlock_Padding/", "[Encryption/Decryption consistency]") {
|
||||
|
||||
// Instanciate our cipher and supply a key
|
||||
const Block key = PasswordToKey("1234");
|
||||
const Cipher cipher(key);
|
||||
|
||||
// Recode the ascii-string to bits
|
||||
const Flexblock cleartext_bits =
|
||||
"1011110011010110000010110001111000111010111101001010100100011101"
|
||||
"0101110101010010100000110100001000011000111010001001110101111111"
|
||||
"1110110101100101110001010101011110001010000010111110011011010111"
|
||||
"1100110100111000000011100101010100110010001110010011000010111001"
|
||||
"0000010000010000011001111010011110111001000000000110101000110001"
|
||||
"0110111110110110100000010100000011010001000011100100111001001011"
|
||||
"1101100100000100010000001011100010010001101111100100101100010001"
|
||||
"0000011110010110111010110110111110011110011010001100100111110101"
|
||||
"1000010010000000000100101011110001000101101101100000010011111011"
|
||||
"1011111010110100100111100111110011100001111101111110000110001100"
|
||||
"0001000111000111101110000111011011101010100010100101100111010100"
|
||||
"0101111110110010110000111111011001101110101101100100100011000100"
|
||||
"1000110010101001000100001001101000011111101011111100100000100101"
|
||||
"1100001100111001011111001101000111011101011101000110010110110110"
|
||||
"0111001010011010010000010110000110010101101100101110111100100011"
|
||||
"0010111110011100010100000101100101110101101011110100100000110110"
|
||||
"1001101110101001001111111000010100011100100000101000111101101111"
|
||||
"0101111011110001101010111010001000111010101111001101100100100100"
|
||||
"1110110111001100011010110000101000011001011100101100111101110000"
|
||||
"1010101111011110000111011011011110000111010110110111111010101010"
|
||||
"0111100101111001010111101000001010100000111010111100111011111001"
|
||||
"0110111000000110100011011100101101010101101000010010011111100100"
|
||||
"0010111000001011101110000110010011101001111010100111110111110101"
|
||||
"1110111000000000101011000100101010000110110111101010011001111010"
|
||||
"1101011110001110000011010111001100001100101000000101000101000010"
|
||||
"0101000011011111010010110010000010101100001110011000110111110111"
|
||||
"1110010101011110111001100010110101101011100111100011101010001011"
|
||||
"0101110010100110101100111100010000111101111100000111000110110110"
|
||||
"1001100111000000011010100000011101011000010010011010001011110000"
|
||||
"1100100111111001001000011100110000011110001100000000010000001001"
|
||||
"11100000001100100000100110101000110110110000000001111100001";
|
||||
|
||||
const Flexblock cleartext_bits_EXPECTED_RESULT =
|
||||
"1011110011010110000010110001111000111010111101001010100100011101"
|
||||
"0101110101010010100000110100001000011000111010001001110101111111"
|
||||
"1110110101100101110001010101011110001010000010111110011011010111"
|
||||
"1100110100111000000011100101010100110010001110010011000010111001"
|
||||
"0000010000010000011001111010011110111001000000000110101000110001"
|
||||
"0110111110110110100000010100000011010001000011100100111001001011"
|
||||
"1101100100000100010000001011100010010001101111100100101100010001"
|
||||
"0000011110010110111010110110111110011110011010001100100111110101"
|
||||
"1000010010000000000100101011110001000101101101100000010011111011"
|
||||
"1011111010110100100111100111110011100001111101111110000110001100"
|
||||
"0001000111000111101110000111011011101010100010100101100111010100"
|
||||
"0101111110110010110000111111011001101110101101100100100011000100"
|
||||
"1000110010101001000100001001101000011111101011111100100000100101"
|
||||
"1100001100111001011111001101000111011101011101000110010110110110"
|
||||
"0111001010011010010000010110000110010101101100101110111100100011"
|
||||
"0010111110011100010100000101100101110101101011110100100000110110"
|
||||
"1001101110101001001111111000010100011100100000101000111101101111"
|
||||
"0101111011110001101010111010001000111010101111001101100100100100"
|
||||
"1110110111001100011010110000101000011001011100101100111101110000"
|
||||
"1010101111011110000111011011011110000111010110110111111010101010"
|
||||
"0111100101111001010111101000001010100000111010111100111011111001"
|
||||
"0110111000000110100011011100101101010101101000010010011111100100"
|
||||
"0010111000001011101110000110010011101001111010100111110111110101"
|
||||
"1110111000000000101011000100101010000110110111101010011001111010"
|
||||
"1101011110001110000011010111001100001100101000000101000101000010"
|
||||
"0101000011011111010010110010000010101100001110011000110111110111"
|
||||
"1110010101011110111001100010110101101011100111100011101010001011"
|
||||
"0101110010100110101100111100010000111101111100000111000110110110"
|
||||
"1001100111000000011010100000011101011000010010011010001011110000"
|
||||
"1100100111111001001000011100110000011110001100000000010000001001"
|
||||
"1110000000110010000010011010100011011011000000000111110000100000"
|
||||
"0000000000000000000000000000000000000000000000000000000000000000";
|
||||
|
||||
// Encrypt our cleartext bits
|
||||
const Flexblock ciphertext_bits = cipher.Encipher(cleartext_bits);
|
||||
|
||||
// Decipher it again
|
||||
const Flexblock decryptedBits = cipher.Decipher(ciphertext_bits);
|
||||
|
||||
// Assert that the decrypted text equals the plaintext
|
||||
REQUIRE(
|
||||
cleartext_bits_EXPECTED_RESULT ==
|
||||
decryptedBits
|
||||
cleartext_blocks ==
|
||||
deciphered_blocks
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1,58 +1,86 @@
|
||||
#include <GCrypt/GCryptWrapper.h>
|
||||
#include <GCrypt/Flexblock.h>
|
||||
#include <GCrypt/GWrapper.h>
|
||||
#include <GCrypt/Util.h>
|
||||
#include "Catch2.h"
|
||||
|
||||
using namespace Leonetienne::GCrypt;
|
||||
|
||||
// Tests that encrypting and decrypting strings using the wrapper works.
|
||||
// Tests that encrypting and decrypting short strings using the wrapper works.
|
||||
// This test will start from scratch after encryption, to ensure EVERYTHING has to be re-calculated.
|
||||
TEST_CASE(__FILE__"/Encrypting and decrypting strings works", "[Wrapper]") {
|
||||
TEST_CASE(__FILE__"/Encrypting and decrypting strings works, Single block", "[Wrapper]") {
|
||||
|
||||
// Setup
|
||||
const std::string plaintext = "Hello, World!";
|
||||
const std::string password = "Der Affe will Zucker";
|
||||
// Setup
|
||||
const std::string plaintext = "Hello, World!";
|
||||
const Key key = Key::FromPassword("Der Affe will Zucker");
|
||||
|
||||
std::string ciphertext;
|
||||
std::string decrypted;
|
||||
std::string ciphertext;
|
||||
std::string decrypted;
|
||||
|
||||
// Encryption
|
||||
ciphertext = GCryptWrapper::EncryptString(plaintext, password);
|
||||
// Encryption
|
||||
ciphertext = GWrapper::EncryptString(plaintext, key);
|
||||
|
||||
// Decryption
|
||||
decrypted = GCryptWrapper::DecryptString(ciphertext, password);
|
||||
// Decryption
|
||||
decrypted = GWrapper::DecryptString(ciphertext, key);
|
||||
|
||||
// Assertion
|
||||
REQUIRE(plaintext == decrypted);
|
||||
// Assertion
|
||||
REQUIRE(plaintext == decrypted);
|
||||
}
|
||||
|
||||
// Tests that encrypting and decrypting very long strings using the wrapper works.
|
||||
// This test will start from scratch after encryption, to ensure EVERYTHING has to be re-calculated.
|
||||
TEST_CASE(__FILE__"/Encrypting and decrypting strings works, Many blocks block", "[Wrapper]") {
|
||||
|
||||
// Setup
|
||||
|
||||
// Read an not-multiple-of-blocksize amount of random chars, that's very large (about 200kb long string)
|
||||
srand(time(0));
|
||||
|
||||
std::stringstream ss;
|
||||
const std::string charset = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
for (std::size_t i = 0; i < 198273; i++) {
|
||||
ss << charset[rand() % charset.length()];
|
||||
}
|
||||
|
||||
const std::string plaintext = ss.str();
|
||||
const Key key = Key::FromPassword("Der Affe will Zucker");
|
||||
|
||||
std::string ciphertext;
|
||||
std::string decrypted;
|
||||
|
||||
// Encryption
|
||||
ciphertext = GWrapper::EncryptString(plaintext, key);
|
||||
|
||||
// Decryption
|
||||
decrypted = GWrapper::DecryptString(ciphertext, key);
|
||||
|
||||
// Assertion
|
||||
REQUIRE(plaintext == decrypted);
|
||||
}
|
||||
|
||||
// Tests that encrypting and decrypting files using the wrapper works.
|
||||
// This test will start from scratch after encryption, to ensure EVERYTHING has to be re-calculated.
|
||||
TEST_CASE(__FILE__"/Encrypting and decrypting files works", "[Wrapper]") {
|
||||
|
||||
// Setup
|
||||
const std::string testfile_dir = "testAssets/";
|
||||
// Setup
|
||||
const std::string testfile_dir = "testAssets/";
|
||||
|
||||
const std::string filename_plain = testfile_dir + "testfile.png";
|
||||
const std::string filename_encrypted = testfile_dir + "testfile.png.crypt";
|
||||
const std::string filename_decrypted = testfile_dir + "testfile.png.clear.png";
|
||||
const std::string password = "Der Affe will Zucker";
|
||||
const std::string filename_plain = testfile_dir + "testfile.png";
|
||||
const std::string filename_encrypted = testfile_dir + "testfile.png.crypt";
|
||||
const std::string filename_decrypted = testfile_dir + "testfile.png.clear.png";
|
||||
const Key key = Key::FromPassword("Der Affe will Zucker");
|
||||
|
||||
|
||||
// Encryption
|
||||
GCryptWrapper::EncryptFile(filename_plain, filename_encrypted, password);
|
||||
// Encryption
|
||||
GWrapper::EncryptFile(filename_plain, filename_encrypted, key);
|
||||
|
||||
// Decryption
|
||||
GCryptWrapper::DecryptFile(filename_encrypted, filename_decrypted, password);
|
||||
// Decryption
|
||||
GWrapper::DecryptFile(filename_encrypted, filename_decrypted, key);
|
||||
|
||||
// Read in both the base, and the decrypted file
|
||||
const Flexblock plainfile = ReadFileToBits(filename_plain);
|
||||
const Flexblock decryptfile = ReadFileToBits(filename_decrypted);
|
||||
// Read in both the base, and the decrypted file
|
||||
const std::vector<Block> plainfile = ReadFileToBlocks(filename_plain);
|
||||
const std::vector<Block> decryptfile = ReadFileToBlocks(filename_decrypted);
|
||||
|
||||
// Assertion (If this fails, maybe check if the image is even readable by an image viewer)
|
||||
REQUIRE(
|
||||
PadStringToLength(plainfile, decryptfile.length(), '0', false) ==
|
||||
decryptfile
|
||||
);
|
||||
// Assertion (If this fails, maybe check if the image is even readable by an image viewer)
|
||||
REQUIRE(plainfile.size() == decryptfile.size());
|
||||
REQUIRE(plainfile == decryptfile);
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include <GCrypt/Util.h>
|
||||
#include <GCrypt/Block.h>
|
||||
#include <GCrypt/Config.h>
|
||||
#include <unordered_map>
|
||||
#include <sstream>
|
||||
@ -55,39 +56,39 @@ inline std::string Base10_2_X(const unsigned long long int i, const std::string
|
||||
// Already validated range: Password 0 - 1.000.000
|
||||
TEST_CASE(__FILE__"/Password to key transformation collision resistance", "[Key extrapolation]") {
|
||||
|
||||
// To test resistence set this to a high number around a million.
|
||||
// This will take a LONG while to execute though (about 2.5hrs on my machine), hence why it's set so low.
|
||||
constexpr std::size_t NUM_RUN_TESTS = 1000;
|
||||
// To test resistence set this to a high number around a million.
|
||||
// This will take a LONG while to execute though (about 2.5hrs on my machine), hence why it's set so low.
|
||||
constexpr std::size_t NUM_RUN_TESTS = 10;
|
||||
|
||||
std::unordered_map<std::bitset<BLOCK_SIZE>, std::string> keys; // <key, password>
|
||||
std::unordered_map<std::string, std::string> keys; // <key, password>
|
||||
|
||||
// Try NUM_RUN_TESTS passwords
|
||||
const std::string charset = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
// Try NUM_RUN_TESTS passwords
|
||||
const std::string charset = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
|
||||
for (std::size_t i = 0; i < NUM_RUN_TESTS; i++) {
|
||||
// Get password
|
||||
const std::string password = Base10_2_X(i, charset, 0);
|
||||
for (std::size_t i = 0; i < NUM_RUN_TESTS; i++) {
|
||||
// Get password
|
||||
const std::string password = Base10_2_X(i, charset, 0);
|
||||
|
||||
// Generate key
|
||||
const std::bitset<BLOCK_SIZE> newKey = PasswordToKey(password).Get();
|
||||
// Generate key
|
||||
const std::string newKeyBits = Key::FromPassword(password).ToBinaryString();
|
||||
|
||||
// Check if this block is already in our map
|
||||
if (keys.find(newKey) != keys.cend()) {
|
||||
std::cout << "Collision found between password \""
|
||||
<< password
|
||||
<< "\" and \""
|
||||
<< keys[newKey]
|
||||
<< "\". The key is \""
|
||||
<< newKey
|
||||
<< "\".";
|
||||
// Check if this block is already in our map
|
||||
if (keys.find(newKeyBits) != keys.cend()) {
|
||||
std::cout << "Collision found between password \""
|
||||
<< password
|
||||
<< "\" and \""
|
||||
<< keys[newKeyBits]
|
||||
<< "\". The key is \""
|
||||
<< newKeyBits
|
||||
<< "\".";
|
||||
|
||||
FAIL();
|
||||
}
|
||||
FAIL();
|
||||
}
|
||||
|
||||
// All good? Insert it into our map
|
||||
keys[newKey] = password;
|
||||
}
|
||||
// All good? Insert it into our map
|
||||
keys[newKeyBits] = password;
|
||||
}
|
||||
|
||||
return;
|
||||
return;
|
||||
}
|
||||
|
||||
|
51
GCryptLib/visualizations/create-visualizations.sh
Executable file
@ -0,0 +1,51 @@
|
||||
#!zsh
|
||||
|
||||
echo "Make sure to have run all visualization executables in ../build/ (cmake -B build)!"
|
||||
echo "These generate the base images!"
|
||||
|
||||
# Copy all images over, but as pngs, and a bit larger
|
||||
find ../build/ -maxdepth 1 -type f -name '*.bmp' |\
|
||||
xargs -I {}\
|
||||
convert "{}"\
|
||||
-filter box\
|
||||
-resize 256x\
|
||||
"{}.png"
|
||||
|
||||
mv ../build/*.png .
|
||||
|
||||
# Create a few gifs
|
||||
|
||||
# Singleblock diffusion
|
||||
convert -delay 10 -loop 0 -dispose previous \
|
||||
"visualize-singleblock-diffusion-input.bmp.png"\
|
||||
"visualize-singleblock-diffusion-input-flip.bmp.png"\
|
||||
"visualize-singleblock-diffusion-input.gif"
|
||||
|
||||
convert -delay 10 -loop 0 -dispose previous \
|
||||
"visualize-singleblock-diffusion-output.bmp.png"\
|
||||
"visualize-singleblock-diffusion-output-flip.bmp.png"\
|
||||
"visualize-singleblock-diffusion-output.gif"
|
||||
|
||||
|
||||
# Multiblock diffusion
|
||||
convert -delay 10 -loop 0 -dispose previous \
|
||||
"visualize-multiblock-diffusion-input.bmp.png"\
|
||||
"visualize-multiblock-diffusion-input-flip.bmp.png"\
|
||||
"visualize-multiblock-diffusion-input.gif"
|
||||
|
||||
convert -delay 10 -loop 0 -dispose previous \
|
||||
"visualize-multiblock-diffusion-output.bmp.png"\
|
||||
"visualize-multiblock-diffusion-output-flip.bmp.png"\
|
||||
"visualize-multiblock-diffusion-output.gif"
|
||||
|
||||
# Extreme input diffusion
|
||||
convert -delay 10 -loop 0 -dispose previous \
|
||||
"visualize-extreme-input-diffusion-input.bmp.png"\
|
||||
"visualize-extreme-input-diffusion-input-flip.bmp.png"\
|
||||
"visualize-extreme-input-diffusion-input.gif"
|
||||
|
||||
convert -delay 10 -loop 0 -dispose previous \
|
||||
"visualize-extreme-input-diffusion-output.bmp.png"\
|
||||
"visualize-extreme-input-diffusion-output-flip.bmp.png"\
|
||||
"visualize-extreme-input-diffusion-output.gif"
|
||||
|
After Width: | Height: | Size: 335 B |
After Width: | Height: | Size: 324 B |
After Width: | Height: | Size: 908 B |
After Width: | Height: | Size: 314 B |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 357 B |
After Width: | Height: | Size: 358 B |
After Width: | Height: | Size: 451 B |
After Width: | Height: | Size: 448 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 7.9 KiB |
After Width: | Height: | Size: 445 B |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 1.4 KiB |