Home | History | Annotate | Download | only in rappor
      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 
     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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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
    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