summary refs log tree commit diff
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2022-10-07 23:27:37 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2022-10-07 23:27:37 +0700
commit15105e796c6cd0c23357aa54ac5a0a05af20c2a0 (patch)
tree435794f619138df8aa7358afb3b9af4fc4c2e372
parentSpan usage in encoders (diff)
downloadBouncyCastle.NET-ed25519-15105e796c6cd0c23357aa54ac5a0a05af20c2a0.tar.xz
More span variants in randomness classes
-rw-r--r--crypto/src/crypto/prng/CryptoApiRandomGenerator.cs37
-rw-r--r--crypto/src/crypto/prng/DigestRandomGenerator.cs71
-rw-r--r--crypto/src/crypto/prng/IRandomGenerator.cs10
-rw-r--r--crypto/src/crypto/prng/SP800SecureRandom.cs13
-rw-r--r--crypto/src/crypto/prng/VMPCRandomGenerator.cs20
-rw-r--r--crypto/src/crypto/prng/X931SecureRandom.cs13
-rw-r--r--crypto/src/crypto/util/Pack.cs7
-rw-r--r--crypto/src/math/BigInteger.cs32
-rw-r--r--crypto/src/security/SecureRandom.cs44
-rw-r--r--crypto/src/tls/AbstractTlsContext.cs6
-rw-r--r--crypto/src/tls/crypto/TlsCrypto.cs4
-rw-r--r--crypto/src/tls/crypto/impl/AbstractTlsCrypto.cs4
-rw-r--r--crypto/src/tls/crypto/impl/bc/BcTlsCrypto.cs27
-rw-r--r--crypto/test/src/security/test/SecureRandomTest.cs10
14 files changed, 247 insertions, 51 deletions
diff --git a/crypto/src/crypto/prng/CryptoApiRandomGenerator.cs b/crypto/src/crypto/prng/CryptoApiRandomGenerator.cs
index 11d29a818..44a9c261f 100644
--- a/crypto/src/crypto/prng/CryptoApiRandomGenerator.cs
+++ b/crypto/src/crypto/prng/CryptoApiRandomGenerator.cs
@@ -6,48 +6,55 @@ namespace Org.BouncyCastle.Crypto.Prng
     /// <summary>
     /// Uses RandomNumberGenerator.Create() to get randomness generator
     /// </summary>
-    public class CryptoApiRandomGenerator
+    public sealed class CryptoApiRandomGenerator
         : IRandomGenerator
     {
-        private readonly RandomNumberGenerator rndProv;
+        private readonly RandomNumberGenerator m_randomNumberGenerator;
 
         public CryptoApiRandomGenerator()
             : this(RandomNumberGenerator.Create())
         {
         }
 
-        public CryptoApiRandomGenerator(RandomNumberGenerator rng)
+        public CryptoApiRandomGenerator(RandomNumberGenerator randomNumberGenerator)
         {
-            this.rndProv = rng;
+            m_randomNumberGenerator = randomNumberGenerator;
         }
 
         #region IRandomGenerator Members
 
-        public virtual void AddSeedMaterial(byte[] seed)
+        public void AddSeedMaterial(byte[] seed)
         {
             // We don't care about the seed
         }
 
-        public virtual void AddSeedMaterial(long seed)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public void AddSeedMaterial(ReadOnlySpan<byte> inSeed)
+        {
+            // We don't care about the seed
+        }
+#endif
+
+        public void AddSeedMaterial(long seed)
         {
             // We don't care about the seed
         }
 
-        public virtual void NextBytes(byte[] bytes)
+        public void NextBytes(byte[] bytes)
         {
-            rndProv.GetBytes(bytes);
+            m_randomNumberGenerator.GetBytes(bytes);
         }
 
-        public virtual void NextBytes(byte[] bytes, int start, int len)
+        public void NextBytes(byte[] bytes, int start, int len)
         {
+#if NETCOREAPP2_0_OR_GREATER || NETSTANDARD2_0_OR_GREATER
+            m_randomNumberGenerator.GetBytes(bytes, start, len);
+#else
             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 NETCOREAPP2_0_OR_GREATER || NETSTANDARD2_0_OR_GREATER
-            rndProv.GetBytes(bytes, start, len);
-#else
             if (bytes.Length == len && start == 0) 
             {
                 NextBytes(bytes);
@@ -62,12 +69,12 @@ namespace Org.BouncyCastle.Crypto.Prng
         }
 
 #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
-        public virtual void NextBytes(Span<byte> bytes)
+        public void NextBytes(Span<byte> bytes)
         {
-            rndProv.GetBytes(bytes);
+            m_randomNumberGenerator.GetBytes(bytes);
         }
 #endif
 
-#endregion
+        #endregion
     }
 }
diff --git a/crypto/src/crypto/prng/DigestRandomGenerator.cs b/crypto/src/crypto/prng/DigestRandomGenerator.cs
index 3587956b6..c466a1585 100644
--- a/crypto/src/crypto/prng/DigestRandomGenerator.cs
+++ b/crypto/src/crypto/prng/DigestRandomGenerator.cs
@@ -12,7 +12,7 @@ namespace Org.BouncyCastle.Crypto.Prng
 	 * Internal access to the digest is synchronized so a single one of these can be shared.
 	 * </p>
 	 */
-	public class DigestRandomGenerator
+	public sealed class DigestRandomGenerator
 		: IRandomGenerator
 	{
 		private const long CYCLE_COUNT = 10;
@@ -23,8 +23,7 @@ namespace Org.BouncyCastle.Crypto.Prng
 		private byte[]	state;
 		private byte[]	seed;
 
-		public DigestRandomGenerator(
-			IDigest digest)
+		public DigestRandomGenerator(IDigest digest)
 		{
 			this.digest = digest;
 
@@ -35,8 +34,7 @@ namespace Org.BouncyCastle.Crypto.Prng
 			this.stateCounter = 1;
 		}
 
-		public void AddSeedMaterial(
-			byte[] inSeed)
+		public void AddSeedMaterial(byte[] inSeed)
 		{
 			lock (this)
 			{
@@ -49,8 +47,22 @@ namespace Org.BouncyCastle.Crypto.Prng
 			}
 		}
 
-		public void AddSeedMaterial(
-			long rSeed)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public void AddSeedMaterial(ReadOnlySpan<byte> inSeed)
+        {
+            lock (this)
+            {
+                if (!inSeed.IsEmpty)
+                {
+                    DigestUpdate(inSeed);
+                }
+                DigestUpdate(seed);
+                DigestDoFinal(seed);
+            }
+        }
+#endif
+
+        public void AddSeedMaterial(long rSeed)
 		{
 			lock (this)
 			{
@@ -60,17 +72,16 @@ namespace Org.BouncyCastle.Crypto.Prng
 			}
 		}
 
-		public void NextBytes(
-			byte[] bytes)
+		public void NextBytes(byte[] bytes)
 		{
 			NextBytes(bytes, 0, bytes.Length);
 		}
 
-		public void NextBytes(
-			byte[]	bytes,
-			int		start,
-			int		len)
+		public void NextBytes(byte[] bytes, int start, int len)
 		{
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+			NextBytes(bytes.AsSpan(start, len));
+#else
 			lock (this)
 			{
 				int stateOff = 0;
@@ -88,10 +99,11 @@ namespace Org.BouncyCastle.Crypto.Prng
 					bytes[i] = state[stateOff++];
 				}
 			}
+#endif
 		}
 
 #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
-		public virtual void NextBytes(Span<byte> bytes)
+		public void NextBytes(Span<byte> bytes)
 		{
 			lock (this)
 			{
@@ -132,21 +144,40 @@ namespace Org.BouncyCastle.Crypto.Prng
 			}
 		}
 
-		private void DigestAddCounter(long seedVal)
-		{
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        private void DigestAddCounter(long seedVal)
+        {
+            Span<byte> bytes = stackalloc byte[8];
+            Pack.UInt64_To_LE((ulong)seedVal, bytes);
+            digest.BlockUpdate(bytes);
+        }
+
+        private void DigestUpdate(ReadOnlySpan<byte> inSeed)
+        {
+            digest.BlockUpdate(inSeed);
+        }
+
+        private void DigestDoFinal(Span<byte> result)
+        {
+            digest.DoFinal(result);
+        }
+#else
+        private void DigestAddCounter(long seedVal)
+        {
             byte[] bytes = new byte[8];
             Pack.UInt64_To_LE((ulong)seedVal, bytes);
             digest.BlockUpdate(bytes, 0, bytes.Length);
-		}
+        }
 
-        private void DigestUpdate(byte[] inSeed)
+		private void DigestUpdate(byte[] inSeed)
 		{
 			digest.BlockUpdate(inSeed, 0, inSeed.Length);
 		}
 
-		private void DigestDoFinal(byte[] result)
+        private void DigestDoFinal(byte[] result)
 		{
 			digest.DoFinal(result, 0);
 		}
-	}
+#endif
+    }
 }
diff --git a/crypto/src/crypto/prng/IRandomGenerator.cs b/crypto/src/crypto/prng/IRandomGenerator.cs
index 051f8f8c6..c22d22f61 100644
--- a/crypto/src/crypto/prng/IRandomGenerator.cs
+++ b/crypto/src/crypto/prng/IRandomGenerator.cs
@@ -9,9 +9,13 @@ namespace Org.BouncyCastle.Crypto.Prng
 		/// <param name="seed">A byte array to be mixed into the generator's state.</param>
 		void AddSeedMaterial(byte[] seed);
 
-		/// <summary>Add more seed material to the generator.</summary>
-		/// <param name="seed">A long value to be mixed into the generator's state.</param>
-		void AddSeedMaterial(long seed);
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        void AddSeedMaterial(ReadOnlySpan<byte> seed);
+#endif
+
+        /// <summary>Add more seed material to the generator.</summary>
+        /// <param name="seed">A long value to be mixed into the generator's state.</param>
+        void AddSeedMaterial(long seed);
 
 		/// <summary>Fill byte array with random values.</summary>
 		/// <param name="bytes">Array to be filled.</param>
diff --git a/crypto/src/crypto/prng/SP800SecureRandom.cs b/crypto/src/crypto/prng/SP800SecureRandom.cs
index e08a0242c..1409df2fa 100644
--- a/crypto/src/crypto/prng/SP800SecureRandom.cs
+++ b/crypto/src/crypto/prng/SP800SecureRandom.cs
@@ -36,6 +36,19 @@ namespace Org.BouncyCastle.Crypto.Prng
             }
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public override void SetSeed(Span<byte> seed)
+        {
+            lock (this)
+            {
+                if (mRandomSource != null)
+                {
+                    this.mRandomSource.SetSeed(seed);
+                }
+            }
+        }
+#endif
+
         public override void SetSeed(long seed)
         {
             lock (this)
diff --git a/crypto/src/crypto/prng/VMPCRandomGenerator.cs b/crypto/src/crypto/prng/VMPCRandomGenerator.cs
index 72e6b5e7d..13b097789 100644
--- a/crypto/src/crypto/prng/VMPCRandomGenerator.cs
+++ b/crypto/src/crypto/prng/VMPCRandomGenerator.cs
@@ -84,9 +84,29 @@ namespace Org.BouncyCastle.Crypto.Prng
             }
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual void AddSeedMaterial(ReadOnlySpan<byte> seed)
+        {
+            for (int m = 0; m < seed.Length; m++)
+            {
+                s = P[(s + P[n & 0xff] + seed[m]) & 0xff];
+                byte temp = P[n & 0xff];
+                P[n & 0xff] = P[s & 0xff];
+                P[s & 0xff] = temp;
+                n = (byte)((n + 1) & 0xff);
+            }
+        }
+#endif
+
         public virtual void AddSeedMaterial(long seed) 
         {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            Span<byte> bytes = stackalloc byte[8];
+            Pack.UInt64_To_BE((ulong)seed, bytes);
+            AddSeedMaterial(bytes);
+#else
             AddSeedMaterial(Pack.UInt64_To_BE((ulong)seed));
+#endif
         }
 
         public virtual void NextBytes(byte[] bytes) 
diff --git a/crypto/src/crypto/prng/X931SecureRandom.cs b/crypto/src/crypto/prng/X931SecureRandom.cs
index d38e6d6d8..2ceff238f 100644
--- a/crypto/src/crypto/prng/X931SecureRandom.cs
+++ b/crypto/src/crypto/prng/X931SecureRandom.cs
@@ -30,6 +30,19 @@ namespace Org.BouncyCastle.Crypto.Prng
             }
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public override void SetSeed(Span<byte> seed)
+        {
+            lock (this)
+            {
+                if (mRandomSource != null)
+                {
+                    this.mRandomSource.SetSeed(seed);
+                }
+            }
+        }
+#endif
+
         public override void SetSeed(long seed)
         {
             lock (this)
diff --git a/crypto/src/crypto/util/Pack.cs b/crypto/src/crypto/util/Pack.cs
index f5bffb08a..3396a7fc7 100644
--- a/crypto/src/crypto/util/Pack.cs
+++ b/crypto/src/crypto/util/Pack.cs
@@ -214,6 +214,13 @@ namespace Org.BouncyCastle.Crypto.Utilities
             }
         }
 
+        internal static ulong BE_To_UInt64(byte[] bs)
+        {
+            uint hi = BE_To_UInt32(bs);
+            uint lo = BE_To_UInt32(bs, 4);
+            return ((ulong)hi << 32) | (ulong)lo;
+        }
+
         internal static ulong BE_To_UInt64(byte[] bs, int off)
         {
             uint hi = BE_To_UInt32(bs, off);
diff --git a/crypto/src/math/BigInteger.cs b/crypto/src/math/BigInteger.cs
index 4cb220d00..dd8b3a85b 100644
--- a/crypto/src/math/BigInteger.cs
+++ b/crypto/src/math/BigInteger.cs
@@ -509,7 +509,14 @@ namespace Org.BouncyCastle.Math
                 else
                 {
                     int numBytes = end - iBval;
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+                    Span<byte> inverse = numBytes <= 512
+                        ? stackalloc byte[numBytes]
+                        : new byte[numBytes];
+#else
                     byte[] inverse = new byte[numBytes];
+#endif
 
                     int index = 0;
                     while (index < numBytes)
@@ -526,7 +533,7 @@ namespace Org.BouncyCastle.Math
 
                     inverse[index]++;
 
-                    this.magnitude = MakeMagnitude(inverse, 0, inverse.Length);
+                    this.magnitude = MakeMagnitude(inverse);
                 }
             }
             else
@@ -537,6 +544,11 @@ namespace Org.BouncyCastle.Math
             }
         }
 
+        private static int[] MakeMagnitude(byte[] bytes)
+        {
+            return MakeMagnitude(bytes, 0, bytes.Length);
+        }
+
         private static int[] MakeMagnitude(byte[] bytes, int offset, int length)
         {
 #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
@@ -702,14 +714,21 @@ namespace Org.BouncyCastle.Math
             }
 
             int nBytes = GetByteLength(sizeInBits);
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            Span<byte> b = nBytes <= 512
+                ? stackalloc byte[nBytes]
+                : new byte[nBytes];
+#else
             byte[] b = new byte[nBytes];
+#endif
             random.NextBytes(b);
 
             // strip off any excess bits in the MSB
             int xBits = BitsPerByte * nBytes - sizeInBits;
             b[0] &= (byte)(255U >> xBits);
 
-            this.magnitude = MakeMagnitude(b, 0, b.Length);
+            this.magnitude = MakeMagnitude(b);
             this.sign = this.magnitude.Length < 1 ? 0 : 1;
         }
 
@@ -733,7 +752,14 @@ namespace Org.BouncyCastle.Math
             }
              
             int nBytes = GetByteLength(bitLength);
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            Span<byte> b = nBytes <= 512
+                ? stackalloc byte[nBytes]
+                : new byte[nBytes];
+#else
             byte[] b = new byte[nBytes];
+#endif
 
             int xBits = BitsPerByte * nBytes - bitLength;
             byte mask = (byte)(255U >> xBits);
@@ -752,7 +778,7 @@ namespace Org.BouncyCastle.Math
                 // ensure the trailing bit is 1 (i.e. must be odd)
                 b[nBytes - 1] |= 1;
 
-                this.magnitude = MakeMagnitude(b, 0, b.Length);
+                this.magnitude = MakeMagnitude(b);
                 this.nBits = -1;
                 this.mQuote = 0;
 
diff --git a/crypto/src/security/SecureRandom.cs b/crypto/src/security/SecureRandom.cs
index 0ab33c4c6..10b837b03 100644
--- a/crypto/src/security/SecureRandom.cs
+++ b/crypto/src/security/SecureRandom.cs
@@ -17,11 +17,7 @@ namespace Org.BouncyCastle.Security
             return Interlocked.Increment(ref counter);
         }
 
-        private static readonly SecureRandom master = new SecureRandom(new CryptoApiRandomGenerator());
-        private static SecureRandom Master
-        {
-            get { return master; }
-        }
+        private static readonly SecureRandom Master = new SecureRandom(new CryptoApiRandomGenerator());
 
         private static DigestRandomGenerator CreatePrng(string digestName, bool autoSeed)
         {
@@ -32,7 +28,17 @@ namespace Org.BouncyCastle.Security
             if (autoSeed)
             {
                 prng.AddSeedMaterial(NextCounterValue());
-                prng.AddSeedMaterial(GetNextBytes(Master, digest.GetDigestSize()));
+
+                int seedLength = digest.GetDigestSize();
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+                Span<byte> seed = seedLength <= 128
+                    ? stackalloc byte[seedLength]
+                    : new byte[seedLength];
+#else
+                byte[] seed = new byte[seedLength];
+#endif
+                Master.NextBytes(seed);
+                prng.AddSeedMaterial(seed);
             }
             return prng;
         }
@@ -102,11 +108,25 @@ namespace Org.BouncyCastle.Security
             return GetNextBytes(Master, length);
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual void GenerateSeed(Span<byte> seed)
+        {
+            Master.NextBytes(seed);
+        }
+#endif
+
         public virtual void SetSeed(byte[] seed)
         {
             generator.AddSeedMaterial(seed);
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual void SetSeed(Span<byte> seed)
+        {
+            generator.AddSeedMaterial(seed);
+        }
+#endif
+
         public virtual void SetSeed(long seed)
         {
             generator.AddSeedMaterial(seed);
@@ -207,16 +227,24 @@ namespace Org.BouncyCastle.Security
 
         public virtual int NextInt()
         {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            Span<byte> bytes = stackalloc byte[4];
+#else
             byte[] bytes = new byte[4];
+#endif
             NextBytes(bytes);
-            return (int)Pack.BE_To_UInt32(bytes, 0);
+            return (int)Pack.BE_To_UInt32(bytes);
         }
 
         public virtual long NextLong()
         {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            Span<byte> bytes = stackalloc byte[8];
+#else
             byte[] bytes = new byte[8];
+#endif
             NextBytes(bytes);
-            return (long)Pack.BE_To_UInt64(bytes, 0);
+            return (long)Pack.BE_To_UInt64(bytes);
         }
     }
 }
diff --git a/crypto/src/tls/AbstractTlsContext.cs b/crypto/src/tls/AbstractTlsContext.cs
index 40a8e8f75..fa9bc80cd 100644
--- a/crypto/src/tls/AbstractTlsContext.cs
+++ b/crypto/src/tls/AbstractTlsContext.cs
@@ -20,9 +20,15 @@ namespace Org.BouncyCastle.Tls
 
         private static TlsNonceGenerator CreateNonceGenerator(TlsCrypto crypto, int connectionEnd)
         {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            Span<byte> additionalSeedMaterial = stackalloc byte[16];
+            Pack.UInt64_To_BE((ulong)NextCounterValue(), additionalSeedMaterial);
+            Pack.UInt64_To_BE((ulong)DateTime.UtcNow.Ticks, additionalSeedMaterial[8..]);
+#else
             byte[] additionalSeedMaterial = new byte[16];
             Pack.UInt64_To_BE((ulong)NextCounterValue(), additionalSeedMaterial, 0);
             Pack.UInt64_To_BE((ulong)DateTime.UtcNow.Ticks, additionalSeedMaterial, 8);
+#endif
             additionalSeedMaterial[0] &= 0x7F;
             additionalSeedMaterial[0] |= (byte)(connectionEnd << 7);
 
diff --git a/crypto/src/tls/crypto/TlsCrypto.cs b/crypto/src/tls/crypto/TlsCrypto.cs
index d9c2c0da1..f515a752b 100644
--- a/crypto/src/tls/crypto/TlsCrypto.cs
+++ b/crypto/src/tls/crypto/TlsCrypto.cs
@@ -180,6 +180,10 @@ namespace Org.BouncyCastle.Tls.Crypto
         /// <returns>a <see cref="TlsNonceGenerator"/>.</returns>
         TlsNonceGenerator CreateNonceGenerator(byte[] additionalSeedMaterial);
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        TlsNonceGenerator CreateNonceGenerator(ReadOnlySpan<byte> additionalSeedMaterial);
+#endif
+
         /// <summary>Create an SRP-6 client.</summary>
         /// <param name="srpConfig">client config.</param>
         /// <returns>an initialised SRP6 client object.</returns>
diff --git a/crypto/src/tls/crypto/impl/AbstractTlsCrypto.cs b/crypto/src/tls/crypto/impl/AbstractTlsCrypto.cs
index 607f12778..a8fb26697 100644
--- a/crypto/src/tls/crypto/impl/AbstractTlsCrypto.cs
+++ b/crypto/src/tls/crypto/impl/AbstractTlsCrypto.cs
@@ -82,6 +82,10 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl
 
         public abstract TlsNonceGenerator CreateNonceGenerator(byte[] additionalSeedMaterial);
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public abstract TlsNonceGenerator CreateNonceGenerator(ReadOnlySpan<byte> additionalSeedMaterial);
+#endif
+
         public abstract TlsSrp6Client CreateSrp6Client(TlsSrpConfig srpConfig);
 
         public abstract TlsSrp6Server CreateSrp6Server(TlsSrpConfig srpConfig, BigInteger srpVerifier);
diff --git a/crypto/src/tls/crypto/impl/bc/BcTlsCrypto.cs b/crypto/src/tls/crypto/impl/bc/BcTlsCrypto.cs
index 3f63f9e83..79c994fc0 100644
--- a/crypto/src/tls/crypto/impl/bc/BcTlsCrypto.cs
+++ b/crypto/src/tls/crypto/impl/bc/BcTlsCrypto.cs
@@ -148,10 +148,14 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
 
         public override TlsNonceGenerator CreateNonceGenerator(byte[] additionalSeedMaterial)
         {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            return CreateNonceGenerator(Spans.FromNullable(additionalSeedMaterial, 0));
+#else
             int cryptoHashAlgorithm = CryptoHashAlgorithm.sha256;
             IDigest digest = CreateDigest(cryptoHashAlgorithm);
 
-            byte[] seed = new byte[TlsCryptoUtilities.GetHashOutputSize(cryptoHashAlgorithm)];
+            int seedLength = TlsCryptoUtilities.GetHashOutputSize(cryptoHashAlgorithm);
+            byte[] seed = new byte[seedLength];
             SecureRandom.NextBytes(seed);
 
             DigestRandomGenerator randomGenerator = new DigestRandomGenerator(digest);
@@ -159,8 +163,29 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
             randomGenerator.AddSeedMaterial(seed);
 
             return new BcTlsNonceGenerator(randomGenerator);
+#endif
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public override TlsNonceGenerator CreateNonceGenerator(ReadOnlySpan<byte> additionalSeedMaterial)
+        {
+            int cryptoHashAlgorithm = CryptoHashAlgorithm.sha256;
+            IDigest digest = CreateDigest(cryptoHashAlgorithm);
+
+            int seedLength = TlsCryptoUtilities.GetHashOutputSize(cryptoHashAlgorithm);
+            Span<byte> seed = seedLength <= 128
+                ? stackalloc byte[seedLength]
+                : new byte[seedLength];
+            SecureRandom.NextBytes(seed);
+
+            DigestRandomGenerator randomGenerator = new DigestRandomGenerator(digest);
+            randomGenerator.AddSeedMaterial(additionalSeedMaterial);
+            randomGenerator.AddSeedMaterial(seed);
+
+            return new BcTlsNonceGenerator(randomGenerator);
+        }
+#endif
+
         public override bool HasAnyStreamVerifiers(IList<SignatureAndHashAlgorithm> signatureAndHashAlgorithms)
         {
             foreach (SignatureAndHashAlgorithm algorithm in signatureAndHashAlgorithms)
diff --git a/crypto/test/src/security/test/SecureRandomTest.cs b/crypto/test/src/security/test/SecureRandomTest.cs
index fb18f98e0..11ebeb276 100644
--- a/crypto/test/src/security/test/SecureRandomTest.cs
+++ b/crypto/test/src/security/test/SecureRandomTest.cs
@@ -231,7 +231,9 @@ namespace Org.BouncyCastle.Security.Tests
 #if NET6_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER
         private static double MeasureChiSquaredSpan(SecureRandom random, int rounds)
         {
-            byte[] opts = random.GenerateSeed(2);
+            Span<byte> opts = stackalloc byte[2];
+            random.GenerateSeed(opts);
+
             Span<int> counts = stackalloc int[256];
 
             Span<byte> bs = stackalloc byte[256];
@@ -295,6 +297,12 @@ namespace Org.BouncyCastle.Security.Tests
             {
             }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            public void AddSeedMaterial(ReadOnlySpan<byte> inSeed)
+            {
+            }
+#endif
+
             public virtual void AddSeedMaterial(long seed)
             {
             }