Merge "Use sha256 of a string as seed in RapporEncoder.insecureEncoder" into pi-dev

This commit is contained in:
Ricky Wai
2018-03-23 15:55:28 +00:00
committed by Android (Google) Code Review
5 changed files with 67 additions and 25 deletions

View File

@@ -20,6 +20,10 @@ import android.privacy.DifferentialPrivacyEncoder;
import com.google.android.rappor.Encoder;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Random;
@@ -66,7 +70,7 @@ public class RapporEncoder implements DifferentialPrivacyEncoder {
random = sSecureRandom;
} else {
// To have deterministic result by hard coding encoder id as seed.
random = new Random((long) config.mEncoderId.hashCode());
random = new Random(getInsecureSeed(config.mEncoderId));
userSecret = INSECURE_SECRET;
}
mEncoder = new Encoder(random, null, null,
@@ -75,6 +79,17 @@ public class RapporEncoder implements DifferentialPrivacyEncoder {
config.mNumCohorts, config.mNumBloomHashes);
}
private long getInsecureSeed(String input) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] bytes = digest.digest(input.getBytes(StandardCharsets.UTF_8));
return ByteBuffer.wrap(bytes).getLong();
} catch (NoSuchAlgorithmException e) {
// Should not happen
throw new AssertionError("Unable generate insecure seed");
}
}
/**
* Create {@link RapporEncoder} with {@link RapporConfig} and user secret provided.
*

View File

@@ -8,7 +8,7 @@ LOCAL_MODULE_TAGS := tests
LOCAL_SRC_FILES := \
$(call all-java-files-under, src)
LOCAL_STATIC_JAVA_LIBRARIES := junit rappor-tests android-support-test
LOCAL_STATIC_JAVA_LIBRARIES := junit rappor-tests android-support-test truth-prebuilt
LOCAL_JAVA_LIBRARIES := android.test.runner
LOCAL_PACKAGE_NAME := FrameworksPrivacyLibraryTests

View File

@@ -16,6 +16,7 @@
package android.privacy;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -72,27 +73,27 @@ public class LongitudinalReportingEncoderTest {
final LongitudinalReportingEncoder encoder =
LongitudinalReportingEncoder.createInsecureEncoderForTest(
config);
assertEquals(0, encoder.encodeBoolean(true)[0]);
assertEquals(0, encoder.encodeBoolean(true)[0]);
assertEquals(1, encoder.encodeBoolean(true)[0]);
assertEquals(1, encoder.encodeBoolean(true)[0]);
assertEquals(1, encoder.encodeBoolean(true)[0]);
assertEquals(0, encoder.encodeBoolean(true)[0]);
assertEquals(1, encoder.encodeBoolean(true)[0]);
assertEquals(1, encoder.encodeBoolean(true)[0]);
assertEquals(1, encoder.encodeBoolean(true)[0]);
assertEquals(1, encoder.encodeBoolean(true)[0]);
assertEquals(1, encoder.encodeBoolean(true)[0]);
assertEquals(1, encoder.encodeBoolean(true)[0]);
assertEquals(0, encoder.encodeBoolean(true)[0]);
assertEquals(0, encoder.encodeBoolean(true)[0]);
assertEquals(0, encoder.encodeBoolean(false)[0]);
assertEquals(1, encoder.encodeBoolean(false)[0]);
assertEquals(1, encoder.encodeBoolean(false)[0]);
assertEquals(1, encoder.encodeBoolean(false)[0]);
assertEquals(0, encoder.encodeBoolean(false)[0]);
assertEquals(0, encoder.encodeBoolean(false)[0]);
assertEquals(1, encoder.encodeBoolean(false)[0]);
assertEquals(0, encoder.encodeBoolean(false)[0]);
assertEquals(1, encoder.encodeBoolean(false)[0]);
assertEquals(1, encoder.encodeBoolean(false)[0]);
assertEquals(0, encoder.encodeBoolean(false)[0]);
assertEquals(0, encoder.encodeBoolean(false)[0]);
assertEquals(0, encoder.encodeBoolean(false)[0]);
assertEquals(0, encoder.encodeBoolean(false)[0]);
// Test if IRR returns original result when f = 0
final LongitudinalReportingConfig config2 = new LongitudinalReportingConfig(
@@ -127,6 +128,31 @@ public class LongitudinalReportingEncoderTest {
}
}
@Test
public void testLongitudinalReportingInsecureEncoder_setSeedCorrectly() throws Exception {
final int n = 10000;
final double f = 0.35;
final double expectedTrueSum = n * f;
final double valueRange = 5 * Math.sqrt(n * f * (1 - f));
int trueSum = 0;
for (int i = 0; i < n; i++) {
final LongitudinalReportingConfig config = new LongitudinalReportingConfig(
"encoder" + i, // encoderId
f, // probabilityF
0, // probabilityP
1); // probabilityQ
final LongitudinalReportingEncoder encoder
= LongitudinalReportingEncoder.createInsecureEncoderForTest(config);
boolean encodedFalse = encoder.encodeBoolean(false)[0] > 0;
if (encodedFalse) {
trueSum += 1;
}
}
// Total number of true(s) should be around the mean (10000 * 0.35)
assertThat((double) trueSum).isLessThan(expectedTrueSum + valueRange);
assertThat((double) trueSum).isAtLeast(expectedTrueSum - valueRange);
}
@Test
public void testLongitudinalReportingEncoder_basicPRRTest() throws Exception {
// Should always return original value when p = 0
@@ -290,8 +316,8 @@ public class LongitudinalReportingEncoderTest {
}
}
// Total number of true(s) should be around the mean (1000 * 0.8)
assertTrue(trueSum1 < expectedTrueSum1 + valueRange1);
assertTrue(trueSum1 > expectedTrueSum1 - valueRange1);
assertThat((double) trueSum1).isLessThan(expectedTrueSum1 + valueRange1);
assertThat((double) trueSum1).isAtLeast(expectedTrueSum1 - valueRange1);
// Confirm if PRR randomizer is working correctly
final int n2 = 1000;
@@ -314,8 +340,8 @@ public class LongitudinalReportingEncoderTest {
}
}
// Total number of true(s) should be around the mean (1000 * 0.2)
assertTrue(trueSum2 < expectedTrueSum2 + valueRange2);
assertTrue(trueSum2 > expectedTrueSum2 - valueRange2);
assertThat((double) trueSum2).isLessThan(expectedTrueSum2 + valueRange2);
assertThat((double) trueSum2).isAtLeast(expectedTrueSum2 - valueRange2);
}
@Test

View File

@@ -79,11 +79,11 @@ public class RapporEncoderTest {
public void testRapporEncoder_IRRWithPRR() throws Exception {
int numBits = 8;
final long inputValue = 254L;
final long prrValue = 250L;
final long prrAndIrrValue = 244L;
final long expectedPrrValue = 126L;
final long expectedPrrAndIrrValue = 79L;
final RapporConfig config1 = new RapporConfig(
"Foo", // encoderId
"Foo2", // encoderId
numBits, // numBits,
0.25, // probabilityF
0, // probabilityP
@@ -93,12 +93,12 @@ public class RapporEncoderTest {
// Use insecure encoder here as we want to get the exact output.
final RapporEncoder encoder1 = RapporEncoder.createInsecureEncoderForTest(config1);
// Verify that PRR is working as expected.
assertEquals(prrValue, toLong(encoder1.encodeBits(toBytes(inputValue))));
assertEquals(expectedPrrValue, toLong(encoder1.encodeBits(toBytes(inputValue))));
assertTrue(encoder1.isInsecureEncoderForTest());
// Verify that IRR is working as expected.
final RapporConfig config2 = new RapporConfig(
"Foo", // encoderId
"Foo2", // encoderId
numBits, // numBits,
0, // probabilityF
0.3, // probabilityP
@@ -107,11 +107,12 @@ public class RapporEncoderTest {
2); // numBloomHashes
// Use insecure encoder here as we want to get the exact output.
final RapporEncoder encoder2 = RapporEncoder.createInsecureEncoderForTest(config2);
assertEquals(prrAndIrrValue, toLong(encoder2.encodeBits(toBytes(prrValue))));
assertEquals(expectedPrrAndIrrValue,
toLong(encoder2.encodeBits(toBytes(expectedPrrValue))));
// Test that end-to-end is the result of PRR + IRR.
final RapporConfig config3 = new RapporConfig(
"Foo", // encoderId
"Foo2", // encoderId
numBits, // numBits,
0.25, // probabilityF
0.3, // probabilityP
@@ -120,7 +121,7 @@ public class RapporEncoderTest {
2); // numBloomHashes
final RapporEncoder encoder3 = RapporEncoder.createInsecureEncoderForTest(config3);
// Verify that PRR is working as expected.
assertEquals(prrAndIrrValue, toLong(encoder3.encodeBits(toBytes(inputValue))));
assertEquals(expectedPrrAndIrrValue, toLong(encoder3.encodeBits(toBytes(inputValue))));
}
@Test

View File

@@ -75,11 +75,11 @@ public class PrivacyUtilsTests {
Map<String, Boolean> result = PrivacyUtils.createDpEncodedReportMap(false, null,
TEST_DIGEST_LIST, TEST_AGGREGATED_RESULT1);
assertEquals(6, result.size());
assertTrue(result.get("C86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB48"));
assertFalse(result.get("C86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB48"));
assertTrue(result.get("C86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB49"));
assertTrue(result.get("C86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB47"));
assertFalse(result.get("E86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB45"));
assertTrue(result.get("C86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB44"));
assertTrue(result.get("E86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB45"));
assertFalse(result.get("C86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB44"));
assertTrue(result.get("B86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB43"));
}