Merge "Add CompatibilityVersion v2 to CT downloader" into main
diff --git a/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyDownloader.java b/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyDownloader.java
index fb42c03..41b58fa 100644
--- a/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyDownloader.java
+++ b/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyDownloader.java
@@ -33,8 +33,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.security.GeneralSecurityException;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.Collection;
import java.util.Optional;
/** Helper class to download certificate transparency log files. */
@@ -48,28 +47,21 @@
private final DownloadHelper mDownloadHelper;
private final SignatureVerifier mSignatureVerifier;
private final CertificateTransparencyLogger mLogger;
-
- private final List<CompatibilityVersion> mCompatVersions = new ArrayList<>();
+ private final Collection<CompatibilityVersion> mCompatVersions;
CertificateTransparencyDownloader(
Context context,
DataStore dataStore,
DownloadHelper downloadHelper,
SignatureVerifier signatureVerifier,
- CertificateTransparencyLogger logger) {
+ CertificateTransparencyLogger logger,
+ Collection<CompatibilityVersion> compatVersions) {
mContext = context;
mSignatureVerifier = signatureVerifier;
mDataStore = dataStore;
mDownloadHelper = downloadHelper;
mLogger = logger;
- }
-
- void addCompatibilityVersion(CompatibilityVersion compatVersion) {
- mCompatVersions.add(compatVersion);
- }
-
- void clearCompatibilityVersions() {
- mCompatVersions.clear();
+ mCompatVersions = compatVersions;
}
long startPublicKeyDownload() {
diff --git a/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyJob.java b/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyJob.java
index f1b9a4f..286f326 100644
--- a/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyJob.java
+++ b/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyJob.java
@@ -28,6 +28,8 @@
import android.os.SystemClock;
import android.util.Log;
+import java.util.Collection;
+
/** Implementation of the Certificate Transparency job */
@RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
public class CertificateTransparencyJob extends BroadcastReceiver {
@@ -37,8 +39,8 @@
private final Context mContext;
private final DataStore mDataStore;
private final CertificateTransparencyDownloader mCertificateTransparencyDownloader;
- private final CompatibilityVersion mCompatVersion;
private final SignatureVerifier mSignatureVerifier;
+ private final Collection<CompatibilityVersion> mCompatVersions;
private final AlarmManager mAlarmManager;
private final PendingIntent mPendingIntent;
@@ -50,13 +52,13 @@
Context context,
DataStore dataStore,
CertificateTransparencyDownloader certificateTransparencyDownloader,
- CompatibilityVersion compatVersion,
- SignatureVerifier signatureVerifier) {
+ SignatureVerifier signatureVerifier,
+ Collection<CompatibilityVersion> compatVersions) {
mContext = context;
mDataStore = dataStore;
mCertificateTransparencyDownloader = certificateTransparencyDownloader;
- mCompatVersion = compatVersion;
mSignatureVerifier = signatureVerifier;
+ mCompatVersions = compatVersions;
mAlarmManager = context.getSystemService(AlarmManager.class);
mPendingIntent =
@@ -99,7 +101,9 @@
}
mDependenciesReady = false;
- mCompatVersion.delete();
+ for (CompatibilityVersion compatVersion : mCompatVersions) {
+ compatVersion.delete();
+ }
if (Config.DEBUG) {
Log.d(TAG, "CertificateTransparencyJob canceled.");
@@ -129,7 +133,6 @@
private void startDependencies() {
mDataStore.load();
- mCertificateTransparencyDownloader.addCompatibilityVersion(mCompatVersion);
mSignatureVerifier.loadAllowedKeys();
mContext.registerReceiver(
mCertificateTransparencyDownloader,
@@ -144,7 +147,6 @@
private void stopDependencies() {
mContext.unregisterReceiver(mCertificateTransparencyDownloader);
mSignatureVerifier.clearAllowedKeys();
- mCertificateTransparencyDownloader.clearCompatibilityVersions();
mDataStore.delete();
if (Config.DEBUG) {
diff --git a/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyService.java b/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyService.java
index 2e910b2..5e530c7 100644
--- a/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyService.java
+++ b/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyService.java
@@ -30,6 +30,8 @@
import com.android.server.SystemService;
+import java.util.Arrays;
+import java.util.Collection;
import java.util.concurrent.Executors;
/** Implementation of the Certificate Transparency service. */
@@ -51,8 +53,18 @@
/** Creates a new {@link CertificateTransparencyService} object. */
public CertificateTransparencyService(Context context) {
DataStore dataStore = new DataStore(Config.PREFERENCES_FILE);
-
SignatureVerifier signatureVerifier = new SignatureVerifier(context);
+ Collection<CompatibilityVersion> compatVersions =
+ Arrays.asList(
+ new CompatibilityVersion(
+ Config.COMPATIBILITY_VERSION_V1,
+ Config.URL_SIGNATURE_V1,
+ Config.URL_LOG_LIST_V1),
+ new CompatibilityVersion(
+ Config.COMPATIBILITY_VERSION_V2,
+ Config.URL_SIGNATURE_V2,
+ Config.URL_LOG_LIST_V2));
+
mCertificateTransparencyJob =
new CertificateTransparencyJob(
context,
@@ -62,13 +74,10 @@
dataStore,
new DownloadHelper(context),
signatureVerifier,
- new CertificateTransparencyLoggerImpl(dataStore)),
- new CompatibilityVersion(
- Config.COMPATIBILITY_VERSION,
- Config.URL_SIGNATURE,
- Config.URL_LOG_LIST,
- Config.CT_ROOT_DIRECTORY_PATH),
- signatureVerifier);
+ new CertificateTransparencyLoggerImpl(dataStore),
+ compatVersions),
+ signatureVerifier,
+ compatVersions);
}
/**
diff --git a/networksecurity/service/src/com/android/server/net/ct/CompatibilityVersion.java b/networksecurity/service/src/com/android/server/net/ct/CompatibilityVersion.java
index e8a6e64..0a91963 100644
--- a/networksecurity/service/src/com/android/server/net/ct/CompatibilityVersion.java
+++ b/networksecurity/service/src/com/android/server/net/ct/CompatibilityVersion.java
@@ -23,6 +23,8 @@
import android.system.Os;
import android.util.Log;
+import androidx.annotation.VisibleForTesting;
+
import com.android.server.net.ct.CertificateTransparencyLogger.CTLogListUpdateState;
import org.json.JSONException;
@@ -40,6 +42,8 @@
private static final String TAG = "CompatibilityVersion";
+ private static File sRootDirectory = new File(Config.CT_ROOT_DIRECTORY_PATH);
+
static final String LOGS_DIR_PREFIX = "logs-";
static final String LOGS_LIST_FILE_NAME = "log_list.json";
static final String CURRENT_LOGS_DIR_SYMLINK_NAME = "current";
@@ -48,23 +52,21 @@
private final String mMetadataUrl;
private final String mContentUrl;
- private final File mRootDirectory;
private final File mVersionDirectory;
private final File mCurrentLogsDirSymlink;
CompatibilityVersion(
- String compatVersion, String metadataUrl, String contentUrl, File rootDirectory) {
+ String compatVersion, String metadataUrl, String contentUrl) {
mCompatVersion = compatVersion;
mMetadataUrl = metadataUrl;
mContentUrl = contentUrl;
- mRootDirectory = rootDirectory;
- mVersionDirectory = new File(rootDirectory, compatVersion);
+ mVersionDirectory = new File(sRootDirectory, compatVersion);
mCurrentLogsDirSymlink = new File(mVersionDirectory, CURRENT_LOGS_DIR_SYMLINK_NAME);
}
- CompatibilityVersion(
- String compatVersion, String metadataUrl, String contentUrl, String rootDirectoryPath) {
- this(compatVersion, metadataUrl, contentUrl, new File(rootDirectoryPath));
+ @VisibleForTesting
+ static void setRootDirectoryForTesting(File rootDirectory) {
+ sRootDirectory = rootDirectory;
}
/**
@@ -75,8 +77,8 @@
* @return true if the log list was installed successfully, false otherwise.
* @throws IOException if the list cannot be saved in the CT directory.
*/
- LogListUpdateStatus install(
- InputStream newContent, LogListUpdateStatus.Builder statusBuilder) throws IOException {
+ LogListUpdateStatus install(InputStream newContent, LogListUpdateStatus.Builder statusBuilder)
+ throws IOException {
String content = new String(newContent.readAllBytes(), UTF_8);
try {
JSONObject contentJson = new JSONObject(content);
@@ -98,7 +100,7 @@
// there's a bunch of steps. We create a new directory with the logs and then do
// an atomic update of the current symlink to point to the new directory.
// 1. Ensure the path to the root and version directories exist and are readable.
- DirectoryUtils.makeDir(mRootDirectory);
+ DirectoryUtils.makeDir(sRootDirectory);
DirectoryUtils.makeDir(mVersionDirectory);
File newLogsDir = new File(mVersionDirectory, LOGS_DIR_PREFIX + version);
diff --git a/networksecurity/service/src/com/android/server/net/ct/Config.java b/networksecurity/service/src/com/android/server/net/ct/Config.java
index 5fdba09..72b715a 100644
--- a/networksecurity/service/src/com/android/server/net/ct/Config.java
+++ b/networksecurity/service/src/com/android/server/net/ct/Config.java
@@ -33,18 +33,14 @@
private static final String PREFERENCES_FILE_NAME = "ct.preferences";
static final File PREFERENCES_FILE = new File(DEVICE_PROTECTED_DATA_DIR, PREFERENCES_FILE_NAME);
- // CT directory
+ // CT paths
static final String CT_ROOT_DIRECTORY_PATH = "/data/misc/keychain/ct/";
- static final String COMPATIBILITY_VERSION = "v1";
+ static final String URL_PREFIX = "https://www.gstatic.com/android/certificate_transparency/";
// Phenotype flags
static final String NAMESPACE_NETWORK_SECURITY = "network_security";
private static final String FLAGS_PREFIX = "CertificateTransparencyLogList__";
static final String FLAG_SERVICE_ENABLED = FLAGS_PREFIX + "service_enabled";
- static final String FLAG_CONTENT_URL = FLAGS_PREFIX + "content_url";
- static final String FLAG_METADATA_URL = FLAGS_PREFIX + "metadata_url";
- static final String FLAG_VERSION = FLAGS_PREFIX + "version";
- static final String FLAG_PUBLIC_KEY = FLAGS_PREFIX + "public_key";
// properties
static final String VERSION = "version";
@@ -53,9 +49,18 @@
static final String PUBLIC_KEY_DOWNLOAD_ID = "public_key_download_id";
static final String LOG_LIST_UPDATE_FAILURE_COUNT = "log_list_update_failure_count";
- // URLs
- static final String URL_PREFIX = "https://www.gstatic.com/android/certificate_transparency/";
- static final String URL_LOG_LIST = URL_PREFIX + "log_list.json";
- static final String URL_SIGNATURE = URL_PREFIX + "log_list.sig";
+ // Public Key URLs
static final String URL_PUBLIC_KEY = URL_PREFIX + "log_list.pub";
+
+ // Compatibility Version v1
+ static final String COMPATIBILITY_VERSION_V1 = "v1";
+ static final String URL_PREFIX_V1 = URL_PREFIX;
+ static final String URL_LOG_LIST_V1 = URL_PREFIX_V1 + "log_list.json";
+ static final String URL_SIGNATURE_V1 = URL_PREFIX_V1 + "log_list.sig";
+
+ // Compatibility Version v2
+ static final String COMPATIBILITY_VERSION_V2 = "v2";
+ static final String URL_PREFIX_V2 = URL_PREFIX + COMPATIBILITY_VERSION_V2 + "/";
+ static final String URL_LOG_LIST_V2 = URL_PREFIX_V2 + "log_list.json";
+ static final String URL_SIGNATURE_V2 = URL_PREFIX_V2 + "log_list.sig";
}
diff --git a/networksecurity/tests/unit/src/com/android/server/net/ct/CertificateTransparencyDownloaderTest.java b/networksecurity/tests/unit/src/com/android/server/net/ct/CertificateTransparencyDownloaderTest.java
index 22dc6ab..956bad5 100644
--- a/networksecurity/tests/unit/src/com/android/server/net/ct/CertificateTransparencyDownloaderTest.java
+++ b/networksecurity/tests/unit/src/com/android/server/net/ct/CertificateTransparencyDownloaderTest.java
@@ -60,6 +60,7 @@
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
+import java.util.Arrays;
import java.util.Base64;
import java.util.Optional;
@@ -94,24 +95,25 @@
mContext = InstrumentationRegistry.getInstrumentation().getContext();
mDataStore = new DataStore(File.createTempFile("datastore-test", ".properties"));
mSignatureVerifier = new SignatureVerifier(mContext);
+
+ CompatibilityVersion.setRootDirectoryForTesting(mContext.getFilesDir());
+ mCompatVersion =
+ new CompatibilityVersion(
+ /* compatVersion= */ "v666",
+ Config.URL_SIGNATURE_V1,
+ Config.URL_LOG_LIST_V1);
mCertificateTransparencyDownloader =
new CertificateTransparencyDownloader(
mContext,
mDataStore,
new DownloadHelper(mDownloadManager),
mSignatureVerifier,
- mLogger);
- mCompatVersion =
- new CompatibilityVersion(
- /* compatVersion= */ "v666",
- Config.URL_SIGNATURE,
- Config.URL_LOG_LIST,
- mContext.getFilesDir());
+ mLogger,
+ Arrays.asList(mCompatVersion));
prepareDownloadManager();
mSignatureVerifier.addAllowedKey(mPublicKey);
mDataStore.load();
- mCertificateTransparencyDownloader.addCompatibilityVersion(mCompatVersion);
}
@After
diff --git a/networksecurity/tests/unit/src/com/android/server/net/ct/CompatibilityVersionTest.java b/networksecurity/tests/unit/src/com/android/server/net/ct/CompatibilityVersionTest.java
index 2b8b3cd..0d15183 100644
--- a/networksecurity/tests/unit/src/com/android/server/net/ct/CompatibilityVersionTest.java
+++ b/networksecurity/tests/unit/src/com/android/server/net/ct/CompatibilityVersionTest.java
@@ -27,6 +27,7 @@
import org.json.JSONException;
import org.json.JSONObject;
import org.junit.After;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -47,9 +48,16 @@
private final File mTestDir =
InstrumentationRegistry.getInstrumentation().getContext().getFilesDir();
- private final CompatibilityVersion mCompatVersion =
- new CompatibilityVersion(
- TEST_VERSION, Config.URL_SIGNATURE, Config.URL_LOG_LIST, mTestDir);
+
+ private CompatibilityVersion mCompatVersion;
+
+ @Before
+ public void setUp() {
+ CompatibilityVersion.setRootDirectoryForTesting(mTestDir);
+ mCompatVersion =
+ new CompatibilityVersion(
+ TEST_VERSION, Config.URL_SIGNATURE_V1, Config.URL_LOG_LIST_V1);
+ }
@After
public void tearDown() {
@@ -111,9 +119,7 @@
JSONObject logList = makeLogList(version, "i_am_content");
try (InputStream inputStream = asStream(logList)) {
- assertThat(
- mCompatVersion.install(
- inputStream, LogListUpdateStatus.builder()))
+ assertThat(mCompatVersion.install(inputStream, LogListUpdateStatus.builder()))
.isEqualTo(getSuccessfulUpdateStatus());
}
@@ -142,9 +148,7 @@
@Test
public void testCompatibilityVersion_deleteSuccessfully() throws Exception {
try (InputStream inputStream = asStream(makeLogList(/* version= */ "123"))) {
- assertThat(
- mCompatVersion.install(
- inputStream, LogListUpdateStatus.builder()))
+ assertThat(mCompatVersion.install(inputStream, LogListUpdateStatus.builder()))
.isEqualTo(getSuccessfulUpdateStatus());
}
@@ -156,9 +160,7 @@
@Test
public void testCompatibilityVersion_invalidLogList() throws Exception {
try (InputStream inputStream = new ByteArrayInputStream(("not_a_valid_list".getBytes()))) {
- assertThat(
- mCompatVersion.install(
- inputStream, LogListUpdateStatus.builder()))
+ assertThat(mCompatVersion.install(inputStream, LogListUpdateStatus.builder()))
.isEqualTo(LogListUpdateStatus.builder().setState(LOG_LIST_INVALID).build());
}
@@ -179,9 +181,7 @@
JSONObject newLogList = makeLogList(existingVersion, "i_am_the_real_content");
try (InputStream inputStream = asStream(newLogList)) {
- assertThat(
- mCompatVersion.install(
- inputStream, LogListUpdateStatus.builder()))
+ assertThat(mCompatVersion.install(inputStream, LogListUpdateStatus.builder()))
.isEqualTo(getSuccessfulUpdateStatus());
}
@@ -193,16 +193,12 @@
String existingVersion = "666";
JSONObject existingLogList = makeLogList(existingVersion, "i_was_installed_successfully");
try (InputStream inputStream = asStream(existingLogList)) {
- assertThat(
- mCompatVersion.install(
- inputStream, LogListUpdateStatus.builder()))
+ assertThat(mCompatVersion.install(inputStream, LogListUpdateStatus.builder()))
.isEqualTo(getSuccessfulUpdateStatus());
}
try (InputStream inputStream = asStream(makeLogList(existingVersion, "i_am_ignored"))) {
- assertThat(
- mCompatVersion.install(
- inputStream, LogListUpdateStatus.builder()))
+ assertThat(mCompatVersion.install(inputStream, LogListUpdateStatus.builder()))
.isEqualTo(
LogListUpdateStatus.builder()
.setState(VERSION_ALREADY_EXISTS)