package org.bouncycastle.crypto.prng.drbg; import org.bouncycastle.crypto.Mac; import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.crypto.prng.EntropySource; import org.bouncycastle.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); } /** * 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; } }