Skip to content

Instantly share code, notes, and snippets.

@mjs3339
Last active January 8, 2018 16:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mjs3339/4e3aa309d13109ae3f63517fa4217f0f to your computer and use it in GitHub Desktop.
Save mjs3339/4e3aa309d13109ae3f63517fa4217f0f to your computer and use it in GitHub Desktop.
C# Implements a Cryptographically Strong GetBytes
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