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

Change-Id: Ie468743be5b369e49eacda9707e76e010639a7fe
diff --git a/core/java/android/privacy/internal/rappor/RapporEncoder.java b/core/java/android/privacy/internal/rappor/RapporEncoder.java
index 9ac2b3e..3bf09ec 100644
--- a/core/java/android/privacy/internal/rappor/RapporEncoder.java
+++ b/core/java/android/privacy/internal/rappor/RapporEncoder.java
@@ -20,6 +20,10 @@
 
 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 @@
             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 @@
                 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.
      *
diff --git a/core/tests/privacytests/Android.mk b/core/tests/privacytests/Android.mk
index 374d0d0..3c1526b 100644
--- a/core/tests/privacytests/Android.mk
+++ b/core/tests/privacytests/Android.mk
@@ -8,7 +8,7 @@
 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
diff --git a/core/tests/privacytests/src/android/privacy/LongitudinalReportingEncoderTest.java b/core/tests/privacytests/src/android/privacy/LongitudinalReportingEncoderTest.java
index 6fe19a2..c88a722 100644
--- a/core/tests/privacytests/src/android/privacy/LongitudinalReportingEncoderTest.java
+++ b/core/tests/privacytests/src/android/privacy/LongitudinalReportingEncoderTest.java
@@ -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 @@
         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(
@@ -128,6 +129,31 @@
     }
 
     @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
         for (int i = 0; i < 10; i++) {
@@ -290,8 +316,8 @@
             }
         }
         // 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 @@
             }
         }
         // 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
diff --git a/core/tests/privacytests/src/android/privacy/RapporEncoderTest.java b/core/tests/privacytests/src/android/privacy/RapporEncoderTest.java
index fa0343d..71bd8f1 100644
--- a/core/tests/privacytests/src/android/privacy/RapporEncoderTest.java
+++ b/core/tests/privacytests/src/android/privacy/RapporEncoderTest.java
@@ -79,11 +79,11 @@
     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 @@
         // 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 @@
                 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 @@
                 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
diff --git a/services/tests/servicestests/src/com/android/server/net/watchlist/PrivacyUtilsTests.java b/services/tests/servicestests/src/com/android/server/net/watchlist/PrivacyUtilsTests.java
index 999dce5..6f2237f 100644
--- a/services/tests/servicestests/src/com/android/server/net/watchlist/PrivacyUtilsTests.java
+++ b/services/tests/servicestests/src/com/android/server/net/watchlist/PrivacyUtilsTests.java
@@ -75,11 +75,11 @@
         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"));
     }