summary refs log tree commit diff
path: root/crypto/src/crypto/prng/drbg/HashSP800Drbg.cs
diff options
context:
space:
mode:
Diffstat (limited to 'crypto/src/crypto/prng/drbg/HashSP800Drbg.cs')
-rw-r--r--crypto/src/crypto/prng/drbg/HashSP800Drbg.cs295
1 files changed, 295 insertions, 0 deletions
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; + } + } +}