init commit
This commit is contained in:
@@ -0,0 +1,303 @@
|
||||
using System;
|
||||
using Org.BouncyCastle.Crypto.Parameters;
|
||||
using Org.BouncyCastle.Utilities;
|
||||
using Org.BouncyCastle.Utilities.Encoders;
|
||||
|
||||
namespace Org.BouncyCastle.Crypto.Prng.Drbg;
|
||||
|
||||
public class CtrSP800Drbg : ISP80090Drbg
|
||||
{
|
||||
private static readonly long TDEA_RESEED_MAX = 2147483648L;
|
||||
|
||||
private static readonly long AES_RESEED_MAX = 140737488355328L;
|
||||
|
||||
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 = 0L;
|
||||
|
||||
private bool mIsTdea = false;
|
||||
|
||||
private static readonly byte[] K_BITS = Hex.Decode("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);
|
||||
byte[] entropy = GetEntropy();
|
||||
CTR_DRBG_Instantiate_algorithm(entropy, nonce, personalizationString);
|
||||
}
|
||||
|
||||
private void CTR_DRBG_Instantiate_algorithm(byte[] entropy, byte[] nonce, byte[] personalisationString)
|
||||
{
|
||||
byte[] inputString = Arrays.ConcatenateAll(entropy, nonce, personalisationString);
|
||||
byte[] seed = Block_Cipher_df(inputString, mSeedLength);
|
||||
int blockSize = mEngine.GetBlockSize();
|
||||
mKey = new byte[(mKeySizeInBits + 7) / 8];
|
||||
mV = new byte[blockSize];
|
||||
CTR_DRBG_Update(seed, mKey, mV);
|
||||
mReseedCounter = 1L;
|
||||
}
|
||||
|
||||
private void CTR_DRBG_Update(byte[] seed, byte[] key, byte[] v)
|
||||
{
|
||||
byte[] array = new byte[seed.Length];
|
||||
byte[] array2 = new byte[mEngine.GetBlockSize()];
|
||||
int i = 0;
|
||||
int blockSize = mEngine.GetBlockSize();
|
||||
mEngine.Init(forEncryption: true, new KeyParameter(ExpandKey(key)));
|
||||
for (; i * blockSize < seed.Length; i++)
|
||||
{
|
||||
AddOneTo(v);
|
||||
mEngine.ProcessBlock(v, 0, array2, 0);
|
||||
int length = ((array.Length - i * blockSize > blockSize) ? blockSize : (array.Length - i * blockSize));
|
||||
Array.Copy(array2, 0, array, i * blockSize, length);
|
||||
}
|
||||
XOR(array, seed, array, 0);
|
||||
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[] inputString = Arrays.Concatenate(GetEntropy(), additionalInput);
|
||||
inputString = Block_Cipher_df(inputString, mSeedLength);
|
||||
CTR_DRBG_Update(inputString, mKey, mV);
|
||||
mReseedCounter = 1L;
|
||||
}
|
||||
|
||||
private void XOR(byte[] output, byte[] a, byte[] b, int bOff)
|
||||
{
|
||||
for (int i = 0; i < output.Length; i++)
|
||||
{
|
||||
output[i] = (byte)(a[i] ^ b[bOff + i]);
|
||||
}
|
||||
}
|
||||
|
||||
private void AddOneTo(byte[] longer)
|
||||
{
|
||||
uint num = 1u;
|
||||
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.Length < (mSecurityStrength + 7) / 8)
|
||||
{
|
||||
throw new InvalidOperationException("Insufficient entropy provided by entropy source");
|
||||
}
|
||||
return entropy;
|
||||
}
|
||||
|
||||
private byte[] Block_Cipher_df(byte[] inputString, int bitLength)
|
||||
{
|
||||
int blockSize = mEngine.GetBlockSize();
|
||||
int num = inputString.Length;
|
||||
int value = bitLength / 8;
|
||||
int num2 = 8 + num + 1;
|
||||
int num3 = (num2 + blockSize - 1) / blockSize * blockSize;
|
||||
byte[] array = new byte[num3];
|
||||
copyIntToByteArray(array, num, 0);
|
||||
copyIntToByteArray(array, value, 4);
|
||||
Array.Copy(inputString, 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);
|
||||
for (; i * blockSize * 8 < mKeySizeInBits + blockSize * 8; i++)
|
||||
{
|
||||
copyIntToByteArray(array4, i, 0);
|
||||
BCC(array3, array5, array4, array);
|
||||
int length = ((array2.Length - i * blockSize > blockSize) ? 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[bitLength / 2];
|
||||
i = 0;
|
||||
mEngine.Init(forEncryption: true, new KeyParameter(ExpandKey(array5)));
|
||||
for (; i * blockSize < array2.Length; i++)
|
||||
{
|
||||
mEngine.ProcessBlock(array6, 0, array6, 0);
|
||||
int length2 = ((array2.Length - i * blockSize > blockSize) ? blockSize : (array2.Length - i * blockSize));
|
||||
Array.Copy(array6, 0, array2, i * blockSize, length2);
|
||||
}
|
||||
return array2;
|
||||
}
|
||||
|
||||
private void BCC(byte[] bccOut, byte[] k, byte[] iV, byte[] data)
|
||||
{
|
||||
int blockSize = mEngine.GetBlockSize();
|
||||
byte[] array = new byte[blockSize];
|
||||
int num = data.Length / blockSize;
|
||||
byte[] array2 = new byte[blockSize];
|
||||
mEngine.Init(forEncryption: true, new KeyParameter(ExpandKey(k)));
|
||||
mEngine.ProcessBlock(iV, 0, array, 0);
|
||||
for (int i = 0; i < num; i++)
|
||||
{
|
||||
XOR(array2, array, data, i * blockSize);
|
||||
mEngine.ProcessBlock(array2, 0, array, 0);
|
||||
}
|
||||
Array.Copy(array, 0, bccOut, 0, bccOut.Length);
|
||||
}
|
||||
|
||||
private void copyIntToByteArray(byte[] buf, int value, int offSet)
|
||||
{
|
||||
buf[offSet] = (byte)(value >> 24);
|
||||
buf[offSet + 1] = (byte)(value >> 16);
|
||||
buf[offSet + 2] = (byte)(value >> 8);
|
||||
buf[offSet + 3] = (byte)value;
|
||||
}
|
||||
|
||||
public int Generate(byte[] output, byte[] additionalInput, bool predictionResistant)
|
||||
{
|
||||
if (mIsTdea)
|
||||
{
|
||||
if (mReseedCounter > TDEA_RESEED_MAX)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (DrbgUtilities.IsTooLarge(output, TDEA_MAX_BITS_REQUEST / 8))
|
||||
{
|
||||
throw new ArgumentException("Number of bits per request limited to " + TDEA_MAX_BITS_REQUEST, "output");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mReseedCounter > AES_RESEED_MAX)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (DrbgUtilities.IsTooLarge(output, AES_MAX_BITS_REQUEST / 8))
|
||||
{
|
||||
throw new ArgumentException("Number of bits per request limited to " + AES_MAX_BITS_REQUEST, "output");
|
||||
}
|
||||
}
|
||||
if (predictionResistant)
|
||||
{
|
||||
CTR_DRBG_Reseed_algorithm(additionalInput);
|
||||
additionalInput = null;
|
||||
}
|
||||
if (additionalInput != null)
|
||||
{
|
||||
additionalInput = Block_Cipher_df(additionalInput, mSeedLength);
|
||||
CTR_DRBG_Update(additionalInput, mKey, mV);
|
||||
}
|
||||
else
|
||||
{
|
||||
additionalInput = new byte[mSeedLength];
|
||||
}
|
||||
byte[] array = new byte[mV.Length];
|
||||
mEngine.Init(forEncryption: true, new KeyParameter(ExpandKey(mKey)));
|
||||
for (int i = 0; i <= output.Length / array.Length; i++)
|
||||
{
|
||||
int num = ((output.Length - i * array.Length > array.Length) ? array.Length : (output.Length - i * mV.Length));
|
||||
if (num != 0)
|
||||
{
|
||||
AddOneTo(mV);
|
||||
mEngine.ProcessBlock(mV, 0, array, 0);
|
||||
Array.Copy(array, 0, output, i * array.Length, num);
|
||||
}
|
||||
}
|
||||
CTR_DRBG_Update(additionalInput, mKey, mV);
|
||||
mReseedCounter++;
|
||||
return output.Length * 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 byte[] ExpandKey(byte[] key)
|
||||
{
|
||||
if (mIsTdea)
|
||||
{
|
||||
byte[] array = new byte[24];
|
||||
PadKey(key, 0, array, 0);
|
||||
PadKey(key, 7, array, 8);
|
||||
PadKey(key, 14, array, 16);
|
||||
return array;
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
private void PadKey(byte[] keyMaster, int keyOff, byte[] tmp, int tmpOff)
|
||||
{
|
||||
tmp[tmpOff] = (byte)(keyMaster[keyOff] & 0xFE);
|
||||
tmp[tmpOff + 1] = (byte)((keyMaster[keyOff] << 7) | ((keyMaster[keyOff + 1] & 0xFC) >> 1));
|
||||
tmp[tmpOff + 2] = (byte)((keyMaster[keyOff + 1] << 6) | ((keyMaster[keyOff + 2] & 0xF8) >> 2));
|
||||
tmp[tmpOff + 3] = (byte)((keyMaster[keyOff + 2] << 5) | ((keyMaster[keyOff + 3] & 0xF0) >> 3));
|
||||
tmp[tmpOff + 4] = (byte)((keyMaster[keyOff + 3] << 4) | ((keyMaster[keyOff + 4] & 0xE0) >> 4));
|
||||
tmp[tmpOff + 5] = (byte)((keyMaster[keyOff + 4] << 3) | ((keyMaster[keyOff + 5] & 0xC0) >> 5));
|
||||
tmp[tmpOff + 6] = (byte)((keyMaster[keyOff + 5] << 2) | ((keyMaster[keyOff + 6] & 0x80) >> 6));
|
||||
tmp[tmpOff + 7] = (byte)(keyMaster[keyOff + 6] << 1);
|
||||
DesParameters.SetOddParity(tmp, tmpOff, 8);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using Org.BouncyCastle.Utilities;
|
||||
|
||||
namespace Org.BouncyCastle.Crypto.Prng.Drbg;
|
||||
|
||||
internal class DrbgUtilities
|
||||
{
|
||||
private static readonly IDictionary maxSecurityStrengths;
|
||||
|
||||
static DrbgUtilities()
|
||||
{
|
||||
maxSecurityStrengths = Platform.CreateHashtable();
|
||||
maxSecurityStrengths.Add("SHA-1", 128);
|
||||
maxSecurityStrengths.Add("SHA-224", 192);
|
||||
maxSecurityStrengths.Add("SHA-256", 256);
|
||||
maxSecurityStrengths.Add("SHA-384", 256);
|
||||
maxSecurityStrengths.Add("SHA-512", 256);
|
||||
maxSecurityStrengths.Add("SHA-512/224", 192);
|
||||
maxSecurityStrengths.Add("SHA-512/256", 256);
|
||||
}
|
||||
|
||||
internal static int GetMaxSecurityStrength(IDigest d)
|
||||
{
|
||||
return (int)maxSecurityStrengths[d.AlgorithmName];
|
||||
}
|
||||
|
||||
internal static int GetMaxSecurityStrength(IMac m)
|
||||
{
|
||||
string algorithmName = m.AlgorithmName;
|
||||
return (int)maxSecurityStrengths[algorithmName.Substring(0, algorithmName.IndexOf("/"))];
|
||||
}
|
||||
|
||||
internal static byte[] HashDF(IDigest digest, byte[] seedMaterial, int seedLength)
|
||||
{
|
||||
byte[] array = new byte[(seedLength + 7) / 8];
|
||||
int num = array.Length / digest.GetDigestSize();
|
||||
int num2 = 1;
|
||||
byte[] array2 = new byte[digest.GetDigestSize()];
|
||||
for (int i = 0; i <= num; i++)
|
||||
{
|
||||
digest.Update((byte)num2);
|
||||
digest.Update((byte)(seedLength >> 24));
|
||||
digest.Update((byte)(seedLength >> 16));
|
||||
digest.Update((byte)(seedLength >> 8));
|
||||
digest.Update((byte)seedLength);
|
||||
digest.BlockUpdate(seedMaterial, 0, seedMaterial.Length);
|
||||
digest.DoFinal(array2, 0);
|
||||
int length = ((array.Length - i * array2.Length > array2.Length) ? array2.Length : (array.Length - i * array2.Length));
|
||||
Array.Copy(array2, 0, array, i * array2.Length, length);
|
||||
num2++;
|
||||
}
|
||||
if (seedLength % 8 != 0)
|
||||
{
|
||||
int num3 = 8 - seedLength % 8;
|
||||
uint num4 = 0u;
|
||||
for (int j = 0; j != array.Length; j++)
|
||||
{
|
||||
uint num5 = array[j];
|
||||
array[j] = (byte)((num5 >> num3) | (num4 << 8 - num3));
|
||||
num4 = num5;
|
||||
}
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
internal static bool IsTooLarge(byte[] bytes, int maxBytes)
|
||||
{
|
||||
if (bytes != null)
|
||||
{
|
||||
return bytes.Length > maxBytes;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
using System;
|
||||
using Org.BouncyCastle.Crypto.Parameters;
|
||||
using Org.BouncyCastle.Utilities;
|
||||
|
||||
namespace Org.BouncyCastle.Crypto.Prng.Drbg;
|
||||
|
||||
public class HMacSP800Drbg : ISP80090Drbg
|
||||
{
|
||||
private static readonly long RESEED_MAX = 140737488355328L;
|
||||
|
||||
private static readonly int MAX_BITS_REQUEST = 262144;
|
||||
|
||||
private readonly byte[] mK;
|
||||
|
||||
private readonly byte[] mV;
|
||||
|
||||
private readonly IEntropySource mEntropySource;
|
||||
|
||||
private readonly IMac mHMac;
|
||||
|
||||
private readonly int mSecurityStrength;
|
||||
|
||||
private long mReseedCounter;
|
||||
|
||||
public int BlockSize => mV.Length * 8;
|
||||
|
||||
public HMacSP800Drbg(IMac hMac, int securityStrength, IEntropySource entropySource, byte[] personalizationString, byte[] nonce)
|
||||
{
|
||||
if (securityStrength > DrbgUtilities.GetMaxSecurityStrength(hMac))
|
||||
{
|
||||
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");
|
||||
}
|
||||
mHMac = hMac;
|
||||
mSecurityStrength = securityStrength;
|
||||
mEntropySource = entropySource;
|
||||
byte[] entropy = GetEntropy();
|
||||
byte[] seedMaterial = Arrays.ConcatenateAll(entropy, nonce, personalizationString);
|
||||
mK = new byte[hMac.GetMacSize()];
|
||||
mV = new byte[mK.Length];
|
||||
Arrays.Fill(mV, 1);
|
||||
hmac_DRBG_Update(seedMaterial);
|
||||
mReseedCounter = 1L;
|
||||
}
|
||||
|
||||
private void hmac_DRBG_Update(byte[] seedMaterial)
|
||||
{
|
||||
hmac_DRBG_Update_Func(seedMaterial, 0);
|
||||
if (seedMaterial != null)
|
||||
{
|
||||
hmac_DRBG_Update_Func(seedMaterial, 1);
|
||||
}
|
||||
}
|
||||
|
||||
private void hmac_DRBG_Update_Func(byte[] seedMaterial, byte vValue)
|
||||
{
|
||||
mHMac.Init(new KeyParameter(mK));
|
||||
mHMac.BlockUpdate(mV, 0, mV.Length);
|
||||
mHMac.Update(vValue);
|
||||
if (seedMaterial != null)
|
||||
{
|
||||
mHMac.BlockUpdate(seedMaterial, 0, seedMaterial.Length);
|
||||
}
|
||||
mHMac.DoFinal(mK, 0);
|
||||
mHMac.Init(new KeyParameter(mK));
|
||||
mHMac.BlockUpdate(mV, 0, mV.Length);
|
||||
mHMac.DoFinal(mV, 0);
|
||||
}
|
||||
|
||||
public int Generate(byte[] output, byte[] additionalInput, bool predictionResistant)
|
||||
{
|
||||
int num = output.Length * 8;
|
||||
if (num > MAX_BITS_REQUEST)
|
||||
{
|
||||
throw new ArgumentException("Number of bits per request limited to " + MAX_BITS_REQUEST, "output");
|
||||
}
|
||||
if (mReseedCounter > RESEED_MAX)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (predictionResistant)
|
||||
{
|
||||
Reseed(additionalInput);
|
||||
additionalInput = null;
|
||||
}
|
||||
if (additionalInput != null)
|
||||
{
|
||||
hmac_DRBG_Update(additionalInput);
|
||||
}
|
||||
byte[] array = new byte[output.Length];
|
||||
int num2 = output.Length / mV.Length;
|
||||
mHMac.Init(new KeyParameter(mK));
|
||||
for (int i = 0; i < num2; i++)
|
||||
{
|
||||
mHMac.BlockUpdate(mV, 0, mV.Length);
|
||||
mHMac.DoFinal(mV, 0);
|
||||
Array.Copy(mV, 0, array, i * mV.Length, mV.Length);
|
||||
}
|
||||
if (num2 * mV.Length < array.Length)
|
||||
{
|
||||
mHMac.BlockUpdate(mV, 0, mV.Length);
|
||||
mHMac.DoFinal(mV, 0);
|
||||
Array.Copy(mV, 0, array, num2 * mV.Length, array.Length - num2 * mV.Length);
|
||||
}
|
||||
hmac_DRBG_Update(additionalInput);
|
||||
mReseedCounter++;
|
||||
Array.Copy(array, 0, output, 0, output.Length);
|
||||
return num;
|
||||
}
|
||||
|
||||
public void Reseed(byte[] additionalInput)
|
||||
{
|
||||
byte[] entropy = GetEntropy();
|
||||
byte[] seedMaterial = Arrays.Concatenate(entropy, additionalInput);
|
||||
hmac_DRBG_Update(seedMaterial);
|
||||
mReseedCounter = 1L;
|
||||
}
|
||||
|
||||
private byte[] GetEntropy()
|
||||
{
|
||||
byte[] entropy = mEntropySource.GetEntropy();
|
||||
if (entropy.Length < (mSecurityStrength + 7) / 8)
|
||||
{
|
||||
throw new InvalidOperationException("Insufficient entropy provided by entropy source");
|
||||
}
|
||||
return entropy;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using Org.BouncyCastle.Utilities;
|
||||
|
||||
namespace Org.BouncyCastle.Crypto.Prng.Drbg;
|
||||
|
||||
public 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 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 = 140737488355328L;
|
||||
MAX_BITS_REQUEST = 262144;
|
||||
seedlens = Platform.CreateHashtable();
|
||||
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 = (int)seedlens[digest.AlgorithmName];
|
||||
byte[] entropy = GetEntropy();
|
||||
byte[] seedMaterial = Arrays.ConcatenateAll(entropy, nonce, personalizationString);
|
||||
byte[] array = DrbgUtilities.HashDF(mDigest, seedMaterial, mSeedLength);
|
||||
mV = array;
|
||||
byte[] array2 = new byte[mV.Length + 1];
|
||||
Array.Copy(mV, 0, array2, 1, mV.Length);
|
||||
mC = DrbgUtilities.HashDF(mDigest, array2, mSeedLength);
|
||||
mReseedCounter = 1L;
|
||||
}
|
||||
|
||||
public int Generate(byte[] output, byte[] additionalInput, bool predictionResistant)
|
||||
{
|
||||
int num = output.Length * 8;
|
||||
if (num > MAX_BITS_REQUEST)
|
||||
{
|
||||
throw new ArgumentException("Number of bits per request limited to " + MAX_BITS_REQUEST, "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, num);
|
||||
byte[] array2 = new byte[mV.Length + 1];
|
||||
Array.Copy(mV, 0, array2, 1, mV.Length);
|
||||
array2[0] = 3;
|
||||
byte[] shorter2 = Hash(array2);
|
||||
AddTo(mV, shorter2);
|
||||
AddTo(mV, mC);
|
||||
AddTo(shorter: new byte[4]
|
||||
{
|
||||
(byte)(mReseedCounter >> 24),
|
||||
(byte)(mReseedCounter >> 16),
|
||||
(byte)(mReseedCounter >> 8),
|
||||
(byte)mReseedCounter
|
||||
}, longer: mV);
|
||||
mReseedCounter++;
|
||||
Array.Copy(sourceArray, 0, output, 0, output.Length);
|
||||
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 = 0u;
|
||||
int num3 = shorter.Length;
|
||||
while (--num3 >= 0)
|
||||
{
|
||||
num2 += (uint)(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);
|
||||
byte[] array = DrbgUtilities.HashDF(mDigest, seedMaterial, mSeedLength);
|
||||
mV = array;
|
||||
byte[] array2 = new byte[mV.Length + 1];
|
||||
array2[0] = 0;
|
||||
Array.Copy(mV, 0, array2, 1, mV.Length);
|
||||
mC = DrbgUtilities.HashDF(mDigest, array2, mSeedLength);
|
||||
mReseedCounter = 1L;
|
||||
}
|
||||
|
||||
private byte[] Hash(byte[] input)
|
||||
{
|
||||
byte[] array = new byte[mDigest.GetDigestSize()];
|
||||
DoHash(input, array);
|
||||
return array;
|
||||
}
|
||||
|
||||
private void DoHash(byte[] input, byte[] output)
|
||||
{
|
||||
mDigest.BlockUpdate(input, 0, input.Length);
|
||||
mDigest.DoFinal(output, 0);
|
||||
}
|
||||
|
||||
private byte[] hashgen(byte[] input, int lengthInBits)
|
||||
{
|
||||
int digestSize = mDigest.GetDigestSize();
|
||||
int num = lengthInBits / 8 / digestSize;
|
||||
byte[] array = new byte[input.Length];
|
||||
Array.Copy(input, 0, array, 0, input.Length);
|
||||
byte[] array2 = new byte[lengthInBits / 8];
|
||||
byte[] array3 = new byte[mDigest.GetDigestSize()];
|
||||
for (int i = 0; i <= num; i++)
|
||||
{
|
||||
DoHash(array, array3);
|
||||
int length = ((array2.Length - i * array3.Length > array3.Length) ? array3.Length : (array2.Length - i * array3.Length));
|
||||
Array.Copy(array3, 0, array2, i * array3.Length, length);
|
||||
AddTo(array, ONE);
|
||||
}
|
||||
return array2;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace Org.BouncyCastle.Crypto.Prng.Drbg;
|
||||
|
||||
public interface ISP80090Drbg
|
||||
{
|
||||
int BlockSize { get; }
|
||||
|
||||
int Generate(byte[] output, byte[] additionalInput, bool predictionResistant);
|
||||
|
||||
void Reseed(byte[] additionalInput);
|
||||
}
|
||||
Reference in New Issue
Block a user