diff options
author | Peter Dettman <peter.dettman@bouncycastle.org> | 2022-08-19 15:13:06 +0700 |
---|---|---|
committer | Peter Dettman <peter.dettman@bouncycastle.org> | 2022-08-19 15:13:06 +0700 |
commit | cf1ad240147c298b6adabad399a8bf0f5756f606 (patch) | |
tree | 32c456ebb4e0470df04d6633cac88af2985286b7 | |
parent | Remove unnecessary TODO (diff) | |
download | BouncyCastle.NET-ed25519-cf1ad240147c298b6adabad399a8bf0f5756f606.tar.xz |
Implement NextBytes(Span<byte)
-rw-r--r-- | crypto/src/crypto/prng/CryptoApiRandomGenerator.cs | 13 | ||||
-rw-r--r-- | crypto/src/crypto/prng/DigestRandomGenerator.cs | 22 | ||||
-rw-r--r-- | crypto/src/crypto/prng/IRandomGenerator.cs | 4 | ||||
-rw-r--r-- | crypto/src/crypto/prng/SP800SecureRandom.cs | 7 | ||||
-rw-r--r-- | crypto/src/crypto/prng/VMPCRandomGenerator.cs | 18 | ||||
-rw-r--r-- | crypto/src/crypto/prng/X931Rng.cs | 61 | ||||
-rw-r--r-- | crypto/src/crypto/prng/X931SecureRandom.cs | 15 | ||||
-rw-r--r-- | crypto/src/security/SecureRandom.cs | 16 | ||||
-rw-r--r-- | crypto/test/src/security/test/SecureRandomTest.cs | 117 | ||||
-rw-r--r-- | crypto/test/src/util/test/FixedSecureRandom.cs | 20 |
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) |