summary refs log tree commit diff
path: root/crypto/src/security/SecureRandom.cs
diff options
context:
space:
mode:
Diffstat (limited to 'crypto/src/security/SecureRandom.cs')
-rw-r--r--crypto/src/security/SecureRandom.cs228
1 files changed, 228 insertions, 0 deletions
diff --git a/crypto/src/security/SecureRandom.cs b/crypto/src/security/SecureRandom.cs
new file mode 100644
index 000000000..ac9d98158
--- /dev/null
+++ b/crypto/src/security/SecureRandom.cs
@@ -0,0 +1,228 @@
+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);
+        }
+
+		/// <summary>Use the specified instance of IRandomGenerator as random source.</summary>
+		/// <remarks>
+		/// This constructor performs no seeding of either the <c>IRandomGenerator</c> or the
+		/// constructed <c>SecureRandom</c>. It is the responsibility of the client to provide
+		/// proper seed material as necessary/appropriate for the given <c>IRandomGenerator</c>
+		/// implementation.
+		/// </remarks>
+		/// <param name="generator">The source to generate all random bytes from.</param>
+		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();
+		}
+    }
+}