using System; namespace Org.BouncyCastle.Crypto.Prng; internal class X931Rng { private const long BLOCK64_RESEED_MAX = 32768L; private const long BLOCK128_RESEED_MAX = 8388608L; private const int BLOCK64_MAX_BITS_REQUEST = 4096; private const int BLOCK128_MAX_BITS_REQUEST = 262144; private readonly IBlockCipher mEngine; private readonly IEntropySource mEntropySource; private readonly byte[] mDT; private readonly byte[] mI; private readonly byte[] mR; private byte[] mV; private long mReseedCounter = 1L; internal IEntropySource EntropySource => mEntropySource; internal X931Rng(IBlockCipher engine, byte[] dateTimeVector, IEntropySource entropySource) { mEngine = engine; mEntropySource = entropySource; mDT = new byte[engine.GetBlockSize()]; Array.Copy(dateTimeVector, 0, mDT, 0, mDT.Length); mI = new byte[engine.GetBlockSize()]; mR = new byte[engine.GetBlockSize()]; } internal int Generate(byte[] output, bool predictionResistant) { if (mR.Length == 8) { if (mReseedCounter > 32768) { return -1; } if (IsTooLarge(output, 512)) { throw new ArgumentException("Number of bits per request limited to " + 4096, "output"); } } else { if (mReseedCounter > 8388608) { return -1; } if (IsTooLarge(output, 32768)) { throw new ArgumentException("Number of bits per request limited to " + 262144, "output"); } } if (predictionResistant || mV == null) { mV = mEntropySource.GetEntropy(); if (mV.Length != mEngine.GetBlockSize()) { throw new InvalidOperationException("Insufficient entropy returned"); } } int num = output.Length / mR.Length; for (int i = 0; i < num; i++) { mEngine.ProcessBlock(mDT, 0, mI, 0); Process(mR, mI, mV); Process(mV, mR, mI); Array.Copy(mR, 0, output, i * mR.Length, mR.Length); Increment(mDT); } int num2 = output.Length - num * mR.Length; if (num2 > 0) { mEngine.ProcessBlock(mDT, 0, mI, 0); Process(mR, mI, mV); Process(mV, mR, mI); Array.Copy(mR, 0, output, num * mR.Length, num2); Increment(mDT); } mReseedCounter++; return output.Length; } internal void Reseed() { mV = mEntropySource.GetEntropy(); if (mV.Length != mEngine.GetBlockSize()) { throw new InvalidOperationException("Insufficient entropy returned"); } mReseedCounter = 1L; } private void Process(byte[] res, byte[] a, byte[] b) { for (int i = 0; i != res.Length; i++) { res[i] = (byte)(a[i] ^ b[i]); } mEngine.ProcessBlock(res, 0, res, 0); } private void Increment(byte[] val) { for (int num = val.Length - 1; num >= 0; num--) { byte[] array2; byte[] array = (array2 = val); int num2 = num; nint num3 = num2; if ((array[num2] = (byte)(array2[num3] + 1)) != 0) { break; } } } private static bool IsTooLarge(byte[] bytes, int maxBytes) { if (bytes != null) { return bytes.Length > maxBytes; } return false; } }