summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--crypto/src/crypto/IEntropySource.cs4
-rw-r--r--crypto/src/crypto/parameters/DesParameters.cs10
-rw-r--r--crypto/src/crypto/prng/BasicEntropySourceProvider.cs9
-rw-r--r--crypto/src/crypto/prng/CryptoApiEntropySourceProvider.cs9
-rw-r--r--crypto/src/crypto/prng/SP800SecureRandom.cs10
-rw-r--r--crypto/src/crypto/prng/X931Rng.cs44
-rw-r--r--crypto/src/crypto/prng/X931SecureRandom.cs4
-rw-r--r--crypto/src/crypto/prng/drbg/CtrSP800Drbg.cs579
-rw-r--r--crypto/src/crypto/prng/drbg/DrbgUtilities.cs106
-rw-r--r--crypto/src/crypto/prng/drbg/HMacSP800Drbg.cs164
-rw-r--r--crypto/src/crypto/prng/drbg/HashSP800Drbg.cs304
-rw-r--r--crypto/src/crypto/prng/drbg/ISP80090Drbg.cs10
-rw-r--r--crypto/test/src/crypto/prng/test/TestEntropySourceProvider.cs12
13 files changed, 906 insertions, 359 deletions
diff --git a/crypto/src/crypto/IEntropySource.cs b/crypto/src/crypto/IEntropySource.cs
index 62e3bc76c..2e088ed27 100644
--- a/crypto/src/crypto/IEntropySource.cs
+++ b/crypto/src/crypto/IEntropySource.cs
@@ -19,6 +19,10 @@ namespace Org.BouncyCastle.Crypto
 		/// <returns>The entropy bytes.</returns>
 		byte[] GetEntropy();
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        int GetEntropy(Span<byte> output);
+#endif
+
 		/// <summary>
 		/// Return the number of bits of entropy this source can produce.
 		/// </summary>
diff --git a/crypto/src/crypto/parameters/DesParameters.cs b/crypto/src/crypto/parameters/DesParameters.cs
index a1f67e2b1..28881f21c 100644
--- a/crypto/src/crypto/parameters/DesParameters.cs
+++ b/crypto/src/crypto/parameters/DesParameters.cs
@@ -135,5 +135,15 @@ namespace Org.BouncyCastle.Crypto.Parameters
                 bytes[off + i] = SetOddParity(bytes[off + i]);
             }
         }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public static void SetOddParity(Span<byte> bytes)
+        {
+            for (int i = 0; i < bytes.Length; i++)
+            {
+                bytes[i] = SetOddParity(bytes[i]);
+            }
+        }
+#endif
     }
 }
diff --git a/crypto/src/crypto/prng/BasicEntropySourceProvider.cs b/crypto/src/crypto/prng/BasicEntropySourceProvider.cs
index 31a8461f0..485cf25ab 100644
--- a/crypto/src/crypto/prng/BasicEntropySourceProvider.cs
+++ b/crypto/src/crypto/prng/BasicEntropySourceProvider.cs
@@ -62,6 +62,15 @@ namespace Org.BouncyCastle.Crypto.Prng
                 return SecureRandom.GetNextBytes(mSecureRandom, (mEntropySize + 7) / 8);
             }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            int IEntropySource.GetEntropy(Span<byte> output)
+            {
+                int length = (mEntropySize + 7) / 8;
+                mSecureRandom.NextBytes(output[..length]);
+                return length;
+            }
+#endif
+
             int IEntropySource.EntropySize
             {
                 get { return mEntropySize; }
diff --git a/crypto/src/crypto/prng/CryptoApiEntropySourceProvider.cs b/crypto/src/crypto/prng/CryptoApiEntropySourceProvider.cs
index 635af2bd9..9a2f6de2c 100644
--- a/crypto/src/crypto/prng/CryptoApiEntropySourceProvider.cs
+++ b/crypto/src/crypto/prng/CryptoApiEntropySourceProvider.cs
@@ -56,6 +56,15 @@ namespace Org.BouncyCastle.Crypto.Prng
                 return result;
             }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            int IEntropySource.GetEntropy(Span<byte> output)
+            {
+                int length = (mEntropySize + 7) / 8;
+                mRng.GetBytes(output[..length]);
+                return length;
+            }
+#endif
+
             int IEntropySource.EntropySize
             {
                 get { return mEntropySize; }
diff --git a/crypto/src/crypto/prng/SP800SecureRandom.cs b/crypto/src/crypto/prng/SP800SecureRandom.cs
index 1409df2fa..a18576d03 100644
--- a/crypto/src/crypto/prng/SP800SecureRandom.cs
+++ b/crypto/src/crypto/prng/SP800SecureRandom.cs
@@ -68,6 +68,9 @@ namespace Org.BouncyCastle.Crypto.Prng
 
         public override void NextBytes(byte[] buf, int off, int len)
         {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            NextBytes(buf.AsSpan(off, len));
+#else
             lock (this)
             {
                 if (mDrbg == null)
@@ -82,6 +85,7 @@ namespace Org.BouncyCastle.Crypto.Prng
                     mDrbg.Generate(buf, off, len, null, mPredictionResistant);
                 }
             }
+#endif
         }
 
 #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
@@ -95,10 +99,10 @@ namespace Org.BouncyCastle.Crypto.Prng
                 }
 
                 // check if a reseed is required...
-                if (mDrbg.Generate(buffer, null, mPredictionResistant) < 0)
+                if (mDrbg.Generate(buffer, mPredictionResistant) < 0)
                 {
-                    mDrbg.Reseed(null);
-                    mDrbg.Generate(buffer, null, mPredictionResistant);
+                    mDrbg.Reseed(ReadOnlySpan<byte>.Empty);
+                    mDrbg.Generate(buffer, mPredictionResistant);
                 }
             }
         }
diff --git a/crypto/src/crypto/prng/X931Rng.cs b/crypto/src/crypto/prng/X931Rng.cs
index f0bfdc9f1..25dba89f5 100644
--- a/crypto/src/crypto/prng/X931Rng.cs
+++ b/crypto/src/crypto/prng/X931Rng.cs
@@ -38,16 +38,11 @@ namespace Org.BouncyCastle.Crypto.Prng
             this.mR = new byte[engine.GetBlockSize()];
         }
 
-        /**
-         * Populate a passed in array with random data.
-         *
-         * @param output output array for generated bits.
-         * @param predictionResistant true if a reseed should be forced, false otherwise.
-         *
-         * @return number of bits generated, -1 if a reseed required.
-         */
-        internal int Generate(byte[] output, int outputOff, int outputLen,  bool predictionResistant)
+#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)
@@ -76,24 +71,24 @@ namespace Org.BouncyCastle.Crypto.Prng
 
             for (int i = 0; i < m; i++)
             {
-                mEngine.ProcessBlock(mDT, 0, mI, 0);
+                mEngine.ProcessBlock(mDT, mI);
                 Process(mR, mI, mV);
                 Process(mV, mR, mI);
 
-                Array.Copy(mR, 0, output, outputOff + i * mR.Length, mR.Length);
+                mR.CopyTo(output[(i * mR.Length)..]);
 
                 Increment(mDT);
             }
 
-            int bytesToCopy = (outputLen - m * mR.Length);
+            int bytesToCopy = outputLen - m * mR.Length;
 
             if (bytesToCopy > 0)
             {
-                mEngine.ProcessBlock(mDT, 0, mI, 0);
+                mEngine.ProcessBlock(mDT, mI);
                 Process(mR, mI, mV);
                 Process(mV, mR, mI);
 
-                Array.Copy(mR, 0, output, outputOff + m * mR.Length, bytesToCopy);
+                mR.AsSpan(0, bytesToCopy).CopyTo(output[(m * mR.Length)..]);
 
                 Increment(mDT);
             }
@@ -102,12 +97,17 @@ 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)
+#else
+        /**
+         * Populate a passed in array with random data.
+         *
+         * @param output output array for generated bits.
+         * @param predictionResistant true if a reseed should be forced, false otherwise.
+         *
+         * @return number of bits generated, -1 if a reseed required.
+         */
+        internal int Generate(byte[] output, int outputOff, int outputLen,  bool predictionResistant)
         {
-            int outputLen = output.Length;
-
             if (mR.Length == 8) // 64 bit block size
             {
                 if (mReseedCounter > BLOCK64_RESEED_MAX)
@@ -140,12 +140,12 @@ namespace Org.BouncyCastle.Crypto.Prng
                 Process(mR, mI, mV);
                 Process(mV, mR, mI);
 
-                mR.CopyTo(output[(i * mR.Length)..]);
+                Array.Copy(mR, 0, output, outputOff + i * mR.Length, mR.Length);
 
                 Increment(mDT);
             }
 
-            int bytesToCopy = (outputLen - m * mR.Length);
+            int bytesToCopy = outputLen - m * mR.Length;
 
             if (bytesToCopy > 0)
             {
@@ -153,7 +153,7 @@ namespace Org.BouncyCastle.Crypto.Prng
                 Process(mR, mI, mV);
                 Process(mV, mR, mI);
 
-                mR.AsSpan(0, bytesToCopy).CopyTo(output[(m * mR.Length)..]);
+                Array.Copy(mR, 0, output, outputOff + m * mR.Length, bytesToCopy);
 
                 Increment(mDT);
             }
diff --git a/crypto/src/crypto/prng/X931SecureRandom.cs b/crypto/src/crypto/prng/X931SecureRandom.cs
index 2ceff238f..d40134851 100644
--- a/crypto/src/crypto/prng/X931SecureRandom.cs
+++ b/crypto/src/crypto/prng/X931SecureRandom.cs
@@ -62,6 +62,9 @@ namespace Org.BouncyCastle.Crypto.Prng
 
         public override void NextBytes(byte[] buf, int off, int len)
         {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            NextBytes(buf.AsSpan(off, len));
+#else
             lock (this)
             {
                 // check if a reseed is required...
@@ -71,6 +74,7 @@ namespace Org.BouncyCastle.Crypto.Prng
                     mDrbg.Generate(buf, off, len, mPredictionResistant);
                 }
             }
+#endif
         }
 
 #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
diff --git a/crypto/src/crypto/prng/drbg/CtrSP800Drbg.cs b/crypto/src/crypto/prng/drbg/CtrSP800Drbg.cs
index ddb503aa0..cf566ff9c 100644
--- a/crypto/src/crypto/prng/drbg/CtrSP800Drbg.cs
+++ b/crypto/src/crypto/prng/drbg/CtrSP800Drbg.cs
@@ -1,6 +1,7 @@
 using System;
 
 using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Utilities;
 using Org.BouncyCastle.Utilities;
 using Org.BouncyCastle.Utilities.Encoders;
 
@@ -9,7 +10,7 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
 	/**
 	 * A SP800-90A CTR DRBG.
 	 */
-	public class CtrSP800Drbg
+	public sealed class CtrSP800Drbg
         :   ISP80090Drbg
 	{
 	    private static readonly long TDEA_RESEED_MAX = 1L << (32 - 1);
@@ -59,20 +60,19 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
 	        mSeedLength = keySizeInBits + engine.GetBlockSize() * 8;
 	        mIsTdea = IsTdea(engine);
 
-	        byte[] entropy = GetEntropy();  // Get_entropy_input
-
-            CTR_DRBG_Instantiate_algorithm(entropy, nonce, personalizationString);
+            CTR_DRBG_Instantiate_algorithm(nonce, personalizationString);
 	    }
 
-        private void CTR_DRBG_Instantiate_algorithm(byte[] entropy, byte[] nonce, byte[] personalisationString)
+        private void CTR_DRBG_Instantiate_algorithm(byte[] nonce, byte[] personalisationString)
 	    {
-	        byte[] seedMaterial = Arrays.ConcatenateAll(entropy, nonce, personalisationString);
-	        byte[] seed = Block_Cipher_df(seedMaterial, mSeedLength);
+            byte[] entropy = GetEntropy();  // Get_entropy_input
+            byte[] seedMaterial = Arrays.ConcatenateAll(entropy, nonce, personalisationString);
+	        byte[] seed = BlockCipherDF(seedMaterial, mSeedLength / 8);
 
-            int outlen = mEngine.GetBlockSize();
+            int blockSize = mEngine.GetBlockSize();
 
             mKey = new byte[(mKeySizeInBits + 7) / 8];
-	        mV = new byte[outlen];
+	        mV = new byte[blockSize];
 
 	        // mKey & mV are modified by this call
 	        CTR_DRBG_Update(seed, mKey, mV); 
@@ -80,55 +80,130 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
             mReseedCounter = 1;
 	    }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        private void CTR_DRBG_Update(ReadOnlySpan<byte> seed, Span<byte> key, Span<byte> v)
+        {
+			int seedLength = seed.Length;
+            Span<byte> temp = seedLength <= 256
+				? stackalloc byte[seedLength]
+                : new byte[seedLength];
+
+			int blockSize = mEngine.GetBlockSize();
+            Span<byte> block = blockSize <= 64
+				? stackalloc byte[blockSize]
+                : new byte[blockSize];
+
+			mEngine.Init(true, ExpandToKeyParameter(key));
+            for (int i = 0; i * blockSize < seed.Length; ++i)
+            {
+                AddOneTo(v);
+                mEngine.ProcessBlock(v, block);
+
+                int bytesToCopy = System.Math.Min(blockSize, temp.Length - i * blockSize);
+				block[..bytesToCopy].CopyTo(temp[(i * blockSize)..]);
+            }
+
+			XorWith(seed, temp);
+
+			key.CopyFrom(temp);
+			v.CopyFrom(temp[key.Length..]);
+        }
+#else
         private void CTR_DRBG_Update(byte[] seed, byte[] key, byte[] v)
 	    {
-	        byte[] temp = new byte[seed.Length];
+			byte[] temp = new byte[seed.Length];
 	        byte[] outputBlock = new byte[mEngine.GetBlockSize()];
 
             int i = 0;
 	        int outLen = mEngine.GetBlockSize();
 
-            mEngine.Init(true, new KeyParameter(ExpandKey(key)));
-	        while (i*outLen < seed.Length)
+			mEngine.Init(true, ExpandToKeyParameter(key));
+	        while (i * outLen < seed.Length)
 	        {
 	            AddOneTo(v);
 	            mEngine.ProcessBlock(v, 0, outputBlock, 0);
 
-	            int bytesToCopy = ((temp.Length - i * outLen) > outLen)
-	                    ? outLen : (temp.Length - i * outLen);
-	            
+				int bytesToCopy = System.Math.Min(outLen, temp.Length - i * outLen);
 	            Array.Copy(outputBlock, 0, temp, i * outLen, bytesToCopy);
 	            ++i;
 	        }
 
-	        XOR(temp, seed, temp, 0);
+	        Xor(temp, seed, temp, 0);
 
 	        Array.Copy(temp, 0, key, 0, key.Length);
 	        Array.Copy(temp, key.Length, v, 0, v.Length);
-	    }
-	    
-	    private void CTR_DRBG_Reseed_algorithm(byte[] additionalInput)
-	    {
-	        byte[] seedMaterial = Arrays.Concatenate(GetEntropy(), additionalInput);
+        }
+#endif
 
-	        seedMaterial = Block_Cipher_df(seedMaterial, mSeedLength);
+        private void CTR_DRBG_Reseed_algorithm(byte[] additionalInput)
+        {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+			CTR_DRBG_Reseed_algorithm(Spans.FromNullableReadOnly(additionalInput));
+#else
+			byte[] seedMaterial = Arrays.Concatenate(GetEntropy(), additionalInput);
+
+            seedMaterial = BlockCipherDF(seedMaterial, mSeedLength / 8);
 
             CTR_DRBG_Update(seedMaterial, mKey, mV);
 
             mReseedCounter = 1;
-	    }
+#endif
+        }
 
-        private void XOR(byte[] output, byte[] a, byte[] b, int bOff)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        private void CTR_DRBG_Reseed_algorithm(ReadOnlySpan<byte> additionalInput)
+        {
+			int entropyLength = GetEntropyLength();
+			int seedLength = entropyLength + additionalInput.Length;
+
+			Span<byte> seedMaterial = seedLength <= 256
+				? stackalloc byte[seedLength]
+				: new byte[seedLength];
+
+			GetEntropy(seedMaterial[..entropyLength]);
+			additionalInput.CopyTo(seedMaterial[entropyLength..]);
+
+            seedMaterial = BlockCipherDF(seedMaterial, mSeedLength / 8);
+
+            CTR_DRBG_Update(seedMaterial, mKey, mV);
+
+            mReseedCounter = 1;
+        }
+#endif
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        private void Xor(ReadOnlySpan<byte> x, ReadOnlySpan<byte> y, Span<byte> z)
+        {
+            for (int i = 0; i < z.Length; ++i)
+            {
+                z[i] = (byte)(x[i] ^ y[i]);
+            }
+        }
+
+        private void XorWith(ReadOnlySpan<byte> x, Span<byte> z)
+        {
+            for (int i = 0; i < z.Length; ++i)
+            {
+				z[i] ^= x[i];
+            }
+        }
+#else
+        private void Xor(byte[] output, byte[] a, byte[] b, int bOff)
 	    {
             for (int i = 0; i < output.Length; i++) 
 	        {
                 output[i] = (byte)(a[i] ^ b[bOff + i]);
 	        }
 	    }
+#endif
 
-        private void AddOneTo(byte[] longer)
-	    {
-	        uint carry = 1;
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        private void AddOneTo(Span<byte> longer)
+#else
+		private void AddOneTo(byte[] longer)
+#endif
+        {
+            uint carry = 1;
             int i = longer.Length;
             while (--i >= 0)
             {
@@ -146,83 +221,101 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
 	        return entropy;
 	    }
 
-	    // -- Internal state migration ---
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        private int GetEntropy(Span<byte> output)
+        {
+			int length = mEntropySource.GetEntropy(output);
+            if (length < (mSecurityStrength + 7) / 8)
+                throw new InvalidOperationException("Insufficient entropy provided by entropy source");
+			return length;
+        }
 
-        private static readonly byte[] K_BITS = Hex.DecodeStrict("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F");
+		private int GetEntropyLength()
+		{
+			return (mEntropySource.EntropySize + 7) / 8;
+		}
+#endif
+
+        // -- Internal state migration ---
+
+        private static readonly byte[] K_BITS = Hex.DecodeStrict(
+			"000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F");
 
         // 1. If (number_of_bits_to_return > max_number_of_bits), then return an
-	    // ERROR_FLAG.
-	    // 2. L = len (input_string)/8.
-	    // 3. N = number_of_bits_to_return/8.
-	    // Comment: L is the bitstring represention of
-	    // the integer resulting from len (input_string)/8.
-	    // L shall be represented as a 32-bit integer.
-	    //
-	    // Comment : N is the bitstring represention of
-	    // the integer resulting from
-	    // number_of_bits_to_return/8. N shall be
-	    // represented as a 32-bit integer.
-	    //
-	    // 4. S = L || N || input_string || 0x80.
-	    // 5. While (len (S) mod outlen)
-	    // Comment : Pad S with zeros, if necessary.
-	    // 0, S = S || 0x00.
-	    //
-	    // Comment : Compute the starting value.
-	    // 6. temp = the Null string.
-	    // 7. i = 0.
-	    // 8. K = Leftmost keylen bits of 0x00010203...1D1E1F.
-	    // 9. While len (temp) < keylen + outlen, do
-	    //
-	    // IV = i || 0outlen - len (i).
-	    //
-	    // 9.1
-	    //
-	    // temp = temp || BCC (K, (IV || S)).
-	    //
-	    // 9.2
-	    //
-	    // i = i + 1.
-	    //
-	    // 9.3
-	    //
-	    // Comment : i shall be represented as a 32-bit
-	    // integer, i.e., len (i) = 32.
-	    //
-	    // Comment: The 32-bit integer represenation of
-	    // i is padded with zeros to outlen bits.
-	    //
-	    // Comment: Compute the requested number of
-	    // bits.
-	    //
-	    // 10. K = Leftmost keylen bits of temp.
-	    //
-	    // 11. X = Next outlen bits of temp.
-	    //
-	    // 12. temp = the Null string.
-	    //
-	    // 13. While len (temp) < number_of_bits_to_return, do
-	    //
-	    // 13.1 X = Block_Encrypt (K, X).
-	    //
-	    // 13.2 temp = temp || X.
-	    //
-	    // 14. requested_bits = Leftmost number_of_bits_to_return of temp.
-	    //
-	    // 15. Return SUCCESS and requested_bits.
-	    private byte[] Block_Cipher_df(byte[] inputString, int bitLength)
-	    {
-	        int outLen = mEngine.GetBlockSize();
-	        int L = inputString.Length; // already in bytes
-	        int N = bitLength / 8;
-	        // 4 S = L || N || inputstring || 0x80
+        // ERROR_FLAG.
+        // 2. L = len (input_string)/8.
+        // 3. N = number_of_bits_to_return/8.
+        // Comment: L is the bitstring represention of
+        // the integer resulting from len (input_string)/8.
+        // L shall be represented as a 32-bit integer.
+        //
+        // Comment : N is the bitstring represention of
+        // the integer resulting from
+        // number_of_bits_to_return/8. N shall be
+        // represented as a 32-bit integer.
+        //
+        // 4. S = L || N || input_string || 0x80.
+        // 5. While (len (S) mod outlen)
+        // Comment : Pad S with zeros, if necessary.
+        // 0, S = S || 0x00.
+        //
+        // Comment : Compute the starting value.
+        // 6. temp = the Null string.
+        // 7. i = 0.
+        // 8. K = Leftmost keylen bits of 0x00010203...1D1E1F.
+        // 9. While len (temp) < keylen + outlen, do
+        //
+        // IV = i || 0outlen - len (i).
+        //
+        // 9.1
+        //
+        // temp = temp || BCC (K, (IV || S)).
+        //
+        // 9.2
+        //
+        // i = i + 1.
+        //
+        // 9.3
+        //
+        // Comment : i shall be represented as a 32-bit
+        // integer, i.e., len (i) = 32.
+        //
+        // Comment: The 32-bit integer represenation of
+        // i is padded with zeros to outlen bits.
+        //
+        // Comment: Compute the requested number of
+        // bits.
+        //
+        // 10. K = Leftmost keylen bits of temp.
+        //
+        // 11. X = Next outlen bits of temp.
+        //
+        // 12. temp = the Null string.
+        //
+        // 13. While len (temp) < number_of_bits_to_return, do
+        //
+        // 13.1 X = Block_Encrypt (K, X).
+        //
+        // 13.2 temp = temp || X.
+        //
+        // 14. requested_bits = Leftmost number_of_bits_to_return of temp.
+        //
+        // 15. Return SUCCESS and requested_bits.
+		private byte[] BlockCipherDF(byte[] input, int N)
+        {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            return BlockCipherDF(input.AsSpan(), N);
+#else
+            int outLen = mEngine.GetBlockSize();
+	        int L = input.Length; // already in bytes
+	        // 4 S = L || N || input || 0x80
 	        int sLen = 4 + 4 + L + 1;
 	        int blockLen = ((sLen + outLen - 1) / outLen) * outLen;
 	        byte[] S = new byte[blockLen];
-	        copyIntToByteArray(S, L, 0);
-	        copyIntToByteArray(S, N, 4);
-	        Array.Copy(inputString, 0, S, 8, L);
-	        S[8 + L] = (byte)0x80;
+            Pack.UInt32_To_BE((uint)L, S, 0);
+            Pack.UInt32_To_BE((uint)N, S, 4);
+			Array.Copy(input, 0, S, 8, L);
+	        S[8 + L] = 0x80;
 	        // S already padded with zeros
 
 	        byte[] temp = new byte[mKeySizeInBits / 8 + outLen];
@@ -233,16 +326,15 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
 	        int i = 0;
 	        byte[] K = new byte[mKeySizeInBits / 8];
 	        Array.Copy(K_BITS, 0, K, 0, K.Length);
+            var K1 = ExpandToKeyParameter(K);
+	        mEngine.Init(true, K1);
 
 	        while (i*outLen*8 < mKeySizeInBits + outLen *8)
 	        {
-	            copyIntToByteArray(IV, i, 0);
-	            BCC(bccOut, K, IV, S);
+                Pack.UInt32_To_BE((uint)i, IV, 0);
+                BCC(bccOut, IV, S);
 
-	            int bytesToCopy = ((temp.Length - i * outLen) > outLen)
-	                    ? outLen
-	                    : (temp.Length - i * outLen);
-	            
+                int bytesToCopy = System.Math.Min(outLen, temp.Length - i * outLen);
 	            Array.Copy(bccOut, 0, temp, i * outLen, bytesToCopy);
 	            ++i;
 	        }
@@ -251,39 +343,120 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
 	        Array.Copy(temp, 0, K, 0, K.Length);
 	        Array.Copy(temp, K.Length, X, 0, X.Length);
 
-	        temp = new byte[bitLength / 2];
+	        temp = new byte[N];
 
 	        i = 0;
-	        mEngine.Init(true, new KeyParameter(ExpandKey(K)));
+	        mEngine.Init(true, ExpandToKeyParameter(K));
 
 	        while (i * outLen < temp.Length)
 	        {
 	            mEngine.ProcessBlock(X, 0, X, 0);
 
-	            int bytesToCopy = ((temp.Length - i * outLen) > outLen)
-	                    ? outLen
-	                    : (temp.Length - i * outLen);
-
+				int bytesToCopy = System.Math.Min(outLen, temp.Length - i * outLen);
 	            Array.Copy(X, 0, temp, i * outLen, bytesToCopy);
 	            i++;
 	        }
 
 	        return temp;
-	    }
+#endif
+        }
 
-	    /*
-	    * 1. chaining_value = 0^outlen    
-	    *    . Comment: Set the first chaining value to outlen zeros.
-	    * 2. n = len (data)/outlen.
-	    * 3. Starting with the leftmost bits of data, split the data into n blocks of outlen bits 
-	    *    each, forming block(1) to block(n). 
-	    * 4. For i = 1 to n do
-	    * 4.1 input_block = chaining_value ^ block(i) .
-	    * 4.2 chaining_value = Block_Encrypt (Key, input_block).
-	    * 5. output_block = chaining_value.
-	    * 6. Return output_block. 
-	     */
-	    private void BCC(byte[] bccOut, byte[] k, byte[] iV, byte[] data)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        private byte[] BlockCipherDF(ReadOnlySpan<byte> input, int N)
+        {
+            int blockSize = mEngine.GetBlockSize();
+            int L = input.Length; // already in bytes
+            // 4 S = L || N || input || 0x80
+            int sLen = 4 + 4 + L + 1;
+            int blockLen = ((sLen + blockSize - 1) / blockSize) * blockSize;
+            Span<byte> S = blockLen <= 256
+                ? stackalloc byte[blockLen]
+                : new byte[blockLen];
+            Pack.UInt32_To_BE((uint)L, S);
+            Pack.UInt32_To_BE((uint)N, S[4..]);
+            input.CopyTo(S[8..]);
+            S[8 + L] = 0x80;
+            // S already padded with zeros
+
+            int keySize = mKeySizeInBits / 8;
+            int tempSize = keySize + blockSize;
+            Span<byte> temp = tempSize <= 128
+                ? stackalloc byte[tempSize]
+                : new byte[tempSize];
+
+            Span<byte> bccOut = blockSize <= 64
+                ? stackalloc byte[blockSize]
+                : new byte[blockSize];
+
+            Span<byte> IV = blockSize <= 64
+                ? stackalloc byte[blockSize]
+                : new byte[blockSize];
+
+            var K1 = ExpandToKeyParameter(K_BITS.AsSpan(0, keySize));
+            mEngine.Init(true, K1);
+
+            for (int i = 0; i * blockSize < tempSize; ++i)
+            {
+                Pack.UInt32_To_BE((uint)i, IV);
+                BCC(bccOut, IV, S);
+
+                int bytesToCopy = System.Math.Min(blockSize, tempSize - i * blockSize);
+                bccOut[..bytesToCopy].CopyTo(temp[(i * blockSize)..]);
+            }
+
+            var K2 = ExpandToKeyParameter(temp[..keySize]);
+            mEngine.Init(true, K2);
+            var X = temp[keySize..];
+
+            byte[] result = new byte[N];
+            for (int i = 0; i * blockSize < result.Length; ++i)
+            {
+                mEngine.ProcessBlock(X, X);
+
+                int bytesToCopy = System.Math.Min(blockSize, result.Length - i * blockSize);
+                X[..bytesToCopy].CopyTo(result.AsSpan(i * blockSize));
+            }
+            return result;
+        }
+#endif
+
+        /*
+        * 1. chaining_value = 0^outlen    
+        *    . Comment: Set the first chaining value to outlen zeros.
+        * 2. n = len (data)/outlen.
+        * 3. Starting with the leftmost bits of data, split the data into n blocks of outlen bits 
+        *    each, forming block(1) to block(n). 
+        * 4. For i = 1 to n do
+        * 4.1 input_block = chaining_value ^ block(i) .
+        * 4.2 chaining_value = Block_Encrypt (Key, input_block).
+        * 5. output_block = chaining_value.
+        * 6. Return output_block. 
+        */
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        private void BCC(Span<byte> bccOut, ReadOnlySpan<byte> iV, ReadOnlySpan<byte> data)
+        {
+            int blockSize = mEngine.GetBlockSize();
+
+            Span<byte> chainingValue = blockSize <= 64
+                ? stackalloc byte[blockSize]
+                : new byte[blockSize];
+            Span<byte> inputBlock = blockSize <= 64
+                ? stackalloc byte[blockSize]
+                : new byte[blockSize];
+
+            mEngine.ProcessBlock(iV, chainingValue);
+
+            int n = data.Length / blockSize;
+            for (int i = 0; i < n; i++)
+            {
+                Xor(chainingValue, data[(i * blockSize)..], inputBlock);
+                mEngine.ProcessBlock(inputBlock, chainingValue);
+            }
+
+            bccOut.CopyFrom(chainingValue);
+        }
+#else
+        private void BCC(byte[] bccOut, byte[] iV, byte[] data)
 	    {
 	        int outlen = mEngine.GetBlockSize();
 	        byte[] chainingValue = new byte[outlen]; // initial values = 0
@@ -291,33 +464,24 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
 
 	        byte[] inputBlock = new byte[outlen];
 
-	        mEngine.Init(true, new KeyParameter(ExpandKey(k)));
-
             mEngine.ProcessBlock(iV, 0, chainingValue, 0);
 
             for (int i = 0; i < n; i++)
 	        {
-	            XOR(inputBlock, chainingValue, data, i*outlen);
+	            Xor(inputBlock, chainingValue, data, i*outlen);
 	            mEngine.ProcessBlock(inputBlock, 0, chainingValue, 0);
 	        }
 
             Array.Copy(chainingValue, 0, bccOut, 0, bccOut.Length);
 	    }
+#endif
 
-	    private void copyIntToByteArray(byte[] buf, int value, int offSet)
-	    {
-	        buf[offSet + 0] = ((byte)(value >> 24));
-	        buf[offSet + 1] = ((byte)(value >> 16));
-	        buf[offSet + 2] = ((byte)(value >> 8));
-	        buf[offSet + 3] = ((byte)(value));
-	    }
-
-	    /**
+        /**
 	     * Return the block size (in bits) of the DRBG.
 	     *
 	     * @return the number of bits produced on each internal round of the DRBG.
 	     */
-	    public int BlockSize
+        public int BlockSize
 	    {
 			get { return mV.Length * 8; }
 	    }
@@ -335,7 +499,9 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
 			bool predictionResistant)
 	    {
 #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
-            return Generate(output.AsSpan(outputOff, outputLen), additionalInput, predictionResistant);
+            return additionalInput == null
+                ? Generate(output.AsSpan(outputOff, outputLen), predictionResistant)
+                : GenerateWithInput(output.AsSpan(outputOff, outputLen), additionalInput.AsSpan(), predictionResistant);
 #else
 			if (mIsTdea)
 	        {
@@ -362,7 +528,7 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
 
 	        if (additionalInput != null)
 	        {
-	            additionalInput = Block_Cipher_df(additionalInput, mSeedLength);
+	            additionalInput = BlockCipherDF(additionalInput, mSeedLength / 8);
 	            CTR_DRBG_Update(additionalInput, mKey, mV);
 	        }
 	        else
@@ -372,7 +538,7 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
 
             byte[] tmp = new byte[mV.Length];
 
-            mEngine.Init(true, new KeyParameter(ExpandKey(mKey)));
+            mEngine.Init(true, ExpandToKeyParameter(mKey));
 
             for (int i = 0, limit = outputLen / tmp.Length; i <= limit; i++)
 	        {
@@ -397,9 +563,9 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
         }
 
 #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
-        public int Generate(Span<byte> output, byte[] additionalInput, bool predictionResistant)
-		{
-			int outputLen = output.Length;
+        public int Generate(Span<byte> output, bool predictionResistant)
+        {
+            int outputLen = output.Length;
             if (mIsTdea)
             {
                 if (mReseedCounter > TDEA_RESEED_MAX)
@@ -419,24 +585,57 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
 
             if (predictionResistant)
             {
-                CTR_DRBG_Reseed_algorithm(additionalInput);
-                additionalInput = null;
+                CTR_DRBG_Reseed_algorithm(ReadOnlySpan<byte>.Empty);
             }
 
-            if (additionalInput != null)
+            byte[] seed = new byte[mSeedLength / 8];
+
+            return ImplGenerate(seed, output);
+        }
+
+        public int GenerateWithInput(Span<byte> output, ReadOnlySpan<byte> additionalInput, bool predictionResistant)
+		{
+			int outputLen = output.Length;
+            if (mIsTdea)
             {
-                additionalInput = Block_Cipher_df(additionalInput, mSeedLength);
-                CTR_DRBG_Update(additionalInput, mKey, mV);
+                if (mReseedCounter > TDEA_RESEED_MAX)
+                    return -1;
+
+                if (outputLen > TDEA_MAX_BITS_REQUEST / 8)
+                    throw new ArgumentException("Number of bits per request limited to " + TDEA_MAX_BITS_REQUEST, "output");
             }
             else
             {
-                additionalInput = new byte[mSeedLength];
+                if (mReseedCounter > AES_RESEED_MAX)
+                    return -1;
+
+                if (outputLen > AES_MAX_BITS_REQUEST / 8)
+                    throw new ArgumentException("Number of bits per request limited to " + AES_MAX_BITS_REQUEST, "output");
             }
 
+            int seedLength = mSeedLength / 8;
+            byte[] seed;
+            if (predictionResistant)
+            {
+                CTR_DRBG_Reseed_algorithm(additionalInput);
+                seed = new byte[seedLength];
+            }
+            else
+			{
+                seed = BlockCipherDF(additionalInput, seedLength);
+                CTR_DRBG_Update(seed, mKey, mV);
+            }
+
+			return ImplGenerate(seed, output);
+        }
+
+		private int ImplGenerate(ReadOnlySpan<byte> seed, Span<byte> output)
+		{
             byte[] tmp = new byte[mV.Length];
 
-            mEngine.Init(true, new KeyParameter(ExpandKey(mKey)));
+            mEngine.Init(true, ExpandToKeyParameter(mKey));
 
+            int outputLen = output.Length;
             for (int i = 0, limit = outputLen / tmp.Length; i <= limit; i++)
             {
                 int bytesToCopy = System.Math.Min(tmp.Length, outputLen - i * tmp.Length);
@@ -447,11 +646,11 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
 
                     mEngine.ProcessBlock(mV, 0, tmp, 0);
 
-					tmp[..bytesToCopy].CopyTo(output[(i * tmp.Length)..]);
+                    tmp[..bytesToCopy].CopyTo(output[(i * tmp.Length)..]);
                 }
             }
 
-            CTR_DRBG_Update(additionalInput, mKey, mV);
+            CTR_DRBG_Update(seed, mKey, mV);
 
             mReseedCounter++;
 
@@ -466,9 +665,20 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
 	      */
         public void Reseed(byte[] additionalInput)
 	    {
-	        CTR_DRBG_Reseed_algorithm(additionalInput);
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+			Reseed(Spans.FromNullableReadOnly(additionalInput));
+#else
+			CTR_DRBG_Reseed_algorithm(additionalInput);
+#endif
 	    }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public void Reseed(ReadOnlySpan<byte> additionalInput)
+		{
+            CTR_DRBG_Reseed_algorithm(additionalInput);
+        }
+#endif
+
         private bool IsTdea(IBlockCipher cipher)
 	    {
 	        return cipher.AlgorithmName.Equals("DESede") || cipher.AlgorithmName.Equals("TDEA");
@@ -488,26 +698,39 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
             return -1;
 	    }
 
-        private byte[] ExpandKey(byte[] key)
+        private KeyParameter ExpandToKeyParameter(byte[] key)
 	    {
-	        if (mIsTdea)
-	        {
-	            // expand key to 192 bits.
-	            byte[] tmp = new byte[24];
+			if (!mIsTdea)
+				return new KeyParameter(key);
 
-                PadKey(key, 0, tmp, 0);
-                PadKey(key, 7, tmp, 8);
-                PadKey(key, 14, tmp, 16);
+	        // expand key to 192 bits.
+	        byte[] tmp = new byte[24];
 
-	            return tmp;
-	        }
-	        else
-	        {
-	            return key;
-	        }
-	    }
+            PadKey(key, 0, tmp, 0);
+            PadKey(key, 7, tmp, 8);
+            PadKey(key, 14, tmp, 16);
 
-	    /**
+            return new KeyParameter(tmp);
+        }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        private KeyParameter ExpandToKeyParameter(ReadOnlySpan<byte> key)
+        {
+			if (!mIsTdea)
+				return new KeyParameter(key);
+
+            // expand key to 192 bits.
+            Span<byte> tmp = stackalloc byte[24];
+
+            PadKey(key, tmp);
+			PadKey(key[7..], tmp[8..]);
+			PadKey(key[14..], tmp[16..]);
+
+            return new KeyParameter(tmp);
+        }
+#endif
+
+        /**
 	     * Pad out a key for TDEA, setting odd parity for each byte.
 	     *
 	     * @param keyMaster
@@ -528,5 +751,21 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
 
             DesParameters.SetOddParity(tmp, tmpOff, 8);
 	    }
-	}
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        private void PadKey(ReadOnlySpan<byte> keyMaster, Span<byte> tmp)
+        {
+            tmp[0] = (byte)(keyMaster[0] & 0xFE);
+            tmp[1] = (byte)((keyMaster[0] << 7) | ((keyMaster[1] & 0xfc) >> 1));
+            tmp[2] = (byte)((keyMaster[1] << 6) | ((keyMaster[2] & 0xf8) >> 2));
+            tmp[3] = (byte)((keyMaster[2] << 5) | ((keyMaster[3] & 0xf0) >> 3));
+            tmp[4] = (byte)((keyMaster[3] << 4) | ((keyMaster[4] & 0xe0) >> 4));
+            tmp[5] = (byte)((keyMaster[4] << 3) | ((keyMaster[5] & 0xc0) >> 5));
+            tmp[6] = (byte)((keyMaster[5] << 2) | ((keyMaster[6] & 0x80) >> 6));
+            tmp[7] = (byte)(keyMaster[6] << 1);
+
+			DesParameters.SetOddParity(tmp[..8]);
+        }
+#endif
+    }
 }
diff --git a/crypto/src/crypto/prng/drbg/DrbgUtilities.cs b/crypto/src/crypto/prng/drbg/DrbgUtilities.cs
index 58baaf5d9..24037b3f1 100644
--- a/crypto/src/crypto/prng/drbg/DrbgUtilities.cs
+++ b/crypto/src/crypto/prng/drbg/DrbgUtilities.cs
@@ -1,7 +1,7 @@
 using System;
 using System.Collections.Generic;
 
-using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Crypto.Utilities;
 
 namespace Org.BouncyCastle.Crypto.Prng.Drbg
 {
@@ -35,65 +35,77 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
             return MaxSecurityStrengths[name.Substring(0, name.IndexOf("/"))];
 	    }
 
-	    /**
+        /**
 	     * Used by both Dual EC and Hash.
 	     */
-	    internal static byte[] HashDF(IDigest digest, byte[] seedMaterial, int seedLength)
-	    {
-	         // 1. temp = the Null string.
-	        // 2. .
-	        // 3. counter = an 8-bit binary value representing the integer "1".
-	        // 4. For i = 1 to len do
-	        // Comment : In step 4.1, no_of_bits_to_return
-	        // is used as a 32-bit string.
-	        // 4.1 temp = temp || Hash (counter || no_of_bits_to_return ||
-	        // input_string).
-	        // 4.2 counter = counter + 1.
-	        // 5. requested_bits = Leftmost (no_of_bits_to_return) of temp.
-	        // 6. Return SUCCESS and requested_bits.
-	        byte[] temp = new byte[(seedLength + 7) / 8];
-
-	        int len = temp.Length / digest.GetDigestSize();
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        internal static void HashDF(IDigest digest, ReadOnlySpan<byte> seedMaterial, int seedLength, Span<byte> output)
+#else
+		internal static void HashDF(IDigest digest, byte[] seedMaterial, int seedLength, byte[] output)
+#endif
+        {
+			// 1. temp = the Null string.
+			// 2. .
+			// 3. counter = an 8-bit binary value representing the integer "1".
+			// 4. For i = 1 to len do
+			// Comment : In step 4.1, no_of_bits_to_return
+			// is used as a 32-bit string.
+			// 4.1 temp = temp || Hash (counter || no_of_bits_to_return ||
+			// input_string).
+			// 4.2 counter = counter + 1.
+			// 5. requested_bits = Leftmost (no_of_bits_to_return) of temp.
+			// 6. Return SUCCESS and requested_bits.
+			int outputLength = (seedLength + 7) / 8;
+
+            int digestSize = digest.GetDigestSize();
+            int len = outputLength / digestSize;
 	        int counter = 1;
 
-	        byte[] dig = new byte[digest.GetDigestSize()];
-
-	        for (int i = 0; i <= len; i++)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+			Span<byte> dig = digestSize <= 128
+				? stackalloc byte[digestSize]
+				: new byte[digestSize];
+			Span<byte> header = stackalloc byte[5];
+            Pack.UInt32_To_BE((uint)seedLength, header[1..]);
+#else
+			byte[] dig = new byte[digestSize];
+			byte[] header = new byte[5];
+            Pack.UInt32_To_BE((uint)seedLength, header, 1);
+#endif
+
+            for (int i = 0; i <= len; i++, counter++)
 	        {
-	            digest.Update((byte)counter);
-
-	            digest.Update((byte)(seedLength >> 24));
-	            digest.Update((byte)(seedLength >> 16));
-	            digest.Update((byte)(seedLength >> 8));
-	            digest.Update((byte)seedLength);
-
-	            digest.BlockUpdate(seedMaterial, 0, seedMaterial.Length);
-
-	            digest.DoFinal(dig, 0);
-
-	            int bytesToCopy = ((temp.Length - i * dig.Length) > dig.Length)
-	                    ? dig.Length
-	                    : (temp.Length - i * dig.Length);
-	            Array.Copy(dig, 0, temp, i * dig.Length, bytesToCopy);
-
-	            counter++;
-	        }
-
-	        // do a left shift to get rid of excess bits.
-	        if (seedLength % 8 != 0)
+                header[0] = (byte)counter;
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+                digest.BlockUpdate(header);
+                digest.BlockUpdate(seedMaterial);
+                digest.DoFinal(dig);
+
+                int bytesToCopy = System.Math.Min(digestSize, outputLength - i * digestSize);
+				dig[..bytesToCopy].CopyTo(output[(i * digestSize)..]);
+#else
+				digest.BlockUpdate(header, 0, header.Length);
+				digest.BlockUpdate(seedMaterial, 0, seedMaterial.Length);
+                digest.DoFinal(dig, 0);
+
+				int bytesToCopy = System.Math.Min(digestSize, outputLength - i * digestSize);
+	            Array.Copy(dig, 0, output, i * digestSize, bytesToCopy);
+#endif
+            }
+
+            // do a left shift to get rid of excess bits.
+            if (seedLength % 8 != 0)
 	        {
 	            int shift = 8 - (seedLength % 8);
 	            uint carry = 0;
 
-                for (int i = 0; i != temp.Length; i++)
+                for (int i = 0; i != outputLength; i++)
 	            {
-	                uint b = temp[i];
-	                temp[i] = (byte)((b >> shift) | (carry << (8 - shift)));
+	                uint b = output[i];
+                    output[i] = (byte)((b >> shift) | (carry << (8 - shift)));
 	                carry = b;
 	            }
 	        }
-
-            return temp;
 	    }
 	}
 }
diff --git a/crypto/src/crypto/prng/drbg/HMacSP800Drbg.cs b/crypto/src/crypto/prng/drbg/HMacSP800Drbg.cs
index 5a684a89d..47e0191dd 100644
--- a/crypto/src/crypto/prng/drbg/HMacSP800Drbg.cs
+++ b/crypto/src/crypto/prng/drbg/HMacSP800Drbg.cs
@@ -8,7 +8,7 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
 	/**
 	 * A SP800-90A HMAC DRBG.
 	 */
-	public class HMacSP800Drbg
+	public sealed class HMacSP800Drbg
         :   ISP80090Drbg
 	{
 	    private readonly static long RESEED_MAX = 1L << (48 - 1);
@@ -33,7 +33,8 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
 	     * @param personalizationString personalization string to distinguish this DRBG (may be null).
 	     * @param nonce nonce to further distinguish this DRBG (may be null).
 	     */
-	    public HMacSP800Drbg(IMac hMac, int securityStrength, IEntropySource entropySource, byte[] personalizationString, byte[] nonce)
+	    public HMacSP800Drbg(IMac hMac, int securityStrength, IEntropySource entropySource,
+            byte[] personalizationString, byte[] nonce)
 	    {
 	        if (securityStrength > DrbgUtilities.GetMaxSecurityStrength(hMac))
 	            throw new ArgumentException("Requested security strength is not supported by the derivation function");
@@ -56,12 +57,41 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
             mReseedCounter = 1;
 	    }
 
-        private void hmac_DRBG_Update(byte[] seedMaterial)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        private void hmac_DRBG_Update()
+        {
+            hmac_DRBG_Update_Func(ReadOnlySpan<byte>.Empty, 0x00);
+        }
+
+        private void hmac_DRBG_Update(ReadOnlySpan<byte> seedMaterial)
+        {
+            hmac_DRBG_Update_Func(seedMaterial, 0x00);
+            hmac_DRBG_Update_Func(seedMaterial, 0x01);
+        }
+
+        private void hmac_DRBG_Update_Func(ReadOnlySpan<byte> seedMaterial, byte vValue)
+        {
+            mHMac.Init(new KeyParameter(mK));
+
+            mHMac.BlockUpdate(mV);
+            mHMac.Update(vValue);
+            if (!seedMaterial.IsEmpty)
+            {
+                mHMac.BlockUpdate(seedMaterial);
+            }
+            mHMac.DoFinal(mK);
+
+            mHMac.Init(new KeyParameter(mK));
+            mHMac.BlockUpdate(mV);
+            mHMac.DoFinal(mV);
+        }
+#else
+		private void hmac_DRBG_Update(byte[] seedMaterial)
 	    {
-	        hmac_DRBG_Update_Func(seedMaterial, (byte)0x00);
+	        hmac_DRBG_Update_Func(seedMaterial, 0x00);
 	        if (seedMaterial != null)
 	        {
-	            hmac_DRBG_Update_Func(seedMaterial, (byte)0x01);
+	            hmac_DRBG_Update_Func(seedMaterial, 0x01);
 	        }
 	    }
 
@@ -84,13 +114,14 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
 
             mHMac.DoFinal(mV, 0);
 	    }
+#endif
 
-	    /**
+        /**
 	     * Return the block size (in bits) of the DRBG.
 	     *
 	     * @return the number of bits produced on each round of the DRBG.
 	     */
-	    public int BlockSize
+        public int BlockSize
 	    {
 			get { return mV.Length * 8; }
 	    }
@@ -108,7 +139,9 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
 			bool predictionResistant)
 	    {
 #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
-			return Generate(output.AsSpan(outputOff, outputLen), additionalInput, predictionResistant);
+			return additionalInput == null
+				? Generate(output.AsSpan(outputOff, outputLen), predictionResistant)
+				: GenerateWithInput(output.AsSpan(outputOff, outputLen), additionalInput.AsSpan(), predictionResistant);
 #else
 			int numberOfBits = outputLen * 8;
 
@@ -164,10 +197,9 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
 	    }
 
 #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
-        public int Generate(Span<byte> output, byte[] additionalInput, bool predictionResistant)
+        public int Generate(Span<byte> output, bool predictionResistant)
         {
-			int outputLen = output.Length;
-            int numberOfBits = outputLen * 8;
+            int numberOfBits = output.Length * 8;
 
             if (numberOfBits > MAX_BITS_REQUEST)
                 throw new ArgumentException("Number of bits per request limited to " + MAX_BITS_REQUEST, "output");
@@ -177,43 +209,79 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
 
             if (predictionResistant)
             {
-                Reseed(additionalInput);
-                additionalInput = null;
+                Reseed(ReadOnlySpan<byte>.Empty);
             }
 
-            // 2.
-            if (additionalInput != null)
+            // 3.
+            ImplGenerate(output);
+
+            hmac_DRBG_Update();
+
+            mReseedCounter++;
+
+            return numberOfBits;
+        }
+
+        public int GenerateWithInput(Span<byte> output, ReadOnlySpan<byte> additionalInput, bool predictionResistant)
+        {
+            int numberOfBits = output.Length * 8;
+
+            if (numberOfBits > MAX_BITS_REQUEST)
+                throw new ArgumentException("Number of bits per request limited to " + MAX_BITS_REQUEST, "output");
+
+            if (mReseedCounter > RESEED_MAX)
+                return -1;
+
+            if (predictionResistant)
             {
+                Reseed(additionalInput);
+            }
+            else
+            {
+                // 2.
                 hmac_DRBG_Update(additionalInput);
             }
 
             // 3.
+            ImplGenerate(output);
+
+            if (predictionResistant)
+            {
+                hmac_DRBG_Update();
+            }
+            else
+            {
+                hmac_DRBG_Update(additionalInput);
+            }
+
+            mReseedCounter++;
+
+            return numberOfBits;
+        }
+
+        private void ImplGenerate(Span<byte> output)
+        {
+            int outputLen = output.Length;
             int m = outputLen / mV.Length;
 
             mHMac.Init(new KeyParameter(mK));
 
             for (int i = 0; i < m; i++)
             {
-                mHMac.BlockUpdate(mV, 0, mV.Length);
-                mHMac.DoFinal(mV, 0);
+                mHMac.BlockUpdate(mV);
+                mHMac.DoFinal(mV);
 
-				mV.CopyTo(output[(i * mV.Length)..]);
+                mV.CopyTo(output[(i * mV.Length)..]);
             }
 
-			int remaining = outputLen - m * mV.Length;
+            int remaining = outputLen - m * mV.Length;
             if (remaining > 0)
             {
-                mHMac.BlockUpdate(mV, 0, mV.Length);
-                mHMac.DoFinal(mV, 0);
+                mHMac.BlockUpdate(mV);
+                mHMac.DoFinal(mV);
 
-				mV[..remaining].CopyTo(output[(m * mV.Length)..]);
+                mV[..remaining].CopyTo(output[(m * mV.Length)..]);
             }
-
-            hmac_DRBG_Update(additionalInput);
-
-            mReseedCounter++;
-
-            return numberOfBits;
         }
 #endif
 
@@ -224,14 +292,35 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
 	      */
         public void Reseed(byte[] additionalInput)
 	    {
-	        byte[] entropy = GetEntropy();
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+			Reseed(Spans.FromNullableReadOnly(additionalInput));
+#else
+			byte[] entropy = GetEntropy();
 	        byte[] seedMaterial = Arrays.Concatenate(entropy, additionalInput);
 
 	        hmac_DRBG_Update(seedMaterial);
 
 	        mReseedCounter = 1;
+#endif
 	    }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public void Reseed(ReadOnlySpan<byte> additionalInput)
+		{
+            int entropyLength = GetEntropyLength();
+            int seedMaterialLength = entropyLength + additionalInput.Length;
+            Span<byte> seedMaterial = seedMaterialLength <= 256
+                ? stackalloc byte[seedMaterialLength]
+                : new byte[seedMaterialLength];
+            GetEntropy(seedMaterial);
+            additionalInput.CopyTo(seedMaterial[entropyLength..]);
+
+            hmac_DRBG_Update(seedMaterial);
+
+            mReseedCounter = 1;
+        }
+#endif
+
         private byte[] GetEntropy()
 	    {
 	        byte[] entropy = mEntropySource.GetEntropy();
@@ -239,5 +328,20 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
 	            throw new InvalidOperationException("Insufficient entropy provided by entropy source");
 	        return entropy;
 	    }
-	}
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        private int GetEntropy(Span<byte> output)
+        {
+            int length = mEntropySource.GetEntropy(output);
+            if (length < (mSecurityStrength + 7) / 8)
+                throw new InvalidOperationException("Insufficient entropy provided by entropy source");
+            return length;
+        }
+
+        private int GetEntropyLength()
+        {
+            return (mEntropySource.EntropySize + 7) / 8;
+        }
+#endif
+    }
 }
diff --git a/crypto/src/crypto/prng/drbg/HashSP800Drbg.cs b/crypto/src/crypto/prng/drbg/HashSP800Drbg.cs
index 2f73b1f4e..3f9cffbcd 100644
--- a/crypto/src/crypto/prng/drbg/HashSP800Drbg.cs
+++ b/crypto/src/crypto/prng/drbg/HashSP800Drbg.cs
@@ -1,6 +1,7 @@
 using System;
 using System.Collections.Generic;
 
+using Org.BouncyCastle.Crypto.Utilities;
 using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Crypto.Prng.Drbg
@@ -8,7 +9,7 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
 	/**
 	 * A SP800-90A Hash DRBG.
 	 */
-	public class HashSP800Drbg
+	public sealed class HashSP800Drbg
         :   ISP80090Drbg
 	{
 	    private readonly static byte[] ONE = { 0x01 };
@@ -72,12 +73,13 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
 
 	        byte[] entropy = GetEntropy();
 	        byte[] seedMaterial = Arrays.ConcatenateAll(entropy, nonce, personalizationString);
-	        byte[] seed = DrbgUtilities.HashDF(mDigest, seedMaterial, mSeedLength);
+            mV = new byte[(mSeedLength + 7) / 8];
+            DrbgUtilities.HashDF(mDigest, seedMaterial, mSeedLength, mV);
 
-            mV = seed;
 	        byte[] subV = new byte[mV.Length + 1];
 	        Array.Copy(mV, 0, subV, 1, mV.Length);
-	        mC = DrbgUtilities.HashDF(mDigest, subV, mSeedLength);
+            mC = new byte[(mSeedLength + 7) / 8];
+            DrbgUtilities.HashDF(mDigest, subV, mSeedLength, mC);
 
             mReseedCounter = 1;
 	    }
@@ -89,7 +91,7 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
 	     */
 	    public int BlockSize
 	    {
-			get { return mDigest.GetDigestSize () * 8; }
+			get { return mDigest.GetDigestSize() * 8; }
 	    }
 
 	    /**
@@ -105,7 +107,9 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
 			bool predictionResistant)
 	    {
 #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
-            return Generate(output.AsSpan(outputOff, outputLen), additionalInput, predictionResistant);
+            return additionalInput == null
+                ? Generate(output.AsSpan(outputOff, outputLen), predictionResistant)
+                : GenerateWithInput(output.AsSpan(outputOff, outputLen), additionalInput.AsSpan(), predictionResistant);
 #else
 			// 1. If reseed_counter > reseed_interval, then return an indication that a
 			// reseed is required.
@@ -147,7 +151,7 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
 	        }
 
             // 3.
-	        byte[] rv = hashgen(mV, numberOfBits);
+	        byte[] rv = Hashgen(mV, outputLen);
 
             // 4.
 	        byte[] subH = new byte[mV.Length + 1];
@@ -159,11 +163,9 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
             // 5.
 	        AddTo(mV, H);
 	        AddTo(mV, mC);
+
 	        byte[] c = new byte[4];
-	        c[0] = (byte)(mReseedCounter >> 24);
-	        c[1] = (byte)(mReseedCounter >> 16);
-	        c[2] = (byte)(mReseedCounter >> 8);
-	        c[3] = (byte)mReseedCounter;
+			Pack.UInt32_To_BE((uint)mReseedCounter, c);
 
 	        AddTo(mV, c);
 
@@ -176,7 +178,38 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
         }
 
 #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
-        public int Generate(Span<byte> output, byte[] additionalInput, bool predictionResistant)
+        public int Generate(Span<byte> output, bool predictionResistant)
+        {
+            // 1. If reseed_counter > reseed_interval, then return an indication that a
+            // reseed is required.
+            // 2. If (additional_input != Null), then do
+            // 2.1 w = Hash (0x02 || V || additional_input).
+            // 2.2 V = (V + w) mod 2^seedlen
+            // .
+            // 3. (returned_bits) = Hashgen (requested_number_of_bits, V).
+            // 4. H = Hash (0x03 || V).
+            // 5. V = (V + H + C + reseed_counter) mod 2^seedlen
+            // .
+            // 6. reseed_counter = reseed_counter + 1.
+            // 7. Return SUCCESS, returned_bits, and the new values of V, C, and
+            // reseed_counter for the new_working_state.
+            int numberOfBits = output.Length * 8;
+
+            if (numberOfBits > MAX_BITS_REQUEST)
+                throw new ArgumentException("Number of bits per request limited to " + MAX_BITS_REQUEST, "output");
+
+            if (mReseedCounter > RESEED_MAX)
+                return -1;
+
+            if (predictionResistant)
+            {
+                Reseed(ReadOnlySpan<byte>.Empty);
+            }
+
+			return ImplGenerate(output);
+        }
+
+        public int GenerateWithInput(Span<byte> output, ReadOnlySpan<byte> additionalInput, bool predictionResistant)
 		{
 			// 1. If reseed_counter > reseed_interval, then return an indication that a
 			// reseed is required.
@@ -191,8 +224,7 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
 			// 6. reseed_counter = reseed_counter + 1.
 			// 7. Return SUCCESS, returned_bits, and the new values of V, C, and
 			// reseed_counter for the new_working_state.
-			int outputLen = output.Length;
-            int numberOfBits = outputLen * 8;
+            int numberOfBits = output.Length * 8;
 
             if (numberOfBits > MAX_BITS_REQUEST)
                 throw new ArgumentException("Number of bits per request limited to " + MAX_BITS_REQUEST, "output");
@@ -203,47 +235,53 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
             if (predictionResistant)
             {
                 Reseed(additionalInput);
-                additionalInput = null;
             }
-
-            // 2.
-            if (additionalInput != null)
-            {
-                byte[] newInput = new byte[1 + mV.Length + additionalInput.Length];
-                newInput[0] = 0x02;
-                Array.Copy(mV, 0, newInput, 1, mV.Length);
-                Array.Copy(additionalInput, 0, newInput, 1 + mV.Length, additionalInput.Length);
-                byte[] w = Hash(newInput);
+			else
+			{
+                // 2.
+                mDigest.Update(0x02);
+                mDigest.BlockUpdate(mV);
+                mDigest.BlockUpdate(additionalInput);
+
+                int digestSize = mDigest.GetDigestSize();
+                Span<byte> w = digestSize <= 128
+					? stackalloc byte[digestSize]
+                    : new byte[digestSize];
+                mDigest.DoFinal(w);
 
                 AddTo(mV, w);
             }
 
+			return ImplGenerate(output);
+        }
+
+		private int ImplGenerate(Span<byte> output)
+		{
             // 3.
-            byte[] rv = hashgen(mV, numberOfBits);
+            Hashgen(mV, output);
 
             // 4.
-            byte[] subH = new byte[mV.Length + 1];
-            Array.Copy(mV, 0, subH, 1, mV.Length);
-            subH[0] = 0x03;
+            mDigest.Update(0x03);
+            mDigest.BlockUpdate(mV);
 
-            byte[] H = Hash(subH);
+            int digestSize = mDigest.GetDigestSize();
+            Span<byte> H = digestSize <= 128
+                ? stackalloc byte[digestSize]
+                : new byte[digestSize];
+            mDigest.DoFinal(H);
 
             // 5.
             AddTo(mV, H);
             AddTo(mV, mC);
-            byte[] c = new byte[4];
-            c[0] = (byte)(mReseedCounter >> 24);
-            c[1] = (byte)(mReseedCounter >> 16);
-            c[2] = (byte)(mReseedCounter >> 8);
-            c[3] = (byte)mReseedCounter;
+
+			Span<byte> c = stackalloc byte[4];
+			Pack.UInt32_To_BE((uint)mReseedCounter, c);
 
             AddTo(mV, c);
 
             mReseedCounter++;
 
-			rv.CopyTo(output);
-
-            return numberOfBits;
+            return output.Length * 8;
         }
 #endif
 
@@ -255,18 +293,37 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
 	        return entropy;
 	    }
 
-	    // this will always add the shorter length byte array mathematically to the
-	    // longer length byte array.
-	    // be careful....
-	    private void AddTo(byte[] longer, byte[] shorter)
-	    {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        private int GetEntropy(Span<byte> output)
+        {
+            int length = mEntropySource.GetEntropy(output);
+            if (length < (mSecurityStrength + 7) / 8)
+                throw new InvalidOperationException("Insufficient entropy provided by entropy source");
+            return length;
+        }
+
+        private int GetEntropyLength()
+        {
+            return (mEntropySource.EntropySize + 7) / 8;
+        }
+#endif
+
+        // this will always add the shorter length byte array mathematically to the
+        // longer length byte array.
+        // be careful....
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        private void AddTo(Span<byte> longer, ReadOnlySpan<byte> shorter)
+#else
+		private void AddTo(byte[] longer, byte[] shorter)
+#endif
+        {
             int off = longer.Length - shorter.Length;
 
             uint carry = 0;
             int i = shorter.Length;
             while (--i >= 0)
             {
-                carry += (uint)longer[off + i] + (uint)shorter[i];
+                carry += (uint)longer[off + i] + shorter[i];
                 longer[off + i] = (byte)carry;
                 carry >>= 8;
             }
@@ -287,78 +344,155 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
 	      */
 	    public void Reseed(byte[] additionalInput)
 	    {
-	        // 1. seed_material = 0x01 || V || entropy_input || additional_input.
-	        //
-	        // 2. seed = Hash_df (seed_material, seedlen).
-	        //
-	        // 3. V = seed.
-	        //
-	        // 4. C = Hash_df ((0x00 || V), seedlen).
-	        //
-	        // 5. reseed_counter = 1.
-	        //
-	        // 6. Return V, C, and reseed_counter for the new_working_state.
-	        //
-	        // Comment: Precede with a byte of all zeros.
-	        byte[] entropy = GetEntropy();
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+			Reseed(Spans.FromNullableReadOnly(additionalInput));
+#else
+			// 1. seed_material = 0x01 || V || entropy_input || additional_input.
+			//
+			// 2. seed = Hash_df (seed_material, seedlen).
+			//
+			// 3. V = seed.
+			//
+			// 4. C = Hash_df ((0x00 || V), seedlen).
+			//
+			// 5. reseed_counter = 1.
+			//
+			// 6. Return V, C, and reseed_counter for the new_working_state.
+			//
+			// Comment: Precede with a byte of all zeros.
+			byte[] entropy = GetEntropy();
 	        byte[] seedMaterial = Arrays.ConcatenateAll(ONE, mV, entropy, additionalInput);
-	        byte[] seed = DrbgUtilities.HashDF(mDigest, seedMaterial, mSeedLength);
+	        DrbgUtilities.HashDF(mDigest, seedMaterial, mSeedLength, mV);
 
-            mV = seed;
 	        byte[] subV = new byte[mV.Length + 1];
 	        subV[0] = 0x00;
 	        Array.Copy(mV, 0, subV, 1, mV.Length);
-	        mC = DrbgUtilities.HashDF(mDigest, subV, mSeedLength);
+	        DrbgUtilities.HashDF(mDigest, subV, mSeedLength, mC);
 
             mReseedCounter = 1;
+#endif
 	    }
 
-        private byte[] Hash(byte[] input)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public void Reseed(ReadOnlySpan<byte> additionalInput)
         {
-            byte[] hash = new byte[mDigest.GetDigestSize()];
-            DoHash(input, hash);
-            return hash;
+			// 1. seed_material = 0x01 || V || entropy_input || additional_input.
+			//
+			// 2. seed = Hash_df (seed_material, seedlen).
+			//
+			// 3. V = seed.
+			//
+			// 4. C = Hash_df ((0x00 || V), seedlen).
+			//
+			// 5. reseed_counter = 1.
+			//
+			// 6. Return V, C, and reseed_counter for the new_working_state.
+			//
+			// Comment: Precede with a byte of all zeros.
+			int entropyLength = GetEntropyLength();
+
+			int seedMaterialLength = 1 + mV.Length + entropyLength + additionalInput.Length;
+			Span<byte> seedMaterial = seedMaterialLength <= 256
+				? stackalloc byte[seedMaterialLength]
+				: new byte[seedMaterialLength];
+
+			seedMaterial[0] = 0x01;
+			mV.CopyTo(seedMaterial[1..]);
+			GetEntropy(seedMaterial[(1 + mV.Length)..]);
+			additionalInput.CopyTo(seedMaterial[(1 + mV.Length + entropyLength)..]);
+
+            DrbgUtilities.HashDF(mDigest, seedMaterial, mSeedLength, mV);
+
+			int subVLength = 1 + mV.Length;
+			Span<byte> subV = subVLength <= 128
+				? stackalloc byte[subVLength]
+				: new byte[subVLength];
+            subV[0] = 0x00;
+			mV.CopyTo(subV[1..]);
+
+            DrbgUtilities.HashDF(mDigest, subV, mSeedLength, mC);
+
+            mReseedCounter = 1;
         }
+#endif
 
-        private void DoHash(byte[] input, byte[] output)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        private void DoHash(ReadOnlySpan<byte> input, Span<byte> output)
+        {
+            mDigest.BlockUpdate(input);
+            mDigest.DoFinal(output);
+        }
+#else
+		private void DoHash(byte[] input, byte[] output)
         {
             mDigest.BlockUpdate(input, 0, input.Length);
             mDigest.DoFinal(output, 0);
         }
 
+        private byte[] Hash(byte[] input)
+        {
+            byte[] hash = new byte[mDigest.GetDigestSize()];
+            DoHash(input, hash);
+            return hash;
+        }
+#endif
+
         // 1. m = [requested_number_of_bits / outlen]
-	    // 2. data = V.
-	    // 3. W = the Null string.
-	    // 4. For i = 1 to m
-	    // 4.1 wi = Hash (data).
-	    // 4.2 W = W || wi.
-	    // 4.3 data = (data + 1) mod 2^seedlen
-	    // .
-	    // 5. returned_bits = Leftmost (requested_no_of_bits) bits of W.
-	    private byte[] hashgen(byte[] input, int lengthInBits)
+        // 2. data = V.
+        // 3. W = the Null string.
+        // 4. For i = 1 to m
+        // 4.1 wi = Hash (data).
+        // 4.2 W = W || wi.
+        // 4.3 data = (data + 1) mod 2^seedlen
+        // .
+        // 5. returned_bits = Leftmost (requested_no_of_bits) bits of W.
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        private void Hashgen(ReadOnlySpan<byte> input, Span<byte> output)
 	    {
 	        int digestSize = mDigest.GetDigestSize();
-	        int m = (lengthInBits / 8) / digestSize;
+	        int m = output.Length / digestSize;
 
-            byte[] data = new byte[input.Length];
-	        Array.Copy(input, 0, data, 0, input.Length);
+			int dataSize = input.Length;
+            Span<byte> data = dataSize <= 256
+				? stackalloc byte[input.Length]
+                : new byte[input.Length];
+			input.CopyTo(data);
 
-	        byte[] W = new byte[lengthInBits / 8];
+            Span<byte> dig = digestSize <= 128
+				? stackalloc byte[digestSize]
+                : new byte[digestSize];
 
-            byte[] dig = new byte[mDigest.GetDigestSize()];
 	        for (int i = 0; i <= m; i++)
 	        {
 	            DoHash(data, dig);
 
-	            int bytesToCopy = ((W.Length - i * dig.Length) > dig.Length)
-	                    ? dig.Length
-	                    : (W.Length - i * dig.Length);
-	            Array.Copy(dig, 0, W, i * dig.Length, bytesToCopy);
+				int bytesToCopy = System.Math.Min(digestSize, output.Length - i * digestSize);
+				dig[..bytesToCopy].CopyTo(output[(i * digestSize)..]);
+                AddTo(data, ONE);
+	        }
+	    }
+#else
+        private byte[] Hashgen(byte[] input, int length)
+	    {
+	        int digestSize = mDigest.GetDigestSize();
+	        int m = length / digestSize;
+
+            byte[] data = (byte[])input.Clone();
+	        byte[] W = new byte[length];
+
+            byte[] dig = new byte[digestSize];
+	        for (int i = 0; i <= m; i++)
+	        {
+	            DoHash(data, dig);
+
+				int bytesToCopy = System.Math.Min(digestSize, length - i * digestSize);
+	            Array.Copy(dig, 0, W, i * digestSize, bytesToCopy);
 
                 AddTo(data, ONE);
 	        }
 
 	        return W;
-	    }    
-	}
+	    }
+#endif
+    }
 }
diff --git a/crypto/src/crypto/prng/drbg/ISP80090Drbg.cs b/crypto/src/crypto/prng/drbg/ISP80090Drbg.cs
index e067f20a3..ab7252a8d 100644
--- a/crypto/src/crypto/prng/drbg/ISP80090Drbg.cs
+++ b/crypto/src/crypto/prng/drbg/ISP80090Drbg.cs
@@ -26,7 +26,9 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
         int Generate(byte[] output, int outputOff, int outputLen, byte[] additionalInput, bool predictionResistant);
 
 #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
-        int Generate(Span<byte> output, byte[] additionalInput, bool predictionResistant);
+        int Generate(Span<byte> output, bool predictionResistant);
+
+        int GenerateWithInput(Span<byte> output, ReadOnlySpan<byte> additionalInput, bool predictionResistant);
 #endif
 
         /**
@@ -35,5 +37,9 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
 	     * @param additionalInput additional input to be added to the DRBG in this step.
 	     */
         void Reseed(byte[] additionalInput);
-	}
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        void Reseed(ReadOnlySpan<byte> additionalInput);
+#endif
+    }
 }
diff --git a/crypto/test/src/crypto/prng/test/TestEntropySourceProvider.cs b/crypto/test/src/crypto/prng/test/TestEntropySourceProvider.cs
index 647799630..9ebbb7ccd 100644
--- a/crypto/test/src/crypto/prng/test/TestEntropySourceProvider.cs
+++ b/crypto/test/src/crypto/prng/test/TestEntropySourceProvider.cs
@@ -48,6 +48,18 @@ namespace Org.BouncyCastle.Crypto.Prng.Test
                 return rv;
             }
 
+            // NOTE: .NET Core 2.1 has Span<T>, but is tested against our .NET Standard 2.0 assembly.
+//#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+#if NET6_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            int IEntropySource.GetEntropy(Span<byte> output)
+            {
+                int length = bitsRequired / 8;
+                data.AsSpan(index, length).CopyTo(output);
+                index += length;
+                return length;
+            }
+#endif
+
             public int EntropySize
             {
                 get { return bitsRequired; }