Created
February 20, 2018 07:45
-
-
Save AyrA/5cbb0ed3cbef52c1a94faf72b76f446d to your computer and use it in GitHub Desktop.
C# Custom Key derivation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Linq; | |
using System.Security.Cryptography; | |
/* | |
I am in no way claiming that this is secure. | |
It merely serves as an example on how a key derivation function could work. | |
Features: | |
- Configurable Hash algorithm and iteration count | |
- Can be reset to initial state | |
- (Probably) Thread safe | |
- Gets slower the more bytes are requested | |
- Fully commented | |
*/ | |
namespace Test | |
{ | |
/// <summary> | |
/// Generic Key Derivation Class | |
/// </summary> | |
public class KDF : DeriveBytes | |
{ | |
/// <summary> | |
/// Default number of bytes for Salt | |
/// </summary> | |
public const int SALT_SIZE = 32; | |
/// <summary> | |
/// Default number of bytes for Key | |
/// </summary> | |
public const int KEY_SIZE = 32; | |
/// <summary> | |
/// Default rounds for each iteration | |
/// </summary> | |
public const int DEFAULT_ROUNDS = 10000; | |
/// <summary> | |
/// Hasher for Key Derivation | |
/// </summary> | |
private HashAlgorithm Hasher; | |
/// <summary> | |
/// Number of Rounds | |
/// </summary> | |
private int Rounds; | |
/// <summary> | |
/// Salt | |
/// </summary> | |
private byte[] _Salt; | |
/// <summary> | |
/// Key | |
/// </summary> | |
private byte[] _Key; | |
/// <summary> | |
/// KDF State | |
/// </summary> | |
private byte[] Data; | |
/// <summary> | |
/// Initial KDF State | |
/// </summary> | |
private byte[] InitialData; | |
/// <summary> | |
/// Gets the user supplied or generated salt | |
/// </summary> | |
public byte[] Salt | |
{ | |
get | |
{ | |
return (byte[])_Salt.Clone(); | |
} | |
} | |
/// <summary> | |
/// Gets the user supplied or generated Key | |
/// </summary> | |
public byte[] Key | |
{ | |
get | |
{ | |
return (byte[])_Key.Clone(); | |
} | |
} | |
/// <summary> | |
/// Initializes a new instance of KDF | |
/// </summary> | |
/// <param name="Hasher">Hash Algorithm for key derivation</param> | |
/// <param name="Salt">Salt, uses random bytes if null</param> | |
/// <param name="Key">Key, uses random bytes if null</param> | |
/// <param name="Rounds">Number of rounds for each requested byte</param> | |
public KDF(HashAlgorithm Hasher, byte[] Salt = null, byte[] Key = null, int Rounds = DEFAULT_ROUNDS) | |
{ | |
var RNG = RandomNumberGenerator.Create(); | |
//Hasher must be specified | |
if (Hasher == null) | |
{ | |
throw new ArgumentNullException("Hasher"); | |
} | |
//Rounds must be sensible number | |
if (Rounds < 1) | |
{ | |
throw new ArgumentOutOfRangeException("Rounds needs to be at least one"); | |
} | |
//Generate salt if needed | |
if (Salt == null || Salt.Length == 0) | |
{ | |
Salt = new byte[SALT_SIZE]; | |
RNG.GetBytes(Salt); | |
} | |
//Generate key if needed | |
if (Key == null || Key.Length == 0) | |
{ | |
Key = new byte[KEY_SIZE]; | |
RNG.GetBytes(Key); | |
} | |
//Store initial State | |
this.Hasher = Hasher; | |
this.Rounds = Rounds; | |
_Salt = (byte[])Salt.Clone(); | |
_Key = (byte[])Key.Clone(); | |
Data = Salt.Concat(Key).ToArray(); | |
InitialData = (byte[])Data.Clone(); | |
} | |
/// <summary> | |
/// Requests a number of bytes from the generator | |
/// </summary> | |
/// <param name="cb">Number of bytes</param> | |
/// <returns>Number of bytes requested</returns> | |
public override byte[] GetBytes(int cb) | |
{ | |
//cb must make sense | |
if (cb < 0) | |
{ | |
throw new ArgumentOutOfRangeException("cb"); | |
} | |
var ret = new byte[cb]; | |
lock (Hasher) | |
{ | |
//Apply for each individual byte | |
for (var pos = 0; pos < cb; pos++) | |
{ | |
//Skip iterations according to user specified number of rounds | |
for (var iter = 0; iter < Rounds; iter++) | |
{ | |
Data = Hasher.ComputeHash(Data); | |
//Optionally do some CONSTANT fuckery with the hash | |
//Data[Data.Length - 1] = Data[Data[0] % Data.Length]; | |
//Data = Data.Reverse().ToArray(); | |
} | |
//Get pseudorandom byte | |
//This doesn't directly adds security but makes an attacker care for the entire result | |
ret[pos] = Data[Data[0] % Data.Length]; | |
//Simple version: | |
//ret[pos] = Data[0]; | |
} | |
} | |
return ret; | |
} | |
/// <summary> | |
/// Reset Component to initial State | |
/// </summary> | |
public override void Reset() | |
{ | |
lock (Hasher) | |
{ | |
Data = (byte[])InitialData.Clone(); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment