summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/core/java/com/android/server/security/AttestationVerificationManagerService.java77
-rw-r--r--services/core/java/com/android/server/security/AttestationVerificationPeerDeviceVerifier.java223
-rw-r--r--tests/AttestationVerificationTest/src/com/android/server/security/AttestationVerificationPeerDeviceVerifierTest.kt131
3 files changed, 358 insertions, 73 deletions
diff --git a/services/core/java/com/android/server/security/AttestationVerificationManagerService.java b/services/core/java/com/android/server/security/AttestationVerificationManagerService.java
index 2bf0b2cd4f4a..55f85ea27c82 100644
--- a/services/core/java/com/android/server/security/AttestationVerificationManagerService.java
+++ b/services/core/java/com/android/server/security/AttestationVerificationManagerService.java
@@ -22,6 +22,8 @@ import static android.security.attestationverification.AttestationVerificationMa
import static android.security.attestationverification.AttestationVerificationManager.RESULT_FAILURE;
import static android.security.attestationverification.AttestationVerificationManager.RESULT_UNKNOWN;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Context;
import android.os.Bundle;
import android.os.IBinder;
@@ -31,12 +33,20 @@ import android.security.attestationverification.AttestationProfile;
import android.security.attestationverification.IAttestationVerificationManagerService;
import android.security.attestationverification.IVerificationResult;
import android.security.attestationverification.VerificationToken;
+import android.text.TextUtils;
import android.util.ExceptionUtils;
+import android.util.IndentingPrintWriter;
import android.util.Slog;
+import android.util.TimeUtils;
import com.android.internal.infra.AndroidFuture;
+import com.android.internal.util.DumpUtils;
import com.android.server.SystemService;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayDeque;
+
/**
* A {@link SystemService} which provides functionality related to verifying attestations of
* (usually) remote computing environments.
@@ -46,11 +56,13 @@ import com.android.server.SystemService;
public class AttestationVerificationManagerService extends SystemService {
private static final String TAG = "AVF";
+ private static final int DUMP_EVENT_LOG_SIZE = 10;
private final AttestationVerificationPeerDeviceVerifier mPeerDeviceVerifier;
+ private final DumpLogger mDumpLogger = new DumpLogger();
public AttestationVerificationManagerService(final Context context) throws Exception {
super(context);
- mPeerDeviceVerifier = new AttestationVerificationPeerDeviceVerifier(context);
+ mPeerDeviceVerifier = new AttestationVerificationPeerDeviceVerifier(context, mDumpLogger);
}
private final IBinder mService = new IAttestationVerificationManagerService.Stub() {
@@ -83,6 +95,28 @@ public class AttestationVerificationManagerService extends SystemService {
private void enforceUsePermission() {
getContext().enforceCallingOrSelfPermission(USE_ATTESTATION_VERIFICATION_SERVICE, null);
}
+
+ @Override
+ protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter writer,
+ @Nullable String[] args) {
+ if (!android.security.Flags.dumpAttestationVerifications()) {
+ super.dump(fd, writer, args);
+ return;
+ }
+
+ if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), TAG, writer)) return;
+
+ final IndentingPrintWriter fout = new IndentingPrintWriter(writer, " ");
+
+ fout.print("AttestationVerificationManagerService");
+ fout.println();
+ fout.increaseIndent();
+
+ fout.println("Event Log:");
+ fout.increaseIndent();
+ mDumpLogger.dumpTo(fout);
+ fout.decreaseIndent();
+ }
};
private void verifyAttestationForAllVerifiers(
@@ -119,4 +153,45 @@ public class AttestationVerificationManagerService extends SystemService {
Slog.d(TAG, "Started");
publishBinderService(Context.ATTESTATION_VERIFICATION_SERVICE, mService);
}
+
+
+ static class DumpLogger {
+ private final ArrayDeque<DumpData> mData = new ArrayDeque<>(DUMP_EVENT_LOG_SIZE);
+ private int mEventsLogged = 0;
+
+ void logAttempt(DumpData data) {
+ synchronized (mData) {
+ if (mData.size() == DUMP_EVENT_LOG_SIZE) {
+ mData.removeFirst();
+ }
+
+ mEventsLogged++;
+ data.mEventNumber = mEventsLogged;
+
+ data.mEventTimeMs = System.currentTimeMillis();
+
+ mData.add(data);
+ }
+ }
+
+ void dumpTo(IndentingPrintWriter writer) {
+ synchronized (mData) {
+ for (DumpData data : mData.reversed()) {
+ writer.println(
+ TextUtils.formatSimple("Verification #%d [%s]", data.mEventNumber,
+ TimeUtils.formatForLogging(data.mEventTimeMs)));
+ writer.increaseIndent();
+ data.dumpTo(writer);
+ writer.decreaseIndent();
+ }
+ }
+ }
+ }
+
+ abstract static class DumpData {
+ protected int mEventNumber = -1;
+ protected long mEventTimeMs = -1;
+
+ abstract void dumpTo(IndentingPrintWriter writer);
+ }
}
diff --git a/services/core/java/com/android/server/security/AttestationVerificationPeerDeviceVerifier.java b/services/core/java/com/android/server/security/AttestationVerificationPeerDeviceVerifier.java
index 72a402d7a58c..945a3400d971 100644
--- a/services/core/java/com/android/server/security/AttestationVerificationPeerDeviceVerifier.java
+++ b/services/core/java/com/android/server/security/AttestationVerificationPeerDeviceVerifier.java
@@ -30,15 +30,19 @@ import static com.android.server.security.AndroidKeystoreAttestationVerification
import static java.nio.charset.StandardCharsets.UTF_8;
import android.annotation.NonNull;
+import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
+import android.security.attestationverification.AttestationVerificationManager;
import android.security.attestationverification.AttestationVerificationManager.LocalBindingType;
+import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.Slog;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.security.AttestationVerificationManagerService.DumpLogger;
import org.json.JSONObject;
@@ -71,7 +75,9 @@ import java.util.Set;
/**
* Verifies Android key attestation according to the
- * {@link android.security.attestationverification.AttestationVerificationManager#PROFILE_PEER_DEVICE PROFILE_PEER_DEVICE}
+ * {@link
+ * android.security.attestationverification.AttestationVerificationManager#PROFILE_PEER_DEVICE
+ * PROFILE_PEER_DEVICE}
* profile.
*
* <p>
@@ -118,9 +124,12 @@ class AttestationVerificationPeerDeviceVerifier {
private final LocalDate mTestLocalPatchDate;
private final CertificateFactory mCertificateFactory;
private final CertPathValidator mCertPathValidator;
+ private final DumpLogger mDumpLogger;
- AttestationVerificationPeerDeviceVerifier(@NonNull Context context) throws Exception {
+ AttestationVerificationPeerDeviceVerifier(@NonNull Context context,
+ @NonNull DumpLogger dumpLogger) throws Exception {
mContext = Objects.requireNonNull(context);
+ mDumpLogger = dumpLogger;
mCertificateFactory = CertificateFactory.getInstance("X.509");
mCertPathValidator = CertPathValidator.getInstance("PKIX");
mTrustAnchors = getTrustAnchors();
@@ -132,9 +141,10 @@ class AttestationVerificationPeerDeviceVerifier {
// Use ONLY for hermetic unit testing.
@VisibleForTesting
AttestationVerificationPeerDeviceVerifier(@NonNull Context context,
- Set<TrustAnchor> trustAnchors, boolean revocationEnabled,
+ DumpLogger dumpLogger, Set<TrustAnchor> trustAnchors, boolean revocationEnabled,
LocalDate systemDate, LocalDate localPatchDate) throws Exception {
mContext = Objects.requireNonNull(context);
+ mDumpLogger = dumpLogger;
mCertificateFactory = CertificateFactory.getInstance("X.509");
mCertPathValidator = CertPathValidator.getInstance("PKIX");
mTrustAnchors = trustAnchors;
@@ -153,63 +163,90 @@ class AttestationVerificationPeerDeviceVerifier {
* bounded at the end by {@code -----END CERTIFICATE-----}.
*
* @param localBindingType Only {@code TYPE_PUBLIC_KEY} and {@code TYPE_CHALLENGE} supported.
- * @param requirements Only {@code PARAM_PUBLIC_KEY} and {@code PARAM_CHALLENGE} supported.
- * @param attestation Certificates should be DER encoded with leaf certificate appended first.
+ * @param requirements Only {@code PARAM_PUBLIC_KEY} and {@code PARAM_CHALLENGE} supported.
+ * @param attestation Certificates should be DER encoded with leaf certificate appended
+ * first.
*/
int verifyAttestation(
@LocalBindingType int localBindingType,
@NonNull Bundle requirements,
@NonNull byte[] attestation) {
+
+ MyDumpData dumpData = new MyDumpData();
+
+ int result =
+ verifyAttestationInternal(localBindingType, requirements, attestation, dumpData);
+ dumpData.mResult = result;
+ mDumpLogger.logAttempt(dumpData);
+ return result;
+ }
+
+ private int verifyAttestationInternal(
+ @LocalBindingType int localBindingType,
+ @NonNull Bundle requirements,
+ @NonNull byte[] attestation,
+ @NonNull MyDumpData dumpData) {
if (mCertificateFactory == null) {
debugVerboseLog("Unable to access CertificateFactory");
return RESULT_FAILURE;
}
+ dumpData.mCertificationFactoryAvailable = true;
if (mCertPathValidator == null) {
debugVerboseLog("Unable to access CertPathValidator");
return RESULT_FAILURE;
}
+ dumpData.mCertPathValidatorAvailable = true;
+
// Check if the provided local binding type is supported and if the provided requirements
// "match" the binding type.
if (!validateAttestationParameters(localBindingType, requirements)) {
return RESULT_FAILURE;
}
+ dumpData.mAttestationParametersOk = true;
+
+ // To provide the most information in the dump logs, we track the failure state but keep
+ // verifying the rest of the attestation. For code safety, there are no transitions past
+ // here to set failed = false
+ boolean failed = false;
try {
// First: parse and validate the certificate chain.
final List<X509Certificate> certificateChain = getCertificates(attestation);
// (returns void, but throws CertificateException and other similar Exceptions)
validateCertificateChain(certificateChain);
+ dumpData.mCertChainOk = true;
final var leafCertificate = certificateChain.get(0);
final var attestationExtension = fromCertificate(leafCertificate);
// Second: verify if the attestation satisfies the "peer device" profile.
- if (!checkAttestationForPeerDeviceProfile(attestationExtension)) {
- return RESULT_FAILURE;
+ if (!checkAttestationForPeerDeviceProfile(attestationExtension, dumpData)) {
+ failed = true;
}
// Third: check if the attestation satisfies local binding requirements.
if (!checkLocalBindingRequirements(
- leafCertificate, attestationExtension, localBindingType, requirements)) {
- return RESULT_FAILURE;
+ leafCertificate, attestationExtension, localBindingType, requirements,
+ dumpData)) {
+ failed = true;
}
-
- return RESULT_SUCCESS;
} catch (CertificateException | CertPathValidatorException
- | InvalidAlgorithmParameterException | IOException e) {
+ | InvalidAlgorithmParameterException | IOException e) {
// Catch all non-RuntimeExpceptions (all of these are thrown by either getCertificates()
// or validateCertificateChain() or
// AndroidKeystoreAttestationVerificationAttributes.fromCertificate())
debugVerboseLog("Unable to parse/validate Android Attestation certificate(s)", e);
- return RESULT_FAILURE;
+ failed = true;
} catch (RuntimeException e) {
// Catch everyting else (RuntimeExpcetions), since we don't want to throw any exceptions
// out of this class/method.
debugVerboseLog("Unexpected error", e);
- return RESULT_FAILURE;
+ failed = true;
}
+
+ return failed ? RESULT_FAILURE : RESULT_SUCCESS;
}
@NonNull
@@ -255,7 +292,7 @@ class AttestationVerificationPeerDeviceVerifier {
private void validateCertificateChain(List<X509Certificate> certificates)
throws CertificateException, CertPathValidatorException,
- InvalidAlgorithmParameterException {
+ InvalidAlgorithmParameterException {
if (certificates.size() < 2) {
debugVerboseLog("Certificate chain less than 2 in size.");
throw new CertificateException("Certificate chain less than 2 in size.");
@@ -277,7 +314,7 @@ class AttestationVerificationPeerDeviceVerifier {
private Set<TrustAnchor> getTrustAnchors() throws CertPathValidatorException {
Set<TrustAnchor> modifiableSet = new HashSet<>();
try {
- for (String certString: getTrustAnchorResources()) {
+ for (String certString : getTrustAnchorResources()) {
modifiableSet.add(
new TrustAnchor((X509Certificate) mCertificateFactory.generateCertificate(
new ByteArrayInputStream(getCertificateBytes(certString))), null));
@@ -307,8 +344,9 @@ class AttestationVerificationPeerDeviceVerifier {
@NonNull X509Certificate leafCertificate,
@NonNull AndroidKeystoreAttestationVerificationAttributes attestationAttributes,
@LocalBindingType int localBindingType,
- @NonNull Bundle requirements) {
+ @NonNull Bundle requirements, MyDumpData dumpData) {
// First: check non-optional (for the given local binding type) requirements.
+ dumpData.mBindingType = localBindingType;
switch (localBindingType) {
case TYPE_PUBLIC_KEY:
// Verify leaf public key matches provided public key.
@@ -336,9 +374,11 @@ class AttestationVerificationPeerDeviceVerifier {
throw new IllegalArgumentException("Unsupported local binding type "
+ localBindingTypeToString(localBindingType));
}
+ dumpData.mBindingOk = true;
// Second: check specified optional requirements.
if (requirements.containsKey(PARAM_OWNED_BY_SYSTEM)) {
+ dumpData.mSystemOwnershipChecked = true;
if (requirements.getBoolean(PARAM_OWNED_BY_SYSTEM)) {
// Verify key is owned by the system.
final boolean ownedBySystem = checkOwnedBySystem(
@@ -347,6 +387,7 @@ class AttestationVerificationPeerDeviceVerifier {
debugVerboseLog("Certificate public key is not owned by the AndroidSystem.");
return false;
}
+ dumpData.mSystemOwned = true;
} else {
throw new IllegalArgumentException("The value of the requirement key "
+ PARAM_OWNED_BY_SYSTEM
@@ -359,73 +400,98 @@ class AttestationVerificationPeerDeviceVerifier {
}
private boolean checkAttestationForPeerDeviceProfile(
- @NonNull AndroidKeystoreAttestationVerificationAttributes attestationAttributes) {
+ @NonNull AndroidKeystoreAttestationVerificationAttributes attestationAttributes,
+ MyDumpData dumpData) {
+ boolean result = true;
+
// Checks for support of Keymaster 4.
if (attestationAttributes.getAttestationVersion() < 3) {
debugVerboseLog("Attestation version is not at least 3 (Keymaster 4).");
- return false;
+ result = false;
+ } else {
+ dumpData.mAttestationVersionAtLeast3 = true;
}
// Checks for support of Keymaster 4.
if (attestationAttributes.getKeymasterVersion() < 4) {
debugVerboseLog("Keymaster version is not at least 4.");
- return false;
+ result = false;
+ } else {
+ dumpData.mKeymasterVersionAtLeast4 = true;
}
// First two characters are Android OS version.
if (attestationAttributes.getKeyOsVersion() < 100000) {
debugVerboseLog("Android OS version is not 10+.");
- return false;
+ result = false;
+ } else {
+ dumpData.mOsVersionAtLeast10 = true;
}
if (!attestationAttributes.isAttestationHardwareBacked()) {
debugVerboseLog("Key is not HW backed.");
- return false;
+ result = false;
+ } else {
+ dumpData.mKeyHwBacked = true;
}
if (!attestationAttributes.isKeymasterHardwareBacked()) {
debugVerboseLog("Keymaster is not HW backed.");
- return false;
+ result = false;
+ } else {
+ dumpData.mKeymasterHwBacked = true;
}
if (attestationAttributes.getVerifiedBootState() != VERIFIED) {
debugVerboseLog("Boot state not Verified.");
- return false;
+ result = false;
+ } else {
+ dumpData.mBootStateIsVerified = true;
}
try {
if (!attestationAttributes.isVerifiedBootLocked()) {
debugVerboseLog("Verified boot state is not locked.");
- return false;
+ result = false;
+ } else {
+ dumpData.mVerifiedBootStateLocked = true;
}
} catch (IllegalStateException e) {
debugVerboseLog("VerifiedBootLocked is not set.", e);
- return false;
+ result = false;
}
// Patch level integer YYYYMM is expected to be within 1 year of today.
if (!isValidPatchLevel(attestationAttributes.getKeyOsPatchLevel())) {
debugVerboseLog("OS patch level is not within valid range.");
- return false;
+ result = false;
+ } else {
+ dumpData.mOsPatchLevelInRange = true;
}
// Patch level integer YYYYMMDD is expected to be within 1 year of today.
if (!isValidPatchLevel(attestationAttributes.getKeyBootPatchLevel())) {
debugVerboseLog("Boot patch level is not within valid range.");
- return false;
+ result = false;
+ } else {
+ dumpData.mKeyBootPatchLevelInRange = true;
}
if (!isValidPatchLevel(attestationAttributes.getKeyVendorPatchLevel())) {
debugVerboseLog("Vendor patch level is not within valid range.");
- return false;
+ result = false;
+ } else {
+ dumpData.mKeyVendorPatchLevelInRange = true;
}
if (!isValidPatchLevel(attestationAttributes.getKeyBootPatchLevel())) {
debugVerboseLog("Boot patch level is not within valid range.");
- return false;
+ result = false;
+ } else {
+ dumpData.mKeyBootPatchLevelInRange = true;
}
- return true;
+ return result;
}
private boolean checkPublicKey(
@@ -609,4 +675,99 @@ class AttestationVerificationPeerDeviceVerifier {
Slog.v(TAG, str);
}
}
+
+ /* Mutable data class for tracking dump data from verifications. */
+ private static class MyDumpData extends AttestationVerificationManagerService.DumpData {
+
+ // Top-Level Result
+ int mResult = -1;
+
+ // Configuration/Setup preconditions
+ boolean mCertificationFactoryAvailable = false;
+ boolean mCertPathValidatorAvailable = false;
+
+ // AttestationParameters (Valid Input Only)
+ boolean mAttestationParametersOk = false;
+
+ // Certificate Chain (Structure & Chaining Conditions)
+ boolean mCertChainOk = false;
+
+ // Binding
+ boolean mBindingOk = false;
+ int mBindingType = -1;
+
+ // System Ownership
+ boolean mSystemOwnershipChecked = false;
+ boolean mSystemOwned = false;
+
+ // Android Keystore attestation properties
+ boolean mOsVersionAtLeast10 = false;
+ boolean mKeyHwBacked = false;
+ boolean mAttestationVersionAtLeast3 = false;
+ boolean mKeymasterVersionAtLeast4 = false;
+ boolean mKeymasterHwBacked = false;
+ boolean mBootStateIsVerified = false;
+ boolean mVerifiedBootStateLocked = false;
+ boolean mOsPatchLevelInRange = false;
+ boolean mKeyBootPatchLevelInRange = false;
+ boolean mKeyVendorPatchLevelInRange = false;
+
+ @SuppressLint("WrongConstant")
+ @Override
+ public void dumpTo(IndentingPrintWriter writer) {
+ writer.println(
+ "Result: " + AttestationVerificationManager.verificationResultCodeToString(
+ mResult));
+ if (!mCertificationFactoryAvailable) {
+ writer.println("Certificate Factory Unavailable");
+ return;
+ }
+ if (!mCertPathValidatorAvailable) {
+ writer.println("Cert Path Validator Unavailable");
+ return;
+ }
+ if (!mAttestationParametersOk) {
+ writer.println("Attestation parameters set incorrectly.");
+ return;
+ }
+
+ writer.println("Certificate Chain Valid (inc. Trust Anchor): " + booleanToOkFail(
+ mCertChainOk));
+ if (!mCertChainOk) {
+ return;
+ }
+
+ // Binding
+ writer.println("Local Binding: " + booleanToOkFail(mBindingOk));
+ writer.increaseIndent();
+ writer.println("Binding Type: " + mBindingType);
+ writer.decreaseIndent();
+
+ if (mSystemOwnershipChecked) {
+ writer.println("System Ownership: " + booleanToOkFail(mSystemOwned));
+ }
+
+ // Keystore Attestation params
+ writer.println("KeyStore Attestation Parameters");
+ writer.increaseIndent();
+ writer.println("OS Version >= 10: " + booleanToOkFail(mOsVersionAtLeast10));
+ writer.println("OS Patch Level in Range: " + booleanToOkFail(mOsPatchLevelInRange));
+ writer.println(
+ "Attestation Version >= 3: " + booleanToOkFail(mAttestationVersionAtLeast3));
+ writer.println("Keymaster Version >= 4: " + booleanToOkFail(mKeymasterVersionAtLeast4));
+ writer.println("Keymaster HW-Backed: " + booleanToOkFail(mKeymasterHwBacked));
+ writer.println("Key is HW Backed: " + booleanToOkFail(mKeyHwBacked));
+ writer.println("Boot State is VERIFIED: " + booleanToOkFail(mBootStateIsVerified));
+ writer.println("Verified Boot is LOCKED: " + booleanToOkFail(mVerifiedBootStateLocked));
+ writer.println(
+ "Key Boot Level in Range: " + booleanToOkFail(mKeyBootPatchLevelInRange));
+ writer.println("Key Vendor Patch Level in Range: " + booleanToOkFail(
+ mKeyVendorPatchLevelInRange));
+ writer.decreaseIndent();
+ }
+
+ private String booleanToOkFail(boolean value) {
+ return value ? "OK" : "FAILURE";
+ }
+ }
}
diff --git a/tests/AttestationVerificationTest/src/com/android/server/security/AttestationVerificationPeerDeviceVerifierTest.kt b/tests/AttestationVerificationTest/src/com/android/server/security/AttestationVerificationPeerDeviceVerifierTest.kt
index dfbbda6c6f5e..afb3593e3e98 100644
--- a/tests/AttestationVerificationTest/src/com/android/server/security/AttestationVerificationPeerDeviceVerifierTest.kt
+++ b/tests/AttestationVerificationTest/src/com/android/server/security/AttestationVerificationPeerDeviceVerifierTest.kt
@@ -9,21 +9,28 @@ import android.security.attestationverification.AttestationVerificationManager.R
import android.security.attestationverification.AttestationVerificationManager.RESULT_SUCCESS
import android.security.attestationverification.AttestationVerificationManager.TYPE_CHALLENGE
import android.security.attestationverification.AttestationVerificationManager.TYPE_PUBLIC_KEY
+import android.util.IndentingPrintWriter
+import android.util.Log
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.security.AttestationVerificationManagerService.DumpLogger
import com.google.common.truth.Truth.assertThat
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.MockitoAnnotations
import java.io.ByteArrayOutputStream
+import java.io.PrintWriter
+import java.io.StringWriter
import java.security.cert.Certificate
import java.security.cert.CertificateFactory
import java.security.cert.TrustAnchor
import java.security.cert.X509Certificate
import java.time.LocalDate
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
/** Test for Peer Device attestation verifier. */
@SmallTest
@@ -31,6 +38,7 @@ import java.time.LocalDate
class AttestationVerificationPeerDeviceVerifierTest {
private val certificateFactory = CertificateFactory.getInstance("X.509")
@Mock private lateinit var context: Context
+ private val dumpLogger = DumpLogger()
private lateinit var trustAnchors: HashSet<TrustAnchor>
@Before
@@ -44,37 +52,50 @@ class AttestationVerificationPeerDeviceVerifierTest {
}
}
+ @After
+ fun dumpAndLog() {
+ val dump = dumpLogger.getDump()
+ Log.d(TAG, "$dump")
+ }
+
@Test
fun verifyAttestation_returnsSuccessTypeChallenge() {
val verifier = AttestationVerificationPeerDeviceVerifier(
- context, trustAnchors, false, LocalDate.of(2022, 2, 1),
- LocalDate.of(2021, 8, 1))
+ context, dumpLogger, trustAnchors, false, LocalDate.of(2022, 2, 1),
+ LocalDate.of(2021, 8, 1)
+ )
val challengeRequirements = Bundle()
challengeRequirements.putByteArray(PARAM_CHALLENGE, "player456".encodeToByteArray())
- val result = verifier.verifyAttestation(TYPE_CHALLENGE, challengeRequirements,
- TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray())
+ val result = verifier.verifyAttestation(
+ TYPE_CHALLENGE, challengeRequirements,
+ TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray()
+ )
assertThat(result).isEqualTo(RESULT_SUCCESS)
}
@Test
fun verifyAttestation_returnsSuccessLocalPatchOlderThanOneYear() {
val verifier = AttestationVerificationPeerDeviceVerifier(
- context, trustAnchors, false, LocalDate.of(2022, 2, 1),
- LocalDate.of(2021, 1, 1))
+ context, dumpLogger, trustAnchors, false, LocalDate.of(2022, 2, 1),
+ LocalDate.of(2021, 1, 1)
+ )
val challengeRequirements = Bundle()
challengeRequirements.putByteArray(PARAM_CHALLENGE, "player456".encodeToByteArray())
- val result = verifier.verifyAttestation(TYPE_CHALLENGE, challengeRequirements,
- TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray())
+ val result = verifier.verifyAttestation(
+ TYPE_CHALLENGE, challengeRequirements,
+ TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray()
+ )
assertThat(result).isEqualTo(RESULT_SUCCESS)
}
@Test
fun verifyAttestation_returnsSuccessTypePublicKey() {
val verifier = AttestationVerificationPeerDeviceVerifier(
- context, trustAnchors, false, LocalDate.of(2022, 2, 1),
- LocalDate.of(2021, 8, 1))
+ context, dumpLogger, trustAnchors, false, LocalDate.of(2022, 2, 1),
+ LocalDate.of(2021, 8, 1)
+ )
val leafCert =
(TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToCerts() as List)[0]
@@ -84,61 +105,75 @@ class AttestationVerificationPeerDeviceVerifierTest {
val result = verifier.verifyAttestation(
TYPE_PUBLIC_KEY, pkRequirements,
- TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray())
+ TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray()
+ )
assertThat(result).isEqualTo(RESULT_SUCCESS)
}
@Test
fun verifyAttestation_returnsSuccessOwnedBySystem() {
val verifier = AttestationVerificationPeerDeviceVerifier(
- context, trustAnchors, false, LocalDate.of(2022, 2, 1),
- LocalDate.of(2021, 1, 1))
+ context, dumpLogger, trustAnchors, false, LocalDate.of(2022, 2, 1),
+ LocalDate.of(2021, 1, 1)
+ )
val challengeRequirements = Bundle()
challengeRequirements.putByteArray(PARAM_CHALLENGE, "activeUnlockValid".encodeToByteArray())
challengeRequirements.putBoolean("android.key_owned_by_system", true)
- val result = verifier.verifyAttestation(TYPE_CHALLENGE, challengeRequirements,
- TEST_OWNED_BY_SYSTEM_FILENAME.fromPEMFileToByteArray())
+ val result = verifier.verifyAttestation(
+ TYPE_CHALLENGE, challengeRequirements,
+ TEST_OWNED_BY_SYSTEM_FILENAME.fromPEMFileToByteArray()
+ )
+
assertThat(result).isEqualTo(RESULT_SUCCESS)
}
@Test
fun verifyAttestation_returnsFailureOwnedBySystem() {
val verifier = AttestationVerificationPeerDeviceVerifier(
- context, trustAnchors, false, LocalDate.of(2022, 2, 1),
- LocalDate.of(2021, 1, 1))
+ context, dumpLogger, trustAnchors, false, LocalDate.of(2022, 2, 1),
+ LocalDate.of(2021, 1, 1)
+ )
val challengeRequirements = Bundle()
challengeRequirements.putByteArray(PARAM_CHALLENGE, "player456".encodeToByteArray())
challengeRequirements.putBoolean("android.key_owned_by_system", true)
- val result = verifier.verifyAttestation(TYPE_CHALLENGE, challengeRequirements,
- TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray())
+ val result = verifier.verifyAttestation(
+ TYPE_CHALLENGE, challengeRequirements,
+ TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray()
+ )
assertThat(result).isEqualTo(RESULT_FAILURE)
}
@Test
fun verifyAttestation_returnsFailurePatchDateNotWithinOneYearLocalPatch() {
val verifier = AttestationVerificationPeerDeviceVerifier(
- context, trustAnchors, false, LocalDate.of(2023, 3, 1),
- LocalDate.of(2023, 2, 1))
+ context, dumpLogger, trustAnchors, false, LocalDate.of(2023, 3, 1),
+ LocalDate.of(2023, 2, 1)
+ )
val challengeRequirements = Bundle()
challengeRequirements.putByteArray(PARAM_CHALLENGE, "player456".encodeToByteArray())
- val result = verifier.verifyAttestation(TYPE_CHALLENGE, challengeRequirements,
- TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray())
+ val result = verifier.verifyAttestation(
+ TYPE_CHALLENGE, challengeRequirements,
+ TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray()
+ )
assertThat(result).isEqualTo(RESULT_FAILURE)
}
@Test
fun verifyAttestation_returnsFailureTrustedAnchorEmpty() {
val verifier = AttestationVerificationPeerDeviceVerifier(
- context, HashSet(), false, LocalDate.of(2022, 1, 1),
- LocalDate.of(2022, 1, 1))
+ context, dumpLogger, HashSet(), false, LocalDate.of(2022, 1, 1),
+ LocalDate.of(2022, 1, 1)
+ )
val challengeRequirements = Bundle()
challengeRequirements.putByteArray(PARAM_CHALLENGE, "player456".encodeToByteArray())
- val result = verifier.verifyAttestation(TYPE_CHALLENGE, challengeRequirements,
- TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray())
+ val result = verifier.verifyAttestation(
+ TYPE_CHALLENGE, challengeRequirements,
+ TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray()
+ )
assertThat(result).isEqualTo(RESULT_FAILURE)
}
@@ -151,32 +186,39 @@ class AttestationVerificationPeerDeviceVerifierTest {
}
val verifier = AttestationVerificationPeerDeviceVerifier(
- context, badTrustAnchors, false, LocalDate.of(2022, 1, 1),
- LocalDate.of(2022, 1, 1))
+ context, dumpLogger, badTrustAnchors, false, LocalDate.of(2022, 1, 1),
+ LocalDate.of(2022, 1, 1)
+ )
val challengeRequirements = Bundle()
challengeRequirements.putByteArray(PARAM_CHALLENGE, "player456".encodeToByteArray())
- val result = verifier.verifyAttestation(TYPE_CHALLENGE, challengeRequirements,
- TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray())
+ val result = verifier.verifyAttestation(
+ TYPE_CHALLENGE, challengeRequirements,
+ TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray()
+ )
assertThat(result).isEqualTo(RESULT_FAILURE)
}
fun verifyAttestation_returnsFailureChallenge() {
val verifier = AttestationVerificationPeerDeviceVerifier(
- context, trustAnchors, false, LocalDate.of(2022, 1, 1),
- LocalDate.of(2022, 1, 1))
+ context, dumpLogger, trustAnchors, false, LocalDate.of(2022, 1, 1),
+ LocalDate.of(2022, 1, 1)
+ )
val challengeRequirements = Bundle()
challengeRequirements.putByteArray(PARAM_CHALLENGE, "wrong".encodeToByteArray())
- val result = verifier.verifyAttestation(TYPE_CHALLENGE, challengeRequirements,
- TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray())
+ val result = verifier.verifyAttestation(
+ TYPE_CHALLENGE, challengeRequirements,
+ TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray()
+ )
assertThat(result).isEqualTo(RESULT_FAILURE)
}
private fun String.fromPEMFileToCerts(): Collection<Certificate> {
return certificateFactory.generateCertificates(
InstrumentationRegistry.getInstrumentation().getContext().getResources().getAssets()
- .open(this))
+ .open(this)
+ )
}
private fun String.fromPEMFileToByteArray(): ByteArray {
@@ -188,6 +230,12 @@ class AttestationVerificationPeerDeviceVerifierTest {
return bos.toByteArray()
}
+ private fun DumpLogger.getDump(): String {
+ val sw = StringWriter()
+ this.dumpTo(IndentingPrintWriter(PrintWriter(sw), " "))
+ return sw.toString()
+ }
+
class TestActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -195,6 +243,7 @@ class AttestationVerificationPeerDeviceVerifierTest {
}
companion object {
+ private const val TAG = "AVFTest"
private const val TEST_ROOT_CERT_FILENAME = "test_root_certs.pem"
private const val TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME =
"test_attestation_with_root_certs.pem"