using System; 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 { // Note: all objects of this class should be deriving their random data from // a single generator appropriate to the digest being used. private static readonly IRandomGenerator sha1Generator = new DigestRandomGenerator(new Sha1Digest()); private static readonly IRandomGenerator sha256Generator = new DigestRandomGenerator(new Sha256Digest()); private static readonly SecureRandom[] master = { null }; private static SecureRandom Master { get { if (master[0] == null) { IRandomGenerator gen = sha256Generator; gen = new ReversedWindowGenerator(gen, 32); SecureRandom sr = master[0] = new SecureRandom(gen); sr.SetSeed(DateTime.Now.Ticks); sr.SetSeed(new ThreadedSeedGenerator().GenerateSeed(24, true)); sr.GenerateSeed(1 + sr.Next(32)); } return master[0]; } } public static SecureRandom GetInstance( string algorithm) { // TODO Compared to JDK, we don't auto-seed if the client forgets - problem? // TODO Support all digests more generally, by stripping PRNG and calling DigestUtilities? string drgName = Platform.ToUpperInvariant(algorithm); IRandomGenerator drg = null; if (drgName == "SHA1PRNG") { drg = sha1Generator; } else if (drgName == "SHA256PRNG") { drg = sha256Generator; } if (drg != null) { return new SecureRandom(drg); } throw new ArgumentException("Unrecognised PRNG algorithm: " + algorithm, "algorithm"); } public static byte[] GetSeed( int length) { return Master.GenerateSeed(length); } protected IRandomGenerator generator; public SecureRandom() : this(sha1Generator) { SetSeed(GetSeed(8)); } public SecureRandom( byte[] inSeed) : this(sha1Generator) { SetSeed(inSeed); } /// 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) { SetSeed(DateTime.Now.Ticks); byte[] rv = new byte[length]; NextBytes(rv); return rv; } public virtual void SetSeed( byte[] inSeed) { generator.AddSeedMaterial(inSeed); } public virtual void SetSeed( long seed) { generator.AddSeedMaterial(seed); } public override int Next() { for (;;) { int i = NextInt() & int.MaxValue; if (i != int.MaxValue) return i; } } 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) == maxValue) { int val = NextInt() & int.MaxValue; long lr = ((long) maxValue * (long) val) >> 31; return (int) lr; } 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[] buffer) { generator.NextBytes(buffer); } public virtual void NextBytes( byte[] buffer, int start, int length) { generator.NextBytes(buffer, start, length); } 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[] intBytes = new byte[4]; NextBytes(intBytes); int result = 0; for (int i = 0; i < 4; i++) { result = (result << 8) + (intBytes[i] & 0xff); } return result; } public virtual long NextLong() { return ((long)(uint) NextInt() << 32) | (long)(uint) NextInt(); } } }