CtrSP800Drbg
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Crypto.Utilities;
using Org.BouncyCastle.Utilities;
using Org.BouncyCastle.Utilities.Encoders;
using System;
namespace Org.BouncyCastle.Crypto.Prng.Drbg
{
public sealed class CtrSP800Drbg : ISP80090Drbg
{
private static readonly long TDEA_RESEED_MAX = 2147483648;
private static readonly long AES_RESEED_MAX = 140737488355328;
private static readonly int TDEA_MAX_BITS_REQUEST = 4096;
private static readonly int AES_MAX_BITS_REQUEST = 262144;
private readonly IEntropySource mEntropySource;
private readonly IBlockCipher mEngine;
private readonly int mKeySizeInBits;
private readonly int mSeedLength;
private readonly int mSecurityStrength;
private byte[] mKey;
private byte[] mV;
private long mReseedCounter;
private bool mIsTdea;
private static readonly byte[] K_BITS = Hex.DecodeStrict("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F");
public int BlockSize => mV.Length * 8;
public CtrSP800Drbg(IBlockCipher engine, int keySizeInBits, int securityStrength, IEntropySource entropySource, byte[] personalizationString, byte[] nonce)
{
if (securityStrength > 256)
throw new ArgumentException("Requested security strength is not supported by the derivation function");
if (GetMaxSecurityStrength(engine, keySizeInBits) < securityStrength)
throw new ArgumentException("Requested security strength is not supported by block cipher and key size");
if (entropySource.EntropySize < securityStrength)
throw new ArgumentException("Not enough entropy for security strength required");
mEntropySource = entropySource;
mEngine = engine;
mKeySizeInBits = keySizeInBits;
mSecurityStrength = securityStrength;
mSeedLength = keySizeInBits + engine.GetBlockSize() * 8;
mIsTdea = IsTdea(engine);
CTR_DRBG_Instantiate_algorithm(personalizationString, nonce);
}
private void CTR_DRBG_Instantiate_algorithm(byte[] personalisationString, byte[] nonce)
{
byte[] entropy = GetEntropy();
byte[] input = Arrays.ConcatenateAll(entropy, nonce, personalisationString);
byte[] seed = BlockCipherDF(input, mSeedLength / 8);
int blockSize = mEngine.GetBlockSize();
mKey = new byte[(mKeySizeInBits + 7) / 8];
mV = new byte[blockSize];
CTR_DRBG_Update(seed, mKey, mV);
mReseedCounter = 1;
}
private void CTR_DRBG_Update(byte[] seed, byte[] key, byte[] v)
{
int num = seed.Length;
byte[] array = new byte[num];
byte[] array2 = new byte[mEngine.GetBlockSize()];
int i = 0;
int blockSize = mEngine.GetBlockSize();
mEngine.Init(true, ExpandToKeyParameter(key));
for (; i * blockSize < num; i++) {
AddOneTo(v);
mEngine.ProcessBlock(v, 0, array2, 0);
int length = System.Math.Min(blockSize, array.Length - i * blockSize);
Array.Copy(array2, 0, array, i * blockSize, length);
}
Bytes.XorTo(num, seed, array);
Array.Copy(array, 0, key, 0, key.Length);
Array.Copy(array, key.Length, v, 0, v.Length);
}
private void CTR_DRBG_Reseed_algorithm(byte[] additionalInput)
{
byte[] entropy = GetEntropy();
byte[] array = Arrays.Concatenate(entropy, additionalInput);
Array.Clear(entropy, 0, entropy.Length);
byte[] array2 = BlockCipherDF(array, mSeedLength / 8);
Array.Clear(array, 0, array.Length);
CTR_DRBG_Update(array2, mKey, mV);
Array.Clear(array2, 0, array2.Length);
mReseedCounter = 1;
}
private void AddOneTo(byte[] longer)
{
uint num = 1;
int num2 = longer.Length;
while (--num2 >= 0) {
num += longer[num2];
longer[num2] = (byte)num;
num >>= 8;
}
}
private byte[] GetEntropy()
{
byte[] entropy = mEntropySource.GetEntropy();
if (entropy == null || entropy.Length < (mSecurityStrength + 7) / 8)
throw new InvalidOperationException("Insufficient entropy provided by entropy source");
return entropy;
}
private byte[] BlockCipherDF(byte[] input, int N)
{
int blockSize = mEngine.GetBlockSize();
int num = input.Length;
byte[] array = new byte[(8 + num + 1 + blockSize - 1) / blockSize * blockSize];
Pack.UInt32_To_BE((uint)num, array, 0);
Pack.UInt32_To_BE((uint)N, array, 4);
Array.Copy(input, 0, array, 8, num);
array[8 + num] = 128;
byte[] array2 = new byte[mKeySizeInBits / 8 + blockSize];
byte[] array3 = new byte[blockSize];
byte[] array4 = new byte[blockSize];
int i = 0;
byte[] array5 = new byte[mKeySizeInBits / 8];
Array.Copy(K_BITS, 0, array5, 0, array5.Length);
KeyParameter parameters = ExpandToKeyParameter(array5);
mEngine.Init(true, parameters);
for (; i * blockSize * 8 < mKeySizeInBits + blockSize * 8; i++) {
Pack.UInt32_To_BE((uint)i, array4, 0);
BCC(array3, array4, array);
int length = System.Math.Min(blockSize, array2.Length - i * blockSize);
Array.Copy(array3, 0, array2, i * blockSize, length);
}
byte[] array6 = new byte[blockSize];
Array.Copy(array2, 0, array5, 0, array5.Length);
Array.Copy(array2, array5.Length, array6, 0, array6.Length);
array2 = new byte[N];
i = 0;
mEngine.Init(true, ExpandToKeyParameter(array5));
for (; i * blockSize < array2.Length; i++) {
mEngine.ProcessBlock(array6, 0, array6, 0);
int length2 = System.Math.Min(blockSize, array2.Length - i * blockSize);
Array.Copy(array6, 0, array2, i * blockSize, length2);
}
return array2;
}
private void BCC(byte[] bccOut, byte[] iV, byte[] data)
{
int blockSize = mEngine.GetBlockSize();
byte[] array = new byte[blockSize];
byte[] array2 = new byte[blockSize];
mEngine.ProcessBlock(iV, 0, array, 0);
int num = data.Length / blockSize;
for (int i = 0; i < num; i++) {
Bytes.Xor(blockSize, array, 0, data, i * blockSize, array2, 0);
mEngine.ProcessBlock(array2, 0, array, 0);
}
Array.Copy(array, 0, bccOut, 0, bccOut.Length);
}
public int Generate(byte[] output, int outputOff, int outputLen, byte[] additionalInput, bool predictionResistant)
{
int num;
if (mIsTdea) {
if (mReseedCounter > TDEA_RESEED_MAX)
return -1;
if (outputLen > TDEA_MAX_BITS_REQUEST / 8) {
num = TDEA_MAX_BITS_REQUEST;
throw new ArgumentException("Number of bits per request limited to " + num.ToString(), "output");
}
} else {
if (mReseedCounter > AES_RESEED_MAX)
return -1;
if (outputLen > AES_MAX_BITS_REQUEST / 8) {
num = AES_MAX_BITS_REQUEST;
throw new ArgumentException("Number of bits per request limited to " + num.ToString(), "output");
}
}
if (predictionResistant) {
CTR_DRBG_Reseed_algorithm(additionalInput);
additionalInput = null;
}
if (additionalInput != null) {
additionalInput = BlockCipherDF(additionalInput, mSeedLength / 8);
CTR_DRBG_Update(additionalInput, mKey, mV);
} else
additionalInput = new byte[mSeedLength];
byte[] array = new byte[mV.Length];
mEngine.Init(true, ExpandToKeyParameter(mKey));
int i = 0;
for (int num2 = outputLen / array.Length; i <= num2; i++) {
int num3 = System.Math.Min(array.Length, outputLen - i * array.Length);
if (num3 != 0) {
AddOneTo(mV);
mEngine.ProcessBlock(mV, 0, array, 0);
Array.Copy(array, 0, output, outputOff + i * array.Length, num3);
}
}
CTR_DRBG_Update(additionalInput, mKey, mV);
mReseedCounter++;
return outputLen * 8;
}
public void Reseed(byte[] additionalInput)
{
CTR_DRBG_Reseed_algorithm(additionalInput);
}
private bool IsTdea(IBlockCipher cipher)
{
if (!cipher.AlgorithmName.Equals("DESede"))
return cipher.AlgorithmName.Equals("TDEA");
return true;
}
private int GetMaxSecurityStrength(IBlockCipher cipher, int keySizeInBits)
{
if (IsTdea(cipher) && keySizeInBits == 168)
return 112;
if (cipher.AlgorithmName.Equals("AES"))
return keySizeInBits;
return -1;
}
private KeyParameter ExpandToKeyParameter(byte[] key)
{
if (!mIsTdea)
return new KeyParameter(key);
byte[] array = new byte[24];
PadKey(key, 0, array, 0);
PadKey(key, 7, array, 8);
PadKey(key, 14, array, 16);
return new KeyParameter(array);
}
private void PadKey(byte[] keyMaster, int keyOff, byte[] tmp, int tmpOff)
{
tmp[tmpOff] = (byte)(keyMaster[keyOff] & 254);
tmp[tmpOff + 1] = (byte)((keyMaster[keyOff] << 7) | ((keyMaster[keyOff + 1] & 252) >> 1));
tmp[tmpOff + 2] = (byte)((keyMaster[keyOff + 1] << 6) | ((keyMaster[keyOff + 2] & 248) >> 2));
tmp[tmpOff + 3] = (byte)((keyMaster[keyOff + 2] << 5) | ((keyMaster[keyOff + 3] & 240) >> 3));
tmp[tmpOff + 4] = (byte)((keyMaster[keyOff + 3] << 4) | ((keyMaster[keyOff + 4] & 224) >> 4));
tmp[tmpOff + 5] = (byte)((keyMaster[keyOff + 4] << 3) | ((keyMaster[keyOff + 5] & 192) >> 5));
tmp[tmpOff + 6] = (byte)((keyMaster[keyOff + 5] << 2) | ((keyMaster[keyOff + 6] & 128) >> 6));
tmp[tmpOff + 7] = (byte)(keyMaster[keyOff + 6] << 1);
DesParameters.SetOddParity(tmp, tmpOff, 8);
}
}
}