diff --git a/ExampleApp/.gitignore b/ExampleApp/.gitignore new file mode 100644 index 0000000..366b7f9 --- /dev/null +++ b/ExampleApp/.gitignore @@ -0,0 +1,2 @@ +/main.cpp.crypt +/main.cpp.clear diff --git a/ExampleApp/main.cpp b/ExampleApp/main.cpp index 52851f2..c75ab7d 100644 --- a/ExampleApp/main.cpp +++ b/ExampleApp/main.cpp @@ -12,13 +12,13 @@ void ExampleString() std::cout << "Example on how to encrypt & decrypt a string:" << std::endl; // Get some string - const std::string input = "I am a super secret message!"; + const std::string input = "I am a super secret message!I am a super secret message!I am a super secret message!I am a super secret message!I am a super secret message!I am a super secret message!I am a super secret message!I am a super secret message!I am a super secret message!I am a super secret message!I am a super secret message!I am a super secret message!I am a super secret message!"; std::cout << input << std::endl; // Encrypt const std::string encrypted = GhettoCryptWrapper::EncryptString(input, "password1"); std::cout << encrypted << std::endl; - + // Decrypt const std::string decrypted = GhettoCryptWrapper::DecryptString(encrypted, "password1"); std::cout << decrypted << std::endl; diff --git a/GhettoCrypt/Cipher.cpp b/GhettoCrypt/Cipher.cpp index c17ca0a..ebd93a0 100644 --- a/GhettoCrypt/Cipher.cpp +++ b/GhettoCrypt/Cipher.cpp @@ -65,7 +65,7 @@ GhettoCipher::Flexblock GhettoCipher::Cipher::Encipher(const Flexblock& data, bo std::cout << "Encrypting... (Block " << i << " / " << blocks.size() << " - " << ((float)i*100 / blocks.size()) << "%)" << std::endl; const Block& lastBlock = (i>0) ? blocks[i-1] : initializationVector; - blocks[i] = feistel.Encipher(blocks[i] ^ lastBlock); + blocks[i] = feistel.Encipher(blocks[i] ^ lastBlock); // Xor last cipher block with new clear text block before E() } // Concatenate ciphertext blocks back into a flexblock @@ -101,7 +101,7 @@ GhettoCipher::Flexblock GhettoCipher::Cipher::Decipher(const Flexblock& data, bo Block tmpCopy = blocks[i]; - blocks[i] = feistel.Decipher(blocks[i]) ^ lastBlock; + blocks[i] = feistel.Decipher(blocks[i]) ^ lastBlock; // Decipher cipher block [i] and then xor it with the last cipher block [i-1] we've had lastBlock = std::move(tmpCopy); } diff --git a/GhettoCrypt/Cipher.h b/GhettoCrypt/Cipher.h index cea2985..c66a8d5 100644 --- a/GhettoCrypt/Cipher.h +++ b/GhettoCrypt/Cipher.h @@ -36,6 +36,6 @@ namespace GhettoCipher void ZeroKeyMemory(); // Initial value for cipher block chaining - const Block initializationVector; + Block initializationVector; }; } diff --git a/GhettoCrypt/Feistel.cpp b/GhettoCrypt/Feistel.cpp index 5ce8baa..0f4c81b 100644 --- a/GhettoCrypt/Feistel.cpp +++ b/GhettoCrypt/Feistel.cpp @@ -22,17 +22,17 @@ void GhettoCipher::Feistel::SetKey(const Block& key) return; } -GhettoCipher::Block GhettoCipher::Feistel::Encipher(const Block& data) const +GhettoCipher::Block GhettoCipher::Feistel::Encipher(const Block& data) { return Run(data, false); } -GhettoCipher::Block GhettoCipher::Feistel::Decipher(const Block& data) const +GhettoCipher::Block GhettoCipher::Feistel::Decipher(const Block& data) { return Run(data, true); } -GhettoCipher::Block GhettoCipher::Feistel::Run(const Block& data, bool reverseKeys) const +GhettoCipher::Block GhettoCipher::Feistel::Run(const Block& data, bool reverseKeys) { const auto splitData = FeistelSplit(data); GhettoCipher::Halfblock l = splitData.first; @@ -55,6 +55,10 @@ GhettoCipher::Block GhettoCipher::Feistel::Run(const Block& data, bool reverseKe l = tmp; } + // Block has finished de*ciphering. + // Let's generate a new set of round keys. + GenerateRoundKeys((Block)roundKeys.back()); + return FeistelCombine(r, l); } @@ -75,7 +79,7 @@ GhettoCipher::Halfblock GhettoCipher::Feistel::F(Halfblock m, const Block& key) std::stringstream ss; const std::string m_str = m_expanded.to_string(); - for (std::size_t i = 0; i < m_str.size(); i += 4) + for (std::size_t i = 0; i < BLOCK_SIZE; i += 4) { ss << SBox(m_str.substr(i, 4)); } @@ -113,7 +117,7 @@ GhettoCipher::Block GhettoCipher::Feistel::ExpansionFunction(const Halfblock& bl expansionMap["11"] = "0111"; // We have to double the bits! - for (std::size_t i = 0; i < bits.size(); i += 2) + for (std::size_t i = 0; i < HALFBLOCK_SIZE; i += 2) { const std::string sub = bits.substr(i, 2); ss << expansionMap[sub]; @@ -146,7 +150,7 @@ GhettoCipher::Halfblock GhettoCipher::Feistel::CompressionFunction(const Block& compressionMap["1111"] = "01"; // We have to half the bits! - for (std::size_t i = 0; i < bits.size(); i += 4) + for (std::size_t i = 0; i < BLOCK_SIZE; i += 4) { const std::string sub = bits.substr(i, 4); ss << compressionMap[sub]; @@ -185,19 +189,67 @@ std::string GhettoCipher::Feistel::SBox(const std::string& in) void GhettoCipher::Feistel::GenerateRoundKeys(const Block& seedKey) { - // Generate round keys via output feedback modus (OFM) method - // Clear initial key memory ZeroKeyMemory(); roundKeys = Keyset(); - // Generate new keys from the seed key - roundKeys[0] = seedKey; - roundKeys[1] = (Shiftl(seedKey, 32) ^ roundKeys[0]); + // Derive the initial two round keys + + // Compress- substitute, and expand the seed key to form the initial and the second-initial round key + // This action is non-linear and irreversible, and thus strenghtens security. + Halfblock compressedSeed1 = CompressionFunction(seedKey); + Halfblock compressedSeed2 = CompressionFunction(Shiftl(seedKey, 1)); // Shifting one key by 1 will result in a completely different compression + + // To add further confusion, let's shift seed1 by 1 aswell (after compression, but before substitution) + // but only if the total number of bits set are a multiple of 3 + // if it is a multiple of 4, we'll shift it by 1 into the opposite direction + const std::size_t setBits1 = compressedSeed1.count(); + + if (setBits1 % 4 == 0) + compressedSeed1 = Shiftr(compressedSeed1, 1); + else if (setBits1 % 3 == 0) + compressedSeed1 = Shiftl(compressedSeed1, 1); + + // Now apply substitution + std::stringstream ssKey1; + std::stringstream ssKey2; + const std::string bitsKey1 = compressedSeed1.to_string(); + const std::string bitsKey2 = compressedSeed2.to_string(); + + for (std::size_t i = 0; i < HALFBLOCK_SIZE; i += 4) + { + ssKey1 << SBox(bitsKey1.substr(i, 4)); + ssKey2 << SBox(bitsKey2.substr(i, 4)); + } + + compressedSeed1 = Halfblock(ssKey1.str()); + compressedSeed2 = Halfblock(ssKey2.str()); + + // Now extrapolate them to BLOCK_SIZE (key size) again + // Xor with the original seed key to get rid of the repititions caused by the expansion + roundKeys[0] = ExpansionFunction(compressedSeed1) ^ seedKey; + roundKeys[1] = ExpansionFunction(compressedSeed2) ^ seedKey; + + + // Now derive all other round keys for (std::size_t i = 2; i < roundKeys.size(); i++) { - roundKeys[i] = Shiftl(roundKeys[i - 1], i + 32) ^ roundKeys[i - 2]; + // Initialize new round key with last round key + Block newKey = roundKeys[i - 1]; + + // Shift to left by how many bits are set, modulo 8 + newKey = Shiftl(newKey, newKey.count() % 8); // This action is irreversible + + // Split into two halfblocks, + // apply F() to one halfblock with rk[i-2], + // xor the other one with it + // and put them back together + auto halfkeys = FeistelSplit(newKey); + Halfblock halfkey1 = F(halfkeys.first, roundKeys[i - 2]); + Halfblock halfkey2 = halfkeys.second ^ halfkey1; + + roundKeys[i] = FeistelCombine(halfkey1, halfkey2); } return; diff --git a/GhettoCrypt/Feistel.h b/GhettoCrypt/Feistel.h index 2dbc297..cae2a51 100644 --- a/GhettoCrypt/Feistel.h +++ b/GhettoCrypt/Feistel.h @@ -22,14 +22,14 @@ namespace GhettoCipher void SetKey(const Block& key); //! Will encipher a data block via the set seed-key - Block Encipher(const Block& data) const; + Block Encipher(const Block& data); //! Will decipher a data block via the set seed-key - Block Decipher(const Block& data) const; + Block Decipher(const Block& data); private: //! Will run the feistel rounds, with either regular key order or reversed key order - Block Run(const Block& data, bool reverseKeys) const; + Block Run(const Block& data, bool reverseKeys); //! Arbitrary cipher function static Halfblock F(Halfblock m, const Block& key); diff --git a/GhettoCrypt/GhettoCryptWrapper.cpp b/GhettoCrypt/GhettoCryptWrapper.cpp index 031b88b..b610ae7 100644 --- a/GhettoCrypt/GhettoCryptWrapper.cpp +++ b/GhettoCrypt/GhettoCryptWrapper.cpp @@ -1,11 +1,13 @@ #include "GhettoCryptWrapper.h" #include "Cipher.h" #include "Util.h" +#include std::string GhettoCipher::GhettoCryptWrapper::EncryptString(const std::string& cleartext, const std::string& password) { // Instanciate our cipher and supply a key - Cipher cipher(password); + const Block key = PasswordToKey(password); + Cipher cipher(key); // Recode the ascii-string to bits const Flexblock cleartext_bits = StringToBits(cleartext); @@ -23,7 +25,8 @@ std::string GhettoCipher::GhettoCryptWrapper::EncryptString(const std::string& c std::string GhettoCipher::GhettoCryptWrapper::DecryptString(const std::string& ciphertext, const std::string& password) { // Instanciate our cipher and supply a key - Cipher cipher(password); + const Block key = PasswordToKey(password); + Cipher cipher(key); // Recode the hex-string to bits const Flexblock ciphertext_bits = HexstringToBits(ciphertext); @@ -46,7 +49,8 @@ bool GhettoCipher::GhettoCryptWrapper::EncryptFile(const std::string& filename_i const Flexblock cleartext_bits = ReadFileToBits(filename_in); // Instanciate our cipher and supply a key - Cipher cipher(password); + const Block key = PasswordToKey(password); + Cipher cipher(key); // Encrypt our cleartext bits const Flexblock ciphertext_bits = cipher.Encipher(cleartext_bits, printProgressReport); @@ -70,7 +74,8 @@ bool GhettoCipher::GhettoCryptWrapper::DecryptFile(const std::string& filename_i const Flexblock ciphertext_bits = ReadFileToBits(filename_in); // Instanciate our cipher and supply a key - Cipher cipher(password); + const Block key = PasswordToKey(password); + Cipher cipher(key); // Decrypt the ciphertext bits const Flexblock cleartext_bits = cipher.Decipher(ciphertext_bits, printProgressReport); diff --git a/GhettoCrypt/InitializationVector.cpp b/GhettoCrypt/InitializationVector.cpp index c9e4a81..c1b9a8f 100644 --- a/GhettoCrypt/InitializationVector.cpp +++ b/GhettoCrypt/InitializationVector.cpp @@ -2,9 +2,18 @@ #include #include -using namespace GhettoCipher; +// It would be REALLY BAD if another compiler/*version would use +// a mersenne twister with different attrbitutes. It would basically mean +// that E_machine1(M,K) != E_machine2(M,K), which would make them incompatible. +// We do NOT want this to happen, so let's be VERY specific about what mersenne twister setup we want. +// This is std::mt19937, as of msvc stl. +using Prng_MT = std::mersenne_twister_engine< + unsigned int, + 32, 624, 397, 31, 0x9908b0df, 11, 0xffffffff, + 7, 0x9d2c5680, 15,0xefc60000, 18, 1812433253 +>; -InitializationVector::InitializationVector(const Block& seed) +GhettoCipher::InitializationVector::InitializationVector(const Block& seed) { // Since an initialization vector does not have to be a secret, // we should be fine just using a mersenne twister seeded with @@ -12,8 +21,7 @@ InitializationVector::InitializationVector(const Block& seed) // Loosely seed mersenne twister with seed // Here is nothing copied. Both Block::Get, and Hash<>::operator() take refs. - std::mt19937 mt = std::mt19937(std::hash>()(seed.Get())); - + Prng_MT mt = Prng_MT(std::hash>()(seed.Get())); // Now generate BLOCK_SIZE urandom bits std::stringstream ss; for (std::size_t i = 0; i < BLOCK_SIZE; i++) @@ -23,7 +31,7 @@ InitializationVector::InitializationVector(const Block& seed) iv = Block(ss.str()); } -InitializationVector::operator GhettoCipher::Block() const +GhettoCipher::InitializationVector::operator GhettoCipher::Block() const { return iv; } diff --git a/GhettoCrypt/InitializationVector.h b/GhettoCrypt/InitializationVector.h index 959504f..0ae2728 100644 --- a/GhettoCrypt/InitializationVector.h +++ b/GhettoCrypt/InitializationVector.h @@ -2,15 +2,18 @@ #include "Config.h" #include "Block.h" -/** Will create a sudo-random Block based on a seed -*/ -class InitializationVector +namespace GhettoCipher { -public: - InitializationVector(const GhettoCipher::Block& seed); + /** Will create a sudo-random Block based on a seed + */ + class InitializationVector + { + public: + InitializationVector(const GhettoCipher::Block& seed); - operator GhettoCipher::Block() const; + operator GhettoCipher::Block() const; -private: - GhettoCipher::Block iv; -}; + private: + GhettoCipher::Block iv; + }; +} diff --git a/GhettoCrypt/Util.h b/GhettoCrypt/Util.h index a4a8cf4..db60f1b 100644 --- a/GhettoCrypt/Util.h +++ b/GhettoCrypt/Util.h @@ -5,6 +5,7 @@ #include "SecureBitset.h" #include "Block.h" #include "Flexblock.h" +#include "InitializationVector.h" namespace GhettoCipher { @@ -90,8 +91,8 @@ namespace GhettoCipher return Flexblock(ss.str()); } - //! Will convert a fixed-size data block to a string - inline std::string BitblockToString(const Block& bits) + //! Will convert a fixed-size data block to a bytestring + inline std::string BitblockToBytes(const Block& bits) { std::stringstream ss; @@ -102,7 +103,15 @@ namespace GhettoCipher ss << (char)std::bitset<8>(bitstring.substr(i, 8)).to_ulong(); } - std::string text = ss.str(); + return ss.str(); + } + + //! Will convert a fixed-size data block to a string + //! The difference to BitblockToBytes() is, that it strips excess nullbytes + inline std::string BitblockToString(const Block& bits) + { + // Decode to bytes + std::string text = BitblockToBytes(bits); // Dümp excess nullbytes text.resize(strlen(text.data())); @@ -110,8 +119,8 @@ namespace GhettoCipher return text; } - //! Will convert a flexible data block to a string - inline std::string BitsToString(const Flexblock& bits) + //! Will convert a flexible data block to a bytestring + inline std::string BitsToBytes(const Flexblock& bits) { std::stringstream ss; @@ -122,7 +131,15 @@ namespace GhettoCipher ss << (char)std::bitset<8>(bitstring.substr(i, 8)).to_ulong(); } - std::string text = ss.str(); + return ss.str(); + } + + //! Will convert a flexible data block to a string + //! //! The difference to BitsToBytes() is, that it strips excess nullbytes + inline std::string BitsToString(const Flexblock& bits) + { + // Decode to bytes + std::string text = BitsToBytes(bits); // Dümp excess nullbytes text.resize(strlen(text.data())); @@ -212,8 +229,10 @@ namespace GhettoCipher } //! Creates a key of size BLOCK_SIZE from a password of arbitrary length. - //! Using passwords larger (in bits) than BLOCK_SIZE is not generally recommended. - //! Note that if your password is shorter (in bits) than BLOCK_SIZE, the rest of the key will be padded with 0x0. Further round-keys will be extrapolated though. + //! Using passwords larger (in bits) than BLOCK_SIZE is generally not recommended. + //! Note that if your password is shorter (in bits) than BLOCK_SIZE, the rest of the key will be padded with 0 (see next line!). + //! To provide a better initial key, (and to get rid of padding zeroes), the raw result (b) will be xor'd with an initialization vector based on b. + //! : return b ^ iv(b) inline Block PasswordToKey(const std::string& in) { Block b; @@ -224,7 +243,7 @@ namespace GhettoCipher PadStringToLength(in.substr(i, BLOCK_SIZE / 8), BLOCK_SIZE / 8, 0, false) ); - return b; + return b ^ InitializationVector(b); } //! Will read a file into a flexblock @@ -255,7 +274,7 @@ namespace GhettoCipher inline void WriteBitsToFile(const std::string& filepath, const Flexblock& bits) { // Convert bits to bytes - const std::string bytes = BitsToString(bits); + const std::string bytes = BitsToBytes(bits); // Write bits to file std::ofstream ofs(filepath, std::ios::binary); diff --git a/GhettoCrypt/Version.h b/GhettoCrypt/Version.h index 4077bbb..5667d5b 100644 --- a/GhettoCrypt/Version.h +++ b/GhettoCrypt/Version.h @@ -1,2 +1,2 @@ #pragma once -#define GHETTOCRYPT_VERSION 0.13 +#define GHETTOCRYPT_VERSION 0.2 diff --git a/SimpleTests/.gitignore b/SimpleTests/.gitignore new file mode 100644 index 0000000..b71c923 --- /dev/null +++ b/SimpleTests/.gitignore @@ -0,0 +1,2 @@ +/testfile.png.crypt +/testfile.png.clear.png diff --git a/SimpleTests/EncryptEqualsDecrypt.cpp b/SimpleTests/EncryptEqualsDecrypt.cpp index c5e2070..0ea059c 100644 --- a/SimpleTests/EncryptEqualsDecrypt.cpp +++ b/SimpleTests/EncryptEqualsDecrypt.cpp @@ -5,28 +5,31 @@ using namespace Microsoft::VisualStudio::CppUnitTestFramework; using namespace GhettoCipher; +// THESE TESTS ASSUME BLOCK_SIZE == 512 + namespace SimpleTests { TEST_CLASS(EncryptEqualsDecrypt) { public: - // Tests that a decrypted ciphertext equals its plaintrext version - TEST_METHOD(tEncryptEqualsDecrypt) + // Tests that encrypting a message of exactly BLOCK_SIZE yields the exact message back + TEST_METHOD(SingleBlock_NoPadding) { - // Yes, this unit test should ideally exclude string conversions, - // But like this it's easier to see what it's doing - - // Define basic input - const std::string cleartext = "Hello, World!"; - const std::string password = "1234"; - - // Instanciate our cipher and supply a key - const Cipher cipher(password); + const Block key = PasswordToKey("1234"); + const Cipher cipher(key); // Recode the ascii-string to bits - const Flexblock cleartext_bits = StringToBits(cleartext); + const Flexblock cleartext_bits = + "1011110011010110000010110001111000111010111101001010100100011101" + "0101110101010010100000110100001000011000111010001001110101111111" + "1110110101100101110001010101011110001010000010111110011011010111" + "1100110100111000000011100101010100110010001110010011000010111001" + "0000010000010000011001111010011110111001000000000110101000110001" + "0110111110110110100000010100000011010001000011100100111001001011" + "1101100100000100010000001011100010010001101111100100101100010001" + "0000011110010110111010110110111110011110011010001100100111110101"; // Encrypt our cleartext bits const Flexblock ciphertext_bits = cipher.Encipher(cleartext_bits); @@ -34,11 +37,194 @@ namespace SimpleTests // Decipher it again const Flexblock decryptedBits = cipher.Decipher(ciphertext_bits); - // Decode it back to ascii - const std::string decryptedText = BitsToString(decryptedBits); + // Assert that the decrypted text equals the plaintext + Assert::AreEqual( + cleartext_bits, + decryptedBits + ); + } + + // Tests that encrypting a message of less than BLOCK_SIZE yields the exact message plus zero-padding back + TEST_METHOD(SingleBlock_Padding) + { + // Instanciate our cipher and supply a key + const Block key = PasswordToKey("1234"); + const Cipher cipher(key); + + // Recode the ascii-string to bits + const Flexblock cleartext_bits = + "1011110011010110000010110001111000111010111101001010100100011101" + "0101110101010010100000110100001000011000111010001001110101111111" + "1110110101100101110001010101011110001010000010111110011011010111" + "1100110100111000000011100101010100110010001110010011000010111001" + "0000010000010000011001111010011110111001000000000110101000110001" + "0110111110110110100000010100000011010001000011100100111001001011" + "1101100100000100"; + + const Flexblock cleartext_bits_EXPECTED_RESULT = + "1011110011010110000010110001111000111010111101001010100100011101" + "0101110101010010100000110100001000011000111010001001110101111111" + "1110110101100101110001010101011110001010000010111110011011010111" + "1100110100111000000011100101010100110010001110010011000010111001" + "0000010000010000011001111010011110111001000000000110101000110001" + "0110111110110110100000010100000011010001000011100100111001001011" + "1101100100000100000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000"; + + // Encrypt our cleartext bits + const Flexblock ciphertext_bits = cipher.Encipher(cleartext_bits); + + // Decipher it again + const Flexblock decryptedBits = cipher.Decipher(ciphertext_bits); // Assert that the decrypted text equals the plaintext - Assert::AreEqual(cleartext.length(), decryptedText.length()); + Assert::AreEqual( + cleartext_bits_EXPECTED_RESULT, + decryptedBits + ); + } + + // Tests that a decrypted ciphertext equals its plaintrext version, using a cleartext that requires A LOT of blocks + TEST_METHOD(MultiBlock_NoPadding) + { + // Instanciate our cipher and supply a key + const Block key = PasswordToKey("1234"); + const Cipher cipher(key); + + // Recode the ascii-string to bits + const Flexblock cleartext_bits = + "1011110011010110000010110001111000111010111101001010100100011101" + "0101110101010010100000110100001000011000111010001001110101111111" + "1110110101100101110001010101011110001010000010111110011011010111" + "1100110100111000000011100101010100110010001110010011000010111001" + "0000010000010000011001111010011110111001000000000110101000110001" + "0110111110110110100000010100000011010001000011100100111001001011" + "1101100100000100010000001011100010010001101111100100101100010001" + "0000011110010110111010110110111110011110011010001100100111110101" + "1000010010000000000100101011110001000101101101100000010011111011" + "1011111010110100100111100111110011100001111101111110000110001100" + "0001000111000111101110000111011011101010100010100101100111010100" + "0101111110110010110000111111011001101110101101100100100011000100" + "1000110010101001000100001001101000011111101011111100100000100101" + "1100001100111001011111001101000111011101011101000110010110110110" + "0111001010011010010000010110000110010101101100101110111100100011" + "0010111110011100010100000101100101110101101011110100100000110110" + "1001101110101001001111111000010100011100100000101000111101101111" + "0101111011110001101010111010001000111010101111001101100100100100" + "1110110111001100011010110000101000011001011100101100111101110000" + "1010101111011110000111011011011110000111010110110111111010101010" + "0111100101111001010111101000001010100000111010111100111011111001" + "0110111000000110100011011100101101010101101000010010011111100100" + "0010111000001011101110000110010011101001111010100111110111110101" + "1110111000000000101011000100101010000110110111101010011001111010" + "1101011110001110000011010111001100001100101000000101000101000010" + "0101000011011111010010110010000010101100001110011000110111110111" + "1110010101011110111001100010110101101011100111100011101010001011" + "0101110010100110101100111100010000111101111100000111000110110110" + "1001100111000000011010100000011101011000010010011010001011110000" + "1100100111111001001000011100110000011110001100000000010000001001" + "1110000000110010000010011010100011011011000000000111110000110111" + "0101110011001101010110010100011001110110000110010001100110011111"; + + // Encrypt our cleartext bits + const Flexblock ciphertext_bits = cipher.Encipher(cleartext_bits); + + // Decipher it again + const Flexblock decryptedBits = cipher.Decipher(ciphertext_bits); + + // Assert that the decrypted text equals the plaintext + Assert::AreEqual( + cleartext_bits, + decryptedBits + ); + } + + // Tests that a decrypted ciphertext equals its plaintrext version, using a cleartext that requires A LOT of blocks + TEST_METHOD(MultiBlock_Padding) + { + // Instanciate our cipher and supply a key + const Block key = PasswordToKey("1234"); + const Cipher cipher(key); + + // Recode the ascii-string to bits + const Flexblock cleartext_bits = + "1011110011010110000010110001111000111010111101001010100100011101" + "0101110101010010100000110100001000011000111010001001110101111111" + "1110110101100101110001010101011110001010000010111110011011010111" + "1100110100111000000011100101010100110010001110010011000010111001" + "0000010000010000011001111010011110111001000000000110101000110001" + "0110111110110110100000010100000011010001000011100100111001001011" + "1101100100000100010000001011100010010001101111100100101100010001" + "0000011110010110111010110110111110011110011010001100100111110101" + "1000010010000000000100101011110001000101101101100000010011111011" + "1011111010110100100111100111110011100001111101111110000110001100" + "0001000111000111101110000111011011101010100010100101100111010100" + "0101111110110010110000111111011001101110101101100100100011000100" + "1000110010101001000100001001101000011111101011111100100000100101" + "1100001100111001011111001101000111011101011101000110010110110110" + "0111001010011010010000010110000110010101101100101110111100100011" + "0010111110011100010100000101100101110101101011110100100000110110" + "1001101110101001001111111000010100011100100000101000111101101111" + "0101111011110001101010111010001000111010101111001101100100100100" + "1110110111001100011010110000101000011001011100101100111101110000" + "1010101111011110000111011011011110000111010110110111111010101010" + "0111100101111001010111101000001010100000111010111100111011111001" + "0110111000000110100011011100101101010101101000010010011111100100" + "0010111000001011101110000110010011101001111010100111110111110101" + "1110111000000000101011000100101010000110110111101010011001111010" + "1101011110001110000011010111001100001100101000000101000101000010" + "0101000011011111010010110010000010101100001110011000110111110111" + "1110010101011110111001100010110101101011100111100011101010001011" + "0101110010100110101100111100010000111101111100000111000110110110" + "1001100111000000011010100000011101011000010010011010001011110000" + "1100100111111001001000011100110000011110001100000000010000001001" + "11100000001100100000100110101000110110110000000001111100001"; + + const Flexblock cleartext_bits_EXPECTED_RESULT = + "1011110011010110000010110001111000111010111101001010100100011101" + "0101110101010010100000110100001000011000111010001001110101111111" + "1110110101100101110001010101011110001010000010111110011011010111" + "1100110100111000000011100101010100110010001110010011000010111001" + "0000010000010000011001111010011110111001000000000110101000110001" + "0110111110110110100000010100000011010001000011100100111001001011" + "1101100100000100010000001011100010010001101111100100101100010001" + "0000011110010110111010110110111110011110011010001100100111110101" + "1000010010000000000100101011110001000101101101100000010011111011" + "1011111010110100100111100111110011100001111101111110000110001100" + "0001000111000111101110000111011011101010100010100101100111010100" + "0101111110110010110000111111011001101110101101100100100011000100" + "1000110010101001000100001001101000011111101011111100100000100101" + "1100001100111001011111001101000111011101011101000110010110110110" + "0111001010011010010000010110000110010101101100101110111100100011" + "0010111110011100010100000101100101110101101011110100100000110110" + "1001101110101001001111111000010100011100100000101000111101101111" + "0101111011110001101010111010001000111010101111001101100100100100" + "1110110111001100011010110000101000011001011100101100111101110000" + "1010101111011110000111011011011110000111010110110111111010101010" + "0111100101111001010111101000001010100000111010111100111011111001" + "0110111000000110100011011100101101010101101000010010011111100100" + "0010111000001011101110000110010011101001111010100111110111110101" + "1110111000000000101011000100101010000110110111101010011001111010" + "1101011110001110000011010111001100001100101000000101000101000010" + "0101000011011111010010110010000010101100001110011000110111110111" + "1110010101011110111001100010110101101011100111100011101010001011" + "0101110010100110101100111100010000111101111100000111000110110110" + "1001100111000000011010100000011101011000010010011010001011110000" + "1100100111111001001000011100110000011110001100000000010000001001" + "1110000000110010000010011010100011011011000000000111110000100000" + "0000000000000000000000000000000000000000000000000000000000000000"; + + // Encrypt our cleartext bits + const Flexblock ciphertext_bits = cipher.Encipher(cleartext_bits); + + // Decipher it again + const Flexblock decryptedBits = cipher.Decipher(ciphertext_bits); + + // Assert that the decrypted text equals the plaintext + Assert::AreEqual( + cleartext_bits_EXPECTED_RESULT, + decryptedBits + ); } }; } diff --git a/SimpleTests/GCWrapper.cpp b/SimpleTests/GCWrapper.cpp new file mode 100644 index 0000000..24da90d --- /dev/null +++ b/SimpleTests/GCWrapper.cpp @@ -0,0 +1,81 @@ +#include "CppUnitTest.h" +#include "../GhettoCrypt/GhettoCryptWrapper.h" +#include "../GhettoCrypt/Flexblock.h" +#include "../GhettoCrypt/Util.h" + +using namespace Microsoft::VisualStudio::CppUnitTestFramework; +using namespace GhettoCipher; + +namespace SimpleTests +{ + TEST_CLASS(GCWrapper) + { + public: + + // Tests that encrypting and decrypting strings using the wrapper works. + // This test will start from scratch after encryption, to ensure EVERYTHING has to be re-calculated. + TEST_METHOD(String) + { + // Setup + const std::string plaintext = "Hello, World!"; + const std::string password = "Der Affe will Zucker"; + + std::string ciphertext; + std::string decrypted; + + // Encryption + { + ciphertext = GhettoCryptWrapper::EncryptString(plaintext, password); + } + + // Decryption + { + decrypted = GhettoCryptWrapper::DecryptString(ciphertext, password); + } + + // Assertion + Assert::AreEqual( + 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_METHOD(File) + { + // Setup + #if defined _WIN64 + const std::string testfile_dir = "../../SimpleTests/"; + #elif defined _WIN32 + const std::string testfile_dir = "../SimpleTests/"; + #endif + + const std::string filename_plain = testfile_dir + "testfile.png"; + const std::string filename_encrypted = testfile_dir + "testfile.png.crypt"; + const std::string filename_decrypted = testfile_dir + "testfile.png.clear.png"; + const std::string password = "Der Affe will Zucker"; + + + // Encryption + { + GhettoCryptWrapper::EncryptFile(filename_plain, filename_encrypted, password); + } + + // Decryption + { + GhettoCryptWrapper::DecryptFile(filename_encrypted, filename_decrypted, password); + } + + // Read in both the base, and the decrypted file + const Flexblock plainfile = ReadFileToBits(filename_plain); + const Flexblock decryptfile = ReadFileToBits(filename_decrypted); + + // Assertion (If this fails, maybe check if the image is even readable by an image viewer) + Assert::AreEqual( + PadStringToLength(plainfile, decryptfile.length(), '0', false), + decryptfile + ); + } + }; +} diff --git a/SimpleTests/SimpleTests.vcxproj b/SimpleTests/SimpleTests.vcxproj index 5c1d799..1318a25 100644 --- a/SimpleTests/SimpleTests.vcxproj +++ b/SimpleTests/SimpleTests.vcxproj @@ -157,6 +157,7 @@ + diff --git a/SimpleTests/SimpleTests.vcxproj.filters b/SimpleTests/SimpleTests.vcxproj.filters index 88a0ce6..a66a1ff 100644 --- a/SimpleTests/SimpleTests.vcxproj.filters +++ b/SimpleTests/SimpleTests.vcxproj.filters @@ -18,5 +18,8 @@ Quelldateien + + Quelldateien + \ No newline at end of file diff --git a/SimpleTests/testfile.png b/SimpleTests/testfile.png new file mode 100644 index 0000000..e8c9786 Binary files /dev/null and b/SimpleTests/testfile.png differ diff --git a/readme.md b/readme.md index 524ecf5..73c3eae 100644 --- a/readme.md +++ b/readme.md @@ -4,7 +4,7 @@ ## What the hell is this? An educational project on implementing a block cipher using a feistel network. -To provide at least some security this is using some DES-inspired tactics like *cipher block chaining* and *output feedback modus*. +To provide at least some security this is using some DES-inspired modes of operation like *cipher block chaining*. This way this provides relatively good diffusion. ## Features