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[] array = BlockCipherDF(input, mSeedLength / 8);
int blockSize = mEngine.GetBlockSize();
mKey = new byte[(mKeySizeInBits + 7) / 8];
mV = new byte[blockSize];
CTR_DRBG_Update(array, mKey, mV);
mReseedCounter = 1;
}
private unsafe void CTR_DRBG_Update(ReadOnlySpan<byte> seed, Span<byte> key, Span<byte> v)
{
int length = seed.Length;
Span<byte> span;
int num;
if (length <= 256) {
num = length;
span = new Span<byte>(stackalloc byte[(int)(uint)num], num);
} else
span = new byte[length];
Span<byte> span2 = span;
int blockSize = mEngine.GetBlockSize();
if (blockSize <= 64) {
num = blockSize;
span = new Span<byte>(stackalloc byte[(int)(uint)num], num);
} else
span = new byte[blockSize];
Span<byte> output = span;
mEngine.Init(true, ExpandToKeyParameter(key));
for (int i = 0; i * blockSize < seed.Length; i++) {
AddOneTo(v);
mEngine.ProcessBlock(v, output);
int length2 = System.Math.Min(blockSize, span2.Length - i * blockSize);
span = output.Slice(0, length2);
num = i * blockSize;
span.CopyTo(span2.Slice(num, span2.Length - num));
}
Bytes.XorTo(length, seed, span2);
key.CopyFrom(span2);
num = key.Length;
v.CopyFrom(span2.Slice(num, span2.Length - num));
}
private unsafe void CTR_DRBG_Reseed_algorithm(ReadOnlySpan<byte> additionalInput)
{
int entropyLength = GetEntropyLength();
int num = entropyLength + additionalInput.Length;
Span<byte> span;
int num2;
if (num <= 256) {
num2 = num;
span = new Span<byte>(stackalloc byte[(int)(uint)num2], num2);
} else
span = new byte[num];
Span<byte> span2 = span;
GetEntropy(span2.Slice(0, entropyLength));
num2 = entropyLength;
additionalInput.CopyTo(span2.Slice(num2, span2.Length - num2));
byte[] array = BlockCipherDF(span2, mSeedLength / 8);
span2.Fill(0);
CTR_DRBG_Update(array, mKey, mV);
Array.Clear(array, 0, array.Length);
mReseedCounter = 1;
}
private void AddOneTo(Span<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 int GetEntropy(Span<byte> output)
{
int entropy = mEntropySource.GetEntropy(output);
if (entropy < (mSecurityStrength + 7) / 8)
throw new InvalidOperationException("Insufficient entropy provided by entropy source");
return entropy;
}
private int GetEntropyLength()
{
return (mEntropySource.EntropySize + 7) / 8;
}
private byte[] BlockCipherDF(byte[] input, int N)
{
return BlockCipherDF(input.AsSpan(), N);
}
private unsafe byte[] BlockCipherDF(ReadOnlySpan<byte> input, int N)
{
int blockSize = mEngine.GetBlockSize();
int length = input.Length;
int num = (8 + length + 1 + blockSize - 1) / blockSize * blockSize;
Span<byte> span;
int num2;
if (num <= 256) {
num2 = num;
span = new Span<byte>(stackalloc byte[(int)(uint)num2], num2);
} else
span = new byte[num];
Span<byte> span2 = span;
Pack.UInt32_To_BE((uint)length, span2);
Pack.UInt32_To_BE((uint)N, span2.Slice(4, span2.Length - 4));
input.CopyTo(span2.Slice(8, span2.Length - 8));
span2[8 + length] = 128;
int num3 = mKeySizeInBits / 8;
int num4 = num3 + blockSize;
if (num4 <= 128) {
num2 = num4;
span = new Span<byte>(stackalloc byte[(int)(uint)num2], num2);
} else
span = new byte[num4];
Span<byte> span3 = span;
if (blockSize <= 64) {
num2 = blockSize;
span = new Span<byte>(stackalloc byte[(int)(uint)num2], num2);
} else
span = new byte[blockSize];
Span<byte> bccOut = span;
if (blockSize <= 64) {
num2 = blockSize;
span = new Span<byte>(stackalloc byte[(int)(uint)num2], num2);
} else
span = new byte[blockSize];
Span<byte> span4 = span;
KeyParameter parameters = ExpandToKeyParameter(K_BITS.AsSpan(0, num3));
mEngine.Init(true, parameters);
for (int i = 0; i * blockSize < num4; i++) {
Pack.UInt32_To_BE((uint)i, span4);
BCC(bccOut, span4, span2);
int length2 = System.Math.Min(blockSize, num4 - i * blockSize);
span = bccOut.Slice(0, length2);
num2 = i * blockSize;
span.CopyTo(span3.Slice(num2, span3.Length - num2));
}
KeyParameter parameters2 = ExpandToKeyParameter(span3.Slice(0, num3));
mEngine.Init(true, parameters2);
num2 = num3;
Span<byte> span5 = span3.Slice(num2, span3.Length - num2);
byte[] array = new byte[N];
for (int j = 0; j * blockSize < array.Length; j++) {
mEngine.ProcessBlock(span5, span5);
int length3 = System.Math.Min(blockSize, array.Length - j * blockSize);
span = span5.Slice(0, length3);
span.CopyTo(array.AsSpan(j * blockSize));
}
return array;
}
private unsafe void BCC(Span<byte> bccOut, ReadOnlySpan<byte> iV, ReadOnlySpan<byte> data)
{
int blockSize = mEngine.GetBlockSize();
Span<byte> span;
if (blockSize <= 64) {
int num = blockSize;
span = new Span<byte>(stackalloc byte[(int)(uint)num], num);
} else
span = new byte[blockSize];
Span<byte> span2 = span;
if (blockSize <= 64) {
int num = blockSize;
span = new Span<byte>(stackalloc byte[(int)(uint)num], num);
} else
span = new byte[blockSize];
Span<byte> span3 = span;
mEngine.ProcessBlock(iV, span2);
int num2 = data.Length / blockSize;
for (int i = 0; i < num2; i++) {
int len = blockSize;
ReadOnlySpan<byte> x = span2;
int num = i * blockSize;
Bytes.Xor(len, x, data.Slice(num, data.Length - num), span3);
mEngine.ProcessBlock(span3, span2);
}
bccOut.CopyFrom(span2);
}
public int Generate(byte[] output, int outputOff, int outputLen, byte[] additionalInput, bool predictionResistant)
{
Span<byte> output2 = output.AsSpan(outputOff, outputLen);
if (additionalInput != null)
return GenerateWithInput(output2, additionalInput.AsSpan(), predictionResistant);
return Generate(output2, predictionResistant);
}
public int Generate(Span<byte> output, bool predictionResistant)
{
int length = output.Length;
if (mIsTdea) {
if (mReseedCounter > TDEA_RESEED_MAX)
return -1;
if (length > TDEA_MAX_BITS_REQUEST / 8)
throw new ArgumentException("Number of bits per request limited to " + TDEA_MAX_BITS_REQUEST.ToString(), "output");
} else {
if (mReseedCounter > AES_RESEED_MAX)
return -1;
if (length > AES_MAX_BITS_REQUEST / 8)
throw new ArgumentException("Number of bits per request limited to " + AES_MAX_BITS_REQUEST.ToString(), "output");
}
if (predictionResistant)
CTR_DRBG_Reseed_algorithm(ReadOnlySpan<byte>.Empty);
byte[] array = new byte[mSeedLength / 8];
return ImplGenerate(array, output);
}
public int GenerateWithInput(Span<byte> output, ReadOnlySpan<byte> additionalInput, bool predictionResistant)
{
int length = output.Length;
if (mIsTdea) {
if (mReseedCounter > TDEA_RESEED_MAX)
return -1;
if (length > TDEA_MAX_BITS_REQUEST / 8)
throw new ArgumentException("Number of bits per request limited to " + TDEA_MAX_BITS_REQUEST.ToString(), "output");
} else {
if (mReseedCounter > AES_RESEED_MAX)
return -1;
if (length > AES_MAX_BITS_REQUEST / 8)
throw new ArgumentException("Number of bits per request limited to " + AES_MAX_BITS_REQUEST.ToString(), "output");
}
int num = mSeedLength / 8;
byte[] array;
if (predictionResistant) {
CTR_DRBG_Reseed_algorithm(additionalInput);
array = new byte[num];
} else {
array = BlockCipherDF(additionalInput, num);
CTR_DRBG_Update(array, mKey, mV);
}
return ImplGenerate(array, output);
}
private unsafe int ImplGenerate(ReadOnlySpan<byte> seed, Span<byte> output)
{
mEngine.Init(true, ExpandToKeyParameter(mKey));
Span<byte> span;
if (mV.Length <= 64) {
int num = mV.Length;
span = new Span<byte>(stackalloc byte[(int)(uint)num], num);
} else
span = new byte[mV.Length];
Span<byte> output2 = span;
int length = output.Length;
int i = 0;
for (int num2 = length / output2.Length; i <= num2; i++) {
int num3 = System.Math.Min(output2.Length, length - i * output2.Length);
if (num3 != 0) {
AddOneTo(mV);
mEngine.ProcessBlock(mV, output2);
span = output2.Slice(0, num3);
int num = i * output2.Length;
span.CopyTo(output.Slice(num, output.Length - num));
}
}
CTR_DRBG_Update(seed, mKey, mV);
mReseedCounter++;
return length * 8;
}
public void Reseed(byte[] additionalInput)
{
Reseed(Spans.FromNullableReadOnly(additionalInput));
}
public void Reseed(ReadOnlySpan<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 unsafe KeyParameter ExpandToKeyParameter(ReadOnlySpan<byte> key)
{
if (!mIsTdea)
return new KeyParameter(key);
Span<byte> span = new Span<byte>(stackalloc byte[24], 24);
PadKey(key, span);
PadKey(key.Slice(7, key.Length - 7), span.Slice(8, span.Length - 8));
PadKey(key.Slice(14, key.Length - 14), span.Slice(16, span.Length - 16));
return new KeyParameter(span);
}
private void PadKey(byte[] keyMaster, int keyOff, byte[] tmp, int tmpOff)
{
PadKey(keyMaster.AsSpan(keyOff), tmp.AsSpan(tmpOff));
}
private void PadKey(ReadOnlySpan<byte> keyMaster, Span<byte> tmp)
{
tmp[0] = (byte)(keyMaster[0] & 254);
tmp[1] = (byte)((keyMaster[0] << 7) | ((keyMaster[1] & 252) >> 1));
tmp[2] = (byte)((keyMaster[1] << 6) | ((keyMaster[2] & 248) >> 2));
tmp[3] = (byte)((keyMaster[2] << 5) | ((keyMaster[3] & 240) >> 3));
tmp[4] = (byte)((keyMaster[3] << 4) | ((keyMaster[4] & 224) >> 4));
tmp[5] = (byte)((keyMaster[4] << 3) | ((keyMaster[5] & 192) >> 5));
tmp[6] = (byte)((keyMaster[5] << 2) | ((keyMaster[6] & 128) >> 6));
tmp[7] = (byte)(keyMaster[6] << 1);
DesParameters.SetOddParity(tmp.Slice(0, 8));
}
}
}