Skip to content

Instantly share code, notes, and snippets.

@mjs3339
Created January 2, 2018 20:36
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/e7c9d19bb4d92c64c5263e738172ebc7 to your computer and use it in GitHub Desktop.
Save mjs3339/e7c9d19bb4d92c64c5263e738172ebc7 to your computer and use it in GitHub Desktop.
C# Generic Cryptographically Strong Random Number Generator
public class CryptoRandomNumberGenerator<T> where T : struct
{
private static BigRational _maxValue;
private readonly byte[] _buffer;
private readonly BigRational _mult;
private readonly CryptoGetBytes _ncsp;
private int _availableCacheEntries;
private BigRational[] _cache;
private int _cacheptr;
private int _cacheSize = 1000000;
private T _type = default(T);
private bool _useCache;
/// <summary>
/// The seed is used to create a pseudo random number pattern.
/// </summary>
public CryptoRandomNumberGenerator(byte[] seed)
{
ValidTypes();
if (seed == null)
throw new ArgumentException("Seed value cannot be null.");
_ncsp = new CryptoGetBytes(seed);
_maxValue = BigIntegerHelper.GetMaxValue(_ncsp.HashSizeInBytes << 3);
_buffer = new byte[_ncsp.HashSizeInBytes];
_mult = BigRational.One / _maxValue;
}
public CryptoRandomNumberGenerator()
{
ValidTypes();
_ncsp = new CryptoGetBytes();
_ncsp.ResetOnFill = true;
_maxValue = BigIntegerHelper.GetMaxValue(_ncsp.HashSizeInBytes << 3);
_buffer = new byte[_ncsp.HashSizeInBytes];
_mult = BigRational.One / _maxValue;
}
public bool ResetOnFill
{
get => _ncsp.ResetOnFill;
set => _ncsp.ResetOnFill = value;
}
public bool UseCache
{
get => _useCache;
set
{
_useCache = value;
_cache = new BigRational[_cacheSize];
_cacheptr = 0;
_availableCacheEntries = 0;
}
}
public int CacheSize
{
get => _cacheSize;
set
{
_cacheSize = value;
_cache = new BigRational[_cacheSize];
_cacheptr = 0;
_availableCacheEntries = 0;
}
}
~CryptoRandomNumberGenerator()
{
_cache = null;
GC.SuppressFinalize(this);
}
/// <summary>
/// Clear the samples cache by overwriting it.
/// </summary>
public void ClearCache()
{
BuildCache();
_cache = new BigRational[_cacheSize];
}
private void BuildCache()
{
for (var i = 0; i < _cacheSize; ++i)
_cache[i] = GetNextSample();
_cacheptr = 0;
_availableCacheEntries = _cacheSize;
}
private BigRational GetNextSampleCache()
{
if (_availableCacheEntries == 0)
BuildCache();
_availableCacheEntries--;
var re = _cache[_cacheptr];
_cacheptr++;
return re;
}
private void ValidTypes()
{
if (_type is byte
|| _type is sbyte
|| _type is char
|| _type is short
|| _type is ushort
|| _type is int
|| _type is uint
|| _type is long
|| _type is ulong
|| _type is float
|| _type is double
|| _type is decimal
|| _type is BigInteger
|| _type is BigRational)
return;
throw new Exception($"The generic type(T)={_type.GetType()} must be a numeric type.");
}
/// <summary>
/// Returns a random number of type T
/// </summary>
public T Next()
{
var result = default(T);
if (_type is byte)
result = (T)(Sample() * (dynamic)byte.MaxValue);
if (_type is sbyte)
result = (T)(Sample() * (dynamic)sbyte.MaxValue);
if (_type is char)
result = (T)(Sample() * (dynamic)char.MaxValue);
if (_type is short)
result = (T)(Sample() * (dynamic)short.MaxValue);
if (_type is ushort)
result = (T)(Sample() * (dynamic)ushort.MaxValue);
if (_type is int)
result = (T)(Sample() * (dynamic)int.MaxValue);
if (_type is uint)
result = (T)(Sample() * (dynamic)uint.MaxValue);
if (_type is long)
result = (T)(Sample() * (dynamic)long.MaxValue);
if (_type is ulong)
result = (T)(Sample() * (dynamic)ulong.MaxValue);
if (_type is float)
result = (T)(Sample() * (dynamic)float.MaxValue);
if (_type is double)
result = (T)(Sample() * (dynamic)double.MaxValue);
if (_type is decimal)
result = (T)(Sample() * (dynamic)decimal.MaxValue);
if (_type is BigInteger)
result = (T)(Sample() * (dynamic)BigInteger.One);
if (_type is BigRational)
result = (T)(Sample() * (dynamic)BigRational.One);
return result;
}
/// <summary>
/// Returns a random number of type T less then maxValue
/// </summary>
public T Next(T maxValue)
{
if ((dynamic)maxValue < 0)
throw new ArgumentException("maxValue must be greater than zero.");
return Sample() * (dynamic)maxValue;
}
/// <summary>
/// Returns a random number of type T greater than minValue and less than maxValue.
/// </summary>
public T Next(T minValue, T maxValue)
{
if ((dynamic)maxValue < (dynamic)minValue)
throw new ArgumentException("maxValue must be greater than or equal to minValue");
return (T)(Sample() * ((dynamic)maxValue - (dynamic)minValue) + (dynamic)minValue);
}
/// <summary>
/// Returns an array of random numbers of type T.
/// </summary>
public T[] NextArray(int size)
{
var arr = new T[size];
for (var i = 0; i < size; ++i)
arr[i] = Next();
return arr;
}
private BigRational Sample()
{
var s = _useCache ? GetNextSampleCache() : GetNextSample() * _mult;
return s;
}
private BigRational GetNextSample()
{
BigInteger n;
var c = 0;
do
{
_ncsp.GetBytes(_buffer);
n = new BigInteger(_buffer.Concat(new byte[] { 0 }).ToArray());
c++;
} while (n >= _maxValue);
return n;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment