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.cs448
1 files changed, 242 insertions, 206 deletions
diff --git a/crypto/src/security/SecureRandom.cs b/crypto/src/security/SecureRandom.cs
index ac9d98158..137a471c1 100644
--- a/crypto/src/security/SecureRandom.cs
+++ b/crypto/src/security/SecureRandom.cs
@@ -1,4 +1,5 @@
 using System;
+using System.Threading;
 
 using Org.BouncyCastle.Crypto;
 using Org.BouncyCastle.Crypto.Digests;
@@ -8,221 +9,256 @@ using Org.BouncyCastle.Utilities;
 namespace Org.BouncyCastle.Security
 {
     public class SecureRandom
-		: Random
+        : 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];
+        private static long counter = Times.NanoTime();
+
+#if NETCF_1_0
+        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(GetSeed(digest.GetDigestSize()));
+            }
+            return prng;
+        }
+
+        /// <summary>
+        /// Create and auto-seed an instance based on the given algorithm.
+        /// </summary>
+        /// <remarks>Equivalent to GetInstance(algorithm, true)</remarks>
+        /// <param name="algorithm">e.g. "SHA256PRNG"</param>
+        public static SecureRandom GetInstance(string algorithm)
+        {
+            return GetInstance(algorithm, true);
+        }
+
+        /// <summary>
+        /// Create an instance based on the given algorithm, with optional auto-seeding
+        /// </summary>
+        /// <param name="algorithm">e.g. "SHA256PRNG"</param>
+        /// <param name="autoSeed">If true, the instance will be auto-seeded.</param>
+        public static SecureRandom GetInstance(string algorithm, bool autoSeed)
+        {
+            string upper = Platform.ToUpperInvariant(algorithm);
+            if (upper.EndsWith("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");
+        }
+
+        public static byte[] GetSeed(int length)
+        {
+#if NETCF_1_0
+            lock (master)
+#endif
+            return Master.GenerateSeed(length);
+        }
+
+        protected readonly IRandomGenerator generator;
+
+        public SecureRandom()
+            : this(CreatePrng("SHA256", true))
+        {
+        }
+
+        /// <remarks>
+        /// To replicate existing predictable output, replace with GetInstance("SHA1PRNG", false), followed by SetSeed(seed)
+        /// </remarks>
+        [Obsolete("Use GetInstance/SetSeed instead")]
+        public SecureRandom(byte[] seed)
+            : this(CreatePrng("SHA1", false))
+        {
+            SetSeed(seed);
+        }
+
+        /// <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[] seed)
+        {
+            generator.AddSeedMaterial(seed);
+        }
+
+        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[] 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[] intBytes = new byte[4];
             NextBytes(intBytes);
 
-			int result = 0;
+            int result = 0;
             for (int i = 0; i < 4; i++)
             {
                 result = (result << 8) + (intBytes[i] & 0xff);
             }
 
-			return result;
+            return result;
         }
 
-		public virtual long NextLong()
-		{
-			return ((long)(uint) NextInt() << 32) | (long)(uint) NextInt();
-		}
+        public virtual long NextLong()
+        {
+            return ((long)(uint) NextInt() << 32) | (long)(uint) NextInt();
+        }
     }
 }