HashSP800Drbg
using Org.BouncyCastle.Crypto.Utilities;
using Org.BouncyCastle.Utilities;
using System;
using System.Collections.Generic;
namespace Org.BouncyCastle.Crypto.Prng.Drbg
{
    public sealed class HashSP800Drbg : ISP80090Drbg
    {
        private static readonly byte[] ONE;
        private static readonly long RESEED_MAX;
        private static readonly int MAX_BITS_REQUEST;
        private static readonly IDictionary<string, int> SeedLens;
        private readonly IDigest mDigest;
        private readonly IEntropySource mEntropySource;
        private readonly int mSecurityStrength;
        private readonly int mSeedLength;
        private byte[] mV;
        private byte[] mC;
        private long mReseedCounter;
        public int BlockSize => mDigest.GetDigestSize() * 8;
        static HashSP800Drbg()
        {
            ONE = new byte[1] {
                1
            };
            RESEED_MAX = 140737488355328;
            MAX_BITS_REQUEST = 262144;
            SeedLens = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
            SeedLens.Add("SHA-1", 440);
            SeedLens.Add("SHA-224", 440);
            SeedLens.Add("SHA-256", 440);
            SeedLens.Add("SHA-512/256", 440);
            SeedLens.Add("SHA-512/224", 440);
            SeedLens.Add("SHA-384", 888);
            SeedLens.Add("SHA-512", 888);
        }
        public HashSP800Drbg(IDigest digest, int securityStrength, IEntropySource entropySource, byte[] personalizationString, byte[] nonce)
        {
            if (securityStrength > DrbgUtilities.GetMaxSecurityStrength(digest))
                throw new ArgumentException("Requested security strength is not supported by the derivation function");
            if (entropySource.EntropySize < securityStrength)
                throw new ArgumentException("Not enough entropy for security strength required");
            mDigest = digest;
            mEntropySource = entropySource;
            mSecurityStrength = securityStrength;
            mSeedLength = SeedLens[digest.AlgorithmName];
            byte[] entropy = GetEntropy();
            byte[] seedMaterial = Arrays.ConcatenateAll(entropy, nonce, personalizationString);
            mV = new byte[(mSeedLength + 7) / 8];
            DrbgUtilities.HashDF(mDigest, seedMaterial, mSeedLength, mV);
            byte[] array = new byte[mV.Length + 1];
            Array.Copy(mV, 0, array, 1, mV.Length);
            mC = new byte[(mSeedLength + 7) / 8];
            DrbgUtilities.HashDF(mDigest, array, mSeedLength, mC);
            mReseedCounter = 1;
        }
        public int Generate(byte[] output, int outputOff, int outputLen, byte[] additionalInput, bool predictionResistant)
        {
            int num = outputLen * 8;
            if (num > MAX_BITS_REQUEST)
                throw new ArgumentException("Number of bits per request limited to " + MAX_BITS_REQUEST.ToString(), "output");
            if (mReseedCounter > RESEED_MAX)
                return -1;
            if (predictionResistant) {
                Reseed(additionalInput);
                additionalInput = null;
            }
            if (additionalInput != null) {
                byte[] array = new byte[1 + mV.Length + additionalInput.Length];
                array[0] = 2;
                Array.Copy(mV, 0, array, 1, mV.Length);
                Array.Copy(additionalInput, 0, array, 1 + mV.Length, additionalInput.Length);
                byte[] shorter = Hash(array);
                AddTo(mV, shorter);
            }
            byte[] sourceArray = Hashgen(mV, outputLen);
            byte[] array2 = new byte[mV.Length + 1];
            Array.Copy(mV, 0, array2, 1, mV.Length);
            array2[0] = 3;
            AddTo(shorter: Hash(array2), longer: mV);
            AddTo(mV, mC);
            byte[] array3 = new byte[4];
            Pack.UInt32_To_BE((uint)mReseedCounter, array3);
            AddTo(mV, array3);
            mReseedCounter++;
            Array.Copy(sourceArray, 0, output, outputOff, outputLen);
            return num;
        }
        private byte[] GetEntropy()
        {
            byte[] entropy = mEntropySource.GetEntropy();
            if (entropy.Length < (mSecurityStrength + 7) / 8)
                throw new InvalidOperationException("Insufficient entropy provided by entropy source");
            return entropy;
        }
        private void AddTo(byte[] longer, byte[] shorter)
        {
            int num = longer.Length - shorter.Length;
            uint num2 = 0;
            int num3 = shorter.Length;
            while (--num3 >= 0) {
                num2 = (uint)((int)num2 + (longer[num + num3] + shorter[num3]));
                longer[num + num3] = (byte)num2;
                num2 >>= 8;
            }
            num3 = num;
            while (--num3 >= 0) {
                num2 += longer[num3];
                longer[num3] = (byte)num2;
                num2 >>= 8;
            }
        }
        public void Reseed(byte[] additionalInput)
        {
            byte[] entropy = GetEntropy();
            byte[] seedMaterial = Arrays.ConcatenateAll(ONE, mV, entropy, additionalInput);
            DrbgUtilities.HashDF(mDigest, seedMaterial, mSeedLength, mV);
            byte[] array = new byte[mV.Length + 1];
            array[0] = 0;
            Array.Copy(mV, 0, array, 1, mV.Length);
            DrbgUtilities.HashDF(mDigest, array, mSeedLength, mC);
            mReseedCounter = 1;
        }
        private void DoHash(byte[] input, byte[] output)
        {
            mDigest.BlockUpdate(input, 0, input.Length);
            mDigest.DoFinal(output, 0);
        }
        private byte[] Hash(byte[] input)
        {
            byte[] array = new byte[mDigest.GetDigestSize()];
            DoHash(input, array);
            return array;
        }
        private byte[] Hashgen(byte[] input, int length)
        {
            int digestSize = mDigest.GetDigestSize();
            int num = length / digestSize;
            byte[] array = (byte[])input.Clone();
            byte[] array2 = new byte[length];
            byte[] array3 = new byte[digestSize];
            for (int i = 0; i <= num; i++) {
                DoHash(array, array3);
                int length2 = System.Math.Min(digestSize, length - i * digestSize);
                Array.Copy(array3, 0, array2, i * digestSize, length2);
                AddTo(array, ONE);
            }
            return array2;
        }
    }
}