Merge "Use sha256 of a string as seed in RapporEncoder.insecureEncoder" into pi-dev
This commit is contained in:
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user