Last active
December 22, 2020 12:42
-
-
Save AdamWhiteHat/177e253ca400c757f7645a6cc65ba958 to your computer and use it in GitHub Desktop.
Cryptographically secure random number generator. Safe, secure, & correct implementation. Numbers are evenly distributed across entire range; avoids modulo bias. Internal buffers always cleared after every value returned--leaves nothing in memory. Singleton intended to be instantiated only once. Remember to call Dispose.
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
/// <summary> | |
/// Cryptographically secure random number generator. Features a safe, secure, and correct implementation. | |
/// Numbers are evenly distributed across the whole range; avoids modulo bias. | |
/// Internal buffers always cleared after every value returned--leaves nothing in memory. | |
/// Singleton intended to be instantiated only once for the lifetime of the application. | |
/// Remember to call Dispose when no longer needed. | |
/// </summary> | |
public static class CryptoRandomSingleton | |
{ | |
private static byte[] rngBytes4 = new byte[4]; | |
private static byte[] rngBytes8 = new byte[8]; | |
private static RNGCryptoServiceProvider _rng; | |
static CryptoRandomSingleton() { _rng = new RNGCryptoServiceProvider(); } | |
private static void ClearBuffer(byte[] buffer) | |
{ | |
if (buffer != null) | |
{ | |
int counter = buffer.Length - 1; | |
while (counter >= 0) | |
{ | |
buffer[counter] = byte.MinValue; | |
counter--; | |
} | |
counter = 0; | |
} | |
} | |
public static void Dispose() | |
{ | |
if (_rng != null) | |
{ | |
_rng.Dispose(); | |
_rng = null; | |
} | |
if (rngBytes4 != null) | |
{ | |
ClearBuffer(rngBytes4); | |
rngBytes4 = null; | |
} | |
if (rngBytes8 != null) | |
{ | |
ClearBuffer(rngBytes8); | |
rngBytes8 = null; | |
} | |
} | |
public static void NextBytes(byte[] buffer) | |
{ | |
_rng.GetBytes(buffer); | |
} | |
public static long Next() | |
{ | |
_rng.GetBytes(rngBytes8); | |
long result = Math.Abs(BitConverter.ToInt64(rngBytes8, 0)); | |
ClearBuffer(rngBytes8); | |
return result; | |
} | |
public static double NextDouble() | |
{ | |
_rng.GetBytes(rngBytes8); | |
double result = Math.Abs(BitConverter.ToInt64(rngBytes8, 0) * (1.0 / long.MaxValue)); | |
ClearBuffer(rngBytes8); | |
return result; | |
} | |
public static double NextDouble(double max) | |
{ | |
return NextDouble() * max; | |
} | |
public static double NextDouble(double lower, double upper) | |
{ | |
return lower + ((upper - lower) * NextDouble()); | |
} | |
public static IEnumerable<BigInteger> RandomRange(BigInteger lower, BigInteger upper, int quantity) | |
{ | |
while (quantity-- > 0) | |
{ | |
yield return RandomRange(lower, upper); | |
} | |
yield break; | |
} | |
public static BigInteger RandomRange(BigInteger lower, BigInteger upper) | |
{ | |
if (lower > upper) { throw new ArgumentOutOfRangeException("Upper must be greater than lower"); } | |
// long implementation | |
if (lower <= long.MaxValue && upper <= long.MaxValue) | |
{ | |
BigInteger range; | |
while (true) | |
{ | |
range = lower + (long)(((long)upper - (long)lower) * NextDouble()); | |
if (range >= lower && range <= upper) | |
{ | |
return range; | |
} | |
} | |
} | |
else // BigInteger implementation | |
{ | |
return RandomRangeBigInteger(lower, upper); | |
} | |
} | |
private static BigInteger RandomRangeBigInteger(BigInteger lower, BigInteger upper) | |
{ | |
if (lower > upper) { throw new ArgumentOutOfRangeException("Upper must be greater than upper"); } | |
BigInteger delta = upper - lower; | |
byte[] deltaBytes = delta.ToByteArray(); | |
byte[] buffer = new byte[deltaBytes.Length]; | |
ClearBuffer(deltaBytes); | |
deltaBytes = null; | |
BigInteger result; | |
while (true) | |
{ | |
NextBytes(buffer); | |
result = new BigInteger(buffer) + lower; | |
if (result >= lower && result <= upper) | |
{ | |
ClearBuffer(buffer); | |
return result; | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment