diff --git a/crypto/src/crypto/IEntropySource.cs b/crypto/src/crypto/IEntropySource.cs
new file mode 100644
index 000000000..33ce1b801
--- /dev/null
+++ b/crypto/src/crypto/IEntropySource.cs
@@ -0,0 +1,29 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto
+{
+ /// <summary>
+ /// Base interface describing an entropy source for a DRGB.
+ /// </summary>
+ public interface IEntropySource
+ {
+ /// <summary>
+ /// Return whether or not this entropy source is regarded as prediction resistant.
+ /// </summary>
+ /// <value><c>true</c> if this instance is prediction resistant; otherwise, <c>false</c>.</value>
+ bool IsPredictionResistant { get; }
+
+ /// <summary>
+ /// Return a byte array of entropy.
+ /// </summary>
+ /// <returns>The entropy bytes.</returns>
+ byte[] GetEntropy();
+
+ /// <summary>
+ /// Return the number of bits of entropy this source can produce.
+ /// </summary>
+ /// <value>The size, in bits, of the return value of getEntropy.</value>
+ int EntropySize { get; }
+ }
+}
+
diff --git a/crypto/src/crypto/IEntropySourceProvider.cs b/crypto/src/crypto/IEntropySourceProvider.cs
new file mode 100644
index 000000000..990c6497b
--- /dev/null
+++ b/crypto/src/crypto/IEntropySourceProvider.cs
@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Org.BouncyCastle.Crypto
+{
+ /// <summary>
+ /// Base interface describing a provider of entropy sources.
+ /// </summary>
+ public interface IEntropySourceProvider
+ {
+ /// <summary>
+ /// Return an entropy source providing a block of entropy.
+ /// </summary>
+ /// <param name="bitsRequired">The size of the block of entropy required.</param>
+ /// <returns>An entropy source providing bitsRequired blocks of entropy.</returns>
+ IEntropySource Get(int bitsRequired);
+ }
+}
diff --git a/crypto/src/crypto/prng/CtrSP800Drbg.cs b/crypto/src/crypto/prng/CtrSP800Drbg.cs
new file mode 100644
index 000000000..089cd17cd
--- /dev/null
+++ b/crypto/src/crypto/prng/CtrSP800Drbg.cs
@@ -0,0 +1,495 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Crypto.Prng.Drbg
+{
+ /**
+ * A SP800-90A CTR DRBG.
+ */
+ public class CtrSP800Drbg: SP80090Drbg
+ {
+ private static readonly long TDEA_RESEED_MAX = 1L << (32 - 1);
+ private static readonly long AES_RESEED_MAX = 1L << (48 - 1);
+ private static readonly int TDEA_MAX_BITS_REQUEST = 1 << (13 - 1);
+ private static readonly int AES_MAX_BITS_REQUEST = 1 << (19 - 1);
+
+ private IEntropySource _entropySource;
+ private IBlockCipher _engine;
+ private int _keySizeInBits;
+ private int _seedLength;
+ private int _securityStrength;
+
+ // internal state
+ private byte[] _Key;
+ private byte[] _V;
+ private long _reseedCounter = 0;
+ private bool _isTDEA = false;
+
+ /**
+ * Construct a SP800-90A CTR DRBG.
+ * <p>
+ * Minimum entropy requirement is the security strength requested.
+ * </p>
+ * @param engine underlying block cipher to use to support DRBG
+ * @param keySizeInBits size of the key to use with the block cipher.
+ * @param securityStrength security strength required (in bits)
+ * @param entropySource source of entropy to use for seeding/reseeding.
+ * @param personalizationString personalization string to distinguish this DRBG (may be null).
+ * @param nonce nonce to further distinguish this DRBG (may be null).
+ */
+ public CtrSP800Drbg(IBlockCipher engine, int keySizeInBits, int securityStrength, IEntropySource entropySource, byte[] personalizationString, byte[] nonce)
+ {
+ _entropySource = entropySource;
+ _engine = engine;
+
+ _keySizeInBits = keySizeInBits;
+ _securityStrength = securityStrength;
+ _seedLength = keySizeInBits + engine.GetBlockSize() * 8;
+ _isTDEA = isTDEA(engine);
+
+ 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");
+ }
+
+ byte[] entropy = getEntropy(); // Get_entropy_input
+
+ CTR_DRBG_Instantiate_algorithm(entropy, nonce, personalizationString);
+ }
+
+ private void CTR_DRBG_Instantiate_algorithm(byte[] entropy, byte[] nonce,
+ byte[] personalisationString)
+ {
+ byte[] seedMaterial = Arrays.Concatenate(entropy, nonce, personalisationString);
+ byte[] seed = Block_Cipher_df(seedMaterial, _seedLength);
+
+ int outlen = _engine.GetBlockSize();
+
+ _Key = new byte[(_keySizeInBits + 7) / 8];
+ _V = new byte[outlen];
+
+ // _Key & _V are modified by this call
+ CTR_DRBG_Update(seed, _Key, _V);
+
+ _reseedCounter = 1;
+ }
+
+ private void CTR_DRBG_Update(byte[] seed, byte[] key, byte[] v)
+ {
+ byte[] temp = new byte[seed.Length];
+ byte[] outputBlock = new byte[_engine.GetBlockSize()];
+
+ int i=0;
+ int outLen = _engine.GetBlockSize();
+
+ _engine.Init(true, new KeyParameter(expandKey(key)));
+ while (i*outLen < seed.Length)
+ {
+ addOneTo(v);
+ _engine.ProcessBlock(v, 0, outputBlock, 0);
+
+ int bytesToCopy = ((temp.Length - i * outLen) > outLen)
+ ? outLen : (temp.Length - i * outLen);
+
+ Array.Copy(outputBlock, 0, temp, i * outLen, bytesToCopy);
+ ++i;
+ }
+
+ XOR(temp, seed, temp, 0);
+
+ Array.Copy(temp, 0, key, 0, key.Length);
+ Array.Copy(temp, key.Length, v, 0, v.Length);
+ }
+
+ private void CTR_DRBG_Reseed_algorithm(byte[] additionalInput)
+ {
+ byte[] seedMaterial = Arrays.Concatenate(getEntropy(), additionalInput);
+
+ seedMaterial = Block_Cipher_df(seedMaterial, _seedLength);
+
+ CTR_DRBG_Update(seedMaterial, _Key, _V);
+
+ _reseedCounter = 1;
+ }
+
+ 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[i+bOff]);
+ }
+ }
+
+ private void addOneTo(byte[] longer)
+ {
+ int carry = 1;
+ for (int i = 1; i <= longer.Length; i++) // warning
+ {
+ int res = (longer[longer.Length - i] & 0xff) + carry;
+ carry = (res > 0xff) ? 1 : 0;
+ longer[longer.Length - i] = (byte)res;
+ }
+ }
+
+ private byte[] getEntropy()
+ {
+ byte[] entropy = _entropySource.GetEntropy();
+ if (entropy.Length < (_securityStrength + 7) / 8)
+ {
+ throw new InvalidOperationException("Insufficient entropy provided by entropy source");
+ }
+ return entropy;
+ }
+
+ // -- Internal state migration ---
+
+ private static readonly byte[] K_BITS = Hex.Decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F");
+
+ // 1. If (number_of_bits_to_return > max_number_of_bits), then return an
+ // ERROR_FLAG.
+ // 2. L = len (input_string)/8.
+ // 3. N = number_of_bits_to_return/8.
+ // Comment: L is the bitstring represention of
+ // the integer resulting from len (input_string)/8.
+ // L shall be represented as a 32-bit integer.
+ //
+ // Comment : N is the bitstring represention of
+ // the integer resulting from
+ // number_of_bits_to_return/8. N shall be
+ // represented as a 32-bit integer.
+ //
+ // 4. S = L || N || input_string || 0x80.
+ // 5. While (len (S) mod outlen)
+ // Comment : Pad S with zeros, if necessary.
+ // 0, S = S || 0x00.
+ //
+ // Comment : Compute the starting value.
+ // 6. temp = the Null string.
+ // 7. i = 0.
+ // 8. K = Leftmost keylen bits of 0x00010203...1D1E1F.
+ // 9. While len (temp) < keylen + outlen, do
+ //
+ // IV = i || 0outlen - len (i).
+ //
+ // 9.1
+ //
+ // temp = temp || BCC (K, (IV || S)).
+ //
+ // 9.2
+ //
+ // i = i + 1.
+ //
+ // 9.3
+ //
+ // Comment : i shall be represented as a 32-bit
+ // integer, i.e., len (i) = 32.
+ //
+ // Comment: The 32-bit integer represenation of
+ // i is padded with zeros to outlen bits.
+ //
+ // Comment: Compute the requested number of
+ // bits.
+ //
+ // 10. K = Leftmost keylen bits of temp.
+ //
+ // 11. X = Next outlen bits of temp.
+ //
+ // 12. temp = the Null string.
+ //
+ // 13. While len (temp) < number_of_bits_to_return, do
+ //
+ // 13.1 X = Block_Encrypt (K, X).
+ //
+ // 13.2 temp = temp || X.
+ //
+ // 14. requested_bits = Leftmost number_of_bits_to_return of temp.
+ //
+ // 15. Return SUCCESS and requested_bits.
+ private byte[] Block_Cipher_df(byte[] inputString, int bitLength)
+ {
+ int outLen = _engine.GetBlockSize();
+ int L = inputString.Length; // already in bytes
+ int N = bitLength / 8;
+ // 4 S = L || N || inputstring || 0x80
+ int sLen = 4 + 4 + L + 1;
+ int blockLen = ((sLen + outLen - 1) / outLen) * outLen;
+ byte[] S = new byte[blockLen];
+ copyIntToByteArray(S, L, 0);
+ copyIntToByteArray(S, N, 4);
+ Array.Copy(inputString, 0, S, 8, L);
+ S[8 + L] = (byte)0x80;
+ // S already padded with zeros
+
+ byte[] temp = new byte[_keySizeInBits / 8 + outLen];
+ byte[] bccOut = new byte[outLen];
+
+ byte[] IV = new byte[outLen];
+
+ int i = 0;
+ byte[] K = new byte[_keySizeInBits / 8];
+ Array.Copy(K_BITS, 0, K, 0, K.Length);
+
+ while (i*outLen*8 < _keySizeInBits + outLen *8)
+ {
+ copyIntToByteArray(IV, i, 0);
+ BCC(bccOut, K, IV, S);
+
+ int bytesToCopy = ((temp.Length - i * outLen) > outLen)
+ ? outLen
+ : (temp.Length - i * outLen);
+
+ Array.Copy(bccOut, 0, temp, i * outLen, bytesToCopy);
+ ++i;
+ }
+
+ byte[] X = new byte[outLen];
+ Array.Copy(temp, 0, K, 0, K.Length);
+ Array.Copy(temp, K.Length, X, 0, X.Length);
+
+ temp = new byte[bitLength / 2];
+
+ i = 0;
+ _engine.Init(true, new KeyParameter(expandKey(K)));
+
+ while (i * outLen < temp.Length)
+ {
+ _engine.ProcessBlock(X, 0, X, 0);
+
+ int bytesToCopy = ((temp.Length - i * outLen) > outLen)
+ ? outLen
+ : (temp.Length - i * outLen);
+
+ Array.Copy(X, 0, temp, i * outLen, bytesToCopy);
+ i++;
+ }
+
+ return temp;
+ }
+
+ /*
+ * 1. chaining_value = 0^outlen
+ * . Comment: Set the first chaining value to outlen zeros.
+ * 2. n = len (data)/outlen.
+ * 3. Starting with the leftmost bits of data, split the data into n blocks of outlen bits
+ * each, forming block(1) to block(n).
+ * 4. For i = 1 to n do
+ * 4.1 input_block = chaining_value ^ block(i) .
+ * 4.2 chaining_value = Block_Encrypt (Key, input_block).
+ * 5. output_block = chaining_value.
+ * 6. Return output_block.
+ */
+ private void BCC(byte[] bccOut, byte[] k, byte[] iV, byte[] data)
+ {
+ int outlen = _engine.GetBlockSize();
+ byte[] chainingValue = new byte[outlen]; // initial values = 0
+ int n = data.Length / outlen;
+
+ byte[] inputBlock = new byte[outlen];
+
+ _engine.Init(true, new KeyParameter(expandKey(k)));
+
+ _engine.ProcessBlock(iV, 0, chainingValue, 0);
+
+ for (int i = 0; i < n; i++)
+ {
+ XOR(inputBlock, chainingValue, data, i*outlen);
+ _engine.ProcessBlock(inputBlock, 0, chainingValue, 0);
+ }
+
+ Array.Copy(chainingValue, 0, bccOut, 0, bccOut.Length);
+ }
+
+ private void copyIntToByteArray(byte[] buf, int value, int offSet)
+ {
+ buf[offSet + 0] = ((byte)(value >> 24));
+ buf[offSet + 1] = ((byte)(value >> 16));
+ buf[offSet + 2] = ((byte)(value >> 8));
+ buf[offSet + 3] = ((byte)(value));
+ }
+
+ /**
+ * Return the block size (in bits) of the DRBG.
+ *
+ * @return the number of bits produced on each internal round of the DRBG.
+ */
+ public int BlockSize
+ {
+ get {
+ return _V.Length * 8;
+ }
+ }
+
+ /**
+ * Populate a passed in array with random data.
+ *
+ * @param output output array for generated bits.
+ * @param additionalInput additional input to be added to the DRBG in this step.
+ * @param predictionResistant true if a reseed should be forced, false otherwise.
+ *
+ * @return number of bits generated, -1 if a reseed required.
+ */
+ public int Generate(byte[] output, byte[] additionalInput, bool predictionResistant)
+ {
+ if (_isTDEA)
+ {
+ if (_reseedCounter > TDEA_RESEED_MAX)
+ {
+ return -1;
+ }
+
+ if (Utils.isTooLarge(output, TDEA_MAX_BITS_REQUEST / 8))
+ {
+ throw new ArgumentException("Number of bits per request limited to " + TDEA_MAX_BITS_REQUEST);
+ }
+ }
+ else
+ {
+ if (_reseedCounter > AES_RESEED_MAX)
+ {
+ return -1;
+ }
+
+ if (Utils.isTooLarge(output, AES_MAX_BITS_REQUEST / 8))
+ {
+ throw new ArgumentException("Number of bits per request limited to " + AES_MAX_BITS_REQUEST);
+ }
+ }
+
+ if (predictionResistant)
+ {
+ CTR_DRBG_Reseed_algorithm(additionalInput);
+ additionalInput = null;
+ }
+
+ if (additionalInput != null)
+ {
+ additionalInput = Block_Cipher_df(additionalInput, _seedLength);
+ CTR_DRBG_Update(additionalInput, _Key, _V);
+ }
+ else
+ {
+ additionalInput = new byte[_seedLength];
+ }
+
+ byte[] tmp = new byte[_V.Length];
+
+ _engine.Init(true, new KeyParameter(expandKey(_Key)));
+
+ for (int i = 0; i <= output.Length / tmp.Length; i++)
+ {
+ int bytesToCopy = ((output.Length - i * tmp.Length) > tmp.Length)
+ ? tmp.Length
+ : (output.Length - i * _V.Length);
+
+ if (bytesToCopy != 0)
+ {
+ addOneTo(_V);
+
+ _engine.ProcessBlock(_V, 0, tmp, 0);
+
+ Array.Copy(tmp, 0, output, i * tmp.Length, bytesToCopy);
+ }
+ }
+
+ CTR_DRBG_Update(additionalInput, _Key, _V);
+
+ _reseedCounter++;
+
+ return output.Length * 8;
+ }
+
+ /**
+ * Reseed the DRBG.
+ *
+ * @param additionalInput additional input to be added to the DRBG in this step.
+ */
+ public void Reseed(byte[] additionalInput)
+ {
+ CTR_DRBG_Reseed_algorithm(additionalInput);
+ }
+
+ private bool isTDEA(IBlockCipher cipher)
+ {
+ return cipher.AlgorithmName.Equals("DESede") || cipher.AlgorithmName.Equals("TDEA");
+ }
+
+ private int getMaxSecurityStrength(IBlockCipher cipher, int keySizeInBits)
+ {
+ if (isTDEA(cipher) && keySizeInBits == 168)
+ {
+ return 112;
+ }
+ if (cipher.AlgorithmName.Equals("AES"))
+ {
+ return keySizeInBits;
+ }
+
+ return -1;
+ }
+
+ byte[] expandKey(byte[] key)
+ {
+ if (_isTDEA)
+ {
+ // expand key to 192 bits.
+ byte[] tmp = new byte[24];
+
+ padKey(key, 0, tmp, 0);
+ padKey(key, 7, tmp, 8);
+ padKey(key, 14, tmp, 16);
+
+ return tmp;
+ }
+ else
+ {
+ return key;
+ }
+ }
+
+ /**
+ * Pad out a key for TDEA, setting odd parity for each byte.
+ *
+ * @param keyMaster
+ * @param keyOff
+ * @param tmp
+ * @param tmpOff
+ */
+ private void padKey(byte[] keyMaster, int keyOff, byte[] tmp, int tmpOff)
+ {
+ tmp[tmpOff + 0] = (byte)(keyMaster[keyOff + 0] & 0xfe);
+ tmp[tmpOff + 1] = (byte)((keyMaster[keyOff + 0] << 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);
+
+ for (int i = tmpOff; i <= tmpOff + 7; i++)
+ {
+ int b = tmp[i];
+ tmp[i] = (byte)((b & 0xfe) |
+ ((((b >> 1) ^
+ (b >> 2) ^
+ (b >> 3) ^
+ (b >> 4) ^
+ (b >> 5) ^
+ (b >> 6) ^
+ (b >> 7)) ^ 0x01) & 0x01));
+ }
+ }
+ }
+}
diff --git a/crypto/src/crypto/prng/HMacSP800Drbg.cs b/crypto/src/crypto/prng/HMacSP800Drbg.cs
new file mode 100644
index 000000000..b2903766f
--- /dev/null
+++ b/crypto/src/crypto/prng/HMacSP800Drbg.cs
@@ -0,0 +1,196 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Prng.Drbg
+{
+ /**
+ * A SP800-90A HMAC DRBG.
+ */
+ public class HMacSP800Drbg: SP80090Drbg
+ {
+ private readonly static long RESEED_MAX = 1L << (48 - 1);
+ private readonly static int MAX_BITS_REQUEST = 1 << (19 - 1);
+
+ private byte[] _K;
+ private byte[] _V;
+ private long _reseedCounter;
+ private IEntropySource _entropySource;
+ private IMac _hMac;
+ private int _securityStrength;
+
+ /**
+ * Construct a SP800-90A Hash DRBG.
+ * <p>
+ * Minimum entropy requirement is the security strength requested.
+ * </p>
+ * @param hMac Hash MAC to base the DRBG on.
+ * @param securityStrength security strength required (in bits)
+ * @param entropySource source of entropy to use for seeding/reseeding.
+ * @param personalizationString personalization string to distinguish this DRBG (may be null).
+ * @param nonce nonce to further distinguish this DRBG (may be null).
+ */
+ public HMacSP800Drbg(IMac hMac, int securityStrength, IEntropySource entropySource, byte[] personalizationString, byte[] nonce)
+ {
+ if (securityStrength > Utils.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");
+ }
+
+ _securityStrength = securityStrength;
+ _entropySource = entropySource;
+ _hMac = hMac;
+
+ byte[] entropy = getEntropy();
+ byte[] seedMaterial = Arrays.Concatenate(entropy, nonce, personalizationString);
+
+ _K = new byte[hMac.GetMacSize()];
+ _V = new byte[_K.Length];
+ Arrays.Fill(_V, (byte)1);
+
+ hmac_DRBG_Update(seedMaterial);
+
+ _reseedCounter = 1;
+ }
+
+ private void hmac_DRBG_Update(byte[] seedMaterial)
+ {
+ hmac_DRBG_Update_Func(seedMaterial, (byte)0x00);
+ if (seedMaterial != null)
+ {
+ hmac_DRBG_Update_Func(seedMaterial, (byte)0x01);
+ }
+ }
+
+ private void hmac_DRBG_Update_Func(byte[] seedMaterial, byte vValue)
+ {
+ _hMac.Init(new KeyParameter(_K));
+
+ _hMac.BlockUpdate(_V, 0, _V.Length);
+ _hMac.Update(vValue);
+
+ if (seedMaterial != null)
+ {
+ _hMac.BlockUpdate(seedMaterial, 0, seedMaterial.Length);
+ }
+
+ _hMac.DoFinal(_K, 0);
+
+ _hMac.Init(new KeyParameter(_K));
+ _hMac.BlockUpdate(_V, 0, _V.Length);
+
+ _hMac.DoFinal(_V, 0);
+ }
+
+ /**
+ * Return the block size (in bits) of the DRBG.
+ *
+ * @return the number of bits produced on each round of the DRBG.
+ */
+ public int BlockSize
+ {
+ get {
+ return _V.Length * 8;
+ }
+ }
+
+ /**
+ * Populate a passed in array with random data.
+ *
+ * @param output output array for generated bits.
+ * @param additionalInput additional input to be added to the DRBG in this step.
+ * @param predictionResistant true if a reseed should be forced, false otherwise.
+ *
+ * @return number of bits generated, -1 if a reseed required.
+ */
+ public int Generate(byte[] output, byte[] additionalInput, bool predictionResistant)
+ {
+ int numberOfBits = output.Length * 8;
+
+ if (numberOfBits > MAX_BITS_REQUEST)
+ {
+ throw new ArgumentException("Number of bits per request limited to " + MAX_BITS_REQUEST);
+ }
+
+ if (_reseedCounter > RESEED_MAX)
+ {
+ return -1;
+ }
+
+ if (predictionResistant)
+ {
+ Reseed(additionalInput);
+ additionalInput = null;
+ }
+
+ // 2.
+ if (additionalInput != null)
+ {
+ hmac_DRBG_Update(additionalInput);
+ }
+
+ // 3.
+ byte[] rv = new byte[output.Length];
+
+ int m = output.Length / _V.Length;
+
+ _hMac.Init(new KeyParameter(_K));
+
+ for (int i = 0; i < m; i++)
+ {
+ _hMac.BlockUpdate(_V, 0, _V.Length);
+ _hMac.DoFinal(_V, 0);
+
+ Array.Copy(_V, 0, rv, i * _V.Length, _V.Length);
+ }
+
+ if (m * _V.Length < rv.Length)
+ {
+ _hMac.BlockUpdate(_V, 0, _V.Length);
+ _hMac.DoFinal(_V, 0);
+
+ Array.Copy(_V, 0, rv, m * _V.Length, rv.Length - (m * _V.Length));
+ }
+
+ hmac_DRBG_Update(additionalInput);
+
+ _reseedCounter++;
+
+ Array.Copy(rv, 0, output, 0, output.Length);
+
+ return numberOfBits;
+ }
+
+ /**
+ * Reseed the DRBG.
+ *
+ * @param additionalInput additional input to be added to the DRBG in this step.
+ */
+ public void Reseed(byte[] additionalInput)
+ {
+ byte[] entropy = getEntropy();
+ byte[] seedMaterial = Arrays.Concatenate(entropy, additionalInput);
+
+ hmac_DRBG_Update(seedMaterial);
+
+ _reseedCounter = 1;
+ }
+
+ private byte[] getEntropy()
+ {
+ byte[] entropy = _entropySource.GetEntropy();
+
+ if (entropy.Length < (_securityStrength + 7) / 8)
+ {
+ throw new InvalidOperationException("Insufficient entropy provided by entropy source");
+ }
+ return entropy;
+ }
+ }
+}
diff --git a/crypto/src/crypto/prng/HashSP800Drbg.cs b/crypto/src/crypto/prng/HashSP800Drbg.cs
new file mode 100644
index 000000000..b2b386f17
--- /dev/null
+++ b/crypto/src/crypto/prng/HashSP800Drbg.cs
@@ -0,0 +1,295 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Prng.Drbg
+{
+ /**
+ * A SP800-90A Hash DRBG.
+ */
+ public class HashSP800Drbg: SP80090Drbg
+ {
+ private readonly static byte[] ONE = { 0x01 };
+
+ private readonly static long RESEED_MAX = 1L << (48 - 1);
+ private readonly static int MAX_BITS_REQUEST = 1 << (19 - 1);
+
+ private static readonly IDictionary seedlens = Platform.CreateHashtable();
+
+ static HashSP800Drbg()
+ {
+ 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);
+
+ }
+
+ private IDigest _digest;
+ private byte[] _V;
+ private byte[] _C;
+ private long _reseedCounter;
+ private IEntropySource _entropySource;
+ private int _securityStrength;
+ private int _seedLength;
+
+ /**
+ * Construct a SP800-90A Hash DRBG.
+ * <p>
+ * Minimum entropy requirement is the security strength requested.
+ * </p>
+ * @param digest source digest to use for DRB stream.
+ * @param securityStrength security strength required (in bits)
+ * @param entropySource source of entropy to use for seeding/reseeding.
+ * @param personalizationString personalization string to distinguish this DRBG (may be null).
+ * @param nonce nonce to further distinguish this DRBG (may be null).
+ */
+ public HashSP800Drbg(IDigest digest, int securityStrength, IEntropySource entropySource, byte[] personalizationString, byte[] nonce)
+ {
+ if (securityStrength > Utils.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");
+ }
+
+ _digest = digest;
+ _entropySource = entropySource;
+ _securityStrength = securityStrength;
+ _seedLength = (int)seedlens[digest.AlgorithmName];
+
+ // 1. seed_material = entropy_input || nonce || personalization_string.
+ // 2. seed = Hash_df (seed_material, seedlen).
+ // 3. V = seed.
+ // 4. C = Hash_df ((0x00 || V), seedlen). Comment: Preceed V with a byte
+ // of zeros.
+ // 5. reseed_counter = 1.
+ // 6. Return V, C, and reseed_counter as the initial_working_state
+
+ byte[] entropy = getEntropy();
+ byte[] seedMaterial = Arrays.Concatenate(entropy, nonce, personalizationString);
+ byte[] seed = Utils.hash_df(_digest, seedMaterial, _seedLength);
+
+ _V = seed;
+ byte[] subV = new byte[_V.Length + 1];
+ Array.Copy(_V, 0, subV, 1, _V.Length);
+ _C = Utils.hash_df(_digest, subV, _seedLength);
+
+ _reseedCounter = 1;
+ }
+
+ /**
+ * Return the block size (in bits) of the DRBG.
+ *
+ * @return the number of bits produced on each internal round of the DRBG.
+ */
+ public int BlockSize
+ {
+ get {
+ return _digest.GetDigestSize () * 8;
+ }
+ }
+
+ /**
+ * Populate a passed in array with random data.
+ *
+ * @param output output array for generated bits.
+ * @param additionalInput additional input to be added to the DRBG in this step.
+ * @param predictionResistant true if a reseed should be forced, false otherwise.
+ *
+ * @return number of bits generated, -1 if a reseed required.
+ */
+ public int Generate(byte[] output, byte[] additionalInput, bool predictionResistant)
+ {
+ // 1. If reseed_counter > reseed_interval, then return an indication that a
+ // reseed is required.
+ // 2. If (additional_input != Null), then do
+ // 2.1 w = Hash (0x02 || V || additional_input).
+ // 2.2 V = (V + w) mod 2^seedlen
+ // .
+ // 3. (returned_bits) = Hashgen (requested_number_of_bits, V).
+ // 4. H = Hash (0x03 || V).
+ // 5. V = (V + H + C + reseed_counter) mod 2^seedlen
+ // .
+ // 6. reseed_counter = reseed_counter + 1.
+ // 7. Return SUCCESS, returned_bits, and the new values of V, C, and
+ // reseed_counter for the new_working_state.
+ int numberOfBits = output.Length*8;
+
+ if (numberOfBits > MAX_BITS_REQUEST)
+ {
+ throw new ArgumentException("Number of bits per request limited to " + MAX_BITS_REQUEST);
+ }
+
+ if (_reseedCounter > RESEED_MAX)
+ {
+ return -1;
+ }
+
+ if (predictionResistant)
+ {
+ Reseed(additionalInput);
+ additionalInput = null;
+ }
+
+ // 2.
+ if (additionalInput != null)
+ {
+ byte[] newInput = new byte[1 + _V.Length + additionalInput.Length];
+ newInput[0] = 0x02;
+ Array.Copy(_V, 0, newInput, 1, _V.Length);
+ // TODO: inOff / inLength
+ Array.Copy(additionalInput, 0, newInput, 1 + _V.Length, additionalInput.Length);
+ byte[] w = hash(newInput);
+
+ addTo(_V, w);
+ }
+
+ // 3.
+ byte[] rv = hashgen(_V, numberOfBits);
+
+ // 4.
+ byte[] subH = new byte[_V.Length + 1];
+ Array.Copy(_V, 0, subH, 1, _V.Length);
+ subH[0] = 0x03;
+
+ byte[] H = hash(subH);
+
+ // 5.
+ addTo(_V, H);
+ addTo(_V, _C);
+ byte[] c = new byte[4];
+ c[0] = (byte)(_reseedCounter >> 24);
+ c[1] = (byte)(_reseedCounter >> 16);
+ c[2] = (byte)(_reseedCounter >> 8);
+ c[3] = (byte)_reseedCounter;
+
+ addTo(_V, c);
+
+ _reseedCounter++;
+
+ Array.Copy(rv, 0, output, 0, output.Length);
+
+ return numberOfBits;
+ }
+
+ private byte[] getEntropy()
+ {
+ byte[] entropy = _entropySource.GetEntropy();
+ if (entropy.Length < (_securityStrength + 7) / 8)
+ {
+ throw new InvalidOperationException("Insufficient entropy provided by entropy source");
+ }
+ return entropy;
+ }
+
+ // this will always add the shorter length byte array mathematically to the
+ // longer length byte array.
+ // be careful....
+ private void addTo(byte[] longer, byte[] shorter)
+ {
+ int carry = 0;
+ for (int i=1;i <= shorter.Length; i++) // warning
+ {
+ int res = (longer[longer.Length-i] & 0xff) + (shorter[shorter.Length-i] & 0xff) + carry;
+ carry = (res > 0xff) ? 1 : 0;
+ longer[longer.Length-i] = (byte)res;
+ }
+
+ for (int i=shorter.Length+1;i <= longer.Length; i++) // warning
+ {
+ int res = (longer[longer.Length-i] & 0xff) + carry;
+ carry = (res > 0xff) ? 1 : 0;
+ longer[longer.Length-i] = (byte)res;
+ }
+ }
+
+ /**
+ * Reseed the DRBG.
+ *
+ * @param additionalInput additional input to be added to the DRBG in this step.
+ */
+ public void Reseed(byte[] additionalInput)
+ {
+ // 1. seed_material = 0x01 || V || entropy_input || additional_input.
+ //
+ // 2. seed = Hash_df (seed_material, seedlen).
+ //
+ // 3. V = seed.
+ //
+ // 4. C = Hash_df ((0x00 || V), seedlen).
+ //
+ // 5. reseed_counter = 1.
+ //
+ // 6. Return V, C, and reseed_counter for the new_working_state.
+ //
+ // Comment: Precede with a byte of all zeros.
+ byte[] entropy = getEntropy();
+ byte[] seedMaterial = Arrays.Concatenate(ONE, _V, entropy, additionalInput);
+ byte[] seed = Utils.hash_df(_digest, seedMaterial, _seedLength);
+
+ _V = seed;
+ byte[] subV = new byte[_V.Length + 1];
+ subV[0] = 0x00;
+ Array.Copy(_V, 0, subV, 1, _V.Length);
+ _C = Utils.hash_df(_digest, subV, _seedLength);
+
+ _reseedCounter = 1;
+ }
+
+ private byte[] hash(byte[] input)
+ {
+ byte[] hash = new byte[_digest.GetDigestSize()];
+ doHash(input, hash);
+ return hash;
+ }
+
+ private void doHash(byte[] input, byte[] output)
+ {
+ _digest.BlockUpdate(input, 0, input.Length);
+ _digest.DoFinal(output, 0);
+ }
+
+ // 1. m = [requested_number_of_bits / outlen]
+ // 2. data = V.
+ // 3. W = the Null string.
+ // 4. For i = 1 to m
+ // 4.1 wi = Hash (data).
+ // 4.2 W = W || wi.
+ // 4.3 data = (data + 1) mod 2^seedlen
+ // .
+ // 5. returned_bits = Leftmost (requested_no_of_bits) bits of W.
+ private byte[] hashgen(byte[] input, int lengthInBits)
+ {
+ int digestSize = _digest.GetDigestSize();
+ int m = (lengthInBits / 8) / digestSize;
+
+ byte[] data = new byte[input.Length];
+ Array.Copy(input, 0, data, 0, input.Length);
+
+ byte[] W = new byte[lengthInBits / 8];
+
+ byte[] dig = new byte[_digest.GetDigestSize()];
+ for (int i = 0; i <= m; i++)
+ {
+ doHash(data, dig);
+
+ int bytesToCopy = ((W.Length - i * dig.Length) > dig.Length)
+ ? dig.Length
+ : (W.Length - i * dig.Length);
+ Array.Copy(dig, 0, W, i * dig.Length, bytesToCopy);
+
+ addTo(data, ONE);
+ }
+
+ return W;
+ }
+ }
+}
diff --git a/crypto/src/crypto/prng/SP80090Drbg.cs b/crypto/src/crypto/prng/SP80090Drbg.cs
new file mode 100644
index 000000000..c39cf365f
--- /dev/null
+++ b/crypto/src/crypto/prng/SP80090Drbg.cs
@@ -0,0 +1,33 @@
+namespace Org.BouncyCastle.Crypto.Prng.Drbg
+{
+ /**
+ * Interface to SP800-90A deterministic random bit generators.
+ */
+ public interface SP80090Drbg
+ {
+ /**
+ * Return the block size of the DRBG.
+ *
+ * @return the block size (in bits) produced by each round of the DRBG.
+ */
+ int BlockSize { get; }
+
+ /**
+ * Populate a passed in array with random data.
+ *
+ * @param output output array for generated bits.
+ * @param additionalInput additional input to be added to the DRBG in this step.
+ * @param predictionResistant true if a reseed should be forced, false otherwise.
+ *
+ * @return number of bits generated, -1 if a reseed required.
+ */
+ int Generate(byte[] output, byte[] additionalInput, bool predictionResistant);
+
+ /**
+ * Reseed the DRBG.
+ *
+ * @param additionalInput additional input to be added to the DRBG in this step.
+ */
+ void Reseed(byte[] additionalInput);
+ }
+}
diff --git a/crypto/src/crypto/prng/Utils.cs b/crypto/src/crypto/prng/Utils.cs
new file mode 100644
index 000000000..e5fe7d4b2
--- /dev/null
+++ b/crypto/src/crypto/prng/Utils.cs
@@ -0,0 +1,103 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Prng.Drbg
+{
+ internal class Utils
+ {
+ private static readonly IDictionary maxSecurityStrengths = Platform.CreateHashtable();
+
+ static Utils()
+ {
+ 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 name = m.AlgorithmName;
+
+ return (int)maxSecurityStrengths[name.Substring(0, name.IndexOf("/"))];
+ }
+
+ /**
+ * Used by both Dual EC and Hash.
+ */
+ internal static byte[] hash_df(IDigest digest, byte[] seedMaterial, int seedLength)
+ {
+ // 1. temp = the Null string.
+ // 2. .
+ // 3. counter = an 8-bit binary value representing the integer "1".
+ // 4. For i = 1 to len do
+ // Comment : In step 4.1, no_of_bits_to_return
+ // is used as a 32-bit string.
+ // 4.1 temp = temp || Hash (counter || no_of_bits_to_return ||
+ // input_string).
+ // 4.2 counter = counter + 1.
+ // 5. requested_bits = Leftmost (no_of_bits_to_return) of temp.
+ // 6. Return SUCCESS and requested_bits.
+ byte[] temp = new byte[(seedLength + 7) / 8];
+
+ int len = temp.Length / digest.GetDigestSize();
+ int counter = 1;
+
+ byte[] dig = new byte[digest.GetDigestSize()];
+
+ for (int i = 0; i <= len; i++)
+ {
+ digest.Update((byte)counter);
+
+ 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(dig, 0);
+
+ int bytesToCopy = ((temp.Length - i * dig.Length) > dig.Length)
+ ? dig.Length
+ : (temp.Length - i * dig.Length);
+ Array.Copy(dig, 0, temp, i * dig.Length, bytesToCopy);
+
+ counter++;
+ }
+
+ // do a left shift to get rid of excess bits.
+ if (seedLength % 8 != 0)
+ {
+ int shift = 8 - (seedLength % 8);
+ uint carry = 0;
+
+ for (int i = 0; i != temp.Length; i++)
+ {
+ uint b = (uint)(temp[i] & 0xff);
+ temp[i] = (byte)((b >> shift) | (carry << (8 - shift)));
+ carry = b;
+ }
+ }
+
+ return temp;
+ }
+
+ internal static bool isTooLarge(byte[] bytes, int maxBytes)
+ {
+ return bytes != null && bytes.Length > maxBytes;
+ }
+ }
+}
diff --git a/crypto/src/crypto/prng/drbg/CtrSP800Drbg.cs b/crypto/src/crypto/prng/drbg/CtrSP800Drbg.cs
new file mode 100644
index 000000000..d9595d530
--- /dev/null
+++ b/crypto/src/crypto/prng/drbg/CtrSP800Drbg.cs
@@ -0,0 +1,494 @@
+using System;
+
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Crypto.Prng.Drbg
+{
+ /**
+ * A SP800-90A CTR DRBG.
+ */
+ public class CTRSP800Drbg: SP80090Drbg
+ {
+ private static readonly long TDEA_RESEED_MAX = 1L << (32 - 1);
+ private static readonly long AES_RESEED_MAX = 1L << (48 - 1);
+ private static readonly int TDEA_MAX_BITS_REQUEST = 1 << (13 - 1);
+ private static readonly int AES_MAX_BITS_REQUEST = 1 << (19 - 1);
+
+ private IEntropySource _entropySource;
+ private IBlockCipher _engine;
+ private int _keySizeInBits;
+ private int _seedLength;
+ private int _securityStrength;
+
+ // internal state
+ private byte[] _Key;
+ private byte[] _V;
+ private long _reseedCounter = 0;
+ private bool _isTDEA = false;
+
+ /**
+ * Construct a SP800-90A CTR DRBG.
+ * <p>
+ * Minimum entropy requirement is the security strength requested.
+ * </p>
+ * @param engine underlying block cipher to use to support DRBG
+ * @param keySizeInBits size of the key to use with the block cipher.
+ * @param securityStrength security strength required (in bits)
+ * @param entropySource source of entropy to use for seeding/reseeding.
+ * @param personalizationString personalization string to distinguish this DRBG (may be null).
+ * @param nonce nonce to further distinguish this DRBG (may be null).
+ */
+ public CTRSP800Drbg(IBlockCipher engine, int keySizeInBits, int securityStrength, IEntropySource entropySource, byte[] personalizationString, byte[] nonce)
+ {
+ _entropySource = entropySource;
+ _engine = engine;
+
+ _keySizeInBits = keySizeInBits;
+ _securityStrength = securityStrength;
+ _seedLength = keySizeInBits + engine.getBlockSize() * 8;
+ _isTDEA = isTDEA(engine);
+
+ 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");
+ }
+
+ byte[] entropy = getEntropy(); // Get_entropy_input
+
+ CTR_DRBG_Instantiate_algorithm(entropy, nonce, personalizationString);
+ }
+
+ private void CTR_DRBG_Instantiate_algorithm(byte[] entropy, byte[] nonce,
+ byte[] personalisationString)
+ {
+ byte[] seedMaterial = Arrays.Concatenate(entropy, nonce, personalisationString);
+ byte[] seed = Block_Cipher_df(seedMaterial, _seedLength);
+
+ int outlen = _engine.getBlockSize();
+
+ _Key = new byte[(_keySizeInBits + 7) / 8];
+ _V = new byte[outlen];
+
+ // _Key & _V are modified by this call
+ CTR_DRBG_Update(seed, _Key, _V);
+
+ _reseedCounter = 1;
+ }
+
+ private void CTR_DRBG_Update(byte[] seed, byte[] key, byte[] v)
+ {
+ byte[] temp = new byte[seed.Length];
+ byte[] outputBlock = new byte[_engine.getBlockSize()];
+
+ int i=0;
+ int outLen = _engine.getBlockSize();
+
+ _engine.init(true, new KeyParameter(expandKey(key)));
+ while (i*outLen < seed.Length)
+ {
+ addOneTo(v);
+ _engine.processBlock(v, 0, outputBlock, 0);
+
+ int bytesToCopy = ((temp.Length - i * outLen) > outLen)
+ ? outLen : (temp.Length - i * outLen);
+
+ Array.Copy(outputBlock, 0, temp, i * outLen, bytesToCopy);
+ ++i;
+ }
+
+ XOR(temp, seed, temp, 0);
+
+ Array.Copy(temp, 0, key, 0, key.Length);
+ Array.Copy(temp, key.Length, v, 0, v.Length);
+ }
+
+ private void CTR_DRBG_Reseed_algorithm(byte[] additionalInput)
+ {
+ byte[] seedMaterial = Arrays.Concatenate(getEntropy(), additionalInput);
+
+ seedMaterial = Block_Cipher_df(seedMaterial, _seedLength);
+
+ CTR_DRBG_Update(seedMaterial, _Key, _V);
+
+ _reseedCounter = 1;
+ }
+
+ 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[i+bOff]);
+ }
+ }
+
+ private void addOneTo(byte[] longer)
+ {
+ int carry = 1;
+ for (int i = 1; i <= longer.Length; i++) // warning
+ {
+ int res = (longer[longer.Length - i] & 0xff) + carry;
+ carry = (res > 0xff) ? 1 : 0;
+ longer[longer.Length - i] = (byte)res;
+ }
+ }
+
+ private byte[] getEntropy()
+ {
+ byte[] entropy = _entropySource.getEntropy();
+ if (entropy.Length < (_securityStrength + 7) / 8)
+ {
+ throw new IllegalStateException("Insufficient entropy provided by entropy source");
+ }
+ return entropy;
+ }
+
+ // -- Internal state migration ---
+
+ private static final byte[] K_BITS = Hex.decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F");
+
+ // 1. If (number_of_bits_to_return > max_number_of_bits), then return an
+ // ERROR_FLAG.
+ // 2. L = len (input_string)/8.
+ // 3. N = number_of_bits_to_return/8.
+ // Comment: L is the bitstring represention of
+ // the integer resulting from len (input_string)/8.
+ // L shall be represented as a 32-bit integer.
+ //
+ // Comment : N is the bitstring represention of
+ // the integer resulting from
+ // number_of_bits_to_return/8. N shall be
+ // represented as a 32-bit integer.
+ //
+ // 4. S = L || N || input_string || 0x80.
+ // 5. While (len (S) mod outlen)
+ // Comment : Pad S with zeros, if necessary.
+ // 0, S = S || 0x00.
+ //
+ // Comment : Compute the starting value.
+ // 6. temp = the Null string.
+ // 7. i = 0.
+ // 8. K = Leftmost keylen bits of 0x00010203...1D1E1F.
+ // 9. While len (temp) < keylen + outlen, do
+ //
+ // IV = i || 0outlen - len (i).
+ //
+ // 9.1
+ //
+ // temp = temp || BCC (K, (IV || S)).
+ //
+ // 9.2
+ //
+ // i = i + 1.
+ //
+ // 9.3
+ //
+ // Comment : i shall be represented as a 32-bit
+ // integer, i.e., len (i) = 32.
+ //
+ // Comment: The 32-bit integer represenation of
+ // i is padded with zeros to outlen bits.
+ //
+ // Comment: Compute the requested number of
+ // bits.
+ //
+ // 10. K = Leftmost keylen bits of temp.
+ //
+ // 11. X = Next outlen bits of temp.
+ //
+ // 12. temp = the Null string.
+ //
+ // 13. While len (temp) < number_of_bits_to_return, do
+ //
+ // 13.1 X = Block_Encrypt (K, X).
+ //
+ // 13.2 temp = temp || X.
+ //
+ // 14. requested_bits = Leftmost number_of_bits_to_return of temp.
+ //
+ // 15. Return SUCCESS and requested_bits.
+ private byte[] Block_Cipher_df(byte[] inputString, int bitLength)
+ {
+ int outLen = _engine.getBlockSize();
+ int L = inputString.Length; // already in bytes
+ int N = bitLength / 8;
+ // 4 S = L || N || inputstring || 0x80
+ int sLen = 4 + 4 + L + 1;
+ int blockLen = ((sLen + outLen - 1) / outLen) * outLen;
+ byte[] S = new byte[blockLen];
+ copyIntToByteArray(S, L, 0);
+ copyIntToByteArray(S, N, 4);
+ Array.Copy(inputString, 0, S, 8, L);
+ S[8 + L] = (byte)0x80;
+ // S already padded with zeros
+
+ byte[] temp = new byte[_keySizeInBits / 8 + outLen];
+ byte[] bccOut = new byte[outLen];
+
+ byte[] IV = new byte[outLen];
+
+ int i = 0;
+ byte[] K = new byte[_keySizeInBits / 8];
+ Array.Copy(K_BITS, 0, K, 0, K.Length);
+
+ while (i*outLen*8 < _keySizeInBits + outLen *8)
+ {
+ copyIntToByteArray(IV, i, 0);
+ BCC(bccOut, K, IV, S);
+
+ int bytesToCopy = ((temp.Length - i * outLen) > outLen)
+ ? outLen
+ : (temp.Length - i * outLen);
+
+ Array.Copy(bccOut, 0, temp, i * outLen, bytesToCopy);
+ ++i;
+ }
+
+ byte[] X = new byte[outLen];
+ Array.Copy(temp, 0, K, 0, K.Length);
+ Array.Copy(temp, K.Length, X, 0, X.Length);
+
+ temp = new byte[bitLength / 2];
+
+ i = 0;
+ _engine.init(true, new KeyParameter(expandKey(K)));
+
+ while (i * outLen < temp.Length)
+ {
+ _engine.processBlock(X, 0, X, 0);
+
+ int bytesToCopy = ((temp.Length - i * outLen) > outLen)
+ ? outLen
+ : (temp.Length - i * outLen);
+
+ Array.Copy(X, 0, temp, i * outLen, bytesToCopy);
+ i++;
+ }
+
+ return temp;
+ }
+
+ /*
+ * 1. chaining_value = 0^outlen
+ * . Comment: Set the first chaining value to outlen zeros.
+ * 2. n = len (data)/outlen.
+ * 3. Starting with the leftmost bits of data, split the data into n blocks of outlen bits
+ * each, forming block(1) to block(n).
+ * 4. For i = 1 to n do
+ * 4.1 input_block = chaining_value ^ block(i) .
+ * 4.2 chaining_value = Block_Encrypt (Key, input_block).
+ * 5. output_block = chaining_value.
+ * 6. Return output_block.
+ */
+ private void BCC(byte[] bccOut, byte[] k, byte[] iV, byte[] data)
+ {
+ int outlen = _engine.getBlockSize();
+ byte[] chainingValue = new byte[outlen]; // initial values = 0
+ int n = data.Length / outlen;
+
+ byte[] inputBlock = new byte[outlen];
+
+ _engine.init(true, new KeyParameter(expandKey(k)));
+
+ _engine.processBlock(iV, 0, chainingValue, 0);
+
+ for (int i = 0; i < n; i++)
+ {
+ XOR(inputBlock, chainingValue, data, i*outlen);
+ _engine.processBlock(inputBlock, 0, chainingValue, 0);
+ }
+
+ Array.Copy(chainingValue, 0, bccOut, 0, bccOut.Length);
+ }
+
+ private void copyIntToByteArray(byte[] buf, int value, int offSet)
+ {
+ buf[offSet + 0] = ((byte)(value >> 24));
+ buf[offSet + 1] = ((byte)(value >> 16));
+ buf[offSet + 2] = ((byte)(value >> 8));
+ buf[offSet + 3] = ((byte)(value));
+ }
+
+ /**
+ * Return the block size (in bits) of the DRBG.
+ *
+ * @return the number of bits produced on each internal round of the DRBG.
+ */
+ public int BlockSize
+ {
+ get {
+ return _V.Length * 8;
+ }
+ }
+
+ /**
+ * Populate a passed in array with random data.
+ *
+ * @param output output array for generated bits.
+ * @param additionalInput additional input to be added to the DRBG in this step.
+ * @param predictionResistant true if a reseed should be forced, false otherwise.
+ *
+ * @return number of bits generated, -1 if a reseed required.
+ */
+ public int Generate(byte[] output, byte[] additionalInput, bool predictionResistant)
+ {
+ if (_isTDEA)
+ {
+ if (_reseedCounter > TDEA_RESEED_MAX)
+ {
+ return -1;
+ }
+
+ if (Utils.isTooLarge(output, TDEA_MAX_BITS_REQUEST / 8))
+ {
+ throw new ArgumentException("Number of bits per request limited to " + TDEA_MAX_BITS_REQUEST);
+ }
+ }
+ else
+ {
+ if (_reseedCounter > AES_RESEED_MAX)
+ {
+ return -1;
+ }
+
+ if (Utils.isTooLarge(output, AES_MAX_BITS_REQUEST / 8))
+ {
+ throw new ArgumentException("Number of bits per request limited to " + AES_MAX_BITS_REQUEST);
+ }
+ }
+
+ if (predictionResistant)
+ {
+ CTR_DRBG_Reseed_algorithm(additionalInput);
+ additionalInput = null;
+ }
+
+ if (additionalInput != null)
+ {
+ additionalInput = Block_Cipher_df(additionalInput, _seedLength);
+ CTR_DRBG_Update(additionalInput, _Key, _V);
+ }
+ else
+ {
+ additionalInput = new byte[_seedLength];
+ }
+
+ byte[] tmp = new byte[_V.Length];
+
+ _engine.init(true, new KeyParameter(expandKey(_Key)));
+
+ for (int i = 0; i <= output.Length / tmp.Length; i++)
+ {
+ int bytesToCopy = ((output.Length - i * tmp.Length) > tmp.Length)
+ ? tmp.Length
+ : (output.Length - i * _V.Length);
+
+ if (bytesToCopy != 0)
+ {
+ addOneTo(_V);
+
+ _engine.processBlock(_V, 0, tmp, 0);
+
+ Array.Copy(tmp, 0, output, i * tmp.Length, bytesToCopy);
+ }
+ }
+
+ CTR_DRBG_Update(additionalInput, _Key, _V);
+
+ _reseedCounter++;
+
+ return output.Length * 8;
+ }
+
+ /**
+ * Reseed the DRBG.
+ *
+ * @param additionalInput additional input to be added to the DRBG in this step.
+ */
+ public void Reseed(byte[] additionalInput)
+ {
+ CTR_DRBG_Reseed_algorithm(additionalInput);
+ }
+
+ private boolean isTDEA(BlockCipher cipher)
+ {
+ return cipher.AlgorithmName.Equals("DESede") || cipher.AlgorithmName.Equals("TDEA");
+ }
+
+ private int getMaxSecurityStrength(BlockCipher cipher, int keySizeInBits)
+ {
+ if (isTDEA(cipher) && keySizeInBits == 168)
+ {
+ return 112;
+ }
+ if (cipher.AlgorithmName.Equals("AES"))
+ {
+ return keySizeInBits;
+ }
+
+ return -1;
+ }
+
+ byte[] expandKey(byte[] key)
+ {
+ if (_isTDEA)
+ {
+ // expand key to 192 bits.
+ byte[] tmp = new byte[24];
+
+ padKey(key, 0, tmp, 0);
+ padKey(key, 7, tmp, 8);
+ padKey(key, 14, tmp, 16);
+
+ return tmp;
+ }
+ else
+ {
+ return key;
+ }
+ }
+
+ /**
+ * Pad out a key for TDEA, setting odd parity for each byte.
+ *
+ * @param keyMaster
+ * @param keyOff
+ * @param tmp
+ * @param tmpOff
+ */
+ private void padKey(byte[] keyMaster, int keyOff, byte[] tmp, int tmpOff)
+ {
+ tmp[tmpOff + 0] = (byte)(keyMaster[keyOff + 0] & 0xfe);
+ tmp[tmpOff + 1] = (byte)((keyMaster[keyOff + 0] << 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);
+
+ for (int i = tmpOff; i <= tmpOff + 7; i++)
+ {
+ int b = tmp[i];
+ tmp[i] = (byte)((b & 0xfe) |
+ ((((b >> 1) ^
+ (b >> 2) ^
+ (b >> 3) ^
+ (b >> 4) ^
+ (b >> 5) ^
+ (b >> 6) ^
+ (b >> 7)) ^ 0x01) & 0x01));
+ }
+ }
+ }
+}
diff --git a/crypto/src/crypto/prng/drbg/HMacSP800Drbg.cs b/crypto/src/crypto/prng/drbg/HMacSP800Drbg.cs
new file mode 100644
index 000000000..78a710097
--- /dev/null
+++ b/crypto/src/crypto/prng/drbg/HMacSP800Drbg.cs
@@ -0,0 +1,196 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Prng.Drbg
+{
+ /**
+ * A SP800-90A HMAC DRBG.
+ */
+ public class HMacSP800Drbg: SP80090Drbg
+ {
+ private readonly static long RESEED_MAX = 1L << (48 - 1);
+ private readonly static int MAX_BITS_REQUEST = 1 << (19 - 1);
+
+ private byte[] _K;
+ private byte[] _V;
+ private long _reseedCounter;
+ private EntropySource _entropySource;
+ private Mac _hMac;
+ private int _securityStrength;
+
+ /**
+ * Construct a SP800-90A Hash DRBG.
+ * <p>
+ * Minimum entropy requirement is the security strength requested.
+ * </p>
+ * @param hMac Hash MAC to base the DRBG on.
+ * @param securityStrength security strength required (in bits)
+ * @param entropySource source of entropy to use for seeding/reseeding.
+ * @param personalizationString personalization string to distinguish this DRBG (may be null).
+ * @param nonce nonce to further distinguish this DRBG (may be null).
+ */
+ public HMacSP800Drbg(IMac hMac, int securityStrength, IEntropySource entropySource, byte[] personalizationString, byte[] nonce)
+ {
+ if (securityStrength > Utils.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");
+ }
+
+ _securityStrength = securityStrength;
+ _entropySource = entropySource;
+ _hMac = hMac;
+
+ byte[] entropy = getEntropy();
+ byte[] seedMaterial = Arrays.Concatenate(entropy, nonce, personalizationString);
+
+ _K = new byte[hMac.GetMacSize()];
+ _V = new byte[_K.Length];
+ Arrays.fill(_V, (byte)1);
+
+ hmac_DRBG_Update(seedMaterial);
+
+ _reseedCounter = 1;
+ }
+
+ private void hmac_DRBG_Update(byte[] seedMaterial)
+ {
+ hmac_DRBG_Update_Func(seedMaterial, (byte)0x00);
+ if (seedMaterial != null)
+ {
+ hmac_DRBG_Update_Func(seedMaterial, (byte)0x01);
+ }
+ }
+
+ private void hmac_DRBG_Update_Func(byte[] seedMaterial, byte vValue)
+ {
+ _hMac.Init(new KeyParameter(_K));
+
+ _hMac.BlockUpdate(_V, 0, _V.Length);
+ _hMac.Update(vValue);
+
+ if (seedMaterial != null)
+ {
+ _hMac.update(seedMaterial, 0, seedMaterial.Length);
+ }
+
+ _hMac.DoFinal(_K, 0);
+
+ _hMac.Init(new KeyParameter(_K));
+ _hMac.BlockUpdate(_V, 0, _V.Length);
+
+ _hMac.DoFinal(_V, 0);
+ }
+
+ /**
+ * Return the block size (in bits) of the DRBG.
+ *
+ * @return the number of bits produced on each round of the DRBG.
+ */
+ public int BlockSize
+ {
+ get {
+ return _V.Length * 8;
+ }
+ }
+
+ /**
+ * Populate a passed in array with random data.
+ *
+ * @param output output array for generated bits.
+ * @param additionalInput additional input to be added to the DRBG in this step.
+ * @param predictionResistant true if a reseed should be forced, false otherwise.
+ *
+ * @return number of bits generated, -1 if a reseed required.
+ */
+ public int Generate(byte[] output, byte[] additionalInput, boolean predictionResistant)
+ {
+ int numberOfBits = output.Length * 8;
+
+ if (numberOfBits > MAX_BITS_REQUEST)
+ {
+ throw new IllegalArgumentException("Number of bits per request limited to " + MAX_BITS_REQUEST);
+ }
+
+ if (_reseedCounter > RESEED_MAX)
+ {
+ return -1;
+ }
+
+ if (predictionResistant)
+ {
+ reseed(additionalInput);
+ additionalInput = null;
+ }
+
+ // 2.
+ if (additionalInput != null)
+ {
+ hmac_DRBG_Update(additionalInput);
+ }
+
+ // 3.
+ byte[] rv = new byte[output.Length];
+
+ int m = output.Length / _V.Length;
+
+ _hMac.Init(new KeyParameter(_K));
+
+ for (int i = 0; i < m; i++)
+ {
+ _hMac.BlockUpdate(_V, 0, _V.Length);
+ _hMac.DoFinal(_V, 0);
+
+ Array.Copy(_V, 0, rv, i * _V.Length, _V.Length);
+ }
+
+ if (m * _V.Length < rv.Length)
+ {
+ _hMac.BlockUpdate(_V, 0, _V.Length);
+ _hMac.DoFinal(_V, 0);
+
+ Array.Copy(_V, 0, rv, m * _V.Length, rv.Length - (m * _V.Length));
+ }
+
+ hmac_DRBG_Update(additionalInput);
+
+ _reseedCounter++;
+
+ Array.Copy(rv, 0, output, 0, output.Length);
+
+ return numberOfBits;
+ }
+
+ /**
+ * Reseed the DRBG.
+ *
+ * @param additionalInput additional input to be added to the DRBG in this step.
+ */
+ public void Reseed(byte[] additionalInput)
+ {
+ byte[] entropy = getEntropy();
+ byte[] seedMaterial = Arrays.Concatenate(entropy, additionalInput);
+
+ hmac_DRBG_Update(seedMaterial);
+
+ _reseedCounter = 1;
+ }
+
+ private byte[] getEntropy()
+ {
+ byte[] entropy = _entropySource.GetEntropy();
+
+ if (entropy.Length < (_securityStrength + 7) / 8)
+ {
+ throw new IllegalStateException("Insufficient entropy provided by entropy source");
+ }
+ return entropy;
+ }
+ }
+}
diff --git a/crypto/src/crypto/prng/drbg/HashSP800Drbg.cs b/crypto/src/crypto/prng/drbg/HashSP800Drbg.cs
new file mode 100644
index 000000000..868778817
--- /dev/null
+++ b/crypto/src/crypto/prng/drbg/HashSP800Drbg.cs
@@ -0,0 +1,295 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Prng.Drbg
+{
+ /**
+ * A SP800-90A Hash DRBG.
+ */
+ public class HashSP800Drbg: SP80090Drbg
+ {
+ private readonly static byte[] ONE = { 0x01 };
+
+ private readonly static long RESEED_MAX = 1L << (48 - 1);
+ private readonly static int MAX_BITS_REQUEST = 1 << (19 - 1);
+
+ private static readonly IDictionary seedlens = Platform.CreateHashtable();
+
+ static HashSP800Drbg()
+ {
+ 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);
+
+ }
+
+ private IDigest _digest;
+ private byte[] _V;
+ private byte[] _C;
+ private long _reseedCounter;
+ private IEntropySource _entropySource;
+ private int _securityStrength;
+ private int _seedLength;
+
+ /**
+ * Construct a SP800-90A Hash DRBG.
+ * <p>
+ * Minimum entropy requirement is the security strength requested.
+ * </p>
+ * @param digest source digest to use for DRB stream.
+ * @param securityStrength security strength required (in bits)
+ * @param entropySource source of entropy to use for seeding/reseeding.
+ * @param personalizationString personalization string to distinguish this DRBG (may be null).
+ * @param nonce nonce to further distinguish this DRBG (may be null).
+ */
+ public HashSP800Drbg(IDigest digest, int securityStrength, IEntropySource entropySource, byte[] personalizationString, byte[] nonce)
+ {
+ if (securityStrength > Utils.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");
+ }
+
+ _digest = digest;
+ _entropySource = entropySource;
+ _securityStrength = securityStrength;
+ _seedLength = ((Integer)seedlens.get(digest.getAlgorithmName())).intValue();
+
+ // 1. seed_material = entropy_input || nonce || personalization_string.
+ // 2. seed = Hash_df (seed_material, seedlen).
+ // 3. V = seed.
+ // 4. C = Hash_df ((0x00 || V), seedlen). Comment: Preceed V with a byte
+ // of zeros.
+ // 5. reseed_counter = 1.
+ // 6. Return V, C, and reseed_counter as the initial_working_state
+
+ byte[] entropy = getEntropy();
+ byte[] seedMaterial = Arrays.Concatenate(entropy, nonce, personalizationString);
+ byte[] seed = Utils.hash_df(_digest, seedMaterial, _seedLength);
+
+ _V = seed;
+ byte[] subV = new byte[_V.Length + 1];
+ Array.Copy(_V, 0, subV, 1, _V.Length);
+ _C = Utils.hash_df(_digest, subV, _seedLength);
+
+ _reseedCounter = 1;
+ }
+
+ /**
+ * Return the block size (in bits) of the DRBG.
+ *
+ * @return the number of bits produced on each internal round of the DRBG.
+ */
+ public int BlockSize
+ {
+ get {
+ return _digest.GetDigestSize () * 8;
+ }
+ }
+
+ /**
+ * Populate a passed in array with random data.
+ *
+ * @param output output array for generated bits.
+ * @param additionalInput additional input to be added to the DRBG in this step.
+ * @param predictionResistant true if a reseed should be forced, false otherwise.
+ *
+ * @return number of bits generated, -1 if a reseed required.
+ */
+ public int Generate(byte[] output, byte[] additionalInput, bool predictionResistant)
+ {
+ // 1. If reseed_counter > reseed_interval, then return an indication that a
+ // reseed is required.
+ // 2. If (additional_input != Null), then do
+ // 2.1 w = Hash (0x02 || V || additional_input).
+ // 2.2 V = (V + w) mod 2^seedlen
+ // .
+ // 3. (returned_bits) = Hashgen (requested_number_of_bits, V).
+ // 4. H = Hash (0x03 || V).
+ // 5. V = (V + H + C + reseed_counter) mod 2^seedlen
+ // .
+ // 6. reseed_counter = reseed_counter + 1.
+ // 7. Return SUCCESS, returned_bits, and the new values of V, C, and
+ // reseed_counter for the new_working_state.
+ int numberOfBits = output.Length*8;
+
+ if (numberOfBits > MAX_BITS_REQUEST)
+ {
+ throw new ArgumentException("Number of bits per request limited to " + MAX_BITS_REQUEST);
+ }
+
+ if (_reseedCounter > RESEED_MAX)
+ {
+ return -1;
+ }
+
+ if (predictionResistant)
+ {
+ reseed(additionalInput);
+ additionalInput = null;
+ }
+
+ // 2.
+ if (additionalInput != null)
+ {
+ byte[] newInput = new byte[1 + _V.Length + additionalInput.Length];
+ newInput[0] = 0x02;
+ Array.Copy(_V, 0, newInput, 1, _V.Length);
+ // TODO: inOff / inLength
+ Array.Copy(additionalInput, 0, newInput, 1 + _V.Length, additionalInput.Length);
+ byte[] w = hash(newInput);
+
+ addTo(_V, w);
+ }
+
+ // 3.
+ byte[] rv = hashgen(_V, numberOfBits);
+
+ // 4.
+ byte[] subH = new byte[_V.Length + 1];
+ Array.Copy(_V, 0, subH, 1, _V.Length);
+ subH[0] = 0x03;
+
+ byte[] H = hash(subH);
+
+ // 5.
+ addTo(_V, H);
+ addTo(_V, _C);
+ byte[] c = new byte[4];
+ c[0] = (byte)(_reseedCounter >> 24);
+ c[1] = (byte)(_reseedCounter >> 16);
+ c[2] = (byte)(_reseedCounter >> 8);
+ c[3] = (byte)_reseedCounter;
+
+ addTo(_V, c);
+
+ _reseedCounter++;
+
+ Array.Copy(rv, 0, output, 0, output.Length);
+
+ return numberOfBits;
+ }
+
+ private byte[] getEntropy()
+ {
+ byte[] entropy = _entropySource.getEntropy();
+ if (entropy.Length < (_securityStrength + 7) / 8)
+ {
+ throw new IllegalStateException("Insufficient entropy provided by entropy source");
+ }
+ return entropy;
+ }
+
+ // this will always add the shorter length byte array mathematically to the
+ // longer length byte array.
+ // be careful....
+ private void addTo(byte[] longer, byte[] shorter)
+ {
+ int carry = 0;
+ for (int i=1;i <= shorter.Length; i++) // warning
+ {
+ int res = (longer[longer.Length-i] & 0xff) + (shorter[shorter.Length-i] & 0xff) + carry;
+ carry = (res > 0xff) ? 1 : 0;
+ longer[longer.Length-i] = (byte)res;
+ }
+
+ for (int i=shorter.Length+1;i <= longer.Length; i++) // warning
+ {
+ int res = (longer[longer.Length-i] & 0xff) + carry;
+ carry = (res > 0xff) ? 1 : 0;
+ longer[longer.Length-i] = (byte)res;
+ }
+ }
+
+ /**
+ * Reseed the DRBG.
+ *
+ * @param additionalInput additional input to be added to the DRBG in this step.
+ */
+ public void Reseed(byte[] additionalInput)
+ {
+ // 1. seed_material = 0x01 || V || entropy_input || additional_input.
+ //
+ // 2. seed = Hash_df (seed_material, seedlen).
+ //
+ // 3. V = seed.
+ //
+ // 4. C = Hash_df ((0x00 || V), seedlen).
+ //
+ // 5. reseed_counter = 1.
+ //
+ // 6. Return V, C, and reseed_counter for the new_working_state.
+ //
+ // Comment: Precede with a byte of all zeros.
+ byte[] entropy = getEntropy();
+ byte[] seedMaterial = Arrays.Concatenate(ONE, _V, entropy, additionalInput);
+ byte[] seed = Utils.hash_df(_digest, seedMaterial, _seedLength);
+
+ _V = seed;
+ byte[] subV = new byte[_V.Length + 1];
+ subV[0] = 0x00;
+ Array.Copy(_V, 0, subV, 1, _V.Length);
+ _C = Utils.hash_df(_digest, subV, _seedLength);
+
+ _reseedCounter = 1;
+ }
+
+ private byte[] hash(byte[] input)
+ {
+ byte[] hash = new byte[_digest.GetDigestSize()];
+ doHash(input, hash);
+ return hash;
+ }
+
+ private void doHash(byte[] input, byte[] output)
+ {
+ _digest.BlockUpdate(input, 0, input.Length);
+ _digest.DoFinal(output, 0);
+ }
+
+ // 1. m = [requested_number_of_bits / outlen]
+ // 2. data = V.
+ // 3. W = the Null string.
+ // 4. For i = 1 to m
+ // 4.1 wi = Hash (data).
+ // 4.2 W = W || wi.
+ // 4.3 data = (data + 1) mod 2^seedlen
+ // .
+ // 5. returned_bits = Leftmost (requested_no_of_bits) bits of W.
+ private byte[] hashgen(byte[] input, int lengthInBits)
+ {
+ int digestSize = _digest.GetDigestSize();
+ int m = (lengthInBits / 8) / digestSize;
+
+ byte[] data = new byte[input.Length];
+ Array.Copy(input, 0, data, 0, input.Length);
+
+ byte[] W = new byte[lengthInBits / 8];
+
+ byte[] dig = new byte[_digest.GetDigestSize()];
+ for (int i = 0; i <= m; i++)
+ {
+ doHash(data, dig);
+
+ int bytesToCopy = ((W.Length - i * dig.Length) > dig.Length)
+ ? dig.Length
+ : (W.Length - i * dig.Length);
+ Array.Copy(dig, 0, W, i * dig.Length, bytesToCopy);
+
+ addTo(data, ONE);
+ }
+
+ return W;
+ }
+ }
+}
diff --git a/crypto/src/crypto/prng/drbg/SP80090Drbg.cs b/crypto/src/crypto/prng/drbg/SP80090Drbg.cs
new file mode 100644
index 000000000..c39cf365f
--- /dev/null
+++ b/crypto/src/crypto/prng/drbg/SP80090Drbg.cs
@@ -0,0 +1,33 @@
+namespace Org.BouncyCastle.Crypto.Prng.Drbg
+{
+ /**
+ * Interface to SP800-90A deterministic random bit generators.
+ */
+ public interface SP80090Drbg
+ {
+ /**
+ * Return the block size of the DRBG.
+ *
+ * @return the block size (in bits) produced by each round of the DRBG.
+ */
+ int BlockSize { get; }
+
+ /**
+ * Populate a passed in array with random data.
+ *
+ * @param output output array for generated bits.
+ * @param additionalInput additional input to be added to the DRBG in this step.
+ * @param predictionResistant true if a reseed should be forced, false otherwise.
+ *
+ * @return number of bits generated, -1 if a reseed required.
+ */
+ int Generate(byte[] output, byte[] additionalInput, bool predictionResistant);
+
+ /**
+ * Reseed the DRBG.
+ *
+ * @param additionalInput additional input to be added to the DRBG in this step.
+ */
+ void Reseed(byte[] additionalInput);
+ }
+}
diff --git a/crypto/src/crypto/prng/drbg/Utils.cs b/crypto/src/crypto/prng/drbg/Utils.cs
new file mode 100644
index 000000000..b5f3b5830
--- /dev/null
+++ b/crypto/src/crypto/prng/drbg/Utils.cs
@@ -0,0 +1,103 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Prng.Drbg
+{
+ internal class Utils
+ {
+ private static readonly IDictionary maxSecurityStrengths = Platform.CreateHashtable();
+
+ static Utils()
+ {
+ maxSecurityStrengths.put("SHA-1", 128);
+
+ maxSecurityStrengths.put("SHA-224", 192);
+ maxSecurityStrengths.put("SHA-256", 256);
+ maxSecurityStrengths.put("SHA-384", 256);
+ maxSecurityStrengths.put("SHA-512", 256);
+
+ maxSecurityStrengths.put("SHA-512/224", 192);
+ maxSecurityStrengths.put("SHA-512/256", 256);
+ }
+
+ internal static int getMaxSecurityStrength(IDigest d)
+ {
+ return (int)maxSecurityStrengths[d.AlgorithmName];
+ }
+
+ internal static int getMaxSecurityStrength(IMac m)
+ {
+ String name = m.getAlgorithmName();
+
+ return (int)maxSecurityStrengths[name.substring(0, name.indexOf("/"))];
+ }
+
+ /**
+ * Used by both Dual EC and Hash.
+ */
+ internal static byte[] hash_df(Digest digest, byte[] seedMaterial, int seedLength)
+ {
+ // 1. temp = the Null string.
+ // 2. .
+ // 3. counter = an 8-bit binary value representing the integer "1".
+ // 4. For i = 1 to len do
+ // Comment : In step 4.1, no_of_bits_to_return
+ // is used as a 32-bit string.
+ // 4.1 temp = temp || Hash (counter || no_of_bits_to_return ||
+ // input_string).
+ // 4.2 counter = counter + 1.
+ // 5. requested_bits = Leftmost (no_of_bits_to_return) of temp.
+ // 6. Return SUCCESS and requested_bits.
+ byte[] temp = new byte[(seedLength + 7) / 8];
+
+ int len = temp.Length / digest.getDigestSize();
+ int counter = 1;
+
+ byte[] dig = new byte[digest.getDigestSize()];
+
+ for (int i = 0; i <= len; i++)
+ {
+ digest.Update((byte)counter);
+
+ 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(dig, 0);
+
+ int bytesToCopy = ((temp.Length - i * dig.Length) > dig.Length)
+ ? dig.Length
+ : (temp.Length - i * dig.Length);
+ Array.Copy(dig, 0, temp, i * dig.Length, bytesToCopy);
+
+ counter++;
+ }
+
+ // do a left shift to get rid of excess bits.
+ if (seedLength % 8 != 0)
+ {
+ int shift = 8 - (seedLength % 8);
+ int carry = 0;
+
+ for (int i = 0; i != temp.Length; i++)
+ {
+ uint b = temp[i] & 0xff;
+ temp[i] = (byte)((b >> shift) | (carry << (8 - shift)));
+ carry = b;
+ }
+ }
+
+ return temp;
+ }
+
+ internal static boolean isTooLarge(byte[] bytes, int maxBytes)
+ {
+ return bytes != null && bytes.Length > maxBytes;
+ }
+ }
+}
|