1 package org.bouncycastle.crypto.prng.drbg; 2 3 import org.bouncycastle.crypto.Mac; 4 import org.bouncycastle.crypto.params.KeyParameter; 5 import org.bouncycastle.crypto.prng.EntropySource; 6 import org.bouncycastle.util.Arrays; 7 8 /** 9 * A SP800-90A HMAC DRBG. 10 */ 11 public class HMacSP800DRBG 12 implements SP80090DRBG 13 { 14 private final static long RESEED_MAX = 1L << (48 - 1); 15 private final static int MAX_BITS_REQUEST = 1 << (19 - 1); 16 17 private byte[] _K; 18 private byte[] _V; 19 private long _reseedCounter; 20 private EntropySource _entropySource; 21 private Mac _hMac; 22 private int _securityStrength; 23 24 /** 25 * Construct a SP800-90A Hash DRBG. 26 * <p> 27 * Minimum entropy requirement is the security strength requested. 28 * </p> 29 * @param hMac Hash MAC to base the DRBG on. 30 * @param securityStrength security strength required (in bits) 31 * @param entropySource source of entropy to use for seeding/reseeding. 32 * @param personalizationString personalization string to distinguish this DRBG (may be null). 33 * @param nonce nonce to further distinguish this DRBG (may be null). 34 */ HMacSP800DRBG(Mac hMac, int securityStrength, EntropySource entropySource, byte[] personalizationString, byte[] nonce)35 public HMacSP800DRBG(Mac hMac, int securityStrength, EntropySource entropySource, byte[] personalizationString, byte[] nonce) 36 { 37 if (securityStrength > Utils.getMaxSecurityStrength(hMac)) 38 { 39 throw new IllegalArgumentException("Requested security strength is not supported by the derivation function"); 40 } 41 42 if (entropySource.entropySize() < securityStrength) 43 { 44 throw new IllegalArgumentException("Not enough entropy for security strength required"); 45 } 46 47 _securityStrength = securityStrength; 48 _entropySource = entropySource; 49 _hMac = hMac; 50 51 byte[] entropy = getEntropy(); 52 byte[] seedMaterial = Arrays.concatenate(entropy, nonce, personalizationString); 53 54 _K = new byte[hMac.getMacSize()]; 55 _V = new byte[_K.length]; 56 Arrays.fill(_V, (byte)1); 57 58 hmac_DRBG_Update(seedMaterial); 59 60 _reseedCounter = 1; 61 } 62 hmac_DRBG_Update(byte[] seedMaterial)63 private void hmac_DRBG_Update(byte[] seedMaterial) 64 { 65 hmac_DRBG_Update_Func(seedMaterial, (byte)0x00); 66 if (seedMaterial != null) 67 { 68 hmac_DRBG_Update_Func(seedMaterial, (byte)0x01); 69 } 70 } 71 hmac_DRBG_Update_Func(byte[] seedMaterial, byte vValue)72 private void hmac_DRBG_Update_Func(byte[] seedMaterial, byte vValue) 73 { 74 _hMac.init(new KeyParameter(_K)); 75 76 _hMac.update(_V, 0, _V.length); 77 _hMac.update(vValue); 78 79 if (seedMaterial != null) 80 { 81 _hMac.update(seedMaterial, 0, seedMaterial.length); 82 } 83 84 _hMac.doFinal(_K, 0); 85 86 _hMac.init(new KeyParameter(_K)); 87 _hMac.update(_V, 0, _V.length); 88 89 _hMac.doFinal(_V, 0); 90 } 91 92 /** 93 * Return the block size (in bits) of the DRBG. 94 * 95 * @return the number of bits produced on each round of the DRBG. 96 */ getBlockSize()97 public int getBlockSize() 98 { 99 return _V.length * 8; 100 } 101 102 /** 103 * Populate a passed in array with random data. 104 * 105 * @param output output array for generated bits. 106 * @param additionalInput additional input to be added to the DRBG in this step. 107 * @param predictionResistant true if a reseed should be forced, false otherwise. 108 * 109 * @return number of bits generated, -1 if a reseed required. 110 */ generate(byte[] output, byte[] additionalInput, boolean predictionResistant)111 public int generate(byte[] output, byte[] additionalInput, boolean predictionResistant) 112 { 113 int numberOfBits = output.length * 8; 114 115 if (numberOfBits > MAX_BITS_REQUEST) 116 { 117 throw new IllegalArgumentException("Number of bits per request limited to " + MAX_BITS_REQUEST); 118 } 119 120 if (_reseedCounter > RESEED_MAX) 121 { 122 return -1; 123 } 124 125 if (predictionResistant) 126 { 127 reseed(additionalInput); 128 additionalInput = null; 129 } 130 131 // 2. 132 if (additionalInput != null) 133 { 134 hmac_DRBG_Update(additionalInput); 135 } 136 137 // 3. 138 byte[] rv = new byte[output.length]; 139 140 int m = output.length / _V.length; 141 142 _hMac.init(new KeyParameter(_K)); 143 144 for (int i = 0; i < m; i++) 145 { 146 _hMac.update(_V, 0, _V.length); 147 _hMac.doFinal(_V, 0); 148 149 System.arraycopy(_V, 0, rv, i * _V.length, _V.length); 150 } 151 152 if (m * _V.length < rv.length) 153 { 154 _hMac.update(_V, 0, _V.length); 155 _hMac.doFinal(_V, 0); 156 157 System.arraycopy(_V, 0, rv, m * _V.length, rv.length - (m * _V.length)); 158 } 159 160 hmac_DRBG_Update(additionalInput); 161 162 _reseedCounter++; 163 164 System.arraycopy(rv, 0, output, 0, output.length); 165 166 return numberOfBits; 167 } 168 169 /** 170 * Reseed the DRBG. 171 * 172 * @param additionalInput additional input to be added to the DRBG in this step. 173 */ reseed(byte[] additionalInput)174 public void reseed(byte[] additionalInput) 175 { 176 byte[] entropy = getEntropy(); 177 byte[] seedMaterial = Arrays.concatenate(entropy, additionalInput); 178 179 hmac_DRBG_Update(seedMaterial); 180 181 _reseedCounter = 1; 182 } 183 getEntropy()184 private byte[] getEntropy() 185 { 186 byte[] entropy = _entropySource.getEntropy(); 187 188 if (entropy.length < (_securityStrength + 7) / 8) 189 { 190 throw new IllegalStateException("Insufficient entropy provided by entropy source"); 191 } 192 return entropy; 193 } 194 } 195