using System;
using System.Threading;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Digests;
using Org.BouncyCastle.Crypto.Prng;
using Org.BouncyCastle.Utilities;
namespace Org.BouncyCastle.Security
{
public class SecureRandom
: Random
{
private static long counter = Times.NanoTime();
#if NETCF_1_0 || PORTABLE
private static object counterLock = new object();
private static long NextCounterValue()
{
lock (counterLock)
{
return ++counter;
}
}
private static readonly SecureRandom[] master = { null };
private static SecureRandom Master
{
get
{
lock (master)
{
if (master[0] == null)
{
SecureRandom sr = master[0] = GetInstance("SHA256PRNG", false);
// Even though Ticks has at most 8 or 14 bits of entropy, there's no harm in adding it.
sr.SetSeed(DateTime.Now.Ticks);
// 32 will be enough when ThreadedSeedGenerator is fixed. Until then, ThreadedSeedGenerator returns low
// entropy, and this is not sufficient to be secure. http://www.bouncycastle.org/csharpdevmailarchive/msg00814.html
sr.SetSeed(new ThreadedSeedGenerator().GenerateSeed(32, true));
}
return master[0];
}
}
}
#else
private static long NextCounterValue()
{
return Interlocked.Increment(ref counter);
}
private static readonly SecureRandom master = new SecureRandom(new CryptoApiRandomGenerator());
private static SecureRandom Master
{
get { return master; }
}
#endif
private static DigestRandomGenerator CreatePrng(string digestName, bool autoSeed)
{
IDigest digest = DigestUtilities.GetDigest(digestName);
if (digest == null)
return null;
DigestRandomGenerator prng = new DigestRandomGenerator(digest);
if (autoSeed)
{
prng.AddSeedMaterial(NextCounterValue());
prng.AddSeedMaterial(GetNextBytes(Master, digest.GetDigestSize()));
}
return prng;
}
public static byte[] GetNextBytes(SecureRandom secureRandom, int length)
{
byte[] result = new byte[length];
secureRandom.NextBytes(result);
return result;
}
///
/// Create and auto-seed an instance based on the given algorithm.
///
/// Equivalent to GetInstance(algorithm, true)
/// e.g. "SHA256PRNG"
public static SecureRandom GetInstance(string algorithm)
{
return GetInstance(algorithm, true);
}
///
/// Create an instance based on the given algorithm, with optional auto-seeding
///
/// e.g. "SHA256PRNG"
/// If true, the instance will be auto-seeded.
public static SecureRandom GetInstance(string algorithm, bool autoSeed)
{
string upper = Platform.ToUpperInvariant(algorithm);
if (Platform.EndsWith(upper, "PRNG"))
{
string digestName = upper.Substring(0, upper.Length - "PRNG".Length);
DigestRandomGenerator prng = CreatePrng(digestName, autoSeed);
if (prng != null)
{
return new SecureRandom(prng);
}
}
throw new ArgumentException("Unrecognised PRNG algorithm: " + algorithm, "algorithm");
}
[Obsolete("Call GenerateSeed() on a SecureRandom instance instead")]
public static byte[] GetSeed(int length)
{
return GetNextBytes(Master, length);
}
protected readonly IRandomGenerator generator;
public SecureRandom()
: this(CreatePrng("SHA256", true))
{
}
///
/// To replicate existing predictable output, replace with GetInstance("SHA1PRNG", false), followed by SetSeed(seed)
///
[Obsolete("Use GetInstance/SetSeed instead")]
public SecureRandom(byte[] seed)
: this(CreatePrng("SHA1", false))
{
SetSeed(seed);
}
/// Use the specified instance of IRandomGenerator as random source.
///
/// This constructor performs no seeding of either the IRandomGenerator or the
/// constructed SecureRandom. It is the responsibility of the client to provide
/// proper seed material as necessary/appropriate for the given IRandomGenerator
/// implementation.
///
/// The source to generate all random bytes from.
public SecureRandom(IRandomGenerator generator)
: base(0)
{
this.generator = generator;
}
public virtual byte[] GenerateSeed(int length)
{
return GetNextBytes(Master, length);
}
public virtual void SetSeed(byte[] seed)
{
generator.AddSeedMaterial(seed);
}
public virtual void SetSeed(long seed)
{
generator.AddSeedMaterial(seed);
}
public override int Next()
{
return NextInt() & int.MaxValue;
}
public override int Next(int maxValue)
{
if (maxValue < 2)
{
if (maxValue < 0)
throw new ArgumentOutOfRangeException("maxValue", "cannot be negative");
return 0;
}
// Test whether maxValue is a power of 2
if ((maxValue & (maxValue - 1)) == 0)
{
return NextInt() & (maxValue - 1);
}
int bits, result;
do
{
bits = NextInt() & int.MaxValue;
result = bits % maxValue;
}
while (bits - result + (maxValue - 1) < 0); // Ignore results near overflow
return result;
}
public override int Next(int minValue, int maxValue)
{
if (maxValue <= minValue)
{
if (maxValue == minValue)
return minValue;
throw new ArgumentException("maxValue cannot be less than minValue");
}
int diff = maxValue - minValue;
if (diff > 0)
return minValue + Next(diff);
for (;;)
{
int i = NextInt();
if (i >= minValue && i < maxValue)
return i;
}
}
public override void NextBytes(byte[] buf)
{
generator.NextBytes(buf);
}
public virtual void NextBytes(byte[] buf, int off, int len)
{
generator.NextBytes(buf, off, len);
}
private static readonly double DoubleScale = System.Math.Pow(2.0, 64.0);
public override double NextDouble()
{
return Convert.ToDouble((ulong) NextLong()) / DoubleScale;
}
public virtual int NextInt()
{
byte[] bytes = new byte[4];
NextBytes(bytes);
uint result = bytes[0];
result <<= 8;
result |= bytes[1];
result <<= 8;
result |= bytes[2];
result <<= 8;
result |= bytes[3];
return (int)result;
}
public virtual long NextLong()
{
return ((long)(uint) NextInt() << 32) | (long)(uint) NextInt();
}
}
}