diff options
Diffstat (limited to 'core/src/main/java/org/spongycastle/crypto/prng/drbg/HMacSP800DRBG.java')
-rw-r--r-- | core/src/main/java/org/spongycastle/crypto/prng/drbg/HMacSP800DRBG.java | 181 |
1 files changed, 181 insertions, 0 deletions
diff --git a/core/src/main/java/org/spongycastle/crypto/prng/drbg/HMacSP800DRBG.java b/core/src/main/java/org/spongycastle/crypto/prng/drbg/HMacSP800DRBG.java new file mode 100644 index 00000000..6d2d4247 --- /dev/null +++ b/core/src/main/java/org/spongycastle/crypto/prng/drbg/HMacSP800DRBG.java @@ -0,0 +1,181 @@ +package org.spongycastle.crypto.prng.drbg; + +import org.spongycastle.crypto.Mac; +import org.spongycastle.crypto.params.KeyParameter; +import org.spongycastle.crypto.prng.EntropySource; +import org.spongycastle.util.Arrays; + +/** + * A SP800-90A HMAC DRBG. + */ +public class HMacSP800DRBG + implements SP80090DRBG +{ + private final static long RESEED_MAX = 1L << (48 - 1); + private final static int MAX_BITS_REQUEST = 1 << (19 - 1); + + private byte[] _K; + private byte[] _V; + private long _reseedCounter; + private EntropySource _entropySource; + private Mac _hMac; + + /** + * 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(Mac hMac, int securityStrength, EntropySource entropySource, byte[] personalizationString, byte[] nonce) + { + if (securityStrength > Utils.getMaxSecurityStrength(hMac)) + { + throw new IllegalArgumentException("Requested security strength is not supported by the derivation function"); + } + + if (entropySource.entropySize() < securityStrength) + { + throw new IllegalArgumentException("Not enough entropy for security strength required"); + } + + _entropySource = entropySource; + _hMac = hMac; + + byte[] entropy = entropySource.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.update(_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.update(_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 getBlockSize() + { + 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.update(_V, 0, _V.length); + _hMac.doFinal(_V, 0); + + System.arraycopy(_V, 0, rv, i * _V.length, _V.length); + } + + if (m * _V.length < rv.length) + { + _hMac.update(_V, 0, _V.length); + _hMac.doFinal(_V, 0); + + System.arraycopy(_V, 0, rv, m * _V.length, rv.length - (m * _V.length)); + } + + hmac_DRBG_Update(additionalInput); + + _reseedCounter++; + + System.arraycopy(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 = _entropySource.getEntropy(); + byte[] seedMaterial = Arrays.concatenate(entropy, additionalInput); + + hmac_DRBG_Update(seedMaterial); + + _reseedCounter = 1; + } +} |