Merge "Remove obsolete CtsTetheringTestLatestSdk" 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 45871de..34a2066 100644
--- a/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyDownloader.java
+++ b/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyDownloader.java
@@ -21,7 +21,6 @@
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
-import android.content.IntentFilter;
import android.net.Uri;
import android.os.Build;
import android.util.Log;
@@ -34,7 +33,6 @@
import java.io.IOException;
import java.io.InputStream;
import java.security.GeneralSecurityException;
-import java.security.InvalidKeyException;
import java.util.ArrayList;
import java.util.List;
@@ -52,8 +50,6 @@
private final List<CompatibilityVersion> mCompatVersions = new ArrayList<>();
- private boolean started = false;
-
CertificateTransparencyDownloader(
Context context,
DataStore dataStore,
@@ -71,33 +67,8 @@
mCompatVersions.add(compatVersion);
}
- void start() {
- if (started) {
- return;
- }
- mContext.registerReceiver(
- this,
- new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE),
- Context.RECEIVER_EXPORTED);
- mDataStore.load();
- started = true;
-
- if (Config.DEBUG) {
- Log.d(TAG, "CertificateTransparencyDownloader started.");
- }
- }
-
- void stop() {
- if (!started) {
- return;
- }
- mContext.unregisterReceiver(this);
- mDataStore.delete();
- started = false;
-
- if (Config.DEBUG) {
- Log.d(TAG, "CertificateTransparencyDownloader stopped.");
- }
+ void clearCompatibilityVersions() {
+ mCompatVersions.clear();
}
long startPublicKeyDownload() {
@@ -226,40 +197,24 @@
return;
}
- boolean success = false;
- CTLogListUpdateState failureReason = CTLogListUpdateState.UNKNOWN_STATE;
+ LogListUpdateStatus updateStatus = mSignatureVerifier.verify(contentUri, metadataUri);
+ // TODO(b/391327942): parse file and log the timestamp of the log list
- try {
- success = mSignatureVerifier.verify(contentUri, metadataUri);
- } catch (MissingPublicKeyException e) {
+ if (!updateStatus.isSignatureVerified()) {
updateFailureCount();
- failureReason = CTLogListUpdateState.PUBLIC_KEY_NOT_FOUND;
- Log.e(TAG, "No public key found for log list verification", e);
- } catch (InvalidKeyException e) {
- updateFailureCount();
- failureReason = CTLogListUpdateState.SIGNATURE_INVALID;
- Log.e(TAG, "Signature invalid for log list verification", e);
- } catch (IOException | GeneralSecurityException e) {
- Log.e(TAG, "Could not verify new log list", e);
- }
-
- if (!success) {
Log.w(TAG, "Log list did not pass verification");
- // Avoid logging failure twice
- if (failureReason == CTLogListUpdateState.UNKNOWN_STATE) {
- updateFailureCount();
- failureReason = CTLogListUpdateState.SIGNATURE_VERIFICATION_FAILED;
- }
-
mLogger.logCTLogListUpdateStateChangedEvent(
- failureReason,
+ updateStatus.state(),
mDataStore.getPropertyInt(
- Config.LOG_LIST_UPDATE_FAILURE_COUNT, /* defaultValue= */ 0));
+ Config.LOG_LIST_UPDATE_FAILURE_COUNT, /* defaultValue= */ 0),
+ updateStatus.signature());
return;
}
+ boolean success = false;
+
try (InputStream inputStream = mContext.getContentResolver().openInputStream(contentUri)) {
success = compatVersion.install(inputStream);
} catch (IOException e) {
@@ -276,7 +231,8 @@
mLogger.logCTLogListUpdateStateChangedEvent(
CTLogListUpdateState.VERSION_ALREADY_EXISTS,
mDataStore.getPropertyInt(
- Config.LOG_LIST_UPDATE_FAILURE_COUNT, /* defaultValue= */ 0));
+ Config.LOG_LIST_UPDATE_FAILURE_COUNT, /* defaultValue= */ 0),
+ updateStatus.signature());
}
}
@@ -288,6 +244,7 @@
mDataStore.getPropertyInt(
Config.LOG_LIST_UPDATE_FAILURE_COUNT, /* defaultValue= */ 0);
+ // Unable to log the signature, as that is dependent on successful downloads
if (status.isHttpError()) {
mLogger.logCTLogListUpdateStateChangedEvent(
CTLogListUpdateState.HTTP_ERROR,
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 a8acc60..e6f1379 100644
--- a/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyJob.java
+++ b/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyJob.java
@@ -17,12 +17,12 @@
import android.annotation.RequiresApi;
import android.app.AlarmManager;
+import android.app.DownloadManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.pm.PackageManager;
import android.os.Build;
import android.os.ConfigUpdate;
import android.os.SystemClock;
@@ -33,28 +33,28 @@
public class CertificateTransparencyJob extends BroadcastReceiver {
private static final String TAG = "CertificateTransparencyJob";
- private static final String UPDATE_CONFIG_PERMISSION = "android.permission.UPDATE_CONFIG";
private final Context mContext;
- private final CompatibilityVersion mCompatVersion;
+ private final DataStore mDataStore;
private final CertificateTransparencyDownloader mCertificateTransparencyDownloader;
+ private final CompatibilityVersion mCompatVersion;
private final AlarmManager mAlarmManager;
private final PendingIntent mPendingIntent;
+ private boolean mScheduled = false;
private boolean mDependenciesReady = false;
/** Creates a new {@link CertificateTransparencyJob} object. */
public CertificateTransparencyJob(
- Context context, CertificateTransparencyDownloader certificateTransparencyDownloader) {
+ Context context,
+ DataStore dataStore,
+ CertificateTransparencyDownloader certificateTransparencyDownloader,
+ CompatibilityVersion compatVersion) {
mContext = context;
- mCompatVersion =
- new CompatibilityVersion(
- Config.COMPATIBILITY_VERSION,
- Config.URL_SIGNATURE,
- Config.URL_LOG_LIST,
- Config.CT_ROOT_DIRECTORY_PATH);
+ mDataStore = dataStore;
mCertificateTransparencyDownloader = certificateTransparencyDownloader;
- mCertificateTransparencyDownloader.addCompatibilityVersion(mCompatVersion);
+ mCompatVersion = compatVersion;
+
mAlarmManager = context.getSystemService(AlarmManager.class);
mPendingIntent =
PendingIntent.getBroadcast(
@@ -65,15 +65,19 @@
}
void schedule() {
- mContext.registerReceiver(
- this,
- new IntentFilter(ConfigUpdate.ACTION_UPDATE_CT_LOGS),
- Context.RECEIVER_EXPORTED);
- mAlarmManager.setInexactRepeating(
- AlarmManager.ELAPSED_REALTIME,
- SystemClock.elapsedRealtime(), // schedule first job at earliest convenient time.
- AlarmManager.INTERVAL_DAY,
- mPendingIntent);
+ if (!mScheduled) {
+ mContext.registerReceiver(
+ this,
+ new IntentFilter(ConfigUpdate.ACTION_UPDATE_CT_LOGS),
+ Context.RECEIVER_EXPORTED);
+ mAlarmManager.setInexactRepeating(
+ AlarmManager.ELAPSED_REALTIME,
+ SystemClock
+ .elapsedRealtime(), // schedule first job at earliest convenient time.
+ AlarmManager.INTERVAL_DAY,
+ mPendingIntent);
+ }
+ mScheduled = true;
if (Config.DEBUG) {
Log.d(TAG, "CertificateTransparencyJob scheduled.");
@@ -81,12 +85,19 @@
}
void cancel() {
- mContext.unregisterReceiver(this);
- mAlarmManager.cancel(mPendingIntent);
- mCertificateTransparencyDownloader.stop();
- mCompatVersion.delete();
+ if (mScheduled) {
+ mContext.unregisterReceiver(this);
+ mAlarmManager.cancel(mPendingIntent);
+ }
+ mScheduled = false;
+
+ if (mDependenciesReady) {
+ stopDependencies();
+ }
mDependenciesReady = false;
+ mCompatVersion.delete();
+
if (Config.DEBUG) {
Log.d(TAG, "CertificateTransparencyJob canceled.");
}
@@ -98,16 +109,11 @@
Log.w(TAG, "Received unexpected broadcast with action " + intent);
return;
}
- if (context.checkCallingOrSelfPermission(UPDATE_CONFIG_PERMISSION)
- != PackageManager.PERMISSION_GRANTED) {
- Log.e(TAG, "Caller does not have UPDATE_CONFIG permission.");
- return;
- }
if (Config.DEBUG) {
Log.d(TAG, "Starting CT daily job.");
}
if (!mDependenciesReady) {
- mCertificateTransparencyDownloader.start();
+ startDependencies();
mDependenciesReady = true;
}
@@ -117,4 +123,27 @@
Log.d(TAG, "Public key download started successfully.");
}
}
+
+ private void startDependencies() {
+ mDataStore.load();
+ mCertificateTransparencyDownloader.addCompatibilityVersion(mCompatVersion);
+ mContext.registerReceiver(
+ mCertificateTransparencyDownloader,
+ new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE),
+ Context.RECEIVER_EXPORTED);
+
+ if (Config.DEBUG) {
+ Log.d(TAG, "CertificateTransparencyJob dependencies ready.");
+ }
+ }
+
+ private void stopDependencies() {
+ mContext.unregisterReceiver(mCertificateTransparencyDownloader);
+ mCertificateTransparencyDownloader.clearCompatibilityVersions();
+ mDataStore.delete();
+
+ if (Config.DEBUG) {
+ Log.d(TAG, "CertificateTransparencyJob dependencies stopped.");
+ }
+ }
}
diff --git a/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyLogger.java b/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyLogger.java
index 8d53983..0b415f0 100644
--- a/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyLogger.java
+++ b/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyLogger.java
@@ -33,8 +33,10 @@
*
* @param failureReason reason why the log list wasn't updated
* @param failureCount number of consecutive log list update failures
+ * @param logListSignature signature used during log list verification
*/
- void logCTLogListUpdateStateChangedEvent(CTLogListUpdateState failureReason, int failureCount);
+ void logCTLogListUpdateStateChangedEvent(
+ CTLogListUpdateState failureReason, int failureCount, String logListSignature);
/**
* Logs a CTLogListUpdateStateChanged event to statsd with an HTTP error status code.
@@ -44,7 +46,9 @@
* @param httpErrorStatusCode if relevant, the HTTP error status code from DownloadManager
*/
void logCTLogListUpdateStateChangedEvent(
- CTLogListUpdateState failureReason, int failureCount, int httpErrorStatusCode);
+ CTLogListUpdateState failureReason,
+ int failureCount,
+ int httpErrorStatusCode);
/**
* Intermediate enum for use with CertificateTransparencyStatsLog.
diff --git a/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyLoggerImpl.java b/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyLoggerImpl.java
index 6accdf8..4a0689a 100644
--- a/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyLoggerImpl.java
+++ b/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyLoggerImpl.java
@@ -41,33 +41,40 @@
logCTLogListUpdateStateChangedEvent(
downloadStatusToFailureReason(downloadStatus),
failureCount,
- /* httpErrorStatusCode= */ 0);
+ /* httpErrorStatusCode= */ 0,
+ /* signature= */ "");
}
@Override
public void logCTLogListUpdateStateChangedEvent(
- CTLogListUpdateState failureReason, int failureCount) {
+ CTLogListUpdateState failureReason, int failureCount, String signature) {
logCTLogListUpdateStateChangedEvent(
localEnumToStatsLogEnum(failureReason),
failureCount,
- /* httpErrorStatusCode= */ 0);
+ /* httpErrorStatusCode= */ 0,
+ signature);
}
@Override
public void logCTLogListUpdateStateChangedEvent(
- CTLogListUpdateState failureReason, int failureCount, int httpErrorStatusCode) {
+ CTLogListUpdateState failureReason,
+ int failureCount,
+ int httpErrorStatusCode) {
logCTLogListUpdateStateChangedEvent(
- localEnumToStatsLogEnum(failureReason), failureCount, httpErrorStatusCode);
+ localEnumToStatsLogEnum(failureReason),
+ failureCount,
+ httpErrorStatusCode,
+ /* signature= */ "");
}
private void logCTLogListUpdateStateChangedEvent(
- int failureReason, int failureCount, int httpErrorStatusCode) {
+ int failureReason, int failureCount, int httpErrorStatusCode, String signature) {
CertificateTransparencyStatsLog.write(
CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_STATE_CHANGED,
failureReason,
failureCount,
httpErrorStatusCode,
- /* signature= */ "",
+ signature,
/* logListTimestampMs= */ 0);
}
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 ed98056..7edc35a 100644
--- a/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyService.java
+++ b/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyService.java
@@ -41,8 +41,6 @@
private final CertificateTransparencyJob mCertificateTransparencyJob;
- private boolean started = false;
-
/**
* @return true if the CertificateTransparency service is enabled.
*/
@@ -53,16 +51,22 @@
/** Creates a new {@link CertificateTransparencyService} object. */
public CertificateTransparencyService(Context context) {
DataStore dataStore = new DataStore(Config.PREFERENCES_FILE);
- DownloadHelper downloadHelper = new DownloadHelper(context);
- SignatureVerifier signatureVerifier = new SignatureVerifier(context);
- CertificateTransparencyDownloader downloader =
- new CertificateTransparencyDownloader(
+
+ mCertificateTransparencyJob =
+ new CertificateTransparencyJob(
context,
dataStore,
- downloadHelper,
- signatureVerifier,
- new CertificateTransparencyLoggerImpl());
- mCertificateTransparencyJob = new CertificateTransparencyJob(context, downloader);
+ new CertificateTransparencyDownloader(
+ context,
+ dataStore,
+ new DownloadHelper(context),
+ new SignatureVerifier(context),
+ new CertificateTransparencyLoggerImpl()),
+ new CompatibilityVersion(
+ Config.COMPATIBILITY_VERSION,
+ Config.URL_SIGNATURE,
+ Config.URL_LOG_LIST,
+ Config.CT_ROOT_DIRECTORY_PATH));
}
/**
@@ -104,19 +108,13 @@
if (Config.DEBUG) {
Log.d(TAG, "CertificateTransparencyService start");
}
- if (!started) {
- mCertificateTransparencyJob.schedule();
- started = true;
- }
+ mCertificateTransparencyJob.schedule();
}
private void stopService() {
if (Config.DEBUG) {
Log.d(TAG, "CertificateTransparencyService stop");
}
- if (started) {
- mCertificateTransparencyJob.cancel();
- started = false;
- }
+ mCertificateTransparencyJob.cancel();
}
}
diff --git a/networksecurity/service/src/com/android/server/net/ct/LogListUpdateStatus.java b/networksecurity/service/src/com/android/server/net/ct/LogListUpdateStatus.java
new file mode 100644
index 0000000..0c75120
--- /dev/null
+++ b/networksecurity/service/src/com/android/server/net/ct/LogListUpdateStatus.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2024 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.server.net.ct;
+
+import static com.android.server.net.ct.CertificateTransparencyLogger.CTLogListUpdateState.PUBLIC_KEY_NOT_FOUND;
+import static com.android.server.net.ct.CertificateTransparencyLogger.CTLogListUpdateState.SIGNATURE_INVALID;
+import static com.android.server.net.ct.CertificateTransparencyLogger.CTLogListUpdateState.SIGNATURE_NOT_FOUND;
+import static com.android.server.net.ct.CertificateTransparencyLogger.CTLogListUpdateState.SIGNATURE_VERIFICATION_FAILED;
+
+import com.android.server.net.ct.CertificateTransparencyLogger.CTLogListUpdateState;
+
+import com.google.auto.value.AutoValue;
+
+/** Class to represent the signature verification status for Certificate Transparency. */
+@AutoValue
+public abstract class LogListUpdateStatus {
+
+ abstract CTLogListUpdateState state();
+
+ abstract String signature();
+
+ abstract long logListTimestamp();
+
+ boolean isSignatureVerified() {
+ // Check that none of the signature verification failures have been set as the state
+ return state() != PUBLIC_KEY_NOT_FOUND
+ && state() != SIGNATURE_INVALID
+ && state() != SIGNATURE_NOT_FOUND
+ && state() != SIGNATURE_VERIFICATION_FAILED;
+ }
+
+ boolean hasSignature() {
+ return signature() != null && signature().length() > 0;
+ }
+
+ @AutoValue.Builder
+ abstract static class Builder {
+ abstract Builder setState(CTLogListUpdateState updateState);
+
+ abstract Builder setSignature(String signature);
+
+ abstract Builder setLogListTimestamp(long timestamp);
+
+ abstract LogListUpdateStatus build();
+ }
+
+ abstract LogListUpdateStatus.Builder toBuilder();
+
+ static Builder builder() {
+ return new AutoValue_LogListUpdateStatus.Builder()
+ .setState(CTLogListUpdateState.UNKNOWN_STATE)
+ .setSignature("")
+ .setLogListTimestamp(0L);
+ }
+}
diff --git a/networksecurity/service/src/com/android/server/net/ct/MissingPublicKeyException.java b/networksecurity/service/src/com/android/server/net/ct/MissingPublicKeyException.java
deleted file mode 100644
index 80607f6..0000000
--- a/networksecurity/service/src/com/android/server/net/ct/MissingPublicKeyException.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2024 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.server.net.ct;
-
-/**
- * An exception thrown when the public key is missing for CT signature verification.
- */
-public class MissingPublicKeyException extends Exception {
-
- public MissingPublicKeyException(String message) {
- super(message);
- }
-}
diff --git a/networksecurity/service/src/com/android/server/net/ct/SignatureVerifier.java b/networksecurity/service/src/com/android/server/net/ct/SignatureVerifier.java
index 67ef63f..3ba56db 100644
--- a/networksecurity/service/src/com/android/server/net/ct/SignatureVerifier.java
+++ b/networksecurity/service/src/com/android/server/net/ct/SignatureVerifier.java
@@ -15,6 +15,11 @@
*/
package com.android.server.net.ct;
+import static com.android.server.net.ct.CertificateTransparencyLogger.CTLogListUpdateState.PUBLIC_KEY_NOT_FOUND;
+import static com.android.server.net.ct.CertificateTransparencyLogger.CTLogListUpdateState.SIGNATURE_INVALID;
+import static com.android.server.net.ct.CertificateTransparencyLogger.CTLogListUpdateState.SIGNATURE_NOT_FOUND;
+import static com.android.server.net.ct.CertificateTransparencyLogger.CTLogListUpdateState.SIGNATURE_VERIFICATION_FAILED;
+
import android.annotation.NonNull;
import android.annotation.RequiresApi;
import android.content.ContentResolver;
@@ -27,7 +32,9 @@
import java.io.IOException;
import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.Signature;
@@ -40,6 +47,7 @@
public class SignatureVerifier {
private final Context mContext;
+ private static final String TAG = "SignatureVerifier";
@NonNull private Optional<PublicKey> mPublicKey = Optional.empty();
@@ -79,30 +87,54 @@
mPublicKey = Optional.of(publicKey);
}
- boolean verify(Uri file, Uri signature)
- throws GeneralSecurityException, IOException, MissingPublicKeyException {
+ LogListUpdateStatus verify(Uri file, Uri signature) {
+ LogListUpdateStatus.Builder statusBuilder = LogListUpdateStatus.builder();
+
if (!mPublicKey.isPresent()) {
- throw new MissingPublicKeyException("Missing public key for signature verification");
+ statusBuilder.setState(PUBLIC_KEY_NOT_FOUND);
+ Log.e(TAG, "No public key found for log list verification");
+ return statusBuilder.build();
}
- Signature verifier = Signature.getInstance("SHA256withRSA");
- verifier.initVerify(mPublicKey.get());
+
ContentResolver contentResolver = mContext.getContentResolver();
- boolean success = false;
try (InputStream fileStream = contentResolver.openInputStream(file);
InputStream signatureStream = contentResolver.openInputStream(signature)) {
+ Signature verifier = Signature.getInstance("SHA256withRSA");
+ verifier.initVerify(mPublicKey.get());
verifier.update(fileStream.readAllBytes());
byte[] signatureBytes = signatureStream.readAllBytes();
try {
- success = verifier.verify(Base64.getDecoder().decode(signatureBytes));
+ byte[] decodedSigBytes = Base64.getDecoder().decode(signatureBytes);
+ statusBuilder.setSignature(new String(decodedSigBytes, StandardCharsets.UTF_8));
+
+ if (!verifier.verify(decodedSigBytes)) {
+ // Leave the UpdateState as UNKNOWN_STATE if successful as there are other
+ // potential failures past the signature verification step
+ statusBuilder.setState(SIGNATURE_VERIFICATION_FAILED);
+ }
} catch (IllegalArgumentException e) {
- Log.w("CertificateTransparencyDownloader", "Invalid signature base64 encoding", e);
- // TODO: remove the fallback once the signature base64 is published
- Log.i("CertificateTransparencyDownloader", "Signature verification as raw bytes");
- success = verifier.verify(signatureBytes);
+ Log.w(TAG, "Invalid signature base64 encoding", e);
+ statusBuilder.setSignature(new String(signatureBytes, StandardCharsets.UTF_8));
+ statusBuilder.setState(SIGNATURE_INVALID);
+ return statusBuilder.build();
}
+ } catch (InvalidKeyException e) {
+ Log.e(TAG, "Signature invalid for log list verification", e);
+ statusBuilder.setState(SIGNATURE_INVALID);
+ return statusBuilder.build();
+ } catch (IOException | GeneralSecurityException e) {
+ Log.e(TAG, "Could not verify new log list", e);
+ statusBuilder.setState(SIGNATURE_VERIFICATION_FAILED);
+ return statusBuilder.build();
}
- return success;
+
+ // Double check if the signature is empty that we set the state correctly
+ if (!statusBuilder.build().hasSignature()) {
+ statusBuilder.setState(SIGNATURE_NOT_FOUND);
+ }
+
+ return statusBuilder.build();
}
}
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 ec4d6be..fab28b7 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
@@ -16,10 +16,12 @@
package com.android.server.net.ct;
+import static com.google.common.io.Files.toByteArray;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
@@ -57,6 +59,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
@@ -108,15 +111,15 @@
mContext.getFilesDir());
prepareDownloadManager();
+ mDataStore.load();
mCertificateTransparencyDownloader.addCompatibilityVersion(mCompatVersion);
- mCertificateTransparencyDownloader.start();
}
@After
public void tearDown() {
mSignatureVerifier.resetPublicKey();
- mCertificateTransparencyDownloader.stop();
mCompatVersion.delete();
+ mDataStore.delete();
}
@Test
@@ -335,7 +338,7 @@
@Test
public void
- testDownloader_contentDownloadSuccess_noSignatureFound_logsSingleFailure()
+ testDownloader_contentDownloadSuccess_noPublicKeyFound_logsSingleFailure()
throws Exception {
File logListFile = makeLogListFile("456");
File metadataFile = sign(logListFile);
@@ -356,19 +359,23 @@
verify(mLogger, times(1))
.logCTLogListUpdateStateChangedEvent(
CTLogListUpdateState.PUBLIC_KEY_NOT_FOUND,
- /* failureCount= */ 1);
+ /* failureCount= */ 1,
+ "");
verify(mLogger, never())
.logCTLogListUpdateStateChangedEvent(
eq(CTLogListUpdateState.SIGNATURE_NOT_FOUND),
- anyInt());
+ anyInt(),
+ anyString());
verify(mLogger, never())
.logCTLogListUpdateStateChangedEvent(
eq(CTLogListUpdateState.SIGNATURE_INVALID),
- anyInt());
+ anyInt(),
+ anyString());
verify(mLogger, never())
.logCTLogListUpdateStateChangedEvent(
eq(CTLogListUpdateState.SIGNATURE_VERIFICATION_FAILED),
- anyInt());
+ anyInt(),
+ anyString());
}
@Test
@@ -398,19 +405,23 @@
verify(mLogger, times(1))
.logCTLogListUpdateStateChangedEvent(
CTLogListUpdateState.SIGNATURE_INVALID,
- /* failureCount= */ 1);
+ /* failureCount= */ 1,
+ ""); // Should be empty b/c invalid key exception thrown
verify(mLogger, never())
.logCTLogListUpdateStateChangedEvent(
eq(CTLogListUpdateState.SIGNATURE_VERIFICATION_FAILED),
- anyInt());
+ anyInt(),
+ anyString());
verify(mLogger, never())
.logCTLogListUpdateStateChangedEvent(
eq(CTLogListUpdateState.PUBLIC_KEY_NOT_FOUND),
- anyInt());
+ anyInt(),
+ anyString());
verify(mLogger, never())
.logCTLogListUpdateStateChangedEvent(
eq(CTLogListUpdateState.SIGNATURE_NOT_FOUND),
- anyInt());
+ anyInt(),
+ anyString());
}
@Test
@@ -440,19 +451,24 @@
verify(mLogger, never())
.logCTLogListUpdateStateChangedEvent(
eq(CTLogListUpdateState.SIGNATURE_NOT_FOUND),
- anyInt());
+ anyInt(),
+ anyString());
verify(mLogger, never())
.logCTLogListUpdateStateChangedEvent(
eq(CTLogListUpdateState.SIGNATURE_INVALID),
- anyInt());
+ anyInt(),
+ anyString());
verify(mLogger, never())
.logCTLogListUpdateStateChangedEvent(
eq(CTLogListUpdateState.PUBLIC_KEY_NOT_FOUND),
- anyInt());
+ anyInt(),
+ anyString());
+ byte[] signatureBytes = Base64.getDecoder().decode(toByteArray(metadataFile));
verify(mLogger, times(1))
.logCTLogListUpdateStateChangedEvent(
CTLogListUpdateState.SIGNATURE_VERIFICATION_FAILED,
- /* failureCount= */ 1);
+ /* failureCount= */ 1,
+ new String(signatureBytes, StandardCharsets.UTF_8));
}
@Test
@@ -473,10 +489,12 @@
mDataStore.getPropertyInt(
Config.LOG_LIST_UPDATE_FAILURE_COUNT, /* defaultValue= */ 0))
.isEqualTo(1);
+ byte[] signatureBytes = Base64.getDecoder().decode(toByteArray(metadataFile));
verify(mLogger, times(1))
.logCTLogListUpdateStateChangedEvent(
CTLogListUpdateState.VERSION_ALREADY_EXISTS,
- /* failureCount= */ 1);
+ /* failureCount= */ 1,
+ new String(signatureBytes, StandardCharsets.UTF_8));
}
@Test
diff --git a/staticlibs/testutils/devicetests/com/android/testutils/CarrierConfigRule.kt b/staticlibs/testutils/devicetests/com/android/testutils/CarrierConfigRule.kt
index a93ae3e..ae0de79 100644
--- a/staticlibs/testutils/devicetests/com/android/testutils/CarrierConfigRule.kt
+++ b/staticlibs/testutils/devicetests/com/android/testutils/CarrierConfigRule.kt
@@ -18,28 +18,36 @@
import android.Manifest.permission.MODIFY_PHONE_STATE
import android.Manifest.permission.READ_PHONE_STATE
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.os.ConditionVariable
import android.os.PersistableBundle
import android.telephony.CarrierConfigManager
+import android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED
+import android.telephony.SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX
import android.util.Log
import androidx.test.platform.app.InstrumentationRegistry
import com.android.modules.utils.build.SdkLevel.isAtLeastU
import com.android.testutils.runAsShell
import com.android.testutils.tryTest
import kotlin.test.assertNotNull
+import kotlin.test.assertTrue
import org.junit.rules.TestRule
import org.junit.runner.Description
import org.junit.runners.model.Statement
private val TAG = CarrierConfigRule::class.simpleName
+private const val CARRIER_CONFIG_CHANGE_TIMEOUT_MS = 10_000L
/**
* A [TestRule] that helps set [CarrierConfigManager] overrides for tests and clean up the test
* configuration automatically on teardown.
*/
class CarrierConfigRule : TestRule {
- private val ccm by lazy { InstrumentationRegistry.getInstrumentation().context.getSystemService(
- CarrierConfigManager::class.java
- ) }
+ private val context by lazy { InstrumentationRegistry.getInstrumentation().context }
+ private val ccm by lazy { context.getSystemService(CarrierConfigManager::class.java) }
// Map of (subId) -> (original values of overridden settings)
private val originalConfigs = mutableMapOf<Int, PersistableBundle>()
@@ -61,6 +69,33 @@
}
}
+ private class ConfigChangeReceiver(private val subId: Int) : BroadcastReceiver() {
+ val cv = ConditionVariable()
+ override fun onReceive(context: Context, intent: Intent) {
+ if (intent.action != ACTION_CARRIER_CONFIG_CHANGED ||
+ intent.getIntExtra(EXTRA_SUBSCRIPTION_INDEX, -1) != subId) {
+ return
+ }
+ // This may race with other config changes for the same subId, but there is no way to
+ // know which update is being reported, and querying the override would return the
+ // latest values even before the config is applied. Config changes should be rare, so it
+ // is unlikely they would happen exactly after the override applied here and cause
+ // flakes.
+ cv.open()
+ }
+ }
+
+ private fun overrideConfigAndWait(subId: Int, config: PersistableBundle) {
+ val changeReceiver = ConfigChangeReceiver(subId)
+ context.registerReceiver(changeReceiver, IntentFilter(ACTION_CARRIER_CONFIG_CHANGED))
+ ccm.overrideConfig(subId, config)
+ assertTrue(
+ changeReceiver.cv.block(CARRIER_CONFIG_CHANGE_TIMEOUT_MS),
+ "Timed out waiting for config change for subId $subId"
+ )
+ context.unregisterReceiver(changeReceiver)
+ }
+
/**
* Add carrier config overrides with the specified configuration.
*
@@ -79,7 +114,7 @@
originalConfig.putAll(previousValues)
runAsShell(MODIFY_PHONE_STATE) {
- ccm.overrideConfig(subId, config)
+ overrideConfigAndWait(subId, config)
}
}
@@ -93,10 +128,10 @@
runAsShell(MODIFY_PHONE_STATE) {
originalConfigs.forEach { (subId, config) ->
try {
- // Do not use overrideConfig with null, as it would reset configs that may
+ // Do not use null as the config to reset, as it would reset configs that may
// have been set by target preparers such as
// ConnectivityTestTargetPreparer / CarrierConfigSetupTest.
- ccm.overrideConfig(subId, config)
+ overrideConfigAndWait(subId, config)
} catch (e: Throwable) {
Log.e(TAG, "Error resetting carrier config for subId $subId")
}
diff --git a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
index 4ba41cd..8fcc703 100644
--- a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
+++ b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
@@ -743,7 +743,6 @@
}
return@tryTest
}
- cv.close()
if (hold) {
carrierConfigRule.addConfigOverrides(subId, PersistableBundle().also {
it.putStringArray(CarrierConfigManager.KEY_CARRIER_CERTIFICATE_STRING_ARRAY,
@@ -752,7 +751,6 @@
} else {
carrierConfigRule.cleanUpNow()
}
- assertTrue(cv.block(DEFAULT_TIMEOUT_MS), "Can't change carrier privilege")
} cleanup @JvmSerializableLambda {
runAsShell(READ_PRIVILEGED_PHONE_STATE) @JvmSerializableLambda {
tm.unregisterCarrierPrivilegesCallback(cpb)