Last active
January 8, 2018 16:32
-
-
Save mjs3339/4e3aa309d13109ae3f63517fa4217f0f to your computer and use it in GitHub Desktop.
C# Implements a Cryptographically Strong GetBytes
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
public class CryptoGetBytes | |
{ | |
private readonly SHA512Managed _Hasher = new SHA512Managed(); | |
private readonly SeedBytes _SeedBytes; | |
private int _AvailableCacheBytes; | |
private byte[] _Buffer; | |
private byte[] _Cache; | |
private int _Ptr; | |
public CryptoGetBytes() | |
{ | |
_Cache = new byte[CacheSize]; | |
_AvailableCacheBytes = 0; | |
_Ptr = 0; | |
_SeedBytes = new SeedBytes(); | |
_Buffer = _Hasher.ComputeHash(_SeedBytes.LargeSeedBuffer); | |
HashSizeInBytes = _Buffer.Length; | |
} | |
public CryptoGetBytes(byte[] seed) | |
{ | |
_Cache = new byte[CacheSize]; | |
_AvailableCacheBytes = 0; | |
_Ptr = 0; | |
_Buffer = _Hasher.ComputeHash(seed); | |
HashSizeInBytes = _Buffer.Length; | |
} | |
/// <summary> | |
/// The hash size in bytes determined by the hashing algorithm used. | |
/// </summary> | |
public int HashSizeInBytes { get; } | |
/// <summary> | |
/// The size of the cache in bytes | |
/// </summary> | |
public int CacheSize { get; set; } = 100000; | |
/// <summary> | |
/// Used to tune the SeedBytes class. | |
/// </summary> | |
public int CacheFills { get; private set; } | |
/// <summary> | |
/// Use the cache | |
/// </summary> | |
public bool UseCache { get; set; } = true; | |
/// <summary> | |
/// Each time the cache is filled a new seed will be generated. | |
/// </summary> | |
public bool ResetOnFill { get; set; } = false; | |
public void Reseed(byte[] seed = null) | |
{ | |
_Cache = new byte[CacheSize]; | |
_AvailableCacheBytes = 0; | |
_Ptr = 0; | |
_Buffer = seed == null ? _Hasher.ComputeHash(_SeedBytes.LargeSeedBuffer) : _Hasher.ComputeHash(seed); | |
} | |
/// <summary> | |
/// Returns the size data buffer filled with random bytes | |
/// </summary> | |
public byte[] GetBytes(int size) | |
{ | |
if (size == 0) | |
return null; | |
var data = new byte[size]; | |
if (!UseCache) | |
if (size <= _Hasher.HashSize >> 3) | |
{ | |
_Buffer = _Hasher.ComputeHash(_Buffer); | |
Buffer.BlockCopy(_Buffer, 0, data, 0, size); | |
return data; | |
} | |
else | |
{ | |
throw new Exception($"The data length requested '{size} Bytes' exceeds the maximum hashing buffer length '{_Hasher.HashSize << 3} Bytes' without caching enabled."); | |
} | |
var DataRequestLength = size; | |
if (_AvailableCacheBytes == 0 || _AvailableCacheBytes < DataRequestLength) | |
{ | |
if (CacheSize < DataRequestLength) | |
{ | |
CacheSize = DataRequestLength; | |
_Cache = new byte[CacheSize]; | |
} | |
FillCache(); | |
} | |
if (_Ptr + DataRequestLength > CacheSize) | |
FillCache(); | |
Buffer.BlockCopy(_Cache, _Ptr, data, 0, DataRequestLength); | |
_Ptr += DataRequestLength; | |
_AvailableCacheBytes -= DataRequestLength; | |
return data; | |
} | |
private void FillCache() | |
{ | |
CacheFills++; | |
_AvailableCacheBytes = 0; | |
_Ptr = 0; | |
var p = 0; | |
var moveSize = _Hasher.HashSize >> 3; | |
if (ResetOnFill) _Buffer = _Hasher.ComputeHash(_SeedBytes.LargeSeedBuffer); | |
do | |
{ | |
_Buffer = _Hasher.ComputeHash(_Buffer); | |
var blit = CacheSize - _AvailableCacheBytes; | |
if (blit < moveSize) | |
moveSize = blit; | |
if (p + moveSize >= CacheSize) | |
break; | |
if (_Cache.Length != CacheSize) | |
_Cache = new byte[CacheSize]; | |
Buffer.BlockCopy(_Buffer, 0, _Cache, p, moveSize); | |
p += moveSize; | |
_AvailableCacheBytes += moveSize; | |
} while (_AvailableCacheBytes < CacheSize); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment