2022-01-21 18:38:13 +01:00
# include <iostream>
# include <fstream>
# include <cstring>
2022-03-13 15:08:29 +01:00
# include <sstream>
# include <vector>
2022-01-21 18:38:13 +01:00
# include "CommandlineInterface.h"
# include "../GhettoCrypt/Util.h"
# include "../GhettoCrypt/Config.h"
# include "../GhettoCrypt/Cipher.h"
# include "../GhettoCrypt/Flexblock.h"
# include "../GhettoCrypt/Block.h"
2022-03-13 15:08:29 +01:00
# include "Bases.h"
# include "BaseConversion.h"
2022-03-13 18:10:23 +01:00
# include "StringTools.h"
2022-03-13 15:08:29 +01:00
// Required for hidden password input
2022-01-21 20:11:48 +01:00
# if defined _WIN32 || defined _WIN64
# include <Windows.h>
# elif defined __GNUG__
# include <termios.h>
# include <unistd.h>
# endif
2022-01-21 18:38:13 +01:00
using namespace GhettoCipher ;
2022-03-16 01:31:28 +01:00
enum class OPERATION_MODE {
ENCRYPT ,
DECRYPT ,
HASH
} ;
2022-03-13 15:08:29 +01:00
namespace {
2022-03-13 18:10:23 +01:00
inline std : : string Bin2CustomBase ( const std : : string & bin , const std : : vector < std : : string > & customSet , const std : : string & seperator = " " )
2022-03-13 15:08:29 +01:00
{
std : : stringstream ss ;
// Translate to custom set
2022-03-13 18:10:23 +01:00
const std : : vector < std : : string > vec_base_custom =
Leonetienne : : GeneralUtility : : BaseConversion : : BaseX_2_Y < std : : string , std : : vector < std : : string > > ( bin , " 01 " , customSet ) ;
2022-03-13 15:08:29 +01:00
// 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 ( ) ;
}
2022-03-13 18:10:23 +01:00
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 ;
}
2022-03-13 15:08:29 +01:00
}
2022-01-21 20:11:48 +01:00
//! 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 ;
}
2022-01-21 18:38:13 +01:00
Block GetEncryptionKey ( )
{
// Easy-case: key supplied as param
if ( CommandlineInterface : : Get ( ) . HasParam ( " --key " ) )
2022-03-16 01:31:28 +01:00
return PasswordToKey ( CommandlineInterface : : Get ( ) [ " --key " ] . GetString ( ) ) ;
2022-01-21 18:38:13 +01:00
// Case: Ask for key
else if ( CommandlineInterface : : Get ( ) . HasParam ( " --keyask " ) )
2022-03-16 01:31:28 +01:00
return PasswordToKey ( PasswordPrompt ( ) ) ;
2022-01-21 18:38:13 +01:00
// 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 = GhettoCipher : : 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. " ) ;
}
2022-03-16 01:31:28 +01:00
const Flexblock GetInputText ( const OPERATION_MODE operationMode )
2022-01-21 18:38:13 +01:00
{
// Easy-case: input text supplied as param
2022-03-16 01:31:28 +01:00
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 :
2022-01-21 18:38:13 +01:00
return StringToBits ( CommandlineInterface : : Get ( ) [ " --intext " ] . GetString ( ) ) ;
2022-03-13 18:10:23 +01:00
// Decryption mode: We need to first convert our input to a bitstring
2022-03-16 01:31:28 +01:00
case OPERATION_MODE : : DECRYPT :
2022-03-13 18:10:23 +01:00
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 ) ;
}
2022-03-16 01:31:28 +01:00
}
2022-01-21 18:38:13 +01:00
// Case: Read from file
else if ( CommandlineInterface : : Get ( ) . HasParam ( " --infile " ) )
return ReadFileToBits ( CommandlineInterface : : Get ( ) [ " --infile " ] . GetString ( ) ) ;
// Unreachable
2022-01-21 21:07:44 +01:00
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. " ) ;
2022-01-21 18:38:13 +01:00
}
2022-03-16 01:31:28 +01:00
const std : : string GetOutfileName ( const OPERATION_MODE operationMode )
2022-01-21 18:38:13 +01:00
{
// 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
2022-03-16 01:31:28 +01:00
return CommandlineInterface : : Get ( ) [ " --infile " ] . GetString ( ) +
( operationMode = = OPERATION_MODE : : ENCRYPT ? " .crypt " : " .plain " ) ;
2022-01-21 18:38:13 +01:00
}
int main ( int argc , char * * argv )
{
// Init cmdargs
CommandlineInterface : : Init ( argc , argv ) ;
// Get operation modes
2022-03-16 01:31:28 +01:00
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. " ) ;
2022-01-21 18:38:13 +01:00
const bool isFileMode = CommandlineInterface : : Get ( ) . HasParam ( " --infile " ) ;
2022-03-16 01:31:28 +01:00
const bool printDigestionProgress = CommandlineInterface : : Get ( ) . HasParam ( " --progress " ) ;
2022-01-21 18:38:13 +01:00
// Digest
Flexblock output ;
2022-03-16 01:31:28 +01:00
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 ;
}
}
2022-01-21 18:38:13 +01:00
// Now output the darn thing.
2022-03-16 01:31:28 +01:00
// Outputting to a file (only if not hashing)
if ( ( isFileMode ) & & ( opMode ! = OPERATION_MODE : : HASH ) )
2022-01-21 18:38:13 +01:00
{
// File mode is a bit different.
// Dump to stdout?
if ( CommandlineInterface : : Get ( ) . HasParam ( " --ostdout " ) )
{
2022-02-19 22:40:23 +01:00
const std : : string outstr = BitsToBytes ( output ) ;
2022-01-21 18:38:13 +01:00
// 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
{
2022-03-16 01:31:28 +01:00
const std : : string outfileName = GetOutfileName ( opMode ) ;
2022-01-21 18:38:13 +01:00
WriteBitsToFile ( outfileName , output ) ;
}
}
2022-03-16 01:31:28 +01:00
// Else: we are just dealing with a string, or want to print a hashsum
2022-01-21 18:38:13 +01:00
else
{
2022-03-16 01:31:28 +01:00
switch ( opMode ) {
case OPERATION_MODE : : ENCRYPT :
case OPERATION_MODE : : HASH : {
// We are encrypting or hashing:
2022-03-13 18:10:23 +01:00
// Output to stdout as a formatted bytestring
2022-03-13 15:08:29 +01:00
std : : string formattedCiphertext ;
// Are we using iobase 2?
if ( CommandlineInterface : : Get ( ) . HasParam ( " --iobase-2 " ) )
// Yes: convert hex to base 2
2022-03-13 18:10:23 +01:00
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 ) ;
2022-03-13 15:08:29 +01:00
// Are we using iobase 10?
else if ( CommandlineInterface : : Get ( ) . HasParam ( " --iobase-10 " ) )
2022-03-13 18:10:23 +01:00
// Yes: convert binary to base 10
formattedCiphertext = Bin2CustomBase ( output , BASE_10 ) ;
2022-03-13 15:08:29 +01:00
// Are we using iobase 64?
else if ( CommandlineInterface : : Get ( ) . HasParam ( " --iobase-64 " ) )
2022-03-13 18:10:23 +01:00
// Yes: convert binary to base 64
formattedCiphertext = Bin2CustomBase ( output , BASE_64 ) ;
2022-03-13 15:08:29 +01:00
// Are we using iobase uwu?
else if ( CommandlineInterface : : Get ( ) . HasParam ( " --iobase-uwu " ) )
2022-03-13 18:10:23 +01:00
// Yes: convert binary to base 64
formattedCiphertext = Bin2CustomBase ( output , BASE_UWU , " " ) ;
2022-03-13 15:08:29 +01:00
2022-03-16 01:31:28 +01:00
// Are we using iobase ugh?
2022-03-13 18:10:23 +01:00
else if ( CommandlineInterface : : Get ( ) . HasParam ( " --iobase-ugh " ) )
// Yes: convert binary to base 64
formattedCiphertext = Bin2CustomBase ( output , BASE_UGH , " " ) ;
2022-03-13 15:08:29 +01:00
// Else, no special output format specified, use hex
else
2022-03-13 18:10:23 +01:00
formattedCiphertext = BitsToHexstring ( output ) ;
2022-03-13 15:08:29 +01:00
std : : cout < < formattedCiphertext < < std : : endl ;
2022-03-16 01:31:28 +01:00
break ;
2022-03-13 15:08:29 +01:00
}
2022-03-16 01:31:28 +01:00
case OPERATION_MODE : : DECRYPT :
2022-03-13 18:10:23 +01:00
// We are just decrypting:
// Just print it string-formatted
2022-01-21 18:38:13 +01:00
std : : cout < < BitsToString ( output ) < < std : : endl ;
2022-03-16 01:31:28 +01:00
break ;
2022-03-13 15:08:29 +01:00
}
2022-01-21 18:38:13 +01:00
}
return 0 ;
}