Merge branch 'feature/relaunch' into master

This commit is contained in:
Leonetienne
2022-06-01 03:30:55 +02:00
115 changed files with 24190 additions and 1947 deletions

View File

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

View File

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

View File

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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View File

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

View File

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

View File

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

View 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;

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

View 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;

View 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;

View 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;

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

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

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

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

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

View File

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

File diff suppressed because it is too large Load Diff

View 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
View File

@@ -0,0 +1,2 @@
#define CATCH_CONFIG_MAIN
#include "Catch2.h"