Created
January 2, 2018 20:36
-
-
Save mjs3339/e7c9d19bb4d92c64c5263e738172ebc7 to your computer and use it in GitHub Desktop.
C# Generic Cryptographically Strong Random Number Generator
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 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