Updated readme

This commit is contained in:
Leonetienne 2022-02-07 01:50:00 +01:00
parent 9413e686f3
commit 4d083e6acf
4 changed files with 145 additions and 4 deletions

View File

@ -0,0 +1,110 @@
#include "CppUnitTest.h"
#include "../GhettoCrypt/Util.h"
#include "../GhettoCrypt/Config.h"
#include <unordered_map>
#include <codecvt>
#include <sstream>
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
using namespace GhettoCipher;
// We can generate passwords by just translating a decimal number to base "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
inline std::string Base10_2_X(const unsigned long long int i, const std::string set, unsigned int padding)
{
if (set.length() == 0)
return ""; // Return empty string, if set is empty. Play stupid games, win stupid prizes.
std::stringstream ss;
if (i != 0)
{
{
unsigned long long int buf = i;
while (buf != 0)
{
const unsigned long long int mod = buf % set.length();
buf /= set.length();
ss << set[(std::size_t)mod];
}
}
{
const std::string buf = ss.str();
ss.str("");
for (long long int i = buf.length() - 1; i >= 0; i--)
ss << buf[(std::size_t)i];
}
}
else
{
ss << set[0]; // If i is 0, just pass a null-value. The algorithm would hang otherwise.
}
// Add as much null-values to the left as requested.
if (ss.str().length() < padding)
{
const std::size_t cachedLen = ss.str().length();
const std::string cachedStr = ss.str();
ss.str("");
for (std::size_t i = 0; i < padding - cachedLen; i++)
ss << set[0];
ss << cachedStr;
}
return ss.str();
}
using convert_t = std::codecvt_utf8<wchar_t>;
namespace SimpleTests
{
TEST_CLASS(Password2Key)
{
public:
// Run a few thousand random passwords through the keygen and see if we'll find a collision.
// This test passing does NOT mean that it's resistant! Maybe good, maybe shit! But if it fails, it's definitely shit.
// Already validated range: Password 0 - 1.000.000
TEST_METHOD(CollisionResistance)
{
// To test resistence set this to a high number around a million.
// This will take a LONG while to execute though (about 2.5hrs on my machine), hence why it's set so low.
constexpr std::size_t NUM_RUN_TESTS = 1000;
std::unordered_map<std::bitset<BLOCK_SIZE>, std::string> keys; // <key, password>
// Try NUM_RUN_TESTS passwords
const std::string charset = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
std::wstring_convert<convert_t, wchar_t> strconverter;
for (std::size_t i = 0; i < NUM_RUN_TESTS; i++)
{
// Get password
const std::string password = Base10_2_X(i, charset, 0);
// Generate key
const std::bitset<BLOCK_SIZE> newKey = PasswordToKey(password).Get();
// Check if this block is already in our map
if (keys.find(newKey) != keys.cend())
{
std::wstringstream wss;
wss << "Collision found between password \""
<< strconverter.from_bytes(password)
<< "\" and \""
<< strconverter.from_bytes(keys[newKey])
<< "\". The key is \""
<< newKey
<< "\".";
Assert::Fail(wss.str().c_str());
}
// All good? Insert it into our map
keys[newKey] = password;
}
return;
}
};
}

View File

@ -158,6 +158,7 @@
<ItemGroup>
<ClCompile Include="EncryptEqualsDecrypt.cpp" />
<ClCompile Include="GCWrapper.cpp" />
<ClCompile Include="Password2Key_CollisionResistance.cpp" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\GhettoCrypt\GhettoCrypt.vcxproj">

View File

@ -21,5 +21,8 @@
<ClCompile Include="GCWrapper.cpp">
<Filter>Quelldateien</Filter>
</ClCompile>
<ClCompile Include="Password2Key_CollisionResistance.cpp">
<Filter>Quelldateien</Filter>
</ClCompile>
</ItemGroup>
</Project>

View File

@ -4,16 +4,16 @@
## 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 modes of operation like *cipher block chaining*.
This way this provides relatively good diffusion.
This block cipher employs a few modes of operation. Read more about them [here](#modes-of-operation).
## Features
* It has very easy syntax
* It's slow
* It absolutely tanks your ram when working with files
* Even leaves some key fragments in there✨
* It's probably super insecure
* It leaves your keys sprinkled in ram✨
* 512-bit keys <sup>\*</sup>
* But the syntax is pythonlike easy🙇
* But the syntax is pythonlike easy🙇
It's pretty ghetto, you know?
@ -71,6 +71,33 @@ Without saying, this is more advanced and not as-easy as the methods supplied in
---
<sup>\*</sup> A key is always of size `BLOCK_SIZE`. The default block size is 512 (bit), but you can easily change it in [Config.h](https://github.com/Leonetienne/GhettoCrypt/blob/master/GhettoCrypt/Config.h) or wherever it'll be put in the INCLUDE/*.cpp. `BLOCK_SIZE` is also the minimal output length!
## The deets 🍝
### Modes of operation
* [CBC] This block cipher makes use of cipher block chaining. Nothing special.
* [IV] The initialization vector is indeed a bit of special sauce, as it depends on your key instead of being static. It is generated by running the feistel network on *E(m=seed, k=seed)*.
* [RRKM] Never heard of a mode like this, so i've named it **R**olling**R**ound**K**ey**M**ode. This basically means that the round key extrapolation is carried out continously over EVERY round on EVERY block. So in addition to *M<sub>i</sub>* being dependent on *E(M,K<sub>i-1,0</sub>)<sub>i-1</sub>* due to CBC, so is now *K<sub>i</sub>* dependent on *K<sub>i-1,r</sub>* with *r* being the maximum number of extrapolated keys within a call of E(). This is handled within the feistel network class, as an instance lifecycle sees all blocks, if you want to take a peek.
### Password to key
How does *GC* transform a password to a key?
First up, we have to establish what requirements this transformation must fulfill:
* A full key. Not just *len(passwd)\*8* bits and the rest zero-padded.
* Even if *len(passwd\*8) > KEY_SIZE*, every bit of the password should affect the key
* Ideally good collision resistance
Let's be honest, I'm not a cryptographer, i have no idea how collision resistant this is.
This means, it has to be considered *insecure*!
I have tried a few passwords brute-forcibly, experimentally (about 1mil) and have not been able to produce a collision.
Obviously there have to be collisions, because *|P|, len\(p\) &#8712; &#8501; &#8811; |C|*.
How does it work? Basically, what happens is your password gets recoded to binary. It is then split into blocks of
size KEY_SIZE, they are &xoplus; together, and this single block is then encrypted with itself as a key.
The end result is the key corresponding to your password.
This is a one-way operation. Since the key used for this operation is the cleartext itself, you cannot undo it without already
knowing the password(=cleartext) to begin with. *You could make a hashfunction out of this.*
## LICENSE
```
BSD 2-Clause License