1 package com.google.android.rappor; 2 3 // BEGIN android-changed: Removed guava dependency 4 // import com.google.common.hash.HashFunction; 5 // import com.google.common.hash.Hashing; 6 // import com.google.common.primitives.Bytes; 7 // END android-changed 8 9 import java.security.SecureRandom; 10 import java.util.Arrays; 11 import javax.annotation.concurrent.NotThreadSafe; 12 import javax.crypto.Mac; 13 import javax.crypto.spec.SecretKeySpec; 14 15 /** 16 * Deterministic Random Bit Generator based on HMAC-SHA256. 17 * 18 * Also known as: HMAC_DRBG. 19 * See http://csrc.nist.gov/publications/nistpubs/800-90A/SP800-90A.pdf for thorough specification. 20 * 21 * Reseeding is not supported. Instead, construct a new DRBG when reseeding is required. 22 * See http://csrc.nist.gov/publications/nistpubs/800-90A/SP800-90A.pdf Section 8.6.8. 23 */ 24 @NotThreadSafe 25 public class HmacDrbg { 26 // "V" from the the spec. 27 private byte[] value; 28 29 // BEGIN android-changed 30 // An instance of HMAC-SHA256 configured with "Key" from the spec. 31 // private HashFunction hmac; 32 private Mac hmac; 33 // END android-changed 34 35 // The total number of bytes that have been generated from this DRBG so far. 36 private int bytesGenerated; 37 38 // Assume maximum security strength for HMAC-256, which is 256. 39 // See: http://csrc.nist.gov/publications/nistpubs/800-90A/SP800-90A.pdf D.2 #1. 40 public static final int SECURITY_STRENGTH = 256; 41 42 /** 43 * Personalization strings should not exceed this many bytes in length. 44 * 45 * See: http://csrc.nist.gov/publications/nistpubs/800-90A/SP800-90A.pdf D.2 #7. 46 */ 47 public static final int MAX_PERSONALIZATION_STRING_LENGTH_BYTES = 160 / 8; 48 49 /** 50 * The constructor's entropyInput should contain this many high quality random bytes. 51 * HMAC_DRBG requires entropy input to be security_strength bits long, 52 * and nonce to be at least 1/2 security_strength bits long. We 53 * generate them both as a single "extra strong" entropy input. 54 * See: http://csrc.nist.gov/publications/nistpubs/800-90A/SP800-90A.pdf 55 */ 56 public static final int ENTROPY_INPUT_SIZE_BYTES = (SECURITY_STRENGTH / 8) * 3 / 2; 57 58 /** 59 * The maximum total number of bytes that can be generated from this DRBG. 60 * 61 * This is conservative releative to the suggestions in 62 * http://csrc.nist.gov/publications/nistpubs/800-90A/SP800-90A.pdf section D.2, 63 * ensuring that reseeding is never triggered (each call to Generate produces at least one byte, 64 * therefore MAX_BYTES will be reached before RESEED_INTERAL=10000 is exceeded) 65 * and simplifying the interface (so that the client need not worry about MAX_BYTES_PER_REQUEST, 66 * below. 67 */ 68 public static final int MAX_BYTES_TOTAL = 10000; 69 70 // See: http://csrc.nist.gov/publications/nistpubs/800-90A/SP800-90A.pdf D.2 #2. 71 private static final int DIGEST_NUM_BYTES = 256 / 8; 72 73 // floor(7500/8); see: http://csrc.nist.gov/publications/nistpubs/800-90A/SP800-90A.pdf D.2 #5. 74 private static final int MAX_BYTES_PER_REQUEST = 937; 75 76 private static final byte[] BYTE_ARRAY_0 = {0}; 77 private static final byte[] BYTE_ARRAY_1 = {1}; 78 HmacDrbg(byte[] entropyInput, byte[] personalizationString)79 public HmacDrbg(byte[] entropyInput, byte[] personalizationString) { 80 // HMAC_DRBG Instantiate Process 81 // See: http://csrc.nist.gov/publications/nistpubs/800-90A/SP800-90A.pdf 10.1.1.2 82 83 // 1. seed_material = entropy_input + nonce + personalization_string 84 // Note: We are using the 8.6.7 interpretation, where the entropy_input and 85 // nonce are acquired at the same time from the same source. 86 // BEGIN android-changed 87 // byte[] seedMaterial = Bytes.concat(entropyInput, emptyIfNull(personalizationString)); 88 byte[] seedMaterial = bytesConcat(entropyInput, emptyIfNull(personalizationString)); 89 // END android-changed 90 91 // 2. Key = 0x00 00...00 92 setKey(new byte[256 / 8]); 93 94 // 3. V = 0x01 01...01 95 value = new byte[DIGEST_NUM_BYTES]; 96 Arrays.fill(value, (byte) 0x01); 97 98 // 4. (Key, V) = HMAC_DRBG_Update(seed_material, Key, V) 99 hmacDrbgUpdate(seedMaterial); 100 101 bytesGenerated = 0; 102 } 103 104 /** 105 * Returns an 0-length byte array if b is null, otherwise returns b. 106 */ emptyIfNull(byte[] b)107 private static byte[] emptyIfNull(byte[] b) { 108 return b == null ? new byte[0] : b; 109 } 110 111 /** 112 * Set's the "Key" state from the spec. 113 */ setKey(byte[] key)114 private void setKey(byte[] key) { 115 // BEGIN android-changed 116 // hmac = Hashing.hmacSha256(key); 117 try { 118 hmac = Mac.getInstance("HmacSHA256"); 119 SecretKeySpec secretKey = new SecretKeySpec(key, "HmacSHA256"); 120 hmac.init(secretKey); 121 } catch (Exception e) {} 122 // END android-changed 123 } 124 125 /** 126 * Computes hmac("key" from the spec, x). 127 */ hash(byte[] x)128 private byte[] hash(byte[] x) { 129 // BEGIN android-changed 130 // return hmac.hashBytes(x).asBytes(); 131 try { 132 return hmac.doFinal(x); 133 } catch (Exception e) { 134 return null; 135 } 136 // END android-changed 137 } 138 139 /** 140 * HMAC_DRBG Update Process 141 * 142 * See: http://csrc.nist.gov/publications/nistpubs/800-90A/SP800-90A.pdf 10.1.2.2 143 */ hmacDrbgUpdate(byte[] providedData)144 private void hmacDrbgUpdate(byte[] providedData) { 145 146 // 1. K = HMAC(K, V || 0x00 || provided_data) 147 // BEGIN android-changed 148 // setKey(hash(Bytes.concat(value, BYTE_ARRAY_0, emptyIfNull(providedData)))); 149 setKey(hash(bytesConcat(value, BYTE_ARRAY_0, emptyIfNull(providedData)))); 150 // END android-changed 151 152 // 2. V = HMAC(K, V); 153 value = hash(value); 154 155 // 3. If (provided_data = Null), then return K and V. 156 if (providedData == null) { 157 return; 158 } 159 160 // 4. K = HMAC (K, V || 0x01 || provided_data). 161 // BEGIN android-changed 162 // setKey(hash(Bytes.concat(value, BYTE_ARRAY_1, providedData))); 163 setKey(hash(bytesConcat(value, BYTE_ARRAY_1, providedData))); 164 // END android-changed 165 166 // 5. V = HMAC (K, V). 167 value = hash(value); 168 } 169 170 /** 171 * HMAC_DRBG Generate Process 172 * 173 * See: http://csrc.nist.gov/publications/nistpubs/800-90A/SP800-90A.pdf 10.1.2.5 174 * 175 * We do not support additional_input, assuming it to be always null. 176 * 177 * We guarantee that reseeding is never required through the use of MAX_BYTES_TOTAL 178 * rather than RESEED_INTERVAL. 179 */ hmacDrbgGenerate(byte[] out, int start, int count)180 private void hmacDrbgGenerate(byte[] out, int start, int count) { 181 // 3. temp = Null. 182 int bytesWritten = 0; 183 184 // 4. While (len (temp) < requested_number_of_bits) do: 185 while (bytesWritten < count) { 186 // 4.1 V = HMAC (Key, V). 187 value = hash(value); 188 189 // 4.2 temp = temp || V. 190 // 5. returned_bits = Leftmost requested_number_of_bits of temp 191 int bytesToWrite = Math.min(count - bytesWritten, DIGEST_NUM_BYTES); 192 System.arraycopy(value, 0, out, start + bytesWritten, bytesToWrite); 193 bytesWritten += bytesToWrite; 194 } 195 196 // 6. (Key, V) = HMAC_DRBG_Update (additional_input, Key, V). 197 hmacDrbgUpdate(null); 198 } 199 200 /** 201 * Generates entropy byte-string suitable for use as the constructor's entropyInput. 202 * 203 * Uses SecureRandom to generate entropy. 204 */ generateEntropyInput()205 public static byte[] generateEntropyInput() { 206 byte result[] = new byte[ENTROPY_INPUT_SIZE_BYTES]; 207 new SecureRandom().nextBytes(result); 208 return result; 209 } 210 211 /** 212 * Returns the next length pseudo-random bytes. 213 */ nextBytes(int length)214 public byte[] nextBytes(int length) { 215 byte result[] = new byte[length]; 216 nextBytes(result); 217 return result; 218 } 219 220 /** 221 * Fills the output vector with pseudo-random bytes. 222 */ nextBytes(byte[] out)223 public void nextBytes(byte[] out) { 224 nextBytes(out, 0, out.length); 225 } 226 227 /** 228 * Fills out[start] through out[start+count-1] (inclusive) with pseudo-random bytes. 229 */ nextBytes(byte[] out, int start, int count)230 public void nextBytes(byte[] out, int start, int count) { 231 if (count == 0) { 232 return; 233 } 234 if (bytesGenerated + count > MAX_BYTES_TOTAL) { 235 throw new IllegalStateException("Cannot generate more than a total of " + count + " bytes."); 236 } 237 try { 238 int bytesWritten = 0; 239 while (bytesWritten < count) { 240 int bytesToWrite = Math.min(count - bytesWritten, MAX_BYTES_PER_REQUEST); 241 hmacDrbgGenerate(out, start + bytesWritten, bytesToWrite); 242 bytesWritten += bytesToWrite; 243 } 244 } finally { 245 bytesGenerated += count; 246 } 247 } 248 249 // BEGIN android-changed bytesConcat(byte[]... arrays)250 private static byte[] bytesConcat(byte[]... arrays) { 251 int length = 0; 252 for (byte[] array : arrays) { 253 length += array.length; 254 } 255 byte[] result = new byte[length]; 256 int pos = 0; 257 for (byte[] array : arrays) { 258 System.arraycopy(array, 0, result, pos, array.length); 259 pos += array.length; 260 } 261 return result; 262 } 263 // END android-changed 264 } 265