diff --git a/GCryptLib/include/GCrypt/Feistel.h b/GCryptLib/include/GCrypt/Feistel.h index 5d30334..5e3ce1a 100644 --- a/GCryptLib/include/GCrypt/Feistel.h +++ b/GCryptLib/include/GCrypt/Feistel.h @@ -10,6 +10,10 @@ namespace Leonetienne::GCrypt { */ class Feistel { public: + //! Empty initializer. If you use this, you must call SetKey()! + Feistel(); + + //! Will initialize the feistel cipher with a key explicit Feistel(const Key& key); Feistel(const Feistel& other) = delete; @@ -59,6 +63,8 @@ namespace Leonetienne::GCrypt { void ZeroKeyMemory(); Keyset roundKeys; + + bool isInitialized = false; }; } diff --git a/GCryptLib/include/GCrypt/Flexblock.h b/GCryptLib/include/GCrypt/Flexblock.h deleted file mode 100644 index 5e551b2..0000000 --- a/GCryptLib/include/GCrypt/Flexblock.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef GCRYPT_FLEXBLOCK_H -#define GCRYPT_FLEXBLOCK_H - -#include - -namespace Leonetienne::GCrypt { - //! A type used for conveying "bitstrings". e.g. "10101001001" - //! These should generally not be used, as they are really really slow. - //! The only valid usecase I can think of is when using GHash for example, because for hashing - //! an absolute input length is required. - //! If you need to, you can use the StringToBits() and BitsToString() functions defined in Util.h. - typedef std::string Flexblock; -} - -#endif - diff --git a/GCryptLib/include/GCrypt/GCipher.h b/GCryptLib/include/GCrypt/GCipher.h index 6050233..d496c37 100644 --- a/GCryptLib/include/GCrypt/GCipher.h +++ b/GCryptLib/include/GCrypt/GCipher.h @@ -2,7 +2,6 @@ #define GCRYPT_GCIPHER_H #include "GCrypt/Feistel.h" -#include "GCrypt/Flexblock.h" namespace Leonetienne::GCrypt { /** Class to apply a block/-stream cipher to messages of arbitrary length in a distributed manner @@ -15,6 +14,9 @@ namespace Leonetienne::GCrypt { DECIPHER }; + //! Empty initializer. If you use this, you must call Initialize()! + GCipher(); + //! Will initialize this cipher with a key explicit GCipher(const Key& key, const DIRECTION direction); @@ -30,6 +32,11 @@ namespace Leonetienne::GCrypt { void operator=(const GCipher& other); + + //! Will initialize the cipher with a key, and a mode. + //! If called on an existing object, it will reset its state. + void Initialize(const Key& key, const DIRECTION direction); + private: DIRECTION direction; @@ -38,6 +45,8 @@ namespace Leonetienne::GCrypt { //! The last block, required for CBC. Block lastBlock; + + bool isInitialized = false; }; } diff --git a/GCryptLib/include/GCrypt/GHash.h b/GCryptLib/include/GCrypt/GHash.h index 4cf6080..28e906a 100644 --- a/GCryptLib/include/GCrypt/GHash.h +++ b/GCryptLib/include/GCrypt/GHash.h @@ -1,7 +1,6 @@ #ifndef GCRYPT_GHASH_H #define GCRYPT_GHASH_H -#include "GCrypt/Flexblock.h" #include "GCrypt/Block.h" #include "GCrypt/GCipher.h" #include diff --git a/GCryptLib/include/GCrypt/GPrng.h b/GCryptLib/include/GCrypt/GPrng.h index 01845f5..89b7ab7 100644 --- a/GCryptLib/include/GCrypt/GPrng.h +++ b/GCryptLib/include/GCrypt/GPrng.h @@ -36,7 +36,12 @@ namespace Leonetienne::GCrypt { } // Transform to bytes - const std::string bytes = BitsToBytes(ss.str()); + const std::string bits = ss.str(); + ss.str(""); + for (std::size_t i = 0; i < bits.size(); i += 8) { + ss << (char)std::bitset<8>(bits.substr(i, 8)).to_ulong(); + } + const std::string bytes = ss.str(); // Cram bytes into type T t; diff --git a/GCryptLib/include/GCrypt/GWrapper.h b/GCryptLib/include/GCrypt/GWrapper.h index e10e550..9d9fab9 100644 --- a/GCryptLib/include/GCrypt/GWrapper.h +++ b/GCryptLib/include/GCrypt/GWrapper.h @@ -1,10 +1,11 @@ -#pragma once -#include -#include "GCrypt/Flexblock.h" +#ifndef GCRYPT_GWRAPPER_H +#define GCRYPT_GWRAPPER_H + #include "GCrypt/Block.h" #include "GCrypt/GCipher.h" #include "GCrypt/Key.h" - +#include +#include namespace Leonetienne::GCrypt { /** This class is a wrapper to make working with the GhettoCipher @@ -31,7 +32,7 @@ namespace Leonetienne::GCrypt { static bool DecryptFile(const std::string& filename_in, const std::string& filename_out, const Key& key, bool printProgressReport = false); //! Will enncrypt or decrypt an entire flexblock of binary data, given a key. - static Flexblock CipherFlexblock(const Flexblock& data, const Key& key, const GCipher::DIRECTION direction); + static std::vector CipherBlocks(const std::vector& data, const Key& key, const GCipher::DIRECTION direction); private: @@ -40,3 +41,5 @@ namespace Leonetienne::GCrypt { }; } +#endif + diff --git a/GCryptLib/include/GCrypt/Util.h b/GCryptLib/include/GCrypt/Util.h index 1d9dce7..d1a6869 100644 --- a/GCryptLib/include/GCrypt/Util.h +++ b/GCryptLib/include/GCrypt/Util.h @@ -7,7 +7,6 @@ #include #include #include "GCrypt/Block.h" -#include "GCrypt/Flexblock.h" #include "GCrypt/Config.h" #include "GCrypt/GCipher.h" #include "GCrypt/InitializationVector.h" @@ -21,56 +20,16 @@ namespace Leonetienne::GCrypt { //! Will pad a string to a set length with a certain character std::string PadStringToLength(const std::string& str, const std::size_t len, const char pad, const bool padLeft = true); - //! Will convert a string to a fixed-size data block - //! @s: The string to pad - //! padLeft: should padding be added to the left? If not, to the right. - Block StringToBitblock(const std::string& s, bool padLeft = true); - - //! Will convert a string to a flexible data block - Flexblock StringToBits(const std::string& s); - //! Will convert a string to a vector of blocks std::vector StringToBitblocks(const std::string& str); - //! Will convert a fixed-size data block to a bytestring - std::string BitblockToBytes(const Block& block); - //! Will convert an array of data blocks to a bytestring std::string BitblocksToBytes(const std::vector& bits); - //! Will convert a fixed-size data blocks to a textstring - //! The difference to BitblockToBytes() is, that it strips excess nullbytes - std::string BitblockToString(const Block& block); - //! Will convert an array of blocks to a character-string //! The difference to BitblocksToBytes() is, that it strips excess nullbytes std::string BitblocksToString(const std::vector& blocks); - //! Will convert a flexible data block to a bytestring - std::string BitsToBytes(const Flexblock& bits); - - //! Will convert a flexible data block to a string - //! The difference to BitsToBytes() is, that it strips excess nullbytes - std::string BitsToString(const Flexblock& bits); - - //! Turns a fixed-size data block into a hex-string - std::string BitblockToHexstring(const Block& b); - - //! Turns a flexible data block into a hex-string - std::string BitsToHexstring(const Flexblock& b); - - //! Turns a hex string into a fixed-size data block - Block HexstringToBitblock(const std::string& hexstring); - - //! Turns a hex string into a flexible data block - Flexblock HexstringToBits(const std::string& hexstring); - - //! Will read a file into a flexblock - Flexblock ReadFileToBits(const std::string& filepath); - - //! Will save bits to a binary file - void WriteBitsToFile(const std::string& filepath, const Flexblock& bits); - //! Will read a file directly to data blocks, and yield the amount of bytes read std::vector ReadFileToBlocks(const std::string& filepath, std::size_t& bytes_read); diff --git a/GCryptLib/src/Feistel.cpp b/GCryptLib/src/Feistel.cpp index 1c54a7f..51bfd4d 100644 --- a/GCryptLib/src/Feistel.cpp +++ b/GCryptLib/src/Feistel.cpp @@ -6,20 +6,20 @@ namespace Leonetienne::GCrypt { + Feistel::Feistel() { + } + Feistel::Feistel(const Key& key) { SetKey(key); - return; } Feistel::~Feistel() { ZeroKeyMemory(); - - return; } void Feistel::SetKey(const Key& key) { GenerateRoundKeys(key); - return; + isInitialized = true; } Block Feistel::Encipher(const Block& data) { @@ -31,6 +31,10 @@ namespace Leonetienne::GCrypt { } Block Feistel::Run(const Block& data, bool modeEncrypt) { + if (!isInitialized) { + throw std::runtime_error("Attempted to digest data on uninitialized GCipher!"); + } + const auto splitData = FeistelSplit(data); Halfblock l = splitData.first; Halfblock r = splitData.second; @@ -245,6 +249,7 @@ namespace Leonetienne::GCrypt { void Feistel::operator=(const Feistel& other) { roundKeys = other.roundKeys; + isInitialized = other.isInitialized; return; } diff --git a/GCryptLib/src/GCipher.cpp b/GCryptLib/src/GCipher.cpp index 3a2883f..3009142 100644 --- a/GCryptLib/src/GCipher.cpp +++ b/GCryptLib/src/GCipher.cpp @@ -7,17 +7,33 @@ namespace Leonetienne::GCrypt { + GCipher::GCipher() { + } + GCipher::GCipher(const Key& key, const DIRECTION direction) : direction { direction }, lastBlock(InitializationVector(key)), // Initialize our lastBlock with some deterministic initial value, based on the key feistel(key) { + isInitialized = true; + return; + } + + void GCipher::Initialize(const Key& key, const DIRECTION direction) { + feistel = Feistel(key); + lastBlock = InitializationVector(key); + this->direction = direction; + isInitialized = true; return; } Block GCipher::Digest(const Block& input) { + if (!isInitialized) { + throw std::runtime_error("Attempted to digest data on uninitialized GCipher!"); + } + switch (direction) { case DIRECTION::ENCIPHER: { // Rename our input to cleartext @@ -52,6 +68,11 @@ namespace Leonetienne::GCrypt { } void GCipher::SetKey(const Key& key) { + + if (!isInitialized) { + throw std::runtime_error("Attempted to set key on uninitialized GCipher!"); + } + feistel.SetKey(key); return; @@ -61,6 +82,7 @@ namespace Leonetienne::GCrypt { direction = other.direction; feistel = other.feistel; lastBlock = other.lastBlock; + isInitialized = other.isInitialized; return; } diff --git a/GCryptLib/src/GHash.cpp b/GCryptLib/src/GHash.cpp index 24a3614..85b9951 100644 --- a/GCryptLib/src/GHash.cpp +++ b/GCryptLib/src/GHash.cpp @@ -5,17 +5,23 @@ namespace Leonetienne::GCrypt { - GHash::GHash() : + GHash::GHash() { // Initialize our cipher with a static, but randomly distributed key. - cipher( + Block ivSeed; + ivSeed.FromByteString("3J7IipfQTDJbO8jtasz9PgWui6faPaEMOuVuAqyhB1S2CRcLw5caawewgDUEG1WN"); + block = InitializationVector(ivSeed); + + Key key; + key.FromByteString("nsoCZfvdqpRkeVTt9wzvPR3TT26peOW9E2kTHh3pdPCq2M7BpskvUljJHSrobUTI"); + + cipher = GCipher( // The key really does not matter, as it gets changed // each time before digesting anything. - Key(StringToBitblock("nsoCZfvdqpRkeVTt9wzvPR3TT26peOW9E2kTHh3pdPCq2M7BpskvUljJHSrobUTI")), + key, GCipher::DIRECTION::ENCIPHER - ) { - block = InitializationVector(StringToBitblock("3J7IipfQTDJbO8jtasz9PgWui6faPaEMOuVuAqyhB1S2CRcLw5caawewgDUEG1WN")); + ); - return; + return; } void GHash::DigestBlock(const Block& data) { @@ -64,7 +70,8 @@ namespace Leonetienne::GCrypt { std::stringstream ss; ss << n_bytes; - const Block lengthBlock = StringToBitblock(ss.str()); + Block lengthBlock; + lengthBlock.FromTextString(ss.str()); // Digest the length block hasher.DigestBlock(lengthBlock); diff --git a/GCryptLib/src/GWrapper.cpp b/GCryptLib/src/GWrapper.cpp index ced5957..69fd6ba 100644 --- a/GCryptLib/src/GWrapper.cpp +++ b/GCryptLib/src/GWrapper.cpp @@ -134,35 +134,25 @@ namespace Leonetienne::GCrypt { } } - Flexblock GWrapper::CipherFlexblock( - const Flexblock& data, + std::vector GWrapper::CipherBlocks( + const std::vector& data, const Key& key, const GCipher::DIRECTION direction) { - // Split input into blocks - std::vector blocks; - - for (std::size_t i = 0; i < data.size(); i += Block::BLOCK_SIZE_BITS) { - blocks.push_back(Block( - PadStringToLength(data.substr(i, Block::BLOCK_SIZE_BITS), Block::BLOCK_SIZE_BITS, '0', false)) - ); - } - // Create cipher instance GCipher cipher(key, direction); - for (Block& block : blocks) { - block = cipher.Digest(block); - } + std::vector digested; + digested.reserve(data.size()); - // Concatenate ciphertext blocks back into a flexblock - std::stringstream ss; - for (Block& b : blocks) { - ss << b; + // Digest all our blocks + for (const Block& block : data) { + Block digestedBlock = cipher.Digest(block); + digested.emplace_back(digestedBlock); } // Return it - return ss.str(); + return digested; } } diff --git a/GCryptLib/src/Util.cpp b/GCryptLib/src/Util.cpp index 83d412b..b86b8cb 100644 --- a/GCryptLib/src/Util.cpp +++ b/GCryptLib/src/Util.cpp @@ -30,58 +30,16 @@ namespace Leonetienne::GCrypt { return ss.str(); } - Block StringToBitblock(const std::string& s, bool padLeft) { - std::stringstream ss; - - for (std::size_t i = 0; i < s.size(); i++) { - ss << std::bitset<8>(s[i]); - } - - // Pad rest with zeores - return Block(PadStringToLength(ss.str(), Block::BLOCK_SIZE_BITS, '0', padLeft)); - } - - Flexblock StringToBits(const std::string& s) { - std::stringstream ss; - - for (std::size_t i = 0; i < s.size(); i++) { - ss << std::bitset<8>(s[i]); - } - - return Flexblock(ss.str()); - } - - std::string BitblockToBytes(const Block& block) { - std::stringstream ss; - - std::uint8_t* curByte = (std::uint8_t*)(void*)block.Data(); - for (std::size_t j = 0; j < Block::BLOCK_SIZE; j++) { - ss << *curByte++; - } - - return ss.str(); - } - std::string BitblocksToBytes(const std::vector& blocks) { std::stringstream ss; for (const Block& block : blocks) { - ss << BitblockToBytes(block); + ss << block.ToByteString(); } return ss.str(); } - std::string BitblockToString(const Block& bits) { - // Decode to bytes - std::string text = BitblockToBytes(bits); - - // Dümp excess nullbytes - text.resize(strlen(text.data())); - - return text; - } - std::string BitblocksToString(const std::vector& blocks) { // Decode to bytes std::string text = BitblocksToBytes(blocks); @@ -92,146 +50,6 @@ namespace Leonetienne::GCrypt { return text; } - std::string BitsToBytes(const Flexblock& bits) { - std::stringstream ss; - - const std::string bitstring = bits; - - for (std::size_t i = 0; i < bits.size(); i += 8) { - ss << (char)std::bitset<8>(bitstring.substr(i, 8)).to_ulong(); - } - - return ss.str(); - } - - std::string BitsToString(const Flexblock& bits) { - // Decode to bytes - std::string text = BitsToBytes(bits); - - // Dümp excess nullbytes - text.resize(strlen(text.data())); - - return text; - } - - std::string BitblockToHexstring(const Block& b) { - std::stringstream ss; - const std::string charset = "0123456789abcdef"; - const std::string bstr = b.ToBinaryString(); - - for (std::size_t i = 0; i < bstr.size(); i += 4) { - ss << charset[std::bitset<4>(bstr.substr(i, 4)).to_ulong()]; - } - - return ss.str(); - } - - std::string BitsToHexstring(const Flexblock& b) { - std::stringstream ss; - const std::string charset = "0123456789abcdef"; - const std::string bstr = b; - - for (std::size_t i = 0; i < bstr.size(); i += 4) { - ss << charset[std::bitset<4>(bstr.substr(i, 4)).to_ulong()]; - } - - return ss.str(); - } - - Block HexstringToBitblock(const std::string& hexstring) { - std::stringstream ss; - - for (std::size_t i = 0; i < hexstring.size(); i++) { - const char c = hexstring[i]; - - // Get value - std::size_t value; - if ((c >= '0') && (c <= '9')) { - // Is it a number? - value = ((std::size_t)c - '0') + 0; - } - else if ((c >= 'a') && (c <= 'f')) { - // Else, it is a lowercase letter - value = ((std::size_t)c - 'a') + 10; - } - else { - throw std::logic_error("non-hex string detected in HexstringToBits()"); - } - - // Append to our bits - ss << std::bitset<4>(value).to_string(); - } - - return Block(ss.str()); - } - - Flexblock HexstringToBits(const std::string& hexstring) { - std::stringstream ss; - - for (std::size_t i = 0; i < hexstring.size(); i++) { - const char c = hexstring[i]; - - // Get value - std::size_t value; - if ((c >= '0') && (c <= '9')) { - // Is it a number? - value = ((std::size_t)c - '0') + 0; - } - else if ((c >= 'a') && (c <= 'f')) { - // Else, it is a lowercase letter - value = ((std::size_t)c - 'a') + 10; - } - else { - throw std::logic_error("non-hex string detected in HexstringToBits()"); - } - - // Append to our bits - ss << std::bitset<4>(value).to_string(); - } - - return ss.str(); - } - - Flexblock ReadFileToBits(const std::string& filepath) { - // Read file - std::ifstream ifs(filepath, std::ios::binary); - - if (!ifs.good()) { - throw std::runtime_error("Unable to open ifilestream!"); - } - - std::stringstream ss; - std::copy( - std::istreambuf_iterator(ifs), - std::istreambuf_iterator(), - std::ostreambuf_iterator(ss) - ); - - ifs.close(); - - const std::string bytes = ss.str(); - - // Convert bytes to bits - return StringToBits(bytes); - } - - void WriteBitsToFile(const std::string& filepath, const Flexblock& bits) { - // Convert bits to bytes - const std::string bytes = BitsToBytes(bits); - - // Write bits to file - std::ofstream ofs(filepath, std::ios::binary); - - if (!ofs.good()) { - throw std::runtime_error("Unable to open ofilestream!"); - } - - ofs.write(bytes.data(), bytes.length()); - ofs.close(); - - return; - } - std::vector ReadFileToBlocks(const std::string& filepath, std::size_t& bytes_read) { // Read file bytes_read = 0; diff --git a/GCryptLib/test/EncryptEqualsDecrypt.cpp b/GCryptLib/test/EncryptEqualsDecrypt.cpp index e66e371..f79ea87 100644 --- a/GCryptLib/test/EncryptEqualsDecrypt.cpp +++ b/GCryptLib/test/EncryptEqualsDecrypt.cpp @@ -7,13 +7,13 @@ using namespace Leonetienne::GCrypt; // THESE TESTS ASSUME BLOCK_SIZE == 512 // Tests that encrypting a message of exactly BLOCK_SIZE yields the exact message back -TEST_CASE(__FILE__"/SingleBlock_NoPadding", "[Encryption/Decryption consistency]") { +TEST_CASE(__FILE__"/SingleBlock", "[Encryption/Decryption consistency]") { // Instanciate our cipher and supply a key const Key key = Key::FromPassword("1234"); // Recode the ascii-string to bits - const Flexblock cleartext_bits = + const std::string cleartext_bits = "1011110011010110000010110001111000111010111101001010100100011101" "0101110101010010100000110100001000011000111010001001110101111111" "1110110101100101110001010101011110001010000010111110011011010111" @@ -23,196 +23,65 @@ TEST_CASE(__FILE__"/SingleBlock_NoPadding", "[Encryption/Decryption consistency] "1101100100000100010000001011100010010001101111100100101100010001" "0000011110010110111010110110111110011110011010001100100111110101"; + std::vector cleartext_blocks({Block(cleartext_bits)}); + // Encrypt our cleartext bits - const Flexblock ciphertext_bits = GWrapper::CipherFlexblock(cleartext_bits, key, GCipher::DIRECTION::ENCIPHER); + const std::vector ciphertext_blocks = GWrapper::CipherBlocks(cleartext_blocks, key, GCipher::DIRECTION::ENCIPHER); // Decipher it again - const Flexblock decryptedBits = GWrapper::CipherFlexblock(ciphertext_bits, key, GCipher::DIRECTION::DECIPHER); + const std::vector decrypted_blocks = GWrapper::CipherBlocks(ciphertext_blocks, key, GCipher::DIRECTION::DECIPHER); // Assert that the decrypted text equals the plaintext REQUIRE( - cleartext_bits == - decryptedBits - ); + cleartext_blocks == + decrypted_blocks + ); } -// Tests that encrypting a message of less than BLOCK_SIZE yields the exact message plus zero-padding back -TEST_CASE(__FILE__"/SingleBlock_Padding", "[Encryption/Decryption consistency]") { - - // Instanciate our cipher and supply a key - const Key key = Key::FromPassword("1234"); - - // Recode the ascii-string to bits - const Flexblock cleartext_bits = - "1011110011010110000010110001111000111010111101001010100100011101" - "0101110101010010100000110100001000011000111010001001110101111111" - "1110110101100101110001010101011110001010000010111110011011010111" - "1100110100111000000011100101010100110010001110010011000010111001" - "0000010000010000011001111010011110111001000000000110101000110001" - "0110111110110110100000010100000011010001000011100100111001001011" - "1101100100000100"; - - const Flexblock cleartext_bits_EXPECTED_RESULT = - "1011110011010110000010110001111000111010111101001010100100011101" - "0101110101010010100000110100001000011000111010001001110101111111" - "1110110101100101110001010101011110001010000010111110011011010111" - "1100110100111000000011100101010100110010001110010011000010111001" - "0000010000010000011001111010011110111001000000000110101000110001" - "0110111110110110100000010100000011010001000011100100111001001011" - "1101100100000100000000000000000000000000000000000000000000000000" - "0000000000000000000000000000000000000000000000000000000000000000"; - - // Encrypt our cleartext bits - const Flexblock ciphertext_bits = GWrapper::CipherFlexblock(cleartext_bits, key, GCipher::DIRECTION::ENCIPHER); - - // Decipher it again - const Flexblock decryptedBits = GWrapper::CipherFlexblock(ciphertext_bits, key, GCipher::DIRECTION::DECIPHER); - - // Assert that the decrypted text equals the plaintext - REQUIRE( - cleartext_bits_EXPECTED_RESULT == - decryptedBits - ); -} - -// Tests that a decrypted ciphertext equals its plaintrext version, using a cleartext that requires A LOT of blocks +// Tests that a decrypted ciphertext equals its plaintext version, using a cleartext that requires A LOT of blocks TEST_CASE(__FILE__"MultiBlock_NoPadding/", "[Encryption/Decryption consistency]") { // Instanciate our cipher and supply a key const Key key = Key::FromPassword("1234"); - // Recode the ascii-string to bits - const Flexblock cleartext_bits = - "1011110011010110000010110001111000111010111101001010100100011101" - "0101110101010010100000110100001000011000111010001001110101111111" - "1110110101100101110001010101011110001010000010111110011011010111" - "1100110100111000000011100101010100110010001110010011000010111001" - "0000010000010000011001111010011110111001000000000110101000110001" - "0110111110110110100000010100000011010001000011100100111001001011" - "1101100100000100010000001011100010010001101111100100101100010001" - "0000011110010110111010110110111110011110011010001100100111110101" - "1000010010000000000100101011110001000101101101100000010011111011" - "1011111010110100100111100111110011100001111101111110000110001100" - "0001000111000111101110000111011011101010100010100101100111010100" - "0101111110110010110000111111011001101110101101100100100011000100" - "1000110010101001000100001001101000011111101011111100100000100101" - "1100001100111001011111001101000111011101011101000110010110110110" - "0111001010011010010000010110000110010101101100101110111100100011" - "0010111110011100010100000101100101110101101011110100100000110110" - "1001101110101001001111111000010100011100100000101000111101101111" - "0101111011110001101010111010001000111010101111001101100100100100" - "1110110111001100011010110000101000011001011100101100111101110000" - "1010101111011110000111011011011110000111010110110111111010101010" - "0111100101111001010111101000001010100000111010111100111011111001" - "0110111000000110100011011100101101010101101000010010011111100100" - "0010111000001011101110000110010011101001111010100111110111110101" - "1110111000000000101011000100101010000110110111101010011001111010" - "1101011110001110000011010111001100001100101000000101000101000010" - "0101000011011111010010110010000010101100001110011000110111110111" - "1110010101011110111001100010110101101011100111100011101010001011" - "0101110010100110101100111100010000111101111100000111000110110110" - "1001100111000000011010100000011101011000010010011010001011110000" - "1100100111111001001000011100110000011110001100000000010000001001" - "1110000000110010000010011010100011011011000000000111110000110111" - "0101110011001101010110010100011001110110000110010001100110011111"; + std::stringstream ss; + const std::string charset = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + // Read 10 blocks worth of characters + srand(time(0)); + for (std::size_t i = 0; i < 512*10; i++) { + ss << charset[rand() % charset.length()]; + } + const std::string cleartext_str = ss.str(); + + std::vector cleartext_blocks = StringToBitblocks(cleartext_str); // Encrypt our cleartext bits - const Flexblock ciphertext_bits = GWrapper::CipherFlexblock(cleartext_bits, key, GCipher::DIRECTION::ENCIPHER); + std::vector ciphertext_blocks; + ciphertext_blocks.reserve(cleartext_blocks.size()); + { + GCipher cipher(key, GCipher::DIRECTION::ENCIPHER); + + for (const Block& clearBlock : cleartext_blocks) { + ciphertext_blocks.emplace_back(cipher.Digest(clearBlock)); + } + } // Decipher it again - const Flexblock decryptedBits = GWrapper::CipherFlexblock(ciphertext_bits, key, GCipher::DIRECTION::DECIPHER); + std::vector deciphered_blocks; + deciphered_blocks.reserve(ciphertext_blocks.size()); + { + GCipher cipher(key, GCipher::DIRECTION::DECIPHER); + + for (const Block& ciphBlock : ciphertext_blocks) { + deciphered_blocks.emplace_back(cipher.Digest(ciphBlock)); + } + } // Assert that the decrypted text equals the plaintext REQUIRE( - cleartext_bits == - decryptedBits - ); -} - -// Tests that a decrypted ciphertext equals its plaintrext version, using a cleartext that requires A LOT of blocks -TEST_CASE(__FILE__"MultiBlock_Padding/", "[Encryption/Decryption consistency]") { - - // Instanciate our cipher and supply a key - const Key key = Key::FromPassword("1234"); - - // Recode the ascii-string to bits - const Flexblock cleartext_bits = - "1011110011010110000010110001111000111010111101001010100100011101" - "0101110101010010100000110100001000011000111010001001110101111111" - "1110110101100101110001010101011110001010000010111110011011010111" - "1100110100111000000011100101010100110010001110010011000010111001" - "0000010000010000011001111010011110111001000000000110101000110001" - "0110111110110110100000010100000011010001000011100100111001001011" - "1101100100000100010000001011100010010001101111100100101100010001" - "0000011110010110111010110110111110011110011010001100100111110101" - "1000010010000000000100101011110001000101101101100000010011111011" - "1011111010110100100111100111110011100001111101111110000110001100" - "0001000111000111101110000111011011101010100010100101100111010100" - "0101111110110010110000111111011001101110101101100100100011000100" - "1000110010101001000100001001101000011111101011111100100000100101" - "1100001100111001011111001101000111011101011101000110010110110110" - "0111001010011010010000010110000110010101101100101110111100100011" - "0010111110011100010100000101100101110101101011110100100000110110" - "1001101110101001001111111000010100011100100000101000111101101111" - "0101111011110001101010111010001000111010101111001101100100100100" - "1110110111001100011010110000101000011001011100101100111101110000" - "1010101111011110000111011011011110000111010110110111111010101010" - "0111100101111001010111101000001010100000111010111100111011111001" - "0110111000000110100011011100101101010101101000010010011111100100" - "0010111000001011101110000110010011101001111010100111110111110101" - "1110111000000000101011000100101010000110110111101010011001111010" - "1101011110001110000011010111001100001100101000000101000101000010" - "0101000011011111010010110010000010101100001110011000110111110111" - "1110010101011110111001100010110101101011100111100011101010001011" - "0101110010100110101100111100010000111101111100000111000110110110" - "1001100111000000011010100000011101011000010010011010001011110000" - "1100100111111001001000011100110000011110001100000000010000001001" - "11100000001100100000100110101000110110110000000001111100001"; - - const Flexblock cleartext_bits_EXPECTED_RESULT = - "1011110011010110000010110001111000111010111101001010100100011101" - "0101110101010010100000110100001000011000111010001001110101111111" - "1110110101100101110001010101011110001010000010111110011011010111" - "1100110100111000000011100101010100110010001110010011000010111001" - "0000010000010000011001111010011110111001000000000110101000110001" - "0110111110110110100000010100000011010001000011100100111001001011" - "1101100100000100010000001011100010010001101111100100101100010001" - "0000011110010110111010110110111110011110011010001100100111110101" - "1000010010000000000100101011110001000101101101100000010011111011" - "1011111010110100100111100111110011100001111101111110000110001100" - "0001000111000111101110000111011011101010100010100101100111010100" - "0101111110110010110000111111011001101110101101100100100011000100" - "1000110010101001000100001001101000011111101011111100100000100101" - "1100001100111001011111001101000111011101011101000110010110110110" - "0111001010011010010000010110000110010101101100101110111100100011" - "0010111110011100010100000101100101110101101011110100100000110110" - "1001101110101001001111111000010100011100100000101000111101101111" - "0101111011110001101010111010001000111010101111001101100100100100" - "1110110111001100011010110000101000011001011100101100111101110000" - "1010101111011110000111011011011110000111010110110111111010101010" - "0111100101111001010111101000001010100000111010111100111011111001" - "0110111000000110100011011100101101010101101000010010011111100100" - "0010111000001011101110000110010011101001111010100111110111110101" - "1110111000000000101011000100101010000110110111101010011001111010" - "1101011110001110000011010111001100001100101000000101000101000010" - "0101000011011111010010110010000010101100001110011000110111110111" - "1110010101011110111001100010110101101011100111100011101010001011" - "0101110010100110101100111100010000111101111100000111000110110110" - "1001100111000000011010100000011101011000010010011010001011110000" - "1100100111111001001000011100110000011110001100000000010000001001" - "1110000000110010000010011010100011011011000000000111110000100000" - "0000000000000000000000000000000000000000000000000000000000000000"; - - // Encrypt our cleartext bits - const Flexblock ciphertext_bits = GWrapper::CipherFlexblock(cleartext_bits, key, GCipher::DIRECTION::ENCIPHER); - - // Decipher it again - const Flexblock decryptedBits = GWrapper::CipherFlexblock(ciphertext_bits, key, GCipher::DIRECTION::DECIPHER); - - // Assert that the decrypted text equals the plaintext - REQUIRE( - cleartext_bits_EXPECTED_RESULT == - decryptedBits - ); + cleartext_blocks == + deciphered_blocks + ); } diff --git a/GCryptLib/test/GCWrapper.cpp b/GCryptLib/test/GCWrapper.cpp index ef59209..9fd086b 100644 --- a/GCryptLib/test/GCWrapper.cpp +++ b/GCryptLib/test/GCWrapper.cpp @@ -1,13 +1,12 @@ #include -#include #include #include "Catch2.h" using namespace Leonetienne::GCrypt; -// Tests that encrypting and decrypting strings using the wrapper works. +// Tests that encrypting and decrypting short strings using the wrapper works. // This test will start from scratch after encryption, to ensure EVERYTHING has to be re-calculated. -TEST_CASE(__FILE__"/Encrypting and decrypting strings works", "[Wrapper]") { +TEST_CASE(__FILE__"/Encrypting and decrypting strings works, Single block", "[Wrapper]") { // Setup const std::string plaintext = "Hello, World!"; @@ -26,6 +25,37 @@ TEST_CASE(__FILE__"/Encrypting and decrypting strings works", "[Wrapper]") { REQUIRE(plaintext == decrypted); } +// Tests that encrypting and decrypting very long strings using the wrapper works. +// This test will start from scratch after encryption, to ensure EVERYTHING has to be re-calculated. +TEST_CASE(__FILE__"/Encrypting and decrypting strings works, Many blocks block", "[Wrapper]") { + + // Setup + + // Read an not-multiple-of-blocksize amount of random chars, that's very large (about 200kb long string) + srand(time(0)); + + std::stringstream ss; + const std::string charset = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + for (std::size_t i = 0; i < 198273; i++) { + ss << charset[rand() % charset.length()]; + } + + const std::string plaintext = ss.str(); + const Key key = Key::FromPassword("Der Affe will Zucker"); + + std::string ciphertext; + std::string decrypted; + + // Encryption + ciphertext = GWrapper::EncryptString(plaintext, key); + + // Decryption + decrypted = GWrapper::DecryptString(ciphertext, key); + + // Assertion + REQUIRE(plaintext == decrypted); +} + // Tests that encrypting and decrypting files using the wrapper works. // This test will start from scratch after encryption, to ensure EVERYTHING has to be re-calculated. TEST_CASE(__FILE__"/Encrypting and decrypting files works", "[Wrapper]") {