Updated readme
This commit is contained in:
parent
9413e686f3
commit
4d083e6acf
110
SimpleTests/Password2Key_CollisionResistance.cpp
Normal file
110
SimpleTests/Password2Key_CollisionResistance.cpp
Normal 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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
@ -158,6 +158,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="EncryptEqualsDecrypt.cpp" />
|
<ClCompile Include="EncryptEqualsDecrypt.cpp" />
|
||||||
<ClCompile Include="GCWrapper.cpp" />
|
<ClCompile Include="GCWrapper.cpp" />
|
||||||
|
<ClCompile Include="Password2Key_CollisionResistance.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\GhettoCrypt\GhettoCrypt.vcxproj">
|
<ProjectReference Include="..\GhettoCrypt\GhettoCrypt.vcxproj">
|
||||||
|
@ -21,5 +21,8 @@
|
|||||||
<ClCompile Include="GCWrapper.cpp">
|
<ClCompile Include="GCWrapper.cpp">
|
||||||
<Filter>Quelldateien</Filter>
|
<Filter>Quelldateien</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="Password2Key_CollisionResistance.cpp">
|
||||||
|
<Filter>Quelldateien</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
33
readme.md
33
readme.md
@ -4,14 +4,14 @@
|
|||||||
|
|
||||||
## What the hell is this?
|
## What the hell is this?
|
||||||
An educational project on implementing a block cipher using a feistel network.
|
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 block cipher employs a few modes of operation. Read more about them [here](#modes-of-operation).
|
||||||
This way this provides relatively good diffusion.
|
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
* It has very easy syntax
|
* It has very easy syntax
|
||||||
* It's slow
|
* 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's probably super insecure
|
||||||
* It leaves your keys sprinkled in ram✨
|
|
||||||
* 512-bit keys <sup>\*</sup>
|
* 512-bit keys <sup>\*</sup>
|
||||||
* But the syntax is pythonlike easy🙇
|
* But the syntax is pythonlike easy🙇
|
||||||
|
|
||||||
@ -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!
|
<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\) ∈ ℵ ≫ |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 ⨁ 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
|
## LICENSE
|
||||||
```
|
```
|
||||||
BSD 2-Clause License
|
BSD 2-Clause License
|
||||||
|
Loading…
x
Reference in New Issue
Block a user