init commit
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
using Org.BouncyCastle.Security;
|
||||
|
||||
namespace Org.BouncyCastle.Crypto.Prng;
|
||||
|
||||
public class BasicEntropySourceProvider : IEntropySourceProvider
|
||||
{
|
||||
private class BasicEntropySource : IEntropySource
|
||||
{
|
||||
private readonly SecureRandom mSecureRandom;
|
||||
|
||||
private readonly bool mPredictionResistant;
|
||||
|
||||
private readonly int mEntropySize;
|
||||
|
||||
bool IEntropySource.IsPredictionResistant => mPredictionResistant;
|
||||
|
||||
int IEntropySource.EntropySize => mEntropySize;
|
||||
|
||||
internal BasicEntropySource(SecureRandom secureRandom, bool predictionResistant, int entropySize)
|
||||
{
|
||||
mSecureRandom = secureRandom;
|
||||
mPredictionResistant = predictionResistant;
|
||||
mEntropySize = entropySize;
|
||||
}
|
||||
|
||||
byte[] IEntropySource.GetEntropy()
|
||||
{
|
||||
return SecureRandom.GetNextBytes(mSecureRandom, (mEntropySize + 7) / 8);
|
||||
}
|
||||
}
|
||||
|
||||
private readonly SecureRandom mSecureRandom;
|
||||
|
||||
private readonly bool mPredictionResistant;
|
||||
|
||||
public BasicEntropySourceProvider(SecureRandom secureRandom, bool isPredictionResistant)
|
||||
{
|
||||
mSecureRandom = secureRandom;
|
||||
mPredictionResistant = isPredictionResistant;
|
||||
}
|
||||
|
||||
public IEntropySource Get(int bitsRequired)
|
||||
{
|
||||
return new BasicEntropySource(mSecureRandom, mPredictionResistant, bitsRequired);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
using System;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace Org.BouncyCastle.Crypto.Prng;
|
||||
|
||||
public class CryptoApiEntropySourceProvider : IEntropySourceProvider
|
||||
{
|
||||
private class CryptoApiEntropySource : IEntropySource
|
||||
{
|
||||
private readonly RandomNumberGenerator mRng;
|
||||
|
||||
private readonly bool mPredictionResistant;
|
||||
|
||||
private readonly int mEntropySize;
|
||||
|
||||
bool IEntropySource.IsPredictionResistant => mPredictionResistant;
|
||||
|
||||
int IEntropySource.EntropySize => mEntropySize;
|
||||
|
||||
internal CryptoApiEntropySource(RandomNumberGenerator rng, bool predictionResistant, int entropySize)
|
||||
{
|
||||
mRng = rng;
|
||||
mPredictionResistant = predictionResistant;
|
||||
mEntropySize = entropySize;
|
||||
}
|
||||
|
||||
byte[] IEntropySource.GetEntropy()
|
||||
{
|
||||
byte[] array = new byte[(mEntropySize + 7) / 8];
|
||||
mRng.GetBytes(array);
|
||||
return array;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly RandomNumberGenerator mRng;
|
||||
|
||||
private readonly bool mPredictionResistant;
|
||||
|
||||
public CryptoApiEntropySourceProvider()
|
||||
: this(RandomNumberGenerator.Create(), isPredictionResistant: true)
|
||||
{
|
||||
}
|
||||
|
||||
public CryptoApiEntropySourceProvider(RandomNumberGenerator rng, bool isPredictionResistant)
|
||||
{
|
||||
if (rng == null)
|
||||
{
|
||||
throw new ArgumentNullException("rng");
|
||||
}
|
||||
mRng = rng;
|
||||
mPredictionResistant = isPredictionResistant;
|
||||
}
|
||||
|
||||
public IEntropySource Get(int bitsRequired)
|
||||
{
|
||||
return new CryptoApiEntropySource(mRng, mPredictionResistant, bitsRequired);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
using System;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace Org.BouncyCastle.Crypto.Prng;
|
||||
|
||||
public class CryptoApiRandomGenerator : IRandomGenerator
|
||||
{
|
||||
private readonly RandomNumberGenerator rndProv;
|
||||
|
||||
public CryptoApiRandomGenerator()
|
||||
: this(RandomNumberGenerator.Create())
|
||||
{
|
||||
}
|
||||
|
||||
public CryptoApiRandomGenerator(RandomNumberGenerator rng)
|
||||
{
|
||||
rndProv = rng;
|
||||
}
|
||||
|
||||
public virtual void AddSeedMaterial(byte[] seed)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void AddSeedMaterial(long seed)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void NextBytes(byte[] bytes)
|
||||
{
|
||||
rndProv.GetBytes(bytes);
|
||||
}
|
||||
|
||||
public virtual void NextBytes(byte[] bytes, int start, int len)
|
||||
{
|
||||
if (start < 0)
|
||||
{
|
||||
throw new ArgumentException("Start offset cannot be negative", "start");
|
||||
}
|
||||
if (bytes.Length < start + len)
|
||||
{
|
||||
throw new ArgumentException("Byte array too small for requested offset and length");
|
||||
}
|
||||
if (bytes.Length == len && start == 0)
|
||||
{
|
||||
NextBytes(bytes);
|
||||
return;
|
||||
}
|
||||
byte[] array = new byte[len];
|
||||
NextBytes(array);
|
||||
Array.Copy(array, 0, bytes, start, len);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
using Org.BouncyCastle.Crypto.Utilities;
|
||||
|
||||
namespace Org.BouncyCastle.Crypto.Prng;
|
||||
|
||||
public class DigestRandomGenerator : IRandomGenerator
|
||||
{
|
||||
private const long CYCLE_COUNT = 10L;
|
||||
|
||||
private long stateCounter;
|
||||
|
||||
private long seedCounter;
|
||||
|
||||
private IDigest digest;
|
||||
|
||||
private byte[] state;
|
||||
|
||||
private byte[] seed;
|
||||
|
||||
public DigestRandomGenerator(IDigest digest)
|
||||
{
|
||||
this.digest = digest;
|
||||
seed = new byte[digest.GetDigestSize()];
|
||||
seedCounter = 1L;
|
||||
state = new byte[digest.GetDigestSize()];
|
||||
stateCounter = 1L;
|
||||
}
|
||||
|
||||
public void AddSeedMaterial(byte[] inSeed)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
DigestUpdate(inSeed);
|
||||
DigestUpdate(seed);
|
||||
DigestDoFinal(seed);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddSeedMaterial(long rSeed)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
DigestAddCounter(rSeed);
|
||||
DigestUpdate(seed);
|
||||
DigestDoFinal(seed);
|
||||
}
|
||||
}
|
||||
|
||||
public void NextBytes(byte[] bytes)
|
||||
{
|
||||
NextBytes(bytes, 0, bytes.Length);
|
||||
}
|
||||
|
||||
public void NextBytes(byte[] bytes, int start, int len)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
int num = 0;
|
||||
GenerateState();
|
||||
int num2 = start + len;
|
||||
for (int i = start; i < num2; i++)
|
||||
{
|
||||
if (num == state.Length)
|
||||
{
|
||||
GenerateState();
|
||||
num = 0;
|
||||
}
|
||||
bytes[i] = state[num++];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CycleSeed()
|
||||
{
|
||||
DigestUpdate(seed);
|
||||
DigestAddCounter(seedCounter++);
|
||||
DigestDoFinal(seed);
|
||||
}
|
||||
|
||||
private void GenerateState()
|
||||
{
|
||||
DigestAddCounter(stateCounter++);
|
||||
DigestUpdate(state);
|
||||
DigestUpdate(seed);
|
||||
DigestDoFinal(state);
|
||||
if (stateCounter % 10 == 0)
|
||||
{
|
||||
CycleSeed();
|
||||
}
|
||||
}
|
||||
|
||||
private void DigestAddCounter(long seedVal)
|
||||
{
|
||||
byte[] array = new byte[8];
|
||||
Pack.UInt64_To_LE((ulong)seedVal, array);
|
||||
digest.BlockUpdate(array, 0, array.Length);
|
||||
}
|
||||
|
||||
private void DigestUpdate(byte[] inSeed)
|
||||
{
|
||||
digest.BlockUpdate(inSeed, 0, inSeed.Length);
|
||||
}
|
||||
|
||||
private void DigestDoFinal(byte[] result)
|
||||
{
|
||||
digest.DoFinal(result, 0);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
|
||||
namespace Org.BouncyCastle.Crypto.Prng;
|
||||
|
||||
public abstract class EntropyUtilities
|
||||
{
|
||||
public static byte[] GenerateSeed(IEntropySource entropySource, int numBytes)
|
||||
{
|
||||
byte[] array = new byte[numBytes];
|
||||
int num;
|
||||
for (int i = 0; i < numBytes; i += num)
|
||||
{
|
||||
byte[] entropy = entropySource.GetEntropy();
|
||||
num = System.Math.Min(array.Length, numBytes - i);
|
||||
Array.Copy(entropy, 0, array, i, num);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
using Org.BouncyCastle.Crypto.Prng.Drbg;
|
||||
|
||||
namespace Org.BouncyCastle.Crypto.Prng;
|
||||
|
||||
internal interface IDrbgProvider
|
||||
{
|
||||
ISP80090Drbg Get(IEntropySource entropySource);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace Org.BouncyCastle.Crypto.Prng;
|
||||
|
||||
public interface IRandomGenerator
|
||||
{
|
||||
void AddSeedMaterial(byte[] seed);
|
||||
|
||||
void AddSeedMaterial(long seed);
|
||||
|
||||
void NextBytes(byte[] bytes);
|
||||
|
||||
void NextBytes(byte[] bytes, int start, int len);
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
using System;
|
||||
|
||||
namespace Org.BouncyCastle.Crypto.Prng;
|
||||
|
||||
public class ReversedWindowGenerator : IRandomGenerator
|
||||
{
|
||||
private readonly IRandomGenerator generator;
|
||||
|
||||
private byte[] window;
|
||||
|
||||
private int windowCount;
|
||||
|
||||
public ReversedWindowGenerator(IRandomGenerator generator, int windowSize)
|
||||
{
|
||||
if (generator == null)
|
||||
{
|
||||
throw new ArgumentNullException("generator");
|
||||
}
|
||||
if (windowSize < 2)
|
||||
{
|
||||
throw new ArgumentException("Window size must be at least 2", "windowSize");
|
||||
}
|
||||
this.generator = generator;
|
||||
window = new byte[windowSize];
|
||||
}
|
||||
|
||||
public virtual void AddSeedMaterial(byte[] seed)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
windowCount = 0;
|
||||
generator.AddSeedMaterial(seed);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void AddSeedMaterial(long seed)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
windowCount = 0;
|
||||
generator.AddSeedMaterial(seed);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void NextBytes(byte[] bytes)
|
||||
{
|
||||
doNextBytes(bytes, 0, bytes.Length);
|
||||
}
|
||||
|
||||
public virtual void NextBytes(byte[] bytes, int start, int len)
|
||||
{
|
||||
doNextBytes(bytes, start, len);
|
||||
}
|
||||
|
||||
private void doNextBytes(byte[] bytes, int start, int len)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
int num = 0;
|
||||
while (num < len)
|
||||
{
|
||||
if (windowCount < 1)
|
||||
{
|
||||
generator.NextBytes(window, 0, window.Length);
|
||||
windowCount = window.Length;
|
||||
}
|
||||
bytes[start + num++] = window[--windowCount];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
using System;
|
||||
using Org.BouncyCastle.Crypto.Prng.Drbg;
|
||||
using Org.BouncyCastle.Security;
|
||||
|
||||
namespace Org.BouncyCastle.Crypto.Prng;
|
||||
|
||||
public class SP800SecureRandom : SecureRandom
|
||||
{
|
||||
private readonly IDrbgProvider mDrbgProvider;
|
||||
|
||||
private readonly bool mPredictionResistant;
|
||||
|
||||
private readonly SecureRandom mRandomSource;
|
||||
|
||||
private readonly IEntropySource mEntropySource;
|
||||
|
||||
private ISP80090Drbg mDrbg;
|
||||
|
||||
internal SP800SecureRandom(SecureRandom randomSource, IEntropySource entropySource, IDrbgProvider drbgProvider, bool predictionResistant)
|
||||
: base((IRandomGenerator)null)
|
||||
{
|
||||
mRandomSource = randomSource;
|
||||
mEntropySource = entropySource;
|
||||
mDrbgProvider = drbgProvider;
|
||||
mPredictionResistant = predictionResistant;
|
||||
}
|
||||
|
||||
public override void SetSeed(byte[] seed)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (mRandomSource != null)
|
||||
{
|
||||
mRandomSource.SetSeed(seed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void SetSeed(long seed)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (mRandomSource != null)
|
||||
{
|
||||
mRandomSource.SetSeed(seed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void NextBytes(byte[] bytes)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (mDrbg == null)
|
||||
{
|
||||
mDrbg = mDrbgProvider.Get(mEntropySource);
|
||||
}
|
||||
if (mDrbg.Generate(bytes, null, mPredictionResistant) < 0)
|
||||
{
|
||||
mDrbg.Reseed(null);
|
||||
mDrbg.Generate(bytes, null, mPredictionResistant);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void NextBytes(byte[] buf, int off, int len)
|
||||
{
|
||||
byte[] array = new byte[len];
|
||||
NextBytes(array);
|
||||
Array.Copy(array, 0, buf, off, len);
|
||||
}
|
||||
|
||||
public override byte[] GenerateSeed(int numBytes)
|
||||
{
|
||||
return EntropyUtilities.GenerateSeed(mEntropySource, numBytes);
|
||||
}
|
||||
|
||||
public virtual void Reseed(byte[] additionalInput)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (mDrbg == null)
|
||||
{
|
||||
mDrbg = mDrbgProvider.Get(mEntropySource);
|
||||
}
|
||||
mDrbg.Reseed(additionalInput);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
using Org.BouncyCastle.Crypto.Prng.Drbg;
|
||||
using Org.BouncyCastle.Security;
|
||||
|
||||
namespace Org.BouncyCastle.Crypto.Prng;
|
||||
|
||||
public class SP800SecureRandomBuilder
|
||||
{
|
||||
private class HashDrbgProvider : IDrbgProvider
|
||||
{
|
||||
private readonly IDigest mDigest;
|
||||
|
||||
private readonly byte[] mNonce;
|
||||
|
||||
private readonly byte[] mPersonalizationString;
|
||||
|
||||
private readonly int mSecurityStrength;
|
||||
|
||||
public HashDrbgProvider(IDigest digest, byte[] nonce, byte[] personalizationString, int securityStrength)
|
||||
{
|
||||
mDigest = digest;
|
||||
mNonce = nonce;
|
||||
mPersonalizationString = personalizationString;
|
||||
mSecurityStrength = securityStrength;
|
||||
}
|
||||
|
||||
public ISP80090Drbg Get(IEntropySource entropySource)
|
||||
{
|
||||
return new HashSP800Drbg(mDigest, mSecurityStrength, entropySource, mPersonalizationString, mNonce);
|
||||
}
|
||||
}
|
||||
|
||||
private class HMacDrbgProvider : IDrbgProvider
|
||||
{
|
||||
private readonly IMac mHMac;
|
||||
|
||||
private readonly byte[] mNonce;
|
||||
|
||||
private readonly byte[] mPersonalizationString;
|
||||
|
||||
private readonly int mSecurityStrength;
|
||||
|
||||
public HMacDrbgProvider(IMac hMac, byte[] nonce, byte[] personalizationString, int securityStrength)
|
||||
{
|
||||
mHMac = hMac;
|
||||
mNonce = nonce;
|
||||
mPersonalizationString = personalizationString;
|
||||
mSecurityStrength = securityStrength;
|
||||
}
|
||||
|
||||
public ISP80090Drbg Get(IEntropySource entropySource)
|
||||
{
|
||||
return new HMacSP800Drbg(mHMac, mSecurityStrength, entropySource, mPersonalizationString, mNonce);
|
||||
}
|
||||
}
|
||||
|
||||
private class CtrDrbgProvider : IDrbgProvider
|
||||
{
|
||||
private readonly IBlockCipher mBlockCipher;
|
||||
|
||||
private readonly int mKeySizeInBits;
|
||||
|
||||
private readonly byte[] mNonce;
|
||||
|
||||
private readonly byte[] mPersonalizationString;
|
||||
|
||||
private readonly int mSecurityStrength;
|
||||
|
||||
public CtrDrbgProvider(IBlockCipher blockCipher, int keySizeInBits, byte[] nonce, byte[] personalizationString, int securityStrength)
|
||||
{
|
||||
mBlockCipher = blockCipher;
|
||||
mKeySizeInBits = keySizeInBits;
|
||||
mNonce = nonce;
|
||||
mPersonalizationString = personalizationString;
|
||||
mSecurityStrength = securityStrength;
|
||||
}
|
||||
|
||||
public ISP80090Drbg Get(IEntropySource entropySource)
|
||||
{
|
||||
return new CtrSP800Drbg(mBlockCipher, mKeySizeInBits, mSecurityStrength, entropySource, mPersonalizationString, mNonce);
|
||||
}
|
||||
}
|
||||
|
||||
private readonly SecureRandom mRandom;
|
||||
|
||||
private readonly IEntropySourceProvider mEntropySourceProvider;
|
||||
|
||||
private byte[] mPersonalizationString = null;
|
||||
|
||||
private int mSecurityStrength = 256;
|
||||
|
||||
private int mEntropyBitsRequired = 256;
|
||||
|
||||
public SP800SecureRandomBuilder()
|
||||
: this(new SecureRandom(), predictionResistant: false)
|
||||
{
|
||||
}
|
||||
|
||||
public SP800SecureRandomBuilder(SecureRandom entropySource, bool predictionResistant)
|
||||
{
|
||||
mRandom = entropySource;
|
||||
mEntropySourceProvider = new BasicEntropySourceProvider(entropySource, predictionResistant);
|
||||
}
|
||||
|
||||
public SP800SecureRandomBuilder(IEntropySourceProvider entropySourceProvider)
|
||||
{
|
||||
mRandom = null;
|
||||
mEntropySourceProvider = entropySourceProvider;
|
||||
}
|
||||
|
||||
public SP800SecureRandomBuilder SetPersonalizationString(byte[] personalizationString)
|
||||
{
|
||||
mPersonalizationString = personalizationString;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SP800SecureRandomBuilder SetSecurityStrength(int securityStrength)
|
||||
{
|
||||
mSecurityStrength = securityStrength;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SP800SecureRandomBuilder SetEntropyBitsRequired(int entropyBitsRequired)
|
||||
{
|
||||
mEntropyBitsRequired = entropyBitsRequired;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SP800SecureRandom BuildHash(IDigest digest, byte[] nonce, bool predictionResistant)
|
||||
{
|
||||
return new SP800SecureRandom(mRandom, mEntropySourceProvider.Get(mEntropyBitsRequired), new HashDrbgProvider(digest, nonce, mPersonalizationString, mSecurityStrength), predictionResistant);
|
||||
}
|
||||
|
||||
public SP800SecureRandom BuildCtr(IBlockCipher cipher, int keySizeInBits, byte[] nonce, bool predictionResistant)
|
||||
{
|
||||
return new SP800SecureRandom(mRandom, mEntropySourceProvider.Get(mEntropyBitsRequired), new CtrDrbgProvider(cipher, keySizeInBits, nonce, mPersonalizationString, mSecurityStrength), predictionResistant);
|
||||
}
|
||||
|
||||
public SP800SecureRandom BuildHMac(IMac hMac, byte[] nonce, bool predictionResistant)
|
||||
{
|
||||
return new SP800SecureRandom(mRandom, mEntropySourceProvider.Get(mEntropyBitsRequired), new HMacDrbgProvider(hMac, nonce, mPersonalizationString, mSecurityStrength), predictionResistant);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace Org.BouncyCastle.Crypto.Prng;
|
||||
|
||||
public class ThreadedSeedGenerator
|
||||
{
|
||||
private class SeedGenerator
|
||||
{
|
||||
private volatile int counter = 0;
|
||||
|
||||
private volatile bool stop = false;
|
||||
|
||||
private void Run(object ignored)
|
||||
{
|
||||
while (!stop)
|
||||
{
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] GenerateSeed(int numBytes, bool fast)
|
||||
{
|
||||
ThreadPriority priority = Thread.CurrentThread.Priority;
|
||||
try
|
||||
{
|
||||
Thread.CurrentThread.Priority = ThreadPriority.Normal;
|
||||
return DoGenerateSeed(numBytes, fast);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Thread.CurrentThread.Priority = priority;
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] DoGenerateSeed(int numBytes, bool fast)
|
||||
{
|
||||
counter = 0;
|
||||
stop = false;
|
||||
byte[] array = new byte[numBytes];
|
||||
int num = 0;
|
||||
int num2 = (fast ? numBytes : (numBytes * 8));
|
||||
ThreadPool.QueueUserWorkItem(Run);
|
||||
for (int i = 0; i < num2; i++)
|
||||
{
|
||||
while (counter == num)
|
||||
{
|
||||
try
|
||||
{
|
||||
Thread.Sleep(1);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
}
|
||||
num = counter;
|
||||
if (fast)
|
||||
{
|
||||
array[i] = (byte)num;
|
||||
continue;
|
||||
}
|
||||
int num3 = i / 8;
|
||||
array[num3] = (byte)((array[num3] << 1) | (num & 1));
|
||||
}
|
||||
stop = true;
|
||||
return array;
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] GenerateSeed(int numBytes, bool fast)
|
||||
{
|
||||
return new SeedGenerator().GenerateSeed(numBytes, fast);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
using Org.BouncyCastle.Crypto.Utilities;
|
||||
|
||||
namespace Org.BouncyCastle.Crypto.Prng;
|
||||
|
||||
public class VmpcRandomGenerator : IRandomGenerator
|
||||
{
|
||||
private byte n = 0;
|
||||
|
||||
private byte[] P = new byte[256]
|
||||
{
|
||||
187, 44, 98, 127, 181, 170, 212, 13, 129, 254,
|
||||
178, 130, 203, 160, 161, 8, 24, 113, 86, 232,
|
||||
73, 2, 16, 196, 222, 53, 165, 236, 128, 18,
|
||||
184, 105, 218, 47, 117, 204, 162, 9, 54, 3,
|
||||
97, 45, 253, 224, 221, 5, 67, 144, 173, 200,
|
||||
225, 175, 87, 155, 76, 216, 81, 174, 80, 133,
|
||||
60, 10, 228, 243, 156, 38, 35, 83, 201, 131,
|
||||
151, 70, 177, 153, 100, 49, 119, 213, 29, 214,
|
||||
120, 189, 94, 176, 138, 34, 56, 248, 104, 43,
|
||||
42, 197, 211, 247, 188, 111, 223, 4, 229, 149,
|
||||
62, 37, 134, 166, 11, 143, 241, 36, 14, 215,
|
||||
64, 179, 207, 126, 6, 21, 154, 77, 28, 163,
|
||||
219, 50, 146, 88, 17, 39, 244, 89, 208, 78,
|
||||
106, 23, 91, 172, 255, 7, 192, 101, 121, 252,
|
||||
199, 205, 118, 66, 93, 231, 58, 52, 122, 48,
|
||||
40, 15, 115, 1, 249, 209, 210, 25, 233, 145,
|
||||
185, 90, 237, 65, 109, 180, 195, 158, 191, 99,
|
||||
250, 31, 51, 96, 71, 137, 240, 150, 26, 95,
|
||||
147, 61, 55, 75, 217, 168, 193, 27, 246, 57,
|
||||
139, 183, 12, 32, 206, 136, 110, 182, 116, 142,
|
||||
141, 22, 41, 242, 135, 245, 235, 112, 227, 251,
|
||||
85, 159, 198, 68, 74, 69, 125, 226, 107, 92,
|
||||
108, 102, 169, 140, 238, 132, 19, 167, 30, 157,
|
||||
220, 103, 72, 186, 46, 230, 164, 171, 124, 148,
|
||||
0, 33, 239, 234, 190, 202, 114, 79, 82, 152,
|
||||
63, 194, 20, 123, 59, 84
|
||||
};
|
||||
|
||||
private byte s = 190;
|
||||
|
||||
public virtual void AddSeedMaterial(byte[] seed)
|
||||
{
|
||||
for (int i = 0; i < seed.Length; i++)
|
||||
{
|
||||
s = P[(s + P[n & 0xFF] + seed[i]) & 0xFF];
|
||||
byte b = P[n & 0xFF];
|
||||
P[n & 0xFF] = P[s & 0xFF];
|
||||
P[s & 0xFF] = b;
|
||||
n = (byte)((n + 1) & 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void AddSeedMaterial(long seed)
|
||||
{
|
||||
AddSeedMaterial(Pack.UInt64_To_BE((ulong)seed));
|
||||
}
|
||||
|
||||
public virtual void NextBytes(byte[] bytes)
|
||||
{
|
||||
NextBytes(bytes, 0, bytes.Length);
|
||||
}
|
||||
|
||||
public virtual void NextBytes(byte[] bytes, int start, int len)
|
||||
{
|
||||
lock (P)
|
||||
{
|
||||
int num = start + len;
|
||||
for (int i = start; i != num; i++)
|
||||
{
|
||||
s = P[(s + P[n & 0xFF]) & 0xFF];
|
||||
bytes[i] = P[(P[P[s & 0xFF] & 0xFF] + 1) & 0xFF];
|
||||
byte b = P[n & 0xFF];
|
||||
P[n & 0xFF] = P[s & 0xFF];
|
||||
P[s & 0xFF] = b;
|
||||
n = (byte)((n + 1) & 0xFF);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
using System;
|
||||
using Org.BouncyCastle.Security;
|
||||
|
||||
namespace Org.BouncyCastle.Crypto.Prng;
|
||||
|
||||
public class X931SecureRandom : SecureRandom
|
||||
{
|
||||
private readonly bool mPredictionResistant;
|
||||
|
||||
private readonly SecureRandom mRandomSource;
|
||||
|
||||
private readonly X931Rng mDrbg;
|
||||
|
||||
internal X931SecureRandom(SecureRandom randomSource, X931Rng drbg, bool predictionResistant)
|
||||
: base((IRandomGenerator)null)
|
||||
{
|
||||
mRandomSource = randomSource;
|
||||
mDrbg = drbg;
|
||||
mPredictionResistant = predictionResistant;
|
||||
}
|
||||
|
||||
public override void SetSeed(byte[] seed)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (mRandomSource != null)
|
||||
{
|
||||
mRandomSource.SetSeed(seed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void SetSeed(long seed)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (mRandomSource != null)
|
||||
{
|
||||
mRandomSource.SetSeed(seed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void NextBytes(byte[] bytes)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (mDrbg.Generate(bytes, mPredictionResistant) < 0)
|
||||
{
|
||||
mDrbg.Reseed();
|
||||
mDrbg.Generate(bytes, mPredictionResistant);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void NextBytes(byte[] buf, int off, int len)
|
||||
{
|
||||
byte[] array = new byte[len];
|
||||
NextBytes(array);
|
||||
Array.Copy(array, 0, buf, off, len);
|
||||
}
|
||||
|
||||
public override byte[] GenerateSeed(int numBytes)
|
||||
{
|
||||
return EntropyUtilities.GenerateSeed(mDrbg.EntropySource, numBytes);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
using Org.BouncyCastle.Crypto.Parameters;
|
||||
using Org.BouncyCastle.Crypto.Utilities;
|
||||
using Org.BouncyCastle.Security;
|
||||
using Org.BouncyCastle.Utilities.Date;
|
||||
|
||||
namespace Org.BouncyCastle.Crypto.Prng;
|
||||
|
||||
public class X931SecureRandomBuilder
|
||||
{
|
||||
private readonly SecureRandom mRandom;
|
||||
|
||||
private IEntropySourceProvider mEntropySourceProvider;
|
||||
|
||||
private byte[] mDateTimeVector;
|
||||
|
||||
public X931SecureRandomBuilder()
|
||||
: this(new SecureRandom(), predictionResistant: false)
|
||||
{
|
||||
}
|
||||
|
||||
public X931SecureRandomBuilder(SecureRandom entropySource, bool predictionResistant)
|
||||
{
|
||||
mRandom = entropySource;
|
||||
mEntropySourceProvider = new BasicEntropySourceProvider(mRandom, predictionResistant);
|
||||
}
|
||||
|
||||
public X931SecureRandomBuilder(IEntropySourceProvider entropySourceProvider)
|
||||
{
|
||||
mRandom = null;
|
||||
mEntropySourceProvider = entropySourceProvider;
|
||||
}
|
||||
|
||||
public X931SecureRandomBuilder SetDateTimeVector(byte[] dateTimeVector)
|
||||
{
|
||||
mDateTimeVector = dateTimeVector;
|
||||
return this;
|
||||
}
|
||||
|
||||
public X931SecureRandom Build(IBlockCipher engine, KeyParameter key, bool predictionResistant)
|
||||
{
|
||||
if (mDateTimeVector == null)
|
||||
{
|
||||
mDateTimeVector = new byte[engine.GetBlockSize()];
|
||||
Pack.UInt64_To_BE((ulong)DateTimeUtilities.CurrentUnixMs(), mDateTimeVector, 0);
|
||||
}
|
||||
engine.Init(forEncryption: true, key);
|
||||
return new X931SecureRandom(mRandom, new X931Rng(engine, mDateTimeVector, mEntropySourceProvider.Get(engine.GetBlockSize() * 8)), predictionResistant);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user