blob: db99700a0c17caffbacd6e631448760a4cb00e39 [file] [log] [blame]
package com.google.android.rappor;
// BEGIN android-changed: Removed guava dependency
// import com.google.common.hash.HashFunction;
// import com.google.common.hash.Hashing;
// import com.google.common.primitives.Bytes;
// END android-changed
import java.security.SecureRandom;
import java.util.Arrays;
import javax.annotation.concurrent.NotThreadSafe;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
/**
* Deterministic Random Bit Generator based on HMAC-SHA256.
*
* Also known as: HMAC_DRBG.
* See http://csrc.nist.gov/publications/nistpubs/800-90A/SP800-90A.pdf for thorough specification.
*
* Reseeding is not supported. Instead, construct a new DRBG when reseeding is required.
* See http://csrc.nist.gov/publications/nistpubs/800-90A/SP800-90A.pdf Section 8.6.8.
*/
@NotThreadSafe
public class HmacDrbg {
// "V" from the the spec.
private byte[] value;
// BEGIN android-changed
// An instance of HMAC-SHA256 configured with "Key" from the spec.
// private HashFunction hmac;
private Mac hmac;
// END android-changed
// The total number of bytes that have been generated from this DRBG so far.
private int bytesGenerated;
// Assume maximum security strength for HMAC-256, which is 256.
// See: http://csrc.nist.gov/publications/nistpubs/800-90A/SP800-90A.pdf D.2 #1.
public static final int SECURITY_STRENGTH = 256;
/**
* Personalization strings should not exceed this many bytes in length.
*
* See: http://csrc.nist.gov/publications/nistpubs/800-90A/SP800-90A.pdf D.2 #7.
*/
public static final int MAX_PERSONALIZATION_STRING_LENGTH_BYTES = 160 / 8;
/**
* The constructor's entropyInput should contain this many high quality random bytes.
* HMAC_DRBG requires entropy input to be security_strength bits long,
* and nonce to be at least 1/2 security_strength bits long. We
* generate them both as a single "extra strong" entropy input.
* See: http://csrc.nist.gov/publications/nistpubs/800-90A/SP800-90A.pdf
*/
public static final int ENTROPY_INPUT_SIZE_BYTES = (SECURITY_STRENGTH / 8) * 3 / 2;
/**
* The maximum total number of bytes that can be generated from this DRBG.
*
* This is conservative releative to the suggestions in
* http://csrc.nist.gov/publications/nistpubs/800-90A/SP800-90A.pdf section D.2,
* ensuring that reseeding is never triggered (each call to Generate produces at least one byte,
* therefore MAX_BYTES will be reached before RESEED_INTERAL=10000 is exceeded)
* and simplifying the interface (so that the client need not worry about MAX_BYTES_PER_REQUEST,
* below.
*/
public static final int MAX_BYTES_TOTAL = 10000;
// See: http://csrc.nist.gov/publications/nistpubs/800-90A/SP800-90A.pdf D.2 #2.
private static final int DIGEST_NUM_BYTES = 256 / 8;
// floor(7500/8); see: http://csrc.nist.gov/publications/nistpubs/800-90A/SP800-90A.pdf D.2 #5.
private static final int MAX_BYTES_PER_REQUEST = 937;
private static final byte[] BYTE_ARRAY_0 = {0};
private static final byte[] BYTE_ARRAY_1 = {1};
public HmacDrbg(byte[] entropyInput, byte[] personalizationString) {
// HMAC_DRBG Instantiate Process
// See: http://csrc.nist.gov/publications/nistpubs/800-90A/SP800-90A.pdf 10.1.1.2
// 1. seed_material = entropy_input + nonce + personalization_string
// Note: We are using the 8.6.7 interpretation, where the entropy_input and
// nonce are acquired at the same time from the same source.
// BEGIN android-changed
// byte[] seedMaterial = Bytes.concat(entropyInput, emptyIfNull(personalizationString));
byte[] seedMaterial = bytesConcat(entropyInput, emptyIfNull(personalizationString));
// END android-changed
// 2. Key = 0x00 00...00
setKey(new byte[256 / 8]);
// 3. V = 0x01 01...01
value = new byte[DIGEST_NUM_BYTES];
Arrays.fill(value, (byte) 0x01);
// 4. (Key, V) = HMAC_DRBG_Update(seed_material, Key, V)
hmacDrbgUpdate(seedMaterial);
bytesGenerated = 0;
}
/**
* Returns an 0-length byte array if b is null, otherwise returns b.
*/
private static byte[] emptyIfNull(byte[] b) {
return b == null ? new byte[0] : b;
}
/**
* Set's the "Key" state from the spec.
*/
private void setKey(byte[] key) {
// BEGIN android-changed
// hmac = Hashing.hmacSha256(key);
try {
hmac = Mac.getInstance("HmacSHA256");
SecretKeySpec secretKey = new SecretKeySpec(key, "HmacSHA256");
hmac.init(secretKey);
} catch (Exception e) {}
// END android-changed
}
/**
* Computes hmac("key" from the spec, x).
*/
private byte[] hash(byte[] x) {
// BEGIN android-changed
// return hmac.hashBytes(x).asBytes();
try {
return hmac.doFinal(x);
} catch (Exception e) {
return null;
}
// END android-changed
}
/**
* HMAC_DRBG Update Process
*
* See: http://csrc.nist.gov/publications/nistpubs/800-90A/SP800-90A.pdf 10.1.2.2
*/
private void hmacDrbgUpdate(byte[] providedData) {
// 1. K = HMAC(K, V || 0x00 || provided_data)
// BEGIN android-changed
// setKey(hash(Bytes.concat(value, BYTE_ARRAY_0, emptyIfNull(providedData))));
setKey(hash(bytesConcat(value, BYTE_ARRAY_0, emptyIfNull(providedData))));
// END android-changed
// 2. V = HMAC(K, V);
value = hash(value);
// 3. If (provided_data = Null), then return K and V.
if (providedData == null) {
return;
}
// 4. K = HMAC (K, V || 0x01 || provided_data).
// BEGIN android-changed
// setKey(hash(Bytes.concat(value, BYTE_ARRAY_1, providedData)));
setKey(hash(bytesConcat(value, BYTE_ARRAY_1, providedData)));
// END android-changed
// 5. V = HMAC (K, V).
value = hash(value);
}
/**
* HMAC_DRBG Generate Process
*
* See: http://csrc.nist.gov/publications/nistpubs/800-90A/SP800-90A.pdf 10.1.2.5
*
* We do not support additional_input, assuming it to be always null.
*
* We guarantee that reseeding is never required through the use of MAX_BYTES_TOTAL
* rather than RESEED_INTERVAL.
*/
private void hmacDrbgGenerate(byte[] out, int start, int count) {
// 3. temp = Null.
int bytesWritten = 0;
// 4. While (len (temp) < requested_number_of_bits) do:
while (bytesWritten < count) {
// 4.1 V = HMAC (Key, V).
value = hash(value);
// 4.2 temp = temp || V.
// 5. returned_bits = Leftmost requested_number_of_bits of temp
int bytesToWrite = Math.min(count - bytesWritten, DIGEST_NUM_BYTES);
System.arraycopy(value, 0, out, start + bytesWritten, bytesToWrite);
bytesWritten += bytesToWrite;
}
// 6. (Key, V) = HMAC_DRBG_Update (additional_input, Key, V).
hmacDrbgUpdate(null);
}
/**
* Generates entropy byte-string suitable for use as the constructor's entropyInput.
*
* Uses SecureRandom to generate entropy.
*/
public static byte[] generateEntropyInput() {
byte result[] = new byte[ENTROPY_INPUT_SIZE_BYTES];
new SecureRandom().nextBytes(result);
return result;
}
/**
* Returns the next length pseudo-random bytes.
*/
public byte[] nextBytes(int length) {
byte result[] = new byte[length];
nextBytes(result);
return result;
}
/**
* Fills the output vector with pseudo-random bytes.
*/
public void nextBytes(byte[] out) {
nextBytes(out, 0, out.length);
}
/**
* Fills out[start] through out[start+count-1] (inclusive) with pseudo-random bytes.
*/
public void nextBytes(byte[] out, int start, int count) {
if (count == 0) {
return;
}
if (bytesGenerated + count > MAX_BYTES_TOTAL) {
throw new IllegalStateException("Cannot generate more than a total of " + count + " bytes.");
}
try {
int bytesWritten = 0;
while (bytesWritten < count) {
int bytesToWrite = Math.min(count - bytesWritten, MAX_BYTES_PER_REQUEST);
hmacDrbgGenerate(out, start + bytesWritten, bytesToWrite);
bytesWritten += bytesToWrite;
}
} finally {
bytesGenerated += count;
}
}
// BEGIN android-changed
private static byte[] bytesConcat(byte[]... arrays) {
int length = 0;
for (byte[] array : arrays) {
length += array.length;
}
byte[] result = new byte[length];
int pos = 0;
for (byte[] array : arrays) {
System.arraycopy(array, 0, result, pos, array.length);
pos += array.length;
}
return result;
}
// END android-changed
}