138 lines
2.9 KiB
C#
138 lines
2.9 KiB
C#
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;
|
|
}
|
|
}
|