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)
|