Revert "[Topics API] Implement feature flag guarded encryption."
This reverts commit 70884ab1b4ecb9563db68e725b0ea64717b812df.
Reason for revert: TopicsJsonMapperTest#getTopicJson failing b/311487135
Change-Id: Ia9fb8e779c8d92085a614016c1ff13dd28960dce
diff --git a/adservices/service-core/java/com/android/adservices/service/Flags.java b/adservices/service-core/java/com/android/adservices/service/Flags.java
index ec47e94..d8ef9e5 100644
--- a/adservices/service-core/java/com/android/adservices/service/Flags.java
+++ b/adservices/service-core/java/com/android/adservices/service/Flags.java
@@ -104,14 +104,6 @@
return TOPICS_DISABLE_DIRECT_APP_CALLS;
}
- /** Flag to enable encrypted Topics feature for Topics API. */
- boolean TOPICS_ENABLE_ENCRYPTION = false;
-
- /** Returns the feature flag to enable encryption for Topics API. */
- default boolean getTopicsEnableEncryption() {
- return TOPICS_ENABLE_ENCRYPTION;
- }
-
/**
* Returns the number of epochs to look back when deciding if a caller has observed a topic
* before.
diff --git a/adservices/service-core/java/com/android/adservices/service/FlagsConstants.java b/adservices/service-core/java/com/android/adservices/service/FlagsConstants.java
index c734ee4..69eea6f 100644
--- a/adservices/service-core/java/com/android/adservices/service/FlagsConstants.java
+++ b/adservices/service-core/java/com/android/adservices/service/FlagsConstants.java
@@ -91,7 +91,6 @@
public static final String KEY_GLOBAL_BLOCKED_TOPIC_IDS = "topics_global_blocked_topic_ids";
public static final String KEY_TOPICS_DISABLE_DIRECT_APP_CALLS =
"topics_disable_direct_app_calls";
- public static final String KEY_TOPICS_ENABLE_ENCRYPTION = "topics_enable_encryption";
// Topics classifier keys
public static final String KEY_CLASSIFIER_TYPE = "classifier_type";
diff --git a/adservices/service-core/java/com/android/adservices/service/PhFlags.java b/adservices/service-core/java/com/android/adservices/service/PhFlags.java
index c2b2c7b..d8bfa7c 100644
--- a/adservices/service-core/java/com/android/adservices/service/PhFlags.java
+++ b/adservices/service-core/java/com/android/adservices/service/PhFlags.java
@@ -226,15 +226,6 @@
}
@Override
- public boolean getTopicsEnableEncryption() {
- // The priority of applying the flag values: PH (DeviceConfig) and then hard-coded value.
- return DeviceConfig.getBoolean(
- FlagsConstants.NAMESPACE_ADSERVICES,
- /* flagName */ FlagsConstants.KEY_TOPICS_ENABLE_ENCRYPTION,
- /* defaultValue */ TOPICS_ENABLE_ENCRYPTION);
- }
-
- @Override
public int getClassifierType() {
// The priority of applying the flag values: SystemProperties, PH (DeviceConfig), then
// hard-coded value.
diff --git a/adservices/service-core/java/com/android/adservices/service/topics/Encrypter.java b/adservices/service-core/java/com/android/adservices/service/topics/Encrypter.java
deleted file mode 100644
index 767e634..0000000
--- a/adservices/service-core/java/com/android/adservices/service/topics/Encrypter.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.adservices.service.topics;
-
-/** Interface for algorithms to encrypt Topics data. */
-public interface Encrypter {
- /**
- * Encrypt {@code plainText} to cipher text {@code byte[]}.
- *
- * @param publicKey the public key used for encryption
- * @param plainText the plain text string to encrypt
- * @param contextInfo additional context info used for encryption
- * @return the encrypted ciphertext
- */
- byte[] encrypt(byte[] publicKey, byte[] plainText, byte[] contextInfo);
-}
diff --git a/adservices/service-core/java/com/android/adservices/service/topics/EncryptionManager.java b/adservices/service-core/java/com/android/adservices/service/topics/EncryptionManager.java
deleted file mode 100644
index b7e1904..0000000
--- a/adservices/service-core/java/com/android/adservices/service/topics/EncryptionManager.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.adservices.service.topics;
-
-import android.annotation.NonNull;
-import android.content.Context;
-
-import com.android.adservices.LoggerFactory;
-import com.android.adservices.data.topics.EncryptedTopic;
-import com.android.adservices.data.topics.Topic;
-
-import org.json.JSONObject;
-
-import java.nio.charset.StandardCharsets;
-import java.util.Base64;
-import java.util.Objects;
-import java.util.Optional;
-
-/**
- * Class to handle encryption for {@link Topic} objects.
- *
- * <p>Identify the algorithm supported for Encryption.
- *
- * <p>Fetch public key corresponding to sdk(adtech) caller.
- *
- * <p>Generate {@link EncryptedTopic} object from the encrypted cipher text.
- */
-public class EncryptionManager {
- private static final LoggerFactory.Logger sLogger = LoggerFactory.getTopicsLogger();
- private static final byte[] EMPTY_BYTE_ARRAY = new byte[] {};
- private static final int ENCAPSULATED_KEY_LENGTH = 32;
- private static final String PUBLIC_KEY_BASE64 = "rSJBSUYG0ebvfW1AXCWO0CMGMJhDzpfQm3eLyw1uxX8=";
-
- private static EncryptionManager sSingleton;
-
- private Encrypter mEncrypter;
-
- EncryptionManager(Encrypter encrypter) {
- mEncrypter = encrypter;
- }
-
- /** Returns the singleton instance of the {@link EncryptionManager} given a context. */
- @NonNull
- public static EncryptionManager getInstance(@NonNull Context context) {
- synchronized (EncryptionManager.class) {
- if (sSingleton == null) {
- sSingleton = new EncryptionManager(new HpkeEncrypter());
- }
- }
- return sSingleton;
- }
-
- /**
- * Converts plain text {@link Topic} object to {@link EncryptedTopic}.
- *
- * <p>Returns {@link Optional#empty()} if encryption fails.
- *
- * @param topic object to be encrypted
- * @return corresponding encrypted object
- */
- public Optional<EncryptedTopic> encryptTopic(Topic topic, String sdkName) {
- return encryptTopicWithKey(topic, fetchPublicKeyFor(sdkName));
- }
-
- private String fetchPublicKeyFor(String sdkName) {
- sLogger.v("Fetching public key for %s", sdkName);
- // TODO(b/310753075): Update logic to fetch public keys for sdkName.
- return PUBLIC_KEY_BASE64;
- }
-
- /**
- * Serialise {@link Topic} to JSON string with UTF-8 encoding. Encrypt serialised Topic with the
- * given public key.
- */
- private Optional<EncryptedTopic> encryptTopicWithKey(Topic topic, String publicKey) {
- Objects.requireNonNull(topic);
-
- Optional<JSONObject> optionalTopicJSON = TopicsJsonMapper.toJson(topic);
- if (optionalTopicJSON.isPresent()) {
- // UTF-8 is the default encoding for JSON data.
- byte[] unencryptedSerializedTopic =
- optionalTopicJSON.get().toString().getBytes(StandardCharsets.UTF_8);
- byte[] base64DecodedPublicKey = Base64.getDecoder().decode(publicKey);
- byte[] response =
- mEncrypter.encrypt(
- /* publicKey */
- base64DecodedPublicKey, /* plainText */
- unencryptedSerializedTopic, /* contextInfo */
- EMPTY_BYTE_ARRAY);
-
- return buildEncryptedTopic(response, publicKey);
- }
- return Optional.empty();
- }
-
- private static Optional<EncryptedTopic> buildEncryptedTopic(byte[] response, String publicKey) {
- if (response.length < ENCAPSULATED_KEY_LENGTH) {
- sLogger.d(
- "Encrypted response size is smaller than minimum expected size "
- + ENCAPSULATED_KEY_LENGTH);
- return Optional.empty();
- }
-
- // First 32 bytes are the encapsulated key and the remaining array in the cipher text.
- int cipherTextLength = response.length - ENCAPSULATED_KEY_LENGTH;
- byte[] encapsulatedKey = new byte[ENCAPSULATED_KEY_LENGTH];
- byte[] cipherText = new byte[cipherTextLength];
- System.arraycopy(
- response,
- /* srcPos */ 0,
- encapsulatedKey,
- /* destPos */ 0,
- /* length */ ENCAPSULATED_KEY_LENGTH);
- System.arraycopy(
- response,
- /* srcPos */ ENCAPSULATED_KEY_LENGTH,
- cipherText,
- /* destPos */ 0,
- /* length */ cipherTextLength);
-
- return Optional.of(EncryptedTopic.create(cipherText, publicKey, encapsulatedKey));
- }
-}
diff --git a/adservices/service-core/java/com/android/adservices/service/topics/EpochManager.java b/adservices/service-core/java/com/android/adservices/service/topics/EpochManager.java
index 62bfb3a..04831c0 100644
--- a/adservices/service-core/java/com/android/adservices/service/topics/EpochManager.java
+++ b/adservices/service-core/java/com/android/adservices/service/topics/EpochManager.java
@@ -28,7 +28,6 @@
import com.android.adservices.LoggerFactory;
import com.android.adservices.data.DbHelper;
-import com.android.adservices.data.topics.EncryptedTopic;
import com.android.adservices.data.topics.Topic;
import com.android.adservices.data.topics.TopicsDao;
import com.android.adservices.data.topics.TopicsTables;
@@ -48,7 +47,6 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
-import java.util.Optional;
import java.util.Random;
import java.util.Set;
@@ -117,7 +115,6 @@
// Use Clock.SYSTEM_CLOCK except in unit tests, which pass in a local instance of Clock to mock.
private final Clock mClock;
private final ClassifierManager mClassifierManager;
- private final EncryptionManager mEncryptionManager;
@VisibleForTesting
EpochManager(
@@ -127,8 +124,7 @@
@NonNull Classifier classifier,
Flags flags,
@NonNull Clock clock,
- @NonNull ClassifierManager classifierManager,
- @NonNull EncryptionManager encryptionManager) {
+ @NonNull ClassifierManager classifierManager) {
mTopicsDao = topicsDao;
mDbHelper = dbHelper;
mRandom = random;
@@ -136,7 +132,6 @@
mFlags = flags;
mClock = clock;
mClassifierManager = classifierManager;
- mEncryptionManager = encryptionManager;
}
/** Returns an instance of the EpochManager given a context. */
@@ -152,8 +147,7 @@
ClassifierManager.getInstance(context),
FlagsFactory.getFlags(),
Clock.SYSTEM_CLOCK,
- ClassifierManager.getInstance(context),
- EncryptionManager.getInstance(context));
+ ClassifierManager.getInstance(context));
}
return sSingleton;
}
@@ -255,16 +249,6 @@
// And persist the map to DB so that we can reuse later.
mTopicsDao.persistReturnedAppTopicsMap(currentEpochId, returnedAppSdkTopics);
- // Encrypt and store encrypted topics if the feature is enabled and the version 9 db
- // is available.
- if (mFlags.getTopicsEnableEncryption() && mFlags.getEnableDatabaseSchemaVersion9()) {
- // encryptedTopicMapTopics = Map<Pair<App, Sdk>, EncryptedTopic>
- Map<Pair<String, String>, EncryptedTopic> encryptedTopicMapTopics =
- encryptTopicsMap(returnedAppSdkTopics);
- mTopicsDao.persistReturnedAppEncryptedTopicsMap(
- currentEpochId, encryptedTopicMapTopics);
- }
-
// Mark the transaction successful.
db.setTransactionSuccessful();
} finally {
@@ -396,27 +380,6 @@
return callersCanLearnMap;
}
- // Encrypts Topic to corresponding EncryptedTopic.
- // Map<Pair<App, Sdk>, EncryptedTopic>
- private Map<Pair<String, String>, EncryptedTopic> encryptTopicsMap(
- Map<Pair<String, String>, Topic> unencryptedMap) {
- Map<Pair<String, String>, EncryptedTopic> encryptedTopicMap = new HashMap<>();
- for (Map.Entry<Pair<String, String>, Topic> entry : unencryptedMap.entrySet()) {
- Optional<EncryptedTopic> optionalEncryptedTopic =
- mEncryptionManager.encryptTopic(
- /* Topic */ entry.getValue(), /* sdkName */ entry.getKey().second);
- if (optionalEncryptedTopic.isPresent()) {
- encryptedTopicMap.put(entry.getKey(), optionalEncryptedTopic.get());
- } else {
- sLogger.d(
- "Failed to encrypt %s for (%s, %s) caller.",
- entry.getValue(), entry.getKey().first, entry.getKey().second);
- // TODO(b/310699530): Add CEL here to Topics encryption
- }
- }
- return encryptedTopicMap;
- }
-
// Inputs:
// callersCanLearnMap = Map<Topic, Set<Caller>> map from topic to set of callers that can learn
// about the topic. Caller = App or Sdk.
diff --git a/adservices/service-core/java/com/android/adservices/service/topics/HpkeEncrypter.java b/adservices/service-core/java/com/android/adservices/service/topics/HpkeEncrypter.java
deleted file mode 100644
index 0510458..0000000
--- a/adservices/service-core/java/com/android/adservices/service/topics/HpkeEncrypter.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.adservices.service.topics;
-
-import androidx.annotation.NonNull;
-
-import com.android.adservices.HpkeJni;
-import com.android.adservices.LoggerFactory;
-
-import java.util.Arrays;
-import java.util.Objects;
-
-/**
- * One shot implementation of HPKE(Hybrid Public Key Encryption). This particular implementation is
- * referred to as “curve25519”, but “X25519” is a more precise name.
- */
-public class HpkeEncrypter implements Encrypter {
- private static final LoggerFactory.Logger sLogger = LoggerFactory.getTopicsLogger();
-
- private static final byte[] EMPTY_BYTE_ARRAY = new byte[] {};
-
- private static final int X25519_PUBLIC_VALUE_LEN = 32;
-
- /**
- * Encrypt a byte string using HPKE one shot algorithm.
- *
- * @param publicKey the public key used for encryption
- * @param plainText the plain text string to encrypt
- * @param contextInfo HPKE context info used for encryption
- * @return the encrypted ciphertext
- */
- @Override
- public byte[] encrypt(
- @NonNull byte[] publicKey, @NonNull byte[] plainText, @NonNull byte[] contextInfo) {
- Objects.requireNonNull(publicKey);
- Objects.requireNonNull(plainText);
- Objects.requireNonNull(contextInfo);
-
- if (!isValidKey(publicKey)) {
- return EMPTY_BYTE_ARRAY;
- }
-
- return HpkeJni.encrypt(publicKey, plainText, contextInfo);
- }
-
- // Return true if key is compatible with the supported HPKE implementation.
- private boolean isValidKey(byte[] publicKey) {
- // Check if the public key length matches the X25519 public key requirement.
- if (publicKey.length != X25519_PUBLIC_VALUE_LEN) {
- sLogger.d(
- "Invalid HPKE public key = %s. Expected public key length of %d, got %d.",
- Arrays.toString(publicKey), X25519_PUBLIC_VALUE_LEN, publicKey.length);
- // TODO(b/310699530): Add CEL here to Topics encryption
- return false;
- }
- return true;
- }
-}
diff --git a/adservices/service-core/java/com/android/adservices/service/topics/TopicsJsonMapper.java b/adservices/service-core/java/com/android/adservices/service/topics/TopicsJsonMapper.java
deleted file mode 100644
index a53c20d..0000000
--- a/adservices/service-core/java/com/android/adservices/service/topics/TopicsJsonMapper.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.adservices.service.topics;
-
-import com.android.adservices.LoggerFactory;
-import com.android.adservices.data.topics.Topic;
-import com.android.internal.annotations.VisibleForTesting;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import java.util.Optional;
-
-/**
- * Helper class to map {@link Topic} to JSON string.
- *
- * <p>Example JSON string : {@code {"taxonomy_version": 5, "model_version": 2, "topic_id": 10010 }}
- */
-public class TopicsJsonMapper {
- private static final LoggerFactory.Logger sLogger = LoggerFactory.getTopicsLogger();
-
- // Keys for Topics JSON Object.
- /** Key for {@link Topic#getTopic()}. */
- @VisibleForTesting static final String KEY_TOPIC_ID = "topic_id";
-
- /** Key for {@link Topic#getTaxonomyVersion()}. */
- @VisibleForTesting static final String KEY_TAXONOMY_VERSION = "taxonomy_version";
-
- /** Key for {@link Topic#getModelVersion()}. */
- @VisibleForTesting static final String KEY_MODEL_VERSION = "model_version";
-
- // To prevent instantiation.
- private TopicsJsonMapper() {}
- ;
-
- /**
- * Convert {@link Topic} to {@link JSONObject} wrapped in {@link Optional}.
- *
- * @param topic object to convert.
- * @return {@code Optional<JSONObject>} for the corresponding topic. Returns {@link
- * Optional#empty()} if serialization to JSON fails.
- */
- public static Optional<JSONObject> toJson(Topic topic) {
- try {
- return Optional.of(
- new JSONObject()
- .put(KEY_TOPIC_ID, topic.getTopic())
- .put(KEY_MODEL_VERSION, topic.getModelVersion())
- .put(KEY_TAXONOMY_VERSION, topic.getTaxonomyVersion()));
- } catch (JSONException e) {
- sLogger.d("JSON serialization failed for %s", topic);
- // TODO(b/310699530): Add CEL for Encryption.
- return Optional.empty();
- }
- }
-}
diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/PhFlagsTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/PhFlagsTest.java
index eef2443..af962db 100644
--- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/PhFlagsTest.java
+++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/PhFlagsTest.java
@@ -415,7 +415,6 @@
import static com.android.adservices.service.Flags.TOPICS_API_SDK_REQUEST_PERMITS_PER_SECOND;
import static com.android.adservices.service.Flags.TOPICS_COBALT_LOGGING_ENABLED;
import static com.android.adservices.service.Flags.TOPICS_DISABLE_DIRECT_APP_CALLS;
-import static com.android.adservices.service.Flags.TOPICS_ENABLE_ENCRYPTION;
import static com.android.adservices.service.Flags.TOPICS_EPOCH_JOB_FLEX_MS;
import static com.android.adservices.service.Flags.TOPICS_EPOCH_JOB_PERIOD_MS;
import static com.android.adservices.service.Flags.TOPICS_KILL_SWITCH;
@@ -803,7 +802,6 @@
import static com.android.adservices.service.FlagsConstants.KEY_TOPICS_API_SDK_REQUEST_PERMITS_PER_SECOND;
import static com.android.adservices.service.FlagsConstants.KEY_TOPICS_COBALT_LOGGING_ENABLED;
import static com.android.adservices.service.FlagsConstants.KEY_TOPICS_DISABLE_DIRECT_APP_CALLS;
-import static com.android.adservices.service.FlagsConstants.KEY_TOPICS_ENABLE_ENCRYPTION;
import static com.android.adservices.service.FlagsConstants.KEY_TOPICS_EPOCH_JOB_FLEX_MS;
import static com.android.adservices.service.FlagsConstants.KEY_TOPICS_EPOCH_JOB_PERIOD_MS;
import static com.android.adservices.service.FlagsConstants.KEY_TOPICS_KILL_SWITCH;
@@ -1059,21 +1057,6 @@
}
@Test
- public void testGetTopicsEnableEncryption() {
- assertThat(mPhFlags.getTopicsEnableEncryption()).isEqualTo(TOPICS_ENABLE_ENCRYPTION);
-
- // Now overriding with the value from PH.
- boolean phOverridingValue = !TOPICS_ENABLE_ENCRYPTION;
- DeviceConfig.setProperty(
- DeviceConfig.NAMESPACE_ADSERVICES,
- KEY_TOPICS_ENABLE_ENCRYPTION,
- Boolean.toString(phOverridingValue),
- /* makeDefault */ false);
-
- assertThat(mPhFlags.getTopicsEnableEncryption()).isEqualTo(phOverridingValue);
- }
-
- @Test
public void testClassifierType() {
// Without any overriding, the value is the hard coded constant.
assertThat(mPhFlags.getClassifierType()).isEqualTo(DEFAULT_CLASSIFIER_TYPE);
diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/topics/EncryptionManagerTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/topics/EncryptionManagerTest.java
deleted file mode 100644
index 4d2a5fc..0000000
--- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/topics/EncryptionManagerTest.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.adservices.service.topics;
-
-import static com.android.adservices.service.topics.TopicsJsonMapper.KEY_MODEL_VERSION;
-import static com.android.adservices.service.topics.TopicsJsonMapper.KEY_TAXONOMY_VERSION;
-import static com.android.adservices.service.topics.TopicsJsonMapper.KEY_TOPIC_ID;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.junit.Assert.assertThrows;
-
-import android.content.Context;
-
-import androidx.test.core.app.ApplicationProvider;
-
-import com.android.adservices.HpkeJni;
-import com.android.adservices.data.topics.EncryptedTopic;
-import com.android.adservices.data.topics.Topic;
-
-import com.google.common.primitives.Bytes;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-import org.junit.Test;
-
-import java.util.Base64;
-import java.util.Optional;
-
-/** Unit tests for {@link EncryptionManager}. */
-public class EncryptionManagerTest {
- static final String PUBLIC_KEY_BASE64 = "rSJBSUYG0ebvfW1AXCWO0CMGMJhDzpfQm3eLyw1uxX8=";
- static final String PRIVATE_KEY_BASE64 = "f86EzLmGaVmc+PwjJk5ADPE4ijQvliWf0CQyY/Zyy7I=";
- static final byte[] DECODED_PUBLIC_KEY = Base64.getDecoder().decode(PUBLIC_KEY_BASE64);
- static final byte[] DECODED_PRIVATE_KEY = Base64.getDecoder().decode(PRIVATE_KEY_BASE64);
- static final byte[] EMPTY_CONTEXT_INFO = new byte[] {};
-
- private final Context mContext = ApplicationProvider.getApplicationContext();
- EncryptionManager mEncryptionManager = EncryptionManager.getInstance(mContext);
-
- @Test
- public void testEncryption_success() throws JSONException {
- Topic topic = Topic.create(/* topic */ 5, /* taxonomyVersion */ 6L, /* modelVersion */ 7L);
- String sdkName = "sdk";
-
- Optional<EncryptedTopic> optionalEncryptedTopic =
- mEncryptionManager.encryptTopic(topic, sdkName);
-
- // Verify EncryptedTopic is not empty.
- assertThat(optionalEncryptedTopic.isPresent()).isTrue();
- assertThat(optionalEncryptedTopic.get().getEncryptedTopic()).isNotEmpty();
- assertThat(optionalEncryptedTopic.get().getKeyIdentifier()).isEqualTo(PUBLIC_KEY_BASE64);
- assertThat(optionalEncryptedTopic.get().getEncapsulatedKey()).isNotEmpty();
-
- // Decrypt and deserialize to verify correct information.
- byte[] cipherText =
- Bytes.concat(
- optionalEncryptedTopic.get().getEncapsulatedKey(),
- optionalEncryptedTopic.get().getEncryptedTopic());
- byte[] decryptedText = HpkeJni.decrypt(DECODED_PRIVATE_KEY, cipherText, EMPTY_CONTEXT_INFO);
- assertThat(new String(decryptedText))
- .isEqualTo("{\"topic_id\":5,\"model_version\":7," + "\"taxonomy_version\":6}");
- JSONObject returnedJSON = new JSONObject(new String(decryptedText));
- Topic returnedTopic =
- Topic.create(
- returnedJSON.getInt(KEY_TOPIC_ID),
- returnedJSON.getLong(KEY_TAXONOMY_VERSION),
- returnedJSON.getLong(KEY_MODEL_VERSION));
- // Verify decrypted and deserialized object creates the expected Topic.
- assertThat(returnedTopic).isEqualTo(topic);
- }
-
- @Test
- public void testEncryption_nullTopic_throwsNullPointerException() {
- assertThrows(
- NullPointerException.class,
- () -> mEncryptionManager.encryptTopic(/* topic */ null, "sdkName"));
- }
-}
diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/topics/EpochManagerTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/topics/EpochManagerTest.java
index 4405da1..5408aab 100644
--- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/topics/EpochManagerTest.java
+++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/topics/EpochManagerTest.java
@@ -20,7 +20,6 @@
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;
-import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
@@ -67,7 +66,6 @@
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
-import java.util.Optional;
import java.util.Random;
import java.util.Set;
import java.util.stream.Collectors;
@@ -96,7 +94,6 @@
@Mock Clock mMockClock;
@Mock Flags mMockFlag;
@Mock ClassifierManager mClassifierManager;
- @Mock EncryptionManager mEncryptionManager;
@Mock Random mRandom;
@Before
@@ -113,8 +110,7 @@
mMockClassifier,
mFlags,
mMockClock,
- mClassifierManager,
- mEncryptionManager);
+ mClassifierManager);
// Erase all existing data.
DbTestUtil.deleteTable(TopicsTables.TaxonomyContract.TABLE);
@@ -218,8 +214,7 @@
mMockClassifier,
mFlags,
mMockClock,
- mClassifierManager,
- mEncryptionManager);
+ mClassifierManager);
Topic topic1 = Topic.create(/* topic */ 1, TAXONOMY_VERSION, MODEL_VERSION);
Topic topic2 = Topic.create(/* topic */ 2, TAXONOMY_VERSION, MODEL_VERSION);
@@ -260,8 +255,7 @@
mMockClassifier,
mFlags,
mMockClock,
- mClassifierManager,
- mEncryptionManager);
+ mClassifierManager);
Topic topic1 = Topic.create(/* topic */ 1, TAXONOMY_VERSION, MODEL_VERSION);
Topic topic2 = Topic.create(/* topic */ 2, TAXONOMY_VERSION, MODEL_VERSION);
@@ -287,12 +281,12 @@
new long[] {1, 5, 6, 7, 8, 9},
// Use a positive double greater than 1 to ensure that loggedTopic
// must be the same as the topic.
- new double[] {2d}),
+ new double[] {2d}
+ ),
mMockClassifier,
mFlags,
mMockClock,
- mClassifierManager,
- mEncryptionManager);
+ mClassifierManager);
// Note: we iterate over the appSdksUsageMap. For the test to be deterministic, we use
// LinkedHashMap so that the order of iteration is defined.
@@ -424,8 +418,7 @@
mMockClassifier,
mMockFlag,
mMockClock,
- mClassifierManager,
- mEncryptionManager);
+ mClassifierManager);
// For table except CallerCanLearnTopicsContract, epoch to delete from is 7-3-1 = epoch 3
final long epochToDeleteFrom = currentEpoch - epochLookBackNumberForGarbageCollection - 1;
@@ -511,8 +504,7 @@
mMockClassifier,
mMockFlag,
mMockClock,
- mClassifierManager,
- mEncryptionManager);
+ mClassifierManager);
final String app = "app";
final String sdk = "sdk";
@@ -588,8 +580,7 @@
mMockClassifier,
mFlags,
mMockClock,
- mClassifierManager,
- mEncryptionManager));
+ mClassifierManager));
// Mock EpochManager for getCurrentEpochId()
final long epochId = 1L;
doReturn(epochId).when(epochManager).getCurrentEpochId();
@@ -749,77 +740,6 @@
}
@Test
- public void testProcessEpoch_enableEncryptedTopics() {
- // Simplify the setup of epoch computation, to only test the effect of feature flag
- // Mock the flag to make test result deterministic
- EpochManager epochManager =
- Mockito.spy(
- new EpochManager(
- mTopicsDao,
- mDbHelper,
- new Random(),
- mMockClassifier,
- mMockFlag,
- mMockClock,
- mClassifierManager,
- mEncryptionManager));
-
- final String app = "app";
- final String sdk = "sdk";
- final long epochId = 1L;
- final int numberOfLookBackEpochs = 1;
-
- Topic topic1 = Topic.create(/* topic */ 1, TAXONOMY_VERSION, MODEL_VERSION);
- Topic topic2 = Topic.create(/* topic */ 2, TAXONOMY_VERSION, MODEL_VERSION);
- Topic topic3 = Topic.create(/* topic */ 3, TAXONOMY_VERSION, MODEL_VERSION);
- Topic topic4 = Topic.create(/* topic */ 4, TAXONOMY_VERSION, MODEL_VERSION);
- Topic topic5 = Topic.create(/* topic */ 5, TAXONOMY_VERSION, MODEL_VERSION);
- Topic topic6 = Topic.create(/* topic */ 6, TAXONOMY_VERSION, MODEL_VERSION);
- Map<String, List<Topic>> appClassificationTopicsMap =
- Map.of(app, List.of(topic1, topic2, topic3, topic4, topic5, topic6));
- List<Topic> topTopics = List.of(topic1, topic2, topic3, topic4, topic5, topic6);
-
- when(mMockFlag.getTopicsNumberOfLookBackEpochs()).thenReturn(numberOfLookBackEpochs);
- when(mMockFlag.getTopicsNumberOfTopTopics())
- .thenReturn(mFlags.getTopicsNumberOfTopTopics());
- when(mMockFlag.getTopicsNumberOfRandomTopics())
- .thenReturn(mFlags.getTopicsNumberOfRandomTopics());
- doReturn(epochId).when(epochManager).getCurrentEpochId();
-
- mTopicsDao.recordUsageHistory(epochId, app, sdk);
- when(mMockClassifier.classify(any())).thenReturn(appClassificationTopicsMap);
- when(mMockClassifier.getTopTopics(
- appClassificationTopicsMap,
- mFlags.getTopicsNumberOfTopTopics(),
- mFlags.getTopicsNumberOfRandomTopics()))
- .thenReturn(topTopics);
-
- // Mock for encryption data.
- EncryptedTopic expectedEncryptedTopic =
- EncryptedTopic.create(new byte[] {}, "", new byte[] {});
- when(mEncryptionManager.encryptTopic(any(), any()))
- .thenReturn(Optional.of(expectedEncryptedTopic));
-
- // Mock encryption db and feature flag.
- when(mMockFlag.getTopicsEnableEncryption()).thenReturn(true);
- when(mMockFlag.getEnableDatabaseSchemaVersion9()).thenReturn(true);
-
- epochManager.processEpoch();
-
- // ReturnedEncryptedTopics table should not be empty when feature is enabled.
- assertThat(
- mTopicsDao
- .retrieveReturnedEncryptedTopics(epochId, numberOfLookBackEpochs)
- .get(epochId))
- .isNotEmpty();
- assertThat(
- mTopicsDao
- .retrieveReturnedEncryptedTopics(epochId, numberOfLookBackEpochs)
- .get(epochId))
- .containsEntry(Pair.create(app, sdk), expectedEncryptedTopic);
- }
-
- @Test
public void testDump() {
// Trigger the dump to verify no crash
PrintWriter printWriter = new PrintWriter(new Writer() {
@@ -856,8 +776,7 @@
mMockClassifier,
mFlags,
mMockClock,
- mClassifierManager,
- mEncryptionManager));
+ mClassifierManager));
// To mimic the scenario that there was no usage in last epoch.
// i.e. current epoch id is 2, with some usages, while epoch id = 1 has no usage.
@@ -1058,8 +977,7 @@
mMockClassifier,
flags,
mMockClock,
- mClassifierManager,
- mEncryptionManager);
+ mClassifierManager);
// Mock clock so that:
// 1st call: There is no origin and will set 0 as origin.
@@ -1212,8 +1130,7 @@
mMockClassifier,
mMockFlag,
mMockClock,
- mClassifierManager,
- mEncryptionManager);
+ mClassifierManager);
// The first random double is 0.1, it's smaller than 0.595,
// loggedTopic should be a random topic 50.
@@ -1293,7 +1210,6 @@
mMockClassifier,
mMockFlag,
mMockClock,
- mClassifierManager,
- mEncryptionManager);
+ mClassifierManager);
}
}
diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/topics/HpkeEncrypterTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/topics/HpkeEncrypterTest.java
deleted file mode 100644
index 21eb457..0000000
--- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/topics/HpkeEncrypterTest.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.adservices.service.topics;
-
-import static com.android.adservices.service.topics.EncryptionManagerTest.DECODED_PRIVATE_KEY;
-import static com.android.adservices.service.topics.EncryptionManagerTest.DECODED_PUBLIC_KEY;
-import static com.android.adservices.service.topics.EncryptionManagerTest.EMPTY_CONTEXT_INFO;
-import static com.android.adservices.service.topics.TopicsJsonMapper.KEY_MODEL_VERSION;
-import static com.android.adservices.service.topics.TopicsJsonMapper.KEY_TAXONOMY_VERSION;
-import static com.android.adservices.service.topics.TopicsJsonMapper.KEY_TOPIC_ID;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.junit.Assert.assertThrows;
-
-import com.android.adservices.HpkeJni;
-import com.android.adservices.data.topics.Topic;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-import org.junit.Test;
-
-import java.nio.charset.StandardCharsets;
-
-/** Unit tests for {@link HpkeEncrypter}. */
-public class HpkeEncrypterTest {
- HpkeEncrypter mHpkeEncrypter = new HpkeEncrypter();
-
- @Test
- public void testEncryption_success() throws JSONException {
- Topic topic = Topic.create(/* topic */ 5, /* taxonomyVersion */ 6L, /* modelVersion */ 7L);
- byte[] plainText = generateTopicsPlainText(topic);
-
- // Encrypt plain text to cipher text
- byte[] cipherText =
- mHpkeEncrypter.encrypt(DECODED_PUBLIC_KEY, plainText, EMPTY_CONTEXT_INFO);
-
- // Verify cipher text
- assertThat(cipherText).isNotEmpty();
-
- // Decrypt and deserialize to verify correct information.
- byte[] decryptedText = HpkeJni.decrypt(DECODED_PRIVATE_KEY, cipherText, EMPTY_CONTEXT_INFO);
- JSONObject returnedJSON = new JSONObject(new String(decryptedText));
- Topic returnedTopic =
- Topic.create(
- returnedJSON.getInt(KEY_TOPIC_ID),
- returnedJSON.getLong(KEY_TAXONOMY_VERSION),
- returnedJSON.getLong(KEY_MODEL_VERSION));
-
- // Verify decrypted and deserialized object creates the expected Topic.
- assertThat(returnedTopic).isEqualTo(topic);
- }
-
- @Test
- public void testEncryption_invalidKeyLength_returnsEmpty() {
- Topic topic = Topic.create(/* topic */ 5, /* taxonomyVersion */ 6L, /* modelVersion */ 7L);
- byte[] plainText = generateTopicsPlainText(topic);
-
- // Encrypt plain text to cipher text
- byte[] cipherText =
- mHpkeEncrypter.encrypt(
- "invalidKey".getBytes(StandardCharsets.UTF_8),
- plainText,
- EMPTY_CONTEXT_INFO);
-
- // Verify cipher text is null.
- assertThat(cipherText).isNotNull();
- assertThat(cipherText).isEmpty();
- }
-
- @Test
- public void testEncryption_nullPlainText_throwsException() {
- assertThrows(
- NullPointerException.class,
- () ->
- mHpkeEncrypter.encrypt(
- DECODED_PUBLIC_KEY, /* plainText */ null, EMPTY_CONTEXT_INFO));
- }
-
- @Test
- public void testEncryption_nullPublicKey_throwsException() {
- Topic topic = Topic.create(/* topic */ 5, /* taxonomyVersion */ 6L, /* modelVersion */ 7L);
- byte[] plainText = generateTopicsPlainText(topic);
-
- assertThrows(
- NullPointerException.class,
- () -> mHpkeEncrypter.encrypt(/* publicKey */ null, plainText, EMPTY_CONTEXT_INFO));
- }
-
- @Test
- public void testEncryption_nullContextInfo_throwsException() {
- Topic topic = Topic.create(/* topic */ 5, /* taxonomyVersion */ 6L, /* modelVersion */ 7L);
- byte[] plainText = generateTopicsPlainText(topic);
-
- assertThrows(
- NullPointerException.class,
- () ->
- mHpkeEncrypter.encrypt(
- DECODED_PUBLIC_KEY, plainText, /* contextInfo */ null));
- }
-
- private byte[] generateTopicsPlainText(Topic topic) {
- return TopicsJsonMapper.toJson(topic).get().toString().getBytes(StandardCharsets.UTF_8);
- }
-}
diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/topics/TopicsJsonMapperTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/topics/TopicsJsonMapperTest.java
deleted file mode 100644
index 506544e..0000000
--- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/topics/TopicsJsonMapperTest.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.adservices.service.topics;
-
-import static com.android.adservices.service.topics.TopicsJsonMapper.KEY_MODEL_VERSION;
-import static com.android.adservices.service.topics.TopicsJsonMapper.KEY_TAXONOMY_VERSION;
-import static com.android.adservices.service.topics.TopicsJsonMapper.KEY_TOPIC_ID;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import com.android.adservices.data.topics.Topic;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-import org.junit.Test;
-
-/** Unit tests for {@link TopicsJsonMapper}. */
-public class TopicsJsonMapperTest {
- private static final int TOPIC_ID = 7;
- private static final long MODEL_VERSION = 5L;
- private static final long TAXONOMY_VERSION = 2L;
- private static final Topic SAMPLE_TOPIC =
- Topic.create(TOPIC_ID, TAXONOMY_VERSION, MODEL_VERSION);
-
- @Test
- public void getTopicJson() throws JSONException {
- JSONObject topicJson = TopicsJsonMapper.toJson(SAMPLE_TOPIC).get();
-
- // Verify individual fields
- assertThat(topicJson.getInt(KEY_TOPIC_ID)).isEqualTo(TOPIC_ID);
- assertThat(topicJson.getInt(KEY_MODEL_VERSION)).isEqualTo(MODEL_VERSION);
- assertThat(topicJson.getInt(KEY_TAXONOMY_VERSION)).isEqualTo(TAXONOMY_VERSION);
-
- // Validate JSON string
- assertThat(topicJson.toString())
- .isEqualTo(
- String.format(
- "{\"topic_id\":%d,\"model_version\":%d,"
- + "\"taxonomy_version\":%d}",
- TOPIC_ID, MODEL_VERSION, TAXONOMY_VERSION));
- }
-
- @Test
- public void getTopicJson_deserialization() throws JSONException {
- // Create JSONObject from Topic json string.
- JSONObject expectedJSON =
- new JSONObject(
- String.format(
- "{\"topic_id\":%d,\"model_version\":%d,"
- + "\"taxonomy_version\":%d}",
- TOPIC_ID, MODEL_VERSION, TAXONOMY_VERSION));
-
- // Validate same JSONObjects.
- JSONObject returnedJSON = TopicsJsonMapper.toJson(SAMPLE_TOPIC).get();
- assertThat(returnedJSON.toString()).isEqualTo(expectedJSON.toString());
-
- // Validate Topic object created from JSONObject.
- assertThat(
- Topic.create(
- returnedJSON.getInt(KEY_TOPIC_ID),
- returnedJSON.getLong(KEY_TAXONOMY_VERSION),
- returnedJSON.getLong(KEY_MODEL_VERSION)))
- .isEqualTo(SAMPLE_TOPIC);
- }
-}