Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b70d84a6 authored by Ricky Wai's avatar Ricky Wai
Browse files

Use sha256 of a string as seed in RapporEncoder.insecureEncoder

Test: atest android.privacy.LongitudinalReportingEncoderTest
Test: atest android.privacy.RapporEncoderTest
Test: atest android.server.net.watchlist.PrivacyUtilsTests

Bug: 76001602
Change-Id: I42d569f34d29125e7adf898b9553053a42149bd4
parent 90ad1540
Loading
Loading
Loading
Loading
+16 −1
Original line number Diff line number Diff line
@@ -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.
     *
+1 −1
Original line number Diff line number Diff line
@@ -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
+37 −11
Original line number Diff line number Diff line
@@ -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(0, 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(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
+9 −8
Original line number Diff line number Diff line
@@ -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
+3 −3
Original line number Diff line number Diff line
@@ -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"));
    }