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