• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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