summary refs log tree commit diff
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2022-08-19 15:13:06 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2022-08-19 15:13:06 +0700
commitcf1ad240147c298b6adabad399a8bf0f5756f606 (patch)
tree32c456ebb4e0470df04d6633cac88af2985286b7
parentRemove unnecessary TODO (diff)
downloadBouncyCastle.NET-ed25519-cf1ad240147c298b6adabad399a8bf0f5756f606.tar.xz
Implement NextBytes(Span<byte)
-rw-r--r--crypto/src/crypto/prng/CryptoApiRandomGenerator.cs13
-rw-r--r--crypto/src/crypto/prng/DigestRandomGenerator.cs22
-rw-r--r--crypto/src/crypto/prng/IRandomGenerator.cs4
-rw-r--r--crypto/src/crypto/prng/SP800SecureRandom.cs7
-rw-r--r--crypto/src/crypto/prng/VMPCRandomGenerator.cs18
-rw-r--r--crypto/src/crypto/prng/X931Rng.cs61
-rw-r--r--crypto/src/crypto/prng/X931SecureRandom.cs15
-rw-r--r--crypto/src/security/SecureRandom.cs16
-rw-r--r--crypto/test/src/security/test/SecureRandomTest.cs117
-rw-r--r--crypto/test/src/util/test/FixedSecureRandom.cs20
10 files changed, 278 insertions, 15 deletions
diff --git a/crypto/src/crypto/prng/CryptoApiRandomGenerator.cs b/crypto/src/crypto/prng/CryptoApiRandomGenerator.cs
index 7803ddd3d..11d29a818 100644
--- a/crypto/src/crypto/prng/CryptoApiRandomGenerator.cs
+++ b/crypto/src/crypto/prng/CryptoApiRandomGenerator.cs
@@ -45,6 +45,9 @@ namespace Org.BouncyCastle.Crypto.Prng
             if (bytes.Length < (start + len))
                 throw new ArgumentException("Byte array too small for requested offset and length");
 
+#if NETCOREAPP2_0_OR_GREATER || NETSTANDARD2_0_OR_GREATER
+            rndProv.GetBytes(bytes, start, len);
+#else
             if (bytes.Length == len && start == 0) 
             {
                 NextBytes(bytes);
@@ -55,8 +58,16 @@ namespace Org.BouncyCastle.Crypto.Prng
                 NextBytes(tmpBuf);
                 Array.Copy(tmpBuf, 0, bytes, start, len);
             }
+#endif
         }
 
-        #endregion
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual void NextBytes(Span<byte> bytes)
+        {
+            rndProv.GetBytes(bytes);
+        }
+#endif
+
+#endregion
     }
 }
diff --git a/crypto/src/crypto/prng/DigestRandomGenerator.cs b/crypto/src/crypto/prng/DigestRandomGenerator.cs
index 024db2852..3587956b6 100644
--- a/crypto/src/crypto/prng/DigestRandomGenerator.cs
+++ b/crypto/src/crypto/prng/DigestRandomGenerator.cs
@@ -90,6 +90,28 @@ namespace Org.BouncyCastle.Crypto.Prng
 			}
 		}
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+		public virtual void NextBytes(Span<byte> bytes)
+		{
+			lock (this)
+			{
+				int stateOff = 0;
+
+				GenerateState();
+
+				for (int i = 0; i < bytes.Length; ++i)
+				{
+					if (stateOff == state.Length)
+					{
+						GenerateState();
+						stateOff = 0;
+					}
+					bytes[i] = state[stateOff++];
+				}
+			}
+		}
+#endif
+
 		private void CycleSeed()
 		{
 			DigestUpdate(seed);
diff --git a/crypto/src/crypto/prng/IRandomGenerator.cs b/crypto/src/crypto/prng/IRandomGenerator.cs
index 8dbe4068f..051f8f8c6 100644
--- a/crypto/src/crypto/prng/IRandomGenerator.cs
+++ b/crypto/src/crypto/prng/IRandomGenerator.cs
@@ -22,5 +22,9 @@ namespace Org.BouncyCastle.Crypto.Prng
 		/// <param name="start">Index to start filling at.</param>
 		/// <param name="len">Length of segment to fill.</param>
 		void NextBytes(byte[] bytes, int start, int len);
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+		void NextBytes(Span<byte> bytes);
+#endif
 	}
 }
diff --git a/crypto/src/crypto/prng/SP800SecureRandom.cs b/crypto/src/crypto/prng/SP800SecureRandom.cs
index 2e1484125..fb5c3a677 100644
--- a/crypto/src/crypto/prng/SP800SecureRandom.cs
+++ b/crypto/src/crypto/prng/SP800SecureRandom.cs
@@ -70,6 +70,13 @@ namespace Org.BouncyCastle.Crypto.Prng
             }
         }
 
+        // TODO Add efficient override (needs ISP80090Drbg support for spans)
+//#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+//        public override void NextBytes(Span<byte> buffer)
+//        {
+//        }
+//#endif
+
         public override byte[] GenerateSeed(int numBytes)
         {
             return EntropyUtilities.GenerateSeed(mEntropySource, numBytes);
diff --git a/crypto/src/crypto/prng/VMPCRandomGenerator.cs b/crypto/src/crypto/prng/VMPCRandomGenerator.cs
index 64f287d13..72e6b5e7d 100644
--- a/crypto/src/crypto/prng/VMPCRandomGenerator.cs
+++ b/crypto/src/crypto/prng/VMPCRandomGenerator.cs
@@ -110,5 +110,23 @@ namespace Org.BouncyCastle.Crypto.Prng
                 }
             }
         }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual void NextBytes(Span<byte> bytes)
+        {
+            lock (P) 
+            {
+                for (int i = 0; i != bytes.Length; i++) 
+                {
+                    s = P[(s + P[n & 0xff]) & 0xff];
+                    bytes[i] = P[(P[(P[s & 0xff]) & 0xff] + 1) & 0xff];
+                    byte temp = P[n & 0xff];
+                    P[n & 0xff] = P[s & 0xff];
+                    P[s & 0xff] = temp;
+                    n = (byte) ((n + 1) & 0xff);
+                }
+            }
+        }
+#endif
     }
 }
diff --git a/crypto/src/crypto/prng/X931Rng.cs b/crypto/src/crypto/prng/X931Rng.cs
index 53c982c25..f0bfdc9f1 100644
--- a/crypto/src/crypto/prng/X931Rng.cs
+++ b/crypto/src/crypto/prng/X931Rng.cs
@@ -103,6 +103,67 @@ namespace Org.BouncyCastle.Crypto.Prng
             return outputLen * 8;
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        internal int Generate(Span<byte> output, bool predictionResistant)
+        {
+            int outputLen = output.Length;
+
+            if (mR.Length == 8) // 64 bit block size
+            {
+                if (mReseedCounter > BLOCK64_RESEED_MAX)
+                    return -1;
+
+                if (outputLen > BLOCK64_MAX_BITS_REQUEST / 8)
+                    throw new ArgumentException("Number of bits per request limited to " + BLOCK64_MAX_BITS_REQUEST, "output");
+            }
+            else
+            {
+                if (mReseedCounter > BLOCK128_RESEED_MAX)
+                    return -1;
+
+                if (outputLen > BLOCK128_MAX_BITS_REQUEST / 8)
+                    throw new ArgumentException("Number of bits per request limited to " + BLOCK128_MAX_BITS_REQUEST, "output");
+            }
+
+            if (predictionResistant || mV == null)
+            {
+                mV = mEntropySource.GetEntropy();
+                if (mV.Length != mEngine.GetBlockSize())
+                    throw new InvalidOperationException("Insufficient entropy returned");
+            }
+
+            int m = outputLen / mR.Length;
+
+            for (int i = 0; i < m; i++)
+            {
+                mEngine.ProcessBlock(mDT, 0, mI, 0);
+                Process(mR, mI, mV);
+                Process(mV, mR, mI);
+
+                mR.CopyTo(output[(i * mR.Length)..]);
+
+                Increment(mDT);
+            }
+
+            int bytesToCopy = (outputLen - m * mR.Length);
+
+            if (bytesToCopy > 0)
+            {
+                mEngine.ProcessBlock(mDT, 0, mI, 0);
+                Process(mR, mI, mV);
+                Process(mV, mR, mI);
+
+                mR.AsSpan(0, bytesToCopy).CopyTo(output[(m * mR.Length)..]);
+
+                Increment(mDT);
+            }
+
+            mReseedCounter++;
+
+            return outputLen * 8;
+        }
+#endif
+
         /**
          * Reseed the RNG.
          */
diff --git a/crypto/src/crypto/prng/X931SecureRandom.cs b/crypto/src/crypto/prng/X931SecureRandom.cs
index 1402e5c31..01678af8f 100644
--- a/crypto/src/crypto/prng/X931SecureRandom.cs
+++ b/crypto/src/crypto/prng/X931SecureRandom.cs
@@ -60,6 +60,21 @@ namespace Org.BouncyCastle.Crypto.Prng
             }
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public override void NextBytes(Span<byte> buffer)
+        {
+            lock (this)
+            {
+                // check if a reseed is required...
+                if (mDrbg.Generate(buffer, mPredictionResistant) < 0)
+                {
+                    mDrbg.Reseed();
+                    mDrbg.Generate(buffer, mPredictionResistant);
+                }
+            }
+        }
+#endif
+
         public override byte[] GenerateSeed(int numBytes)
         {
             return EntropyUtilities.GenerateSeed(mDrbg.EntropySource, numBytes);
diff --git a/crypto/src/security/SecureRandom.cs b/crypto/src/security/SecureRandom.cs
index e8cac56f5..4e118e77a 100644
--- a/crypto/src/security/SecureRandom.cs
+++ b/crypto/src/security/SecureRandom.cs
@@ -181,6 +181,22 @@ namespace Org.BouncyCastle.Security
             generator.NextBytes(buf, off, len);
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public override void NextBytes(Span<byte> buffer)
+        {
+            if (generator != null)
+            {
+                generator.NextBytes(buffer);
+            }
+            else
+            {
+                byte[] tmp = new byte[buffer.Length];
+                NextBytes(tmp);
+                tmp.CopyTo(buffer);
+            }
+        }
+#endif
+
         private static readonly double DoubleScale = 1.0 / Convert.ToDouble(1L << 53);
 
         public override double NextDouble()
diff --git a/crypto/test/src/security/test/SecureRandomTest.cs b/crypto/test/src/security/test/SecureRandomTest.cs
index 22d138adc..f6d06201f 100644
--- a/crypto/test/src/security/test/SecureRandomTest.cs
+++ b/crypto/test/src/security/test/SecureRandomTest.cs
@@ -1,11 +1,9 @@
 using System;
-using System.Text;
 
 using NUnit.Framework;
 
 using Org.BouncyCastle.Crypto;
 using Org.BouncyCastle.Crypto.Digests;
-using Org.BouncyCastle.Crypto.Engines;
 using Org.BouncyCastle.Crypto.Macs;
 using Org.BouncyCastle.Crypto.Prng;
 using Org.BouncyCastle.Crypto.Parameters;
@@ -16,16 +14,13 @@ namespace Org.BouncyCastle.Security.Tests
     [TestFixture]
     public class SecureRandomTest
     {
-#if !PORTABLE
         [Test]
         public void TestCryptoApi()
         {
-            SecureRandom random = new SecureRandom(
-                new CryptoApiRandomGenerator());
+            SecureRandom random = new SecureRandom(new CryptoApiRandomGenerator());
 
             CheckSecureRandom(random);
         }
-#endif
 
         [Test]
         public void TestDefault()
@@ -129,20 +124,45 @@ namespace Org.BouncyCastle.Security.Tests
 
         private static bool RunChiSquaredTests(SecureRandom random)
         {
-            int passes = 0;
+            {
+                int passes = 0;
 
-            for (int tries = 0; tries < 100; ++tries)
+                for (int tries = 0; tries < 100; ++tries)
+                {
+                    double chi2 = MeasureChiSquared(random, 1000);
+
+                    // 255 degrees of freedom in test => Q ~ 10.0% for 285
+                    if (chi2 < 285.0)
+                    {
+                        ++passes;
+                    }
+                }
+
+                if (passes <= 75)
+                    return false;
+            }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
             {
-                double chi2 = MeasureChiSquared(random, 1000);
+                int passes = 0;
 
-                // 255 degrees of freedom in test => Q ~ 10.0% for 285
-                if (chi2 < 285.0)
+                for (int tries = 0; tries < 100; ++tries)
                 {
-                    ++passes;
+                    double chi2 = MeasureChiSquaredSpan(random, 1000);
+
+                    // 255 degrees of freedom in test => Q ~ 10.0% for 285
+                    if (chi2 < 285.0)
+                    {
+                        ++passes;
+                    }
                 }
+
+                if (passes <= 75)
+                    return false;
             }
+#endif
 
-            return passes > 75;
+            return true;
         }
 
         private static double MeasureChiSquared(SecureRandom random, int rounds)
@@ -203,6 +223,66 @@ namespace Org.BouncyCastle.Security.Tests
             return chi2;
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        private static double MeasureChiSquaredSpan(SecureRandom random, int rounds)
+        {
+            byte[] opts = random.GenerateSeed(2);
+            Span<int> counts = stackalloc int[256];
+
+            Span<byte> bs = stackalloc byte[256];
+            for (int i = 0; i < rounds; ++i)
+            {
+                random.NextBytes(bs);
+
+                for (int b = 0; b < 256; ++b)
+                {
+                    ++counts[bs[b]];
+                }
+            }
+
+            byte mask = opts[0];
+            for (int i = 0; i < rounds; ++i)
+            {
+                random.NextBytes(bs);
+
+                for (int b = 0; b < 256; ++b)
+                {
+                    ++counts[bs[b] ^ mask];
+                }
+
+                ++mask;
+            }
+
+            byte shift = opts[1];
+            for (int i = 0; i < rounds; ++i)
+            {
+                random.NextBytes(bs);
+
+                for (int b = 0; b < 256; ++b)
+                {
+                    ++counts[(byte)(bs[b] + shift)];
+                }
+
+                ++shift;
+            }
+
+            int total = 3 * rounds;
+
+            double chi2 = 0;
+            for (int k = 0; k < counts.Length; ++k)
+            {
+                double diff = ((double)counts[k]) - total;
+                double diff2 = diff * diff;
+
+                chi2 += diff2;
+            }
+
+            chi2 /= total;
+
+            return chi2;
+        }
+#endif
+
         private abstract class TestRandomGenerator
             : IRandomGenerator
         {
@@ -220,6 +300,10 @@ namespace Org.BouncyCastle.Security.Tests
             }
 
             public abstract void NextBytes(byte[] bytes, int start, int len);
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            public abstract void NextBytes(Span<byte> bytes);
+#endif
         }
 
         private sealed class FixedRandomGenerator
@@ -236,6 +320,13 @@ namespace Org.BouncyCastle.Security.Tests
             {
                 Arrays.Fill(bytes, start, start + len, b);
             }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            public override void NextBytes(Span<byte> bytes)
+            {
+                bytes.Fill(b);
+            }
+#endif
         }
     }
 }
diff --git a/crypto/test/src/util/test/FixedSecureRandom.cs b/crypto/test/src/util/test/FixedSecureRandom.cs
index be5b25347..ab74145a4 100644
--- a/crypto/test/src/util/test/FixedSecureRandom.cs
+++ b/crypto/test/src/util/test/FixedSecureRandom.cs
@@ -226,7 +226,16 @@ namespace Org.BouncyCastle.Utilities.Test
 			_index += len;
 		}
 
-		public bool IsExhausted
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public override void NextBytes(Span<byte> buffer)
+        {
+            _data.AsSpan(_index, buffer.Length).CopyTo(buffer);
+
+            _index += buffer.Length;
+        }
+#endif
+
+        public bool IsExhausted
 		{
 			get { return _index == _data.Length; }
 		}
@@ -248,6 +257,15 @@ namespace Org.BouncyCastle.Utilities.Test
 
                 index += len;
             }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            public override void NextBytes(Span<byte> buffer)
+            {
+                data.AsSpan(index, buffer.Length).CopyTo(buffer);
+
+                index += buffer.Length;
+            }
+#endif
         }
 
         private static byte[] ExpandToBitLength(int bitLength, byte[] v)