summary refs log tree commit diff
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2014-09-28 22:10:24 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2014-09-28 22:10:24 +0700
commitb494cecf549016c5db4493d50f3a738963a2eeb6 (patch)
treed2c21b5f37644943613fde0e6a82227e5cfb79ea
parentFormatting (diff)
downloadBouncyCastle.NET-ed25519-b494cecf549016c5db4493d50f3a738963a2eeb6.tar.xz
Use platform RNG as master, where available release-1.8.0-beta.4
Obsolete seeded constructor in favour of GetInstance variant
-rw-r--r--crypto/src/crypto/prng/CryptoApiRandomGenerator.cs94
-rw-r--r--crypto/src/security/SecureRandom.cs153
-rw-r--r--crypto/test/src/security/test/SecureRandomTest.cs22
3 files changed, 154 insertions, 115 deletions
diff --git a/crypto/src/crypto/prng/CryptoApiRandomGenerator.cs b/crypto/src/crypto/prng/CryptoApiRandomGenerator.cs
index e1a0d4012..521fae33e 100644
--- a/crypto/src/crypto/prng/CryptoApiRandomGenerator.cs
+++ b/crypto/src/crypto/prng/CryptoApiRandomGenerator.cs
@@ -5,62 +5,62 @@ using System.Security.Cryptography;
 
 namespace Org.BouncyCastle.Crypto.Prng
 {
-	/// <summary>
-	/// Uses Microsoft's RNGCryptoServiceProvider
-	/// </summary>
-	public class CryptoApiRandomGenerator
-		: IRandomGenerator
-	{
-		private readonly RandomNumberGenerator rndProv;
+    /// <summary>
+    /// Uses Microsoft's RNGCryptoServiceProvider
+    /// </summary>
+    public class CryptoApiRandomGenerator
+        : IRandomGenerator
+    {
+        private readonly RandomNumberGenerator rndProv;
 
-		public CryptoApiRandomGenerator()
-			: this(new RNGCryptoServiceProvider())
-		{
-		}
+        public CryptoApiRandomGenerator()
+            : this(new RNGCryptoServiceProvider())
+        {
+        }
 
-		public CryptoApiRandomGenerator(RandomNumberGenerator rng)
-		{
-			this.rndProv = rng;
-		}
+        public CryptoApiRandomGenerator(RandomNumberGenerator rng)
+        {
+            this.rndProv = rng;
+        }
 
-		#region IRandomGenerator Members
+        #region IRandomGenerator Members
 
-		public virtual void AddSeedMaterial(byte[] seed)
-		{
-			// We don't care about the seed
-		}
+        public virtual void AddSeedMaterial(byte[] seed)
+        {
+            // We don't care about the seed
+        }
 
-		public virtual void AddSeedMaterial(long seed)
-		{
-			// We don't care about the seed
-		}
+        public virtual void AddSeedMaterial(long seed)
+        {
+            // We don't care about the seed
+        }
 
-		public virtual void NextBytes(byte[] bytes)
-		{
-			rndProv.GetBytes(bytes);
-		}
+        public virtual void NextBytes(byte[] bytes)
+        {
+            rndProv.GetBytes(bytes);
+        }
 
-		public virtual void NextBytes(byte[] bytes, int start, int len)
-		{
-			if (start < 0)
-				throw new ArgumentException("Start offset cannot be negative", "start");
-			if (bytes.Length < (start + len))
-				throw new ArgumentException("Byte array too small for requested offset and length");
+        public virtual void NextBytes(byte[] bytes, int start, int len)
+        {
+            if (start < 0)
+                throw new ArgumentException("Start offset cannot be negative", "start");
+            if (bytes.Length < (start + len))
+                throw new ArgumentException("Byte array too small for requested offset and length");
 
-			if (bytes.Length == len && start == 0) 
-			{
-				NextBytes(bytes);
-			}
-			else 
-			{
-				byte[] tmpBuf = new byte[len];
-				rndProv.GetBytes(tmpBuf);
-				Array.Copy(tmpBuf, 0, bytes, start, len);
-			}
-		}
+            if (bytes.Length == len && start == 0) 
+            {
+                NextBytes(bytes);
+            }
+            else 
+            {
+                byte[] tmpBuf = new byte[len];
+                NextBytes(tmpBuf);
+                Array.Copy(tmpBuf, 0, bytes, start, len);
+            }
+        }
 
-		#endregion
-	}
+        #endregion
+    }
 }
 
 #endif
diff --git a/crypto/src/security/SecureRandom.cs b/crypto/src/security/SecureRandom.cs
index 160628d4b..f46427a5c 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;
@@ -10,78 +11,113 @@ 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 long counter = Times.NanoTime();
 
+        private static long NextCounterValue()
+        {
+            return Interlocked.Increment(ref counter);
+        }
+
+#if NETCF_1_0
         private static readonly SecureRandom[] master = { null };
         private static SecureRandom Master
         {
             get
             {
-                if (master[0] == null)
+                lock (master)
                 {
-                    SecureRandom sr = master[0] = new SecureRandom(sha256Generator);
-
-                    // Even though Ticks has at most 8 or 14 bits of entropy, there's no harm in adding it.
-                    sr.SetSeed(DateTime.Now.Ticks);
-                    // In addition to Ticks and ThreadedSeedGenerator, also seed from CryptoApiRandomGenerator
-                    CryptoApiRandomGenerator systemRNG = new CryptoApiRandomGenerator();
-                    byte[] systemSeed = new byte[32];
-                    systemRNG.NextBytes(systemSeed);
-                    sr.SetSeed(systemSeed);
-                    Array.Clear(systemSeed,0,systemSeed.Length);
-                    // 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));
-                }
+                    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];
+                    return master[0];
+                }
             }
         }
-
-        public static SecureRandom GetInstance(
-            string algorithm)
+#else
+        private static readonly SecureRandom master = new SecureRandom(new CryptoApiRandomGenerator());
+        private static SecureRandom Master
         {
-            // TODO Support all digests more generally, by stripping PRNG and calling DigestUtilities?
-            string drgName = Platform.ToUpperInvariant(algorithm);
+            get { return master; }
+        }
+#endif
 
-            if (drgName == "SHA1PRNG")
+        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)
             {
-                SecureRandom newPrng = new SecureRandom(sha1Generator);
-                newPrng.SetSeed(GetSeed(20));
-                return newPrng;
+                prng.AddSeedMaterial(NextCounterValue());
+                prng.AddSeedMaterial(GetSeed(digest.GetDigestSize()));
             }
-            else if (drgName == "SHA256PRNG")
+            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"))
             {
-                SecureRandom newPrng = new SecureRandom(sha256Generator);
-                newPrng.SetSeed(GetSeed(32));
-                return newPrng;
+                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)
+        public static byte[] GetSeed(int length)
         {
+#if NETCF_1_0
+            lock (master)
+#endif
             return Master.GenerateSeed(length);
         }
 
-        protected IRandomGenerator generator;
+        protected readonly IRandomGenerator generator;
 
         public SecureRandom()
-            : this(sha1Generator)
+            : this(CreatePrng("SHA256", true))
         {
-            SetSeed(GetSeed(20));	// 20 bytes because this is sha1Generator
         }
 
-        public SecureRandom(
-            byte[] inSeed)
-            : this(sha1Generator)
+        /// <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(inSeed);
+            SetSeed(seed);
         }
 
         /// <summary>Use the specified instance of IRandomGenerator as random source.</summary>
@@ -92,15 +128,13 @@ namespace Org.BouncyCastle.Security
         /// implementation.
         /// </remarks>
         /// <param name="generator">The source to generate all random bytes from.</param>
-        public SecureRandom(
-            IRandomGenerator generator)
+        public SecureRandom(IRandomGenerator generator)
             : base(0)
         {
             this.generator = generator;
         }
 
-        public virtual byte[] GenerateSeed(
-            int length)
+        public virtual byte[] GenerateSeed(int length)
         {
             SetSeed(DateTime.Now.Ticks);
 
@@ -109,14 +143,12 @@ namespace Org.BouncyCastle.Security
             return rv;
         }
 
-        public virtual void SetSeed(
-            byte[] inSeed)
+        public virtual void SetSeed(byte[] seed)
         {
-            generator.AddSeedMaterial(inSeed);
+            generator.AddSeedMaterial(seed);
         }
 
-        public virtual void SetSeed(
-            long seed)
+        public virtual void SetSeed(long seed)
         {
             generator.AddSeedMaterial(seed);
         }
@@ -132,8 +164,7 @@ namespace Org.BouncyCastle.Security
             }
         }
 
-        public override int Next(
-            int maxValue)
+        public override int Next(int maxValue)
         {
             if (maxValue < 2)
             {
@@ -162,9 +193,7 @@ namespace Org.BouncyCastle.Security
             return result;
         }
 
-        public override int Next(
-            int	minValue,
-            int	maxValue)
+        public override int Next(int minValue, int maxValue)
         {
             if (maxValue <= minValue)
             {
@@ -187,18 +216,14 @@ namespace Org.BouncyCastle.Security
             }
         }
 
-        public override void NextBytes(
-            byte[] buffer)
+        public override void NextBytes(byte[] buf)
         {
-            generator.NextBytes(buffer);
+            generator.NextBytes(buf);
         }
 
-        public virtual void NextBytes(
-            byte[]	buffer,
-            int		start,
-            int		length)
+        public virtual void NextBytes(byte[] buf, int off, int len)
         {
-            generator.NextBytes(buffer, start, length);
+            generator.NextBytes(buf, off, len);
         }
 
         private static readonly double DoubleScale = System.Math.Pow(2.0, 64.0);
diff --git a/crypto/test/src/security/test/SecureRandomTest.cs b/crypto/test/src/security/test/SecureRandomTest.cs
index eedcd0ebc..4f05a286a 100644
--- a/crypto/test/src/security/test/SecureRandomTest.cs
+++ b/crypto/test/src/security/test/SecureRandomTest.cs
@@ -1,8 +1,10 @@
 using System;
+using System.Text;
 
 using NUnit.Framework;
 
 using Org.BouncyCastle.Crypto.Prng;
+using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Security.Tests
 {
@@ -32,16 +34,28 @@ namespace Org.BouncyCastle.Security.Tests
         public void TestSha1Prng()
         {
             SecureRandom random = SecureRandom.GetInstance("SHA1PRNG");
-            random.SetSeed(SecureRandom.GetSeed(20));
 
             CheckSecureRandom(random);
         }
 
         [Test]
+        public void TestSha1PrngBackward()
+        {
+            byte[] seed = Encoding.ASCII.GetBytes("backward compatible");
+
+            SecureRandom sx = new SecureRandom(seed);
+            SecureRandom sy = SecureRandom.GetInstance("SHA1PRNG", false); sy.SetSeed(seed);
+
+            byte[] bx = new byte[128]; sx.NextBytes(bx);
+            byte[] by = new byte[128]; sy.NextBytes(by);
+
+            Assert.IsTrue(Arrays.AreEqual(bx, by));
+        }
+
+        [Test]
         public void TestSha256Prng()
         {
             SecureRandom random = SecureRandom.GetInstance("SHA256PRNG");
-            random.SetSeed(SecureRandom.GetSeed(32));
 
             CheckSecureRandom(random);
         }
@@ -49,8 +63,8 @@ namespace Org.BouncyCastle.Security.Tests
         [Test]
         public void TestThreadedSeed()
         {
-            SecureRandom random = new SecureRandom(
-                new ThreadedSeedGenerator().GenerateSeed(20, false));
+            SecureRandom random = SecureRandom.GetInstance("SHA1PRNG", false);
+            random.SetSeed(new ThreadedSeedGenerator().GenerateSeed(20, false));
 
             CheckSecureRandom(random);
         }