summary refs log tree commit diff
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2022-08-29 23:35:25 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2022-08-29 23:35:25 +0700
commit2fd2b7871f6466666bd422bdf578733aa3e9d197 (patch)
tree2190c275da3fab3364fc3065ce77a559c158277d
parentSecureRandom fixups in tests (diff)
downloadBouncyCastle.NET-ed25519-2fd2b7871f6466666bd422bdf578733aa3e9d197.tar.xz
Span-based variant for ISP80090Drbg.Generate
-rw-r--r--crypto/src/crypto/prng/SP800SecureRandom.cs30
-rw-r--r--crypto/src/crypto/prng/X931SecureRandom.cs2
-rw-r--r--crypto/src/crypto/prng/drbg/CtrSP800Drbg.cs75
-rw-r--r--crypto/src/crypto/prng/drbg/HMacSP800Drbg.cs66
-rw-r--r--crypto/src/crypto/prng/drbg/HashSP800Drbg.cs108
-rw-r--r--crypto/src/crypto/prng/drbg/ISP80090Drbg.cs12
6 files changed, 255 insertions, 38 deletions
diff --git a/crypto/src/crypto/prng/SP800SecureRandom.cs b/crypto/src/crypto/prng/SP800SecureRandom.cs
index fb5c3a677..e08a0242c 100644
--- a/crypto/src/crypto/prng/SP800SecureRandom.cs
+++ b/crypto/src/crypto/prng/SP800SecureRandom.cs
@@ -15,8 +15,9 @@ namespace Org.BouncyCastle.Crypto.Prng
 
         private ISP80090Drbg mDrbg;
 
-        internal SP800SecureRandom(SecureRandom randomSource, IEntropySource entropySource, IDrbgProvider drbgProvider, bool predictionResistant)
-            : base((IRandomGenerator)null)
+        internal SP800SecureRandom(SecureRandom randomSource, IEntropySource entropySource, IDrbgProvider drbgProvider,
+            bool predictionResistant)
+            : base(null)
         {
             this.mRandomSource = randomSource;
             this.mEntropySource = entropySource;
@@ -70,12 +71,25 @@ namespace Org.BouncyCastle.Crypto.Prng
             }
         }
 
-        // TODO Add efficient override (needs ISP80090Drbg support for spans)
-//#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
-//        public override void NextBytes(Span<byte> buffer)
-//        {
-//        }
-//#endif
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public override void NextBytes(Span<byte> buffer)
+        {
+            lock (this)
+            {
+                if (mDrbg == null)
+                {
+                    mDrbg = mDrbgProvider.Get(mEntropySource);
+                }
+
+                // check if a reseed is required...
+                if (mDrbg.Generate(buffer, null, mPredictionResistant) < 0)
+                {
+                    mDrbg.Reseed(null);
+                    mDrbg.Generate(buffer, null, mPredictionResistant);
+                }
+            }
+        }
+#endif
 
         public override byte[] GenerateSeed(int numBytes)
         {
diff --git a/crypto/src/crypto/prng/X931SecureRandom.cs b/crypto/src/crypto/prng/X931SecureRandom.cs
index 01678af8f..d38e6d6d8 100644
--- a/crypto/src/crypto/prng/X931SecureRandom.cs
+++ b/crypto/src/crypto/prng/X931SecureRandom.cs
@@ -12,7 +12,7 @@ namespace Org.BouncyCastle.Crypto.Prng
         private readonly X931Rng        mDrbg;
 
         internal X931SecureRandom(SecureRandom randomSource, X931Rng drbg, bool predictionResistant)
-            : base((IRandomGenerator)null)
+            : base(null)
         {
             this.mRandomSource = randomSource;
             this.mDrbg = drbg;
diff --git a/crypto/src/crypto/prng/drbg/CtrSP800Drbg.cs b/crypto/src/crypto/prng/drbg/CtrSP800Drbg.cs
index a7b1326c3..ddb503aa0 100644
--- a/crypto/src/crypto/prng/drbg/CtrSP800Drbg.cs
+++ b/crypto/src/crypto/prng/drbg/CtrSP800Drbg.cs
@@ -334,7 +334,10 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
 	    public int Generate(byte[] output, int outputOff, int outputLen, byte[] additionalInput,
 			bool predictionResistant)
 	    {
-	        if (mIsTdea)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            return Generate(output.AsSpan(outputOff, outputLen), additionalInput, predictionResistant);
+#else
+			if (mIsTdea)
 	        {
 	            if (mReseedCounter > TDEA_RESEED_MAX)
 	                return -1;
@@ -390,14 +393,78 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
             mReseedCounter++;
 
             return outputLen * 8;
-	    }
+#endif
+        }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public int Generate(Span<byte> output, byte[] additionalInput, bool predictionResistant)
+		{
+			int outputLen = output.Length;
+            if (mIsTdea)
+            {
+                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
+            {
+                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");
+            }
+
+            if (predictionResistant)
+            {
+                CTR_DRBG_Reseed_algorithm(additionalInput);
+                additionalInput = null;
+            }
+
+            if (additionalInput != null)
+            {
+                additionalInput = Block_Cipher_df(additionalInput, mSeedLength);
+                CTR_DRBG_Update(additionalInput, mKey, mV);
+            }
+            else
+            {
+                additionalInput = new byte[mSeedLength];
+            }
+
+            byte[] tmp = new byte[mV.Length];
+
+            mEngine.Init(true, new KeyParameter(ExpandKey(mKey)));
+
+            for (int i = 0, limit = outputLen / tmp.Length; i <= limit; i++)
+            {
+                int bytesToCopy = System.Math.Min(tmp.Length, outputLen - i * tmp.Length);
+
+                if (bytesToCopy != 0)
+                {
+                    AddOneTo(mV);
+
+                    mEngine.ProcessBlock(mV, 0, tmp, 0);
+
+					tmp[..bytesToCopy].CopyTo(output[(i * tmp.Length)..]);
+                }
+            }
+
+            CTR_DRBG_Update(additionalInput, mKey, mV);
+
+            mReseedCounter++;
+
+            return outputLen * 8;
+        }
+#endif
+
+        /**
 	      * Reseed the DRBG.
 	      *
 	      * @param additionalInput additional input to be added to the DRBG in this step.
 	      */
-	    public void Reseed(byte[] additionalInput)
+        public void Reseed(byte[] additionalInput)
 	    {
 	        CTR_DRBG_Reseed_algorithm(additionalInput);
 	    }
diff --git a/crypto/src/crypto/prng/drbg/HMacSP800Drbg.cs b/crypto/src/crypto/prng/drbg/HMacSP800Drbg.cs
index 0ec0e8b71..5a684a89d 100644
--- a/crypto/src/crypto/prng/drbg/HMacSP800Drbg.cs
+++ b/crypto/src/crypto/prng/drbg/HMacSP800Drbg.cs
@@ -107,15 +107,16 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
 	    public int Generate(byte[] output, int outputOff, int outputLen, byte[] additionalInput,
 			bool predictionResistant)
 	    {
-	        int numberOfBits = outputLen * 8;
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+			return Generate(output.AsSpan(outputOff, outputLen), additionalInput, predictionResistant);
+#else
+			int numberOfBits = outputLen * 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)
 	        {
@@ -159,14 +160,69 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
 	        Array.Copy(rv, 0, output, outputOff, outputLen);
 
             return numberOfBits;
+#endif
 	    }
 
-	    /**
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public int Generate(Span<byte> output, byte[] additionalInput, bool predictionResistant)
+        {
+			int outputLen = output.Length;
+            int numberOfBits = outputLen * 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);
+                additionalInput = null;
+            }
+
+            // 2.
+            if (additionalInput != null)
+            {
+                hmac_DRBG_Update(additionalInput);
+            }
+
+            // 3.
+            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);
+
+				mV.CopyTo(output[(i * mV.Length)..]);
+            }
+
+			int remaining = outputLen - m * mV.Length;
+            if (remaining > 0)
+            {
+                mHMac.BlockUpdate(mV, 0, mV.Length);
+                mHMac.DoFinal(mV, 0);
+
+				mV[..remaining].CopyTo(output[(m * mV.Length)..]);
+            }
+
+            hmac_DRBG_Update(additionalInput);
+
+            mReseedCounter++;
+
+            return numberOfBits;
+        }
+#endif
+
+        /**
 	      * Reseed the DRBG.
 	      *
 	      * @param additionalInput additional input to be added to the DRBG in this step.
 	      */
-	    public void Reseed(byte[] additionalInput)
+        public void Reseed(byte[] additionalInput)
 	    {
 	        byte[] entropy = GetEntropy();
 	        byte[] seedMaterial = Arrays.Concatenate(entropy, additionalInput);
diff --git a/crypto/src/crypto/prng/drbg/HashSP800Drbg.cs b/crypto/src/crypto/prng/drbg/HashSP800Drbg.cs
index 3f1c628bd..2f73b1f4e 100644
--- a/crypto/src/crypto/prng/drbg/HashSP800Drbg.cs
+++ b/crypto/src/crypto/prng/drbg/HashSP800Drbg.cs
@@ -104,20 +104,23 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
 	    public int Generate(byte[] output, int outputOff, int outputLen, byte[] additionalInput,
 			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 = outputLen * 8;
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            return Generate(output.AsSpan(outputOff, outputLen), additionalInput, predictionResistant);
+#else
+			// 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 = outputLen * 8;
 
 	        if (numberOfBits > MAX_BITS_REQUEST)
 	            throw new ArgumentException("Number of bits per request limited to " + MAX_BITS_REQUEST, "output");
@@ -169,9 +172,82 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
 	        Array.Copy(rv, 0, output, outputOff, outputLen);
 
 	        return numberOfBits;
-	    }
+#endif
+        }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public int Generate(Span<byte> output, byte[] additionalInput, 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 outputLen = output.Length;
+            int numberOfBits = outputLen * 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);
+                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);
+
+                AddTo(mV, w);
+            }
+
+            // 3.
+            byte[] rv = hashgen(mV, numberOfBits);
+
+            // 4.
+            byte[] subH = new byte[mV.Length + 1];
+            Array.Copy(mV, 0, subH, 1, mV.Length);
+            subH[0] = 0x03;
+
+            byte[] H = Hash(subH);
+
+            // 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;
+
+            AddTo(mV, c);
+
+            mReseedCounter++;
+
+			rv.CopyTo(output);
+
+            return numberOfBits;
+        }
+#endif
 
-	    private byte[] GetEntropy()
+        private byte[] GetEntropy()
 	    {
 	        byte[] entropy = mEntropySource.GetEntropy();
 	        if (entropy.Length < (mSecurityStrength + 7) / 8)
diff --git a/crypto/src/crypto/prng/drbg/ISP80090Drbg.cs b/crypto/src/crypto/prng/drbg/ISP80090Drbg.cs
index 78cbcd92f..e067f20a3 100644
--- a/crypto/src/crypto/prng/drbg/ISP80090Drbg.cs
+++ b/crypto/src/crypto/prng/drbg/ISP80090Drbg.cs
@@ -14,7 +14,7 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
 	     */
 		int BlockSize { get; }
 
-	    /**
+        /**
 	     * Populate a passed in array with random data.
 	     *
 	     * @param output output array for generated bits.
@@ -23,13 +23,17 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
 	     *
 	     * @return number of bits generated, -1 if a reseed required.
 	     */
-	    int Generate(byte[] output, int outputOff, int outputLen, byte[] additionalInput, bool predictionResistant);
+        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);
+#endif
+
+        /**
 	     * Reseed the DRBG.
 	     *
 	     * @param additionalInput additional input to be added to the DRBG in this step.
 	     */
-	    void Reseed(byte[] additionalInput);
+        void Reseed(byte[] additionalInput);
 	}
 }