diff --git a/GCryptLib/visualizations/input-big-flip.bmp.png b/GCryptLib/visualizations/input-big-flip.bmp.png new file mode 100644 index 0000000..18127a3 Binary files /dev/null and b/GCryptLib/visualizations/input-big-flip.bmp.png differ diff --git a/GCryptLib/visualizations/input-big.bmp.png b/GCryptLib/visualizations/input-big.bmp.png new file mode 100644 index 0000000..88608c7 Binary files /dev/null and b/GCryptLib/visualizations/input-big.bmp.png differ diff --git a/GCryptLib/visualizations/input-big.gif b/GCryptLib/visualizations/input-big.gif new file mode 100644 index 0000000..2c251a9 Binary files /dev/null and b/GCryptLib/visualizations/input-big.gif differ diff --git a/GCryptLib/visualizations/input-flip.bmp.png b/GCryptLib/visualizations/input-flip.bmp.png new file mode 100644 index 0000000..72f56cc Binary files /dev/null and b/GCryptLib/visualizations/input-flip.bmp.png differ diff --git a/GCryptLib/visualizations/input.bmp.png b/GCryptLib/visualizations/input.bmp.png new file mode 100644 index 0000000..a6df3ca Binary files /dev/null and b/GCryptLib/visualizations/input.bmp.png differ diff --git a/GCryptLib/visualizations/input.gif b/GCryptLib/visualizations/input.gif new file mode 100644 index 0000000..8a9558d Binary files /dev/null and b/GCryptLib/visualizations/input.gif differ diff --git a/GCryptLib/visualizations/output-big-flip.bmp.png b/GCryptLib/visualizations/output-big-flip.bmp.png new file mode 100644 index 0000000..eca1a58 Binary files /dev/null and b/GCryptLib/visualizations/output-big-flip.bmp.png differ diff --git a/GCryptLib/visualizations/output-big.bmp.png b/GCryptLib/visualizations/output-big.bmp.png new file mode 100644 index 0000000..fdca11b Binary files /dev/null and b/GCryptLib/visualizations/output-big.bmp.png differ diff --git a/GCryptLib/visualizations/output-big.gif b/GCryptLib/visualizations/output-big.gif new file mode 100644 index 0000000..e653f10 Binary files /dev/null and b/GCryptLib/visualizations/output-big.gif differ diff --git a/GCryptLib/visualizations/output-flip.bmp.png b/GCryptLib/visualizations/output-flip.bmp.png new file mode 100644 index 0000000..bf90029 Binary files /dev/null and b/GCryptLib/visualizations/output-flip.bmp.png differ diff --git a/GCryptLib/visualizations/output.bmp.png b/GCryptLib/visualizations/output.bmp.png new file mode 100644 index 0000000..1e62f1a Binary files /dev/null and b/GCryptLib/visualizations/output.bmp.png differ diff --git a/GCryptLib/visualizations/output.gif b/GCryptLib/visualizations/output.gif new file mode 100644 index 0000000..f1ef561 Binary files /dev/null and b/GCryptLib/visualizations/output.gif differ diff --git a/readme.md b/readme.md index e6fd47a..ca84f5f 100644 --- a/readme.md +++ b/readme.md @@ -49,11 +49,11 @@ const std::string input = "I am a super secret message!"; std::cout << input << std::endl; // Encrypt -const std::string encrypted = GCryptWrapper::EncryptString(input, "password1"); +const std::string encrypted = GWrapper::EncryptString(input, Key::FromPassword("password1")); std::cout << encrypted << std::endl; // Decrypt -const std::string decrypted = GCryptWrapper::DecryptString(encrypted, "password1"); +const std::string decrypted = GWrapper::DecryptString(encrypted, Key::FromPassword("password1")); std::cout << decrypted << std::endl; ``` @@ -62,18 +62,38 @@ std::cout << decrypted << std::endl; using namespace Leonetienne::GCrypt; // Encrypt -GCryptWrapper::EncryptFile("main.cpp", "main.cpp.crypt", "password1"); +GCryptWrapper::EncryptFile("main.cpp", "main.cpp.crypt", Key::FromPassword("password1")); // Decrypt -GCryptWrapper::DecryptFile("main.cpp.crypt", "main.cpp.clear", "password1"); +GCryptWrapper::DecryptFile("main.cpp.crypt", "main.cpp.clear", Key::FromPassword("password1")); ``` -If you want to do more complex stuff, use the cipher-class [`GCrypt::Cipher`](https://gitea.leonetienne.de/leonetienne/GCrypt/src/branch/master/GCryptLib/include/GCrypt/Cipher.h) aswell as the conversion methods in [Util.h](https://gitea.leonetienne.de/leonetienne/GCrypt/src/branch/master/GCryptLib/include/GCrypt/Util.h). This way you can cipher on bitlevel. Examples on how to do this are in [GCryptWrapper.cpp](https://gitea.leonetienne.de/leonetienne/GCrypt/src/branch/master/GCryptLib/src/GCryptWrapper.cpp). -This way you could, for example, decrypt an ecrypted file directly into memory. Or use a full-length key instead of a password. +### Prefer keyfiles instead? +```cpp +using namespace Leonetienne::GCrypt; + +// Create a random key +const Key newKey = Key::Random(); // Will create a key from actual randomness (like, hardware events) + +// Use the key +GCryptWrapper::EncryptFile("main.cpp", "main.cpp.crypt", newKey); + +// Save the key to a keyfile +newKey.WriteToFile("/var/stuff/mykeyfile"); + +// ... + +// Load the key +const Key loadedKey = Key::LoadFromFile("/var/stuff/mykeyfile"); +``` + +If you want to do more complex stuff, use the cipher-class [`GCrypt::GCipher`](https://gitea.leonetienne.de/leonetienne/GCrypt/src/branch/master/GCryptLib/include/GCrypt/GCipher.h) aswell as the conversion methods in [Util.h](https://gitea.leonetienne.de/leonetienne/GCrypt/src/branch/master/GCryptLib/include/GCrypt/Util.h). +This way you can cipher on bitlevel. Examples on how to do this are in [GWrapper.cpp](https://gitea.leonetienne.de/leonetienne/GCrypt/src/branch/master/GCryptLib/src/GWrapper.cpp). +This way you could, for example, decrypt an ecrypted file directly into memory. Without saying, this is more advanced and not as-easy as the methods supplied in the wrapper. --- -\* 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://gitea.leonetienne.de/leonetienne/GCrypt/src/branch/master/GCryptLib/include/GCrypt/Config.h) or wherever it'll be put in the INCLUDE/*.cpp. `BLOCK_SIZE` is also the minimal output length! +\* 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://gitea.leonetienne.de/leonetienne/GCrypt/src/branch/master/GCryptLib/include/GCrypt/Config.h) or wherever it'll be put in the INCLUDE/\*.cpp. `BLOCK_SIZE` is also the minimal output length! ## The deets 🍝 @@ -83,23 +103,69 @@ Without saying, this is more advanced and not as-easy as the methods supplied in * [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 *Mi* being dependent on *E(Mi-1,Ki-1,0)* due to CBC, so is now *Ki* dependent on *Ki-1,r* 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. Just in case 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 -* Diffusion -* Ideally good collision resistance +How does GCrypt transform a password to a key?.. +Well, it uses the included hash function [GHash](https://gitea.leonetienne.de/leonetienne/GCrypt/src/branch/feature/relaunch/GCryptLib/include/GCrypt/GHash.h). -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, since *|P|, len\(p\) ∈ ℵ ≫ |C|, len(c)*. +### Hashing with GHash +GHash is a streaming hash function based on the GCipher. +For all intents and purposes, it does the following: +You have a *Block b*, which is initialized with a static random distribution. +Once you give the GHash instance a data block to digest, it will use the GCipher to encrypt it, with itself as a key, and xor that onto *b*. +(*bi = bi-1 ⊕ E(key=b, data=k)*) -How does it work? Basically, what happens is your password gets recoded to binary. It is then split into blocks of -size KEY_SIZE, and they are combined using *ci+1 = ci ⨁ E(c=blocki, k=blocki)*. *c0* is a static initialization vector. The final *c* is they key corresponding to a password. +The lastest *b* represents the current result of the hash function. -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.* +GHash also supports a do-it-all wrapper method that takes a Flexblock (A block of arbitrary length), and returns a hashsum for it. +This wrapper function adds an additional block including the length of the input. This wrapper function is used to transform Passwords to Keys. + +### GPrng...? +Whilst we're at it, why not implement a pseudo-random number generator based on GHash aswell. So here it is, [GPrng](https://gitea.leonetienne.de/leonetienne/GCrypt/src/branch/feature/relaunch/GCryptLib/include/GCrypt/GPrng.h). +GPrng is really nothing special. I just wanted to implement it, mainly to visualize the GCiphers entropy. + +GPrng basically does the following: It creates a GHash instance, which initially digested the prngs seed. This produces a hash result, which is one block in size. +This block gets eaten up, as pseudo-randomness is used. Once there are no bits left, the GHash instance will digest the result of this block ⊕ seed. +The xor operation ensures that an observer will never know the internal state of the GHash instance. This is important, as to ensure an observer won't be able to predict +future output. + +### Speaking of... Visualizations! +
+ +`"Hello :3"` in binary: +!["Hello :3" in binary](https://gitea.leonetienne.de/leonetienne/GCrypt/raw/branch/feature/relaunch/GCryptLib/visualizations/input.bmp.png) + + + +It's ciphertext: +![Ciphertext 1](https://gitea.leonetienne.de/leonetienne/GCrypt/raw/branch/feature/relaunch/GCryptLib/visualizations/output.bmp.png) + +
+ +Now, let's flip a single bit in the input: + +One bit flipped: +![One bit flipped](https://gitea.leonetienne.de/leonetienne/GCrypt/raw/branch/feature/relaunch/GCryptLib/visualizations/input-flip.bmp.png) + +It's ciphertext: +![Ciphertext for flipped bit](https://gitea.leonetienne.de/leonetienne/GCrypt/raw/branch/feature/relaunch/GCryptLib/visualizations/output-flip.bmp.png) + +Let's gif them together, to better see the difference... + +Input: +![Input](https://gitea.leonetienne.de/leonetienne/GCrypt/raw/branch/feature/relaunch/GCryptLib/visualizations/input.gif) + +Ciphertext: +![Ciphertext](https://gitea.leonetienne.de/leonetienne/GCrypt/raw/branch/feature/relaunch/GCryptLib/visualizations/output.gif) + + +What about input longer a single block? + +Input: +![Input](https://gitea.leonetienne.de/leonetienne/GCrypt/raw/branch/feature/relaunch/GCryptLib/visualizations/input-big.gif) + +Ciphertext: +![Ciphertext](https://gitea.leonetienne.de/leonetienne/GCrypt/raw/branch/feature/relaunch/GCryptLib/visualizations/output-big.gif) + +Notice how the ciphertext doesn't change until the block containing the bitflip is reached. This is a limitation of cipher block chaining. ## Noteworthy: * This is no fixed algorithm. Newer versions may very well be unable to decrypt ciphertexts encrypted with earlier versions.