Merge "testVfsCacheConsistency: Error message is startsWith fails"
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/KeyManagementTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/KeyManagementTest.java
index d18a9fa..2f7444d 100755
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/KeyManagementTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/KeyManagementTest.java
@@ -376,7 +376,7 @@
     private void validateDeviceIdAttestationData(Certificate leaf,
             String expectedSerial, String expectedImei, String expectedMeid)
             throws CertificateParsingException {
-        Attestation attestationRecord = new Attestation((X509Certificate) leaf);
+        Attestation attestationRecord = Attestation.loadFromCertificate((X509Certificate) leaf);
         AuthorizationList teeAttestation = attestationRecord.getTeeEnforced();
         assertThat(teeAttestation).isNotNull();
         validateBrandAttestationRecord(teeAttestation);
@@ -402,7 +402,7 @@
         assertThat(attestation).isNotNull();
         assertThat(attestation.size()).isGreaterThan(1);
         X509Certificate leaf = (X509Certificate) attestation.get(0);
-        Attestation attestationRecord = new Attestation(leaf);
+        Attestation attestationRecord = Attestation.loadFromCertificate(leaf);
         assertThat(attestationRecord.getAttestationChallenge()).isEqualTo(providedChallenge);
     }
 
diff --git a/tests/security/Android.bp b/tests/security/Android.bp
index d9786a8..12168cc 100644
--- a/tests/security/Android.bp
+++ b/tests/security/Android.bp
@@ -17,6 +17,7 @@
     static_libs: [
         "bouncycastle-unbundled",
         "bouncycastle-bcpkix-unbundled",
+        "cbor-java",
         "guava",
         "truth-prebuilt",
         "testng",
diff --git a/tests/security/src/android/keystore/cts/Asn1Attestation.java b/tests/security/src/android/keystore/cts/Asn1Attestation.java
new file mode 100644
index 0000000..b454130
--- /dev/null
+++ b/tests/security/src/android/keystore/cts/Asn1Attestation.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2020 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 android.keystore.cts;
+
+import org.bouncycastle.asn1.ASN1Sequence;
+
+import java.security.cert.CertificateParsingException;
+import java.security.cert.X509Certificate;
+
+public class Asn1Attestation extends Attestation {
+    static final int ATTESTATION_VERSION_INDEX = 0;
+    static final int ATTESTATION_SECURITY_LEVEL_INDEX = 1;
+    static final int KEYMASTER_VERSION_INDEX = 2;
+    static final int KEYMASTER_SECURITY_LEVEL_INDEX = 3;
+    static final int ATTESTATION_CHALLENGE_INDEX = 4;
+    static final int UNIQUE_ID_INDEX = 5;
+    static final int SW_ENFORCED_INDEX = 6;
+    static final int TEE_ENFORCED_INDEX = 7;
+
+    int attestationSecurityLevel;
+
+    /**
+     * Constructs an {@code Asn1Attestation} object from the provided {@link X509Certificate},
+     * extracting the attestation data from the attestation extension.
+     *
+     * @throws CertificateParsingException if the certificate does not contain a properly-formatted
+     *     attestation extension.
+     */
+    public Asn1Attestation(X509Certificate x509Cert) throws CertificateParsingException {
+        super(x509Cert);
+        ASN1Sequence seq = getAttestationSequence(x509Cert);
+
+        attestationVersion =
+                Asn1Utils.getIntegerFromAsn1(seq.getObjectAt(ATTESTATION_VERSION_INDEX));
+        attestationSecurityLevel =
+                Asn1Utils.getIntegerFromAsn1(seq.getObjectAt(ATTESTATION_SECURITY_LEVEL_INDEX));
+        keymasterVersion = Asn1Utils.getIntegerFromAsn1(seq.getObjectAt(KEYMASTER_VERSION_INDEX));
+        keymasterSecurityLevel =
+                Asn1Utils.getIntegerFromAsn1(seq.getObjectAt(KEYMASTER_SECURITY_LEVEL_INDEX));
+
+        attestationChallenge =
+                Asn1Utils.getByteArrayFromAsn1(seq.getObjectAt(ATTESTATION_CHALLENGE_INDEX));
+
+        uniqueId = Asn1Utils.getByteArrayFromAsn1(seq.getObjectAt(UNIQUE_ID_INDEX));
+
+        softwareEnforced = new AuthorizationList(seq.getObjectAt(SW_ENFORCED_INDEX));
+        teeEnforced = new AuthorizationList(seq.getObjectAt(TEE_ENFORCED_INDEX));
+    }
+
+    ASN1Sequence getAttestationSequence(X509Certificate x509Cert)
+            throws CertificateParsingException {
+        byte[] attestationExtensionBytes = x509Cert.getExtensionValue(Attestation.ASN1_OID);
+        if (attestationExtensionBytes == null || attestationExtensionBytes.length == 0) {
+            throw new CertificateParsingException("Did not find extension with OID " + ASN1_OID);
+        }
+        return Asn1Utils.getAsn1SequenceFromBytes(attestationExtensionBytes);
+    }
+
+    public int getAttestationSecurityLevel() {
+        return attestationSecurityLevel;
+    }
+
+    public RootOfTrust getRootOfTrust() {
+        return teeEnforced.getRootOfTrust();
+    }
+}
diff --git a/tests/security/src/android/keystore/cts/Attestation.java b/tests/security/src/android/keystore/cts/Attestation.java
index 2285cad..7414908 100644
--- a/tests/security/src/android/keystore/cts/Attestation.java
+++ b/tests/security/src/android/keystore/cts/Attestation.java
@@ -16,71 +16,69 @@
 
 package android.keystore.cts;
 
+import co.nstant.in.cbor.CborException;
+
 import com.google.common.base.CharMatcher;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.io.BaseEncoding;
 
-import org.bouncycastle.asn1.ASN1Sequence;
-
 import java.security.cert.CertificateParsingException;
 import java.security.cert.X509Certificate;
 import java.util.Set;
-import java.util.stream.Collectors;
 
 /**
  * Parses an attestation certificate and provides an easy-to-use interface for examining the
  * contents.
  */
-public class Attestation {
-    static final String KEY_DESCRIPTION_OID = "1.3.6.1.4.1.11129.2.1.17";
-    static final String KEY_USAGE_OID = "2.5.29.15";  // Standard key usage extension.
-    static final int ATTESTATION_VERSION_INDEX = 0;
-    static final int ATTESTATION_SECURITY_LEVEL_INDEX = 1;
-    static final int KEYMASTER_VERSION_INDEX = 2;
-    static final int KEYMASTER_SECURITY_LEVEL_INDEX = 3;
-    static final int ATTESTATION_CHALLENGE_INDEX = 4;
-    static final int UNIQUE_ID_INDEX = 5;
-    static final int SW_ENFORCED_INDEX = 6;
-    static final int TEE_ENFORCED_INDEX = 7;
+public abstract class Attestation {
+    static final String EAT_OID = "1.3.6.1.4.1.11129.2.1.25";
+    static final String ASN1_OID = "1.3.6.1.4.1.11129.2.1.17";
+    static final String KEY_USAGE_OID = "2.5.29.15"; // Standard key usage extension.
 
     public static final int KM_SECURITY_LEVEL_SOFTWARE = 0;
     public static final int KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT = 1;
     public static final int KM_SECURITY_LEVEL_STRONG_BOX = 2;
 
-    private final int attestationVersion;
-    private final int attestationSecurityLevel;
-    private final int keymasterVersion;
-    private final int keymasterSecurityLevel;
-    private final byte[] attestationChallenge;
-    private final byte[] uniqueId;
-    private final AuthorizationList softwareEnforced;
-    private final AuthorizationList teeEnforced;
-    private final Set<String> unexpectedExtensionOids;
-
+    int attestationVersion;
+    int keymasterVersion;
+    int keymasterSecurityLevel;
+    byte[] attestationChallenge;
+    byte[] uniqueId;
+    AuthorizationList softwareEnforced;
+    AuthorizationList teeEnforced;
+    Set<String> unexpectedExtensionOids;
 
     /**
      * Constructs an {@code Attestation} object from the provided {@link X509Certificate},
      * extracting the attestation data from the attestation extension.
      *
+     * <p>This method ensures that at most one attestation extension is included in the certificate.
+     *
      * @throws CertificateParsingException if the certificate does not contain a properly-formatted
-     *                                     attestation extension.
+     *     attestation extension, if it contains multiple attestation extensions, or if the
+     *     attestation extension can not be parsed.
      */
-    public Attestation(X509Certificate x509Cert) throws CertificateParsingException {
-        ASN1Sequence seq = getAttestationSequence(x509Cert);
+    public static Attestation loadFromCertificate(X509Certificate x509Cert)
+            throws CertificateParsingException {
+        if (x509Cert.getExtensionValue(EAT_OID) == null
+                && x509Cert.getExtensionValue(ASN1_OID) == null) {
+            throw new CertificateParsingException("No attestation extensions found");
+        }
+        if (x509Cert.getExtensionValue(EAT_OID) != null) {
+            if (x509Cert.getExtensionValue(ASN1_OID) != null) {
+                throw new CertificateParsingException("Multiple attestation extensions found");
+            }
+            try {
+                return new EatAttestation(x509Cert);
+            } catch (CborException cbe) {
+                throw new CertificateParsingException("Unable to parse EAT extension", cbe);
+            }
+        }
+        return new Asn1Attestation(x509Cert);
+    }
+
+    Attestation(X509Certificate x509Cert) {
         unexpectedExtensionOids = retrieveUnexpectedExtensionOids(x509Cert);
-
-        attestationVersion = Asn1Utils.getIntegerFromAsn1(seq.getObjectAt(ATTESTATION_VERSION_INDEX));
-        attestationSecurityLevel = Asn1Utils.getIntegerFromAsn1(seq.getObjectAt(ATTESTATION_SECURITY_LEVEL_INDEX));
-        keymasterVersion = Asn1Utils.getIntegerFromAsn1(seq.getObjectAt(KEYMASTER_VERSION_INDEX));
-        keymasterSecurityLevel = Asn1Utils.getIntegerFromAsn1(seq.getObjectAt(KEYMASTER_SECURITY_LEVEL_INDEX));
-
-        attestationChallenge =
-                Asn1Utils.getByteArrayFromAsn1(seq.getObjectAt(Attestation.ATTESTATION_CHALLENGE_INDEX));
-
-        uniqueId = Asn1Utils.getByteArrayFromAsn1(seq.getObjectAt(Attestation.UNIQUE_ID_INDEX));
-
-        softwareEnforced = new AuthorizationList(seq.getObjectAt(SW_ENFORCED_INDEX));
-        teeEnforced = new AuthorizationList(seq.getObjectAt(TEE_ENFORCED_INDEX));
     }
 
     public static String securityLevelToString(int attestationSecurityLevel) {
@@ -100,9 +98,9 @@
         return attestationVersion;
     }
 
-    public int getAttestationSecurityLevel() {
-        return attestationSecurityLevel;
-    }
+    public abstract int getAttestationSecurityLevel();
+
+    public abstract RootOfTrust getRootOfTrust();
 
     public int getKeymasterVersion() {
         return keymasterVersion;
@@ -135,13 +133,15 @@
     @Override
     public String toString() {
         StringBuilder s = new StringBuilder();
-        s.append("Attest version: " + attestationVersion);
-        s.append("\nAttest security: " + securityLevelToString(attestationSecurityLevel));
+        s.append("Extension type: " + getClass());
+        s.append("\nAttest version: " + attestationVersion);
+        s.append("\nAttest security: " + securityLevelToString(getAttestationSecurityLevel()));
         s.append("\nKM version: " + keymasterVersion);
         s.append("\nKM security: " + securityLevelToString(keymasterSecurityLevel));
 
         s.append("\nChallenge");
-        String stringChallenge = new String(attestationChallenge);
+        String stringChallenge =
+                attestationChallenge != null ? new String(attestationChallenge) : "null";
         if (CharMatcher.ascii().matchesAllOf(stringChallenge)) {
             s.append(": [" + stringChallenge + "]");
         } else {
@@ -159,26 +159,16 @@
         return s.toString();
     }
 
-    private ASN1Sequence getAttestationSequence(X509Certificate x509Cert)
-            throws CertificateParsingException {
-        byte[] attestationExtensionBytes = x509Cert.getExtensionValue(KEY_DESCRIPTION_OID);
-        if (attestationExtensionBytes == null || attestationExtensionBytes.length == 0) {
-            throw new CertificateParsingException(
-                    "Did not find extension with OID " + KEY_DESCRIPTION_OID);
-        }
-        return Asn1Utils.getAsn1SequenceFromBytes(attestationExtensionBytes);
-    }
-
-    private Set<String> retrieveUnexpectedExtensionOids(X509Certificate x509Cert) {
+    Set<String> retrieveUnexpectedExtensionOids(X509Certificate x509Cert) {
         return new ImmutableSet.Builder<String>()
-                .addAll(x509Cert.getCriticalExtensionOIDs()
-                        .stream()
-                        .filter(s -> !KEY_USAGE_OID.equals(s))
-                        .iterator())
-                .addAll(x509Cert.getNonCriticalExtensionOIDs()
-                        .stream()
-                        .filter(s -> !KEY_DESCRIPTION_OID.equals(s))
-                        .iterator())
+                .addAll(
+                        x509Cert.getCriticalExtensionOIDs().stream()
+                                .filter(s -> !KEY_USAGE_OID.equals(s))
+                                .iterator())
+                .addAll(
+                        x509Cert.getNonCriticalExtensionOIDs().stream()
+                                .filter(s -> !ASN1_OID.equals(s) && !EAT_OID.equals(s))
+                                .iterator())
                 .build();
     }
 }
diff --git a/tests/security/src/android/keystore/cts/AuthorizationList.java b/tests/security/src/android/keystore/cts/AuthorizationList.java
index 85ac115..dce9b2a 100644
--- a/tests/security/src/android/keystore/cts/AuthorizationList.java
+++ b/tests/security/src/android/keystore/cts/AuthorizationList.java
@@ -19,6 +19,10 @@
 import static com.google.common.base.Functions.forMap;
 import static com.google.common.collect.Collections2.transform;
 
+import co.nstant.in.cbor.model.DataItem;
+import co.nstant.in.cbor.model.Number;
+import co.nstant.in.cbor.model.UnsignedInteger;
+
 import com.google.common.base.Joiner;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
@@ -166,6 +170,7 @@
             .put(KM_PURPOSE_VERIFY, "VERIFY")
             .build();
 
+    private Integer securityLevel;
     private Set<Integer> purposes;
     private Integer algorithm;
     private Integer keySize;
@@ -331,6 +336,126 @@
 
     }
 
+    public AuthorizationList(co.nstant.in.cbor.model.Map submodMap)
+            throws CertificateParsingException {
+        for (DataItem key : submodMap.getKeys()) {
+            int keyInt = ((Number) key).getValue().intValue();
+            switch (keyInt) {
+                default:
+                    throw new CertificateParsingException("Unknown EAT tag: " + key);
+
+                case EatClaim.SECURITY_LEVEL:
+                    securityLevel = eatSecurityLevelToKeymasterSecurityLevel(
+                            CborUtils.getInt(submodMap, key));
+                    break;
+                case EatClaim.PURPOSE:
+                    purposes = CborUtils.getIntSet(submodMap, key);
+                    break;
+                case EatClaim.ALGORITHM:
+                    algorithm = CborUtils.getInt(submodMap, key);
+                    break;
+                case EatClaim.KEY_SIZE:
+                    keySize = CborUtils.getInt(submodMap, key);
+                    Log.i("Attestation", "Found KEY SIZE, value: " + keySize);
+                    break;
+                case EatClaim.DIGEST:
+                    digests = CborUtils.getIntSet(submodMap, key);
+                    break;
+                case EatClaim.PADDING:
+                    paddingModes = CborUtils.getIntSet(submodMap, key);
+                    break;
+                case EatClaim.RSA_PUBLIC_EXPONENT:
+                    rsaPublicExponent = CborUtils.getLong(submodMap, key);
+                    break;
+                case EatClaim.NO_AUTH_REQUIRED:
+                    noAuthRequired = true;
+                    break;
+                case EatClaim.IAT:
+                    creationDateTime = CborUtils.getDate(submodMap, key);
+                    break;
+                case EatClaim.ORIGIN:
+                    origin = CborUtils.getInt(submodMap, key);
+                    break;
+                case EatClaim.OS_VERSION:
+                    osVersion = CborUtils.getInt(submodMap, key);
+                    break;
+                case EatClaim.OS_PATCHLEVEL:
+                    osPatchLevel = CborUtils.getInt(submodMap, key);
+                    break;
+                case EatClaim.VENDOR_PATCHLEVEL:
+                    vendorPatchLevel = CborUtils.getInt(submodMap, key);
+                    break;
+                case EatClaim.BOOT_PATCHLEVEL:
+                    bootPatchLevel = CborUtils.getInt(submodMap, key);
+                    break;
+                case EatClaim.ACTIVE_DATETIME:
+                    activeDateTime = CborUtils.getDate(submodMap, key);
+                    break;
+                case EatClaim.ORIGINATION_EXPIRE_DATETIME:
+                    originationExpireDateTime = CborUtils.getDate(submodMap, key);
+                    break;
+                case EatClaim.USAGE_EXPIRE_DATETIME:
+                    usageExpireDateTime = CborUtils.getDate(submodMap, key);
+                    break;
+                case EatClaim.ROLLBACK_RESISTANT:
+                    rollbackResistant = true;
+                    break;
+                case EatClaim.ROLLBACK_RESISTANCE:
+                    rollbackResistance = true;
+                    break;
+                case EatClaim.AUTH_TIMEOUT:
+                    authTimeout = CborUtils.getInt(submodMap, key);
+                    break;
+                case EatClaim.ALLOW_WHILE_ON_BODY:
+                    allowWhileOnBody = true;
+                    break;
+                case EatClaim.EC_CURVE:
+                    ecCurve = CborUtils.getInt(submodMap, key);
+                    break;
+                case EatClaim.USER_AUTH_TYPE:
+                    userAuthType = CborUtils.getInt(submodMap, key);
+                    break;
+                case EatClaim.ATTESTATION_APPLICATION_ID:
+                    // TODO: The attestation application ID is currently still encoded as an ASN.1
+                    // structure. Parse a CBOR structure when it's available instead.
+                    attestationApplicationId = new AttestationApplicationId(
+                        Asn1Utils.getAsn1EncodableFromBytes(CborUtils.getBytes(submodMap, key)));
+                    break;
+                case EatClaim.ATTESTATION_ID_BRAND:
+                    brand = CborUtils.getString(submodMap, key);
+                    break;
+                case EatClaim.ATTESTATION_ID_DEVICE:
+                    device = CborUtils.getString(submodMap, key);
+                    break;
+                case EatClaim.ATTESTATION_ID_PRODUCT:
+                    product = CborUtils.getString(submodMap, key);
+                    break;
+                case EatClaim.ATTESTATION_ID_SERIAL:
+                    serialNumber = CborUtils.getString(submodMap, key);
+                    break;
+                case EatClaim.UEID:
+                    // TODO: Parse depending on encoding chosen in attestation_record.cpp.
+                    imei = CborUtils.getString(submodMap, key);
+                    break;
+                case EatClaim.ATTESTATION_ID_MEID:
+                    meid = CborUtils.getString(submodMap, key);
+                    break;
+                case EatClaim.ATTESTATION_ID_MANUFACTURER:
+                    manufacturer = CborUtils.getString(submodMap, key);
+                    break;
+                case EatClaim.ATTESTATION_ID_MODEL:
+                    model = CborUtils.getString(submodMap, key);
+                    break;
+                case EatClaim.USER_PRESENCE_REQUIRED:
+                    userPresenceRequired = CborUtils.getBoolean(submodMap, key);
+                    break;
+                case EatClaim.TRUSTED_CONFIRMATION_REQUIRED:
+                    confirmationRequired = true;
+                    break;
+            }
+        }
+    }
+
     public static String algorithmToString(int algorithm) {
         switch (algorithm) {
             case KM_ALGORITHM_RSA:
@@ -415,6 +540,10 @@
         }
     }
 
+    public Integer getSecurityLevel() {
+        return securityLevel;
+    }
+
     public Set<Integer> getPurposes() {
         return purposes;
     }
@@ -607,6 +736,19 @@
         return confirmationRequired;
     }
 
+    static int eatSecurityLevelToKeymasterSecurityLevel(int eatSecurityLevel) {
+        switch(eatSecurityLevel) {
+            case EatClaim.SECURITY_LEVEL_UNRESTRICTED:
+                return Attestation.KM_SECURITY_LEVEL_SOFTWARE;
+            case EatClaim.SECURITY_LEVEL_SECURE_RESTRICTED:
+                return Attestation.KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT;
+            case EatClaim.SECURITY_LEVEL_HARDWARE:
+                return Attestation.KM_SECURITY_LEVEL_STRONG_BOX;
+            default:
+                throw new RuntimeException("Invalid EAT security level: " + eatSecurityLevel);
+        }
+    }
+
     private String getStringFromAsn1Value(ASN1Primitive value) throws CertificateParsingException {
         try {
             return Asn1Utils.getStringFromAsn1OctetStreamAssumingUTF8(value);
diff --git a/tests/security/src/android/keystore/cts/CborUtils.java b/tests/security/src/android/keystore/cts/CborUtils.java
new file mode 100644
index 0000000..eeac9b5
--- /dev/null
+++ b/tests/security/src/android/keystore/cts/CborUtils.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2020 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 android.keystore.cts;
+
+import co.nstant.in.cbor.model.Array;
+import co.nstant.in.cbor.model.ByteString;
+import co.nstant.in.cbor.model.DataItem;
+import co.nstant.in.cbor.model.Map;
+import co.nstant.in.cbor.model.NegativeInteger;
+import co.nstant.in.cbor.model.Number;
+import co.nstant.in.cbor.model.SimpleValue;
+import co.nstant.in.cbor.model.SimpleValueType;
+import co.nstant.in.cbor.model.UnsignedInteger;
+
+import java.nio.charset.StandardCharsets;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+
+class CborUtils {
+    public static Number toNumber(long l) {
+        return l >= 0 ? new UnsignedInteger(l) : new NegativeInteger(l);
+    }
+
+    public static int getInt(Map map, long index) {
+        DataItem item = map.get(CborUtils.toNumber(index));
+        return ((Number) item).getValue().intValue();
+    }
+
+    public static int getInt(Map map, DataItem index) {
+        DataItem item = map.get(index);
+        return ((Number) item).getValue().intValue();
+    }
+
+    public static long getLong(Map map, DataItem index) {
+        DataItem item = map.get(index);
+        return ((Number) item).getValue().longValue();
+    }
+
+    public static Set<Integer> getIntSet(Map map, DataItem index) {
+        Array array = (Array) map.get(index);
+        Set<Integer> result = new HashSet();
+        for (DataItem item : array.getDataItems()) {
+            result.add(((Number) item).getValue().intValue());
+        }
+        return result;
+    }
+
+    public static Boolean getBoolean(Map map, DataItem index) {
+        SimpleValueType value = ((SimpleValue) map.get(index)).getSimpleValueType();
+        if (value != SimpleValueType.TRUE && value != SimpleValueType.FALSE) {
+            throw new RuntimeException("Only expecting boolean values for " + index);
+        }
+        return (value == SimpleValueType.TRUE);
+    }
+
+    public static List<Boolean> getBooleanList(Map map, DataItem index) {
+        Array array = (Array) map.get(index);
+        List<Boolean> result = new ArrayList();
+        for (DataItem item : array.getDataItems()) {
+            SimpleValueType value = ((SimpleValue) item).getSimpleValueType();
+            if (value == SimpleValueType.FALSE) {
+                result.add(false);
+            } else if (value == SimpleValueType.TRUE) {
+                result.add(true);
+            } else {
+                throw new RuntimeException("Map contains more than booleans: " + map);
+            }
+        }
+        return result;
+    }
+
+    public static Date getDate(Map map, DataItem index) {
+        DataItem item = map.get(index);
+        long epochMillis = ((Number) item).getValue().longValue();
+        return new Date(epochMillis);
+    }
+
+    public static byte[] getBytes(Map map, DataItem index) {
+        DataItem item = map.get(index);
+        return ((ByteString) item).getBytes();
+    }
+
+    public static String getString(Map map, DataItem index) {
+        byte[] bytes = getBytes(map, index);
+        return new String(bytes, StandardCharsets.UTF_8);
+    }
+}
diff --git a/tests/security/src/android/keystore/cts/EatAttestation.java b/tests/security/src/android/keystore/cts/EatAttestation.java
new file mode 100644
index 0000000..21ef0db
--- /dev/null
+++ b/tests/security/src/android/keystore/cts/EatAttestation.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2020 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 android.keystore.cts;
+
+import android.util.Log;
+
+import co.nstant.in.cbor.CborDecoder;
+import co.nstant.in.cbor.CborException;
+import co.nstant.in.cbor.model.DataItem;
+import co.nstant.in.cbor.model.Map;
+import co.nstant.in.cbor.model.Number;
+import co.nstant.in.cbor.model.UnicodeString;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+
+import java.security.cert.CertificateParsingException;
+import java.security.cert.X509Certificate;
+import java.util.List;
+
+public class EatAttestation extends Attestation {
+    static final String TAG = "EatAttestation";
+    final Map extension;
+    final RootOfTrust rootOfTrust;
+
+    /**
+     * Constructs an {@code EatAttestation} object from the provided {@link X509Certificate},
+     * extracting the attestation data from the attestation extension.
+     *
+     * @throws CertificateParsingException if the certificate does not contain a properly-formatted
+     *     attestation extension.
+     */
+    public EatAttestation(X509Certificate x509Cert)
+            throws CertificateParsingException, CborException {
+        super(x509Cert);
+        extension = getEatExtension(x509Cert);
+
+        RootOfTrust.Builder rootOfTrustBuilder = new RootOfTrust.Builder();
+        List<Boolean> bootState = null;
+        boolean officialBuild = false;
+
+        for (DataItem key : extension.getKeys()) {
+            int keyInt = ((Number) key).getValue().intValue();
+            switch (keyInt) {
+                default:
+                    throw new CertificateParsingException(
+                            "Unknown EAT tag: " + key + "\n in EAT extension:\n" + toString());
+
+                case EatClaim.ATTESTATION_VERSION:
+                    attestationVersion = CborUtils.getInt(extension, key);
+                    break;
+                case EatClaim.KEYMASTER_VERSION:
+                    keymasterVersion = CborUtils.getInt(extension, key);
+                    break;
+                case EatClaim.SECURITY_LEVEL:
+                    keymasterSecurityLevel =
+                            eatSecurityLevelToKeymintSecurityLevel(
+                                    CborUtils.getInt(extension, key));
+                    break;
+                case EatClaim.SUBMODS:
+                    Map submods = (Map) extension.get(key);
+                    softwareEnforced =
+                            new AuthorizationList(
+                                    (Map) submods.get(new UnicodeString(EatClaim.SUBMOD_SOFTWARE)));
+                    teeEnforced =
+                            new AuthorizationList(
+                                    (Map) submods.get(new UnicodeString(EatClaim.SUBMOD_TEE)));
+                    break;
+                case EatClaim.VERIFIED_BOOT_KEY:
+                    rootOfTrustBuilder.setVerifiedBootKey(CborUtils.getBytes(extension, key));
+                    break;
+                case EatClaim.DEVICE_LOCKED:
+                    rootOfTrustBuilder.setDeviceLocked(CborUtils.getBoolean(extension, key));
+                    break;
+                case EatClaim.BOOT_STATE:
+                    bootState = CborUtils.getBooleanList(extension, key);
+                    break;
+                case EatClaim.OFFICIAL_BUILD:
+                    officialBuild = CborUtils.getBoolean(extension, key);
+                    break;
+                case EatClaim.NONCE:
+                    attestationChallenge = CborUtils.getBytes(extension, key);
+                    break;
+                case EatClaim.CTI:
+                    Log.i(TAG, "Got CTI claim: " + CborUtils.getBytes(extension, key));
+                    uniqueId = CborUtils.getBytes(extension, key);
+                    break;
+                case EatClaim.VERIFIED_BOOT_HASH:
+                    // TODO: ignored for now, as this is not checked in original ASN.1 tests
+                    break;
+            }
+        }
+
+        if (bootState != null) {
+            rootOfTrustBuilder.setVerifiedBootState(
+                    eatBootStateTypeToVerifiedBootState(bootState, officialBuild));
+        }
+        rootOfTrust = rootOfTrustBuilder.build();
+    }
+
+    /** Find the submod containing the key information, and return its security level. */
+    public int getAttestationSecurityLevel() {
+        if (teeEnforced != null && teeEnforced.getAlgorithm() != null) {
+            return teeEnforced.getSecurityLevel();
+        } else if (softwareEnforced != null && softwareEnforced.getAlgorithm() != null) {
+            return softwareEnforced.getSecurityLevel();
+        } else {
+            return -1;
+        }
+    }
+
+    public RootOfTrust getRootOfTrust() {
+        return rootOfTrust;
+    }
+
+    public String toString() {
+        return super.toString() + "\nEncoded CBOR: " + extension;
+    }
+
+    Map getEatExtension(X509Certificate x509Cert)
+            throws CertificateParsingException, CborException {
+        byte[] attestationExtensionBytes = x509Cert.getExtensionValue(Attestation.EAT_OID);
+        if (attestationExtensionBytes == null || attestationExtensionBytes.length == 0) {
+            throw new CertificateParsingException("Did not find extension with OID " + EAT_OID);
+        }
+        ASN1Encodable asn1 = Asn1Utils.getAsn1EncodableFromBytes(attestationExtensionBytes);
+        byte[] cborBytes = Asn1Utils.getByteArrayFromAsn1(asn1);
+        List<DataItem> cbor = CborDecoder.decode(cborBytes);
+        return (Map) cbor.get(0);
+    }
+
+    static int eatSecurityLevelToKeymintSecurityLevel(int eatSecurityLevel) {
+        switch (eatSecurityLevel) {
+            case EatClaim.SECURITY_LEVEL_UNRESTRICTED:
+                return Attestation.KM_SECURITY_LEVEL_SOFTWARE;
+            case EatClaim.SECURITY_LEVEL_SECURE_RESTRICTED:
+                return Attestation.KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT;
+            case EatClaim.SECURITY_LEVEL_HARDWARE:
+                return Attestation.KM_SECURITY_LEVEL_STRONG_BOX;
+            default:
+                throw new RuntimeException("Invalid EAT security level: " + eatSecurityLevel);
+        }
+    }
+
+    static int eatBootStateTypeToVerifiedBootState(List<Boolean> bootState, Boolean officialBuild) {
+        if (bootState.size() != 5) {
+            throw new RuntimeException("Boot state map has unexpected size: " + bootState.size());
+        }
+        if (bootState.get(4)) {
+            throw new RuntimeException("debug-permanent-disable must never be true: " + bootState);
+        }
+        boolean verifiedOrSelfSigned = bootState.get(0);
+        if (verifiedOrSelfSigned != bootState.get(1)
+                && verifiedOrSelfSigned != bootState.get(2)
+                && verifiedOrSelfSigned != bootState.get(3)) {
+            throw new RuntimeException("Unexpected boot state: " + bootState);
+        }
+
+        if (officialBuild) {
+            if (!verifiedOrSelfSigned) {
+                throw new AssertionError("Non-verified official build");
+            }
+            return RootOfTrust.KM_VERIFIED_BOOT_VERIFIED;
+        } else {
+            return verifiedOrSelfSigned
+                    ? RootOfTrust.KM_VERIFIED_BOOT_SELF_SIGNED
+                    : RootOfTrust.KM_VERIFIED_BOOT_UNVERIFIED;
+        }
+    }
+}
diff --git a/tests/security/src/android/keystore/cts/EatClaim.java b/tests/security/src/android/keystore/cts/EatClaim.java
new file mode 100644
index 0000000..1130037
--- /dev/null
+++ b/tests/security/src/android/keystore/cts/EatClaim.java
@@ -0,0 +1,112 @@
+package android.keystore.cts;
+
+import android.security.keymaster.KeymasterDefs;
+
+class EatClaim {
+    public static final int IAT = 6;
+    public static final int CTI = 7;
+
+    public static final int NONCE = -75008;
+    public static final int UEID = -75009;
+
+    public static final int SECURITY_LEVEL = -76002;
+    public static final int SECURITY_LEVEL_UNRESTRICTED = 1;
+    public static final int SECURITY_LEVEL_SECURE_RESTRICTED = 3;
+    public static final int SECURITY_LEVEL_HARDWARE = 4;
+
+    public static final int BOOT_STATE = -76003;
+    public static final int SUBMODS = -76000;
+
+    private static final int PRIVATE_BASE = -80000;
+
+    public static final int PURPOSE = PRIVATE_BASE - (0x0FFFFFFF & KeymasterDefs.KM_TAG_PURPOSE);
+    public static final int ALGORITHM =
+            PRIVATE_BASE - (0x0FFFFFFF & KeymasterDefs.KM_TAG_ALGORITHM);
+    public static final int KEY_SIZE = PRIVATE_BASE - (0x0FFFFFFF & KeymasterDefs.KM_TAG_KEY_SIZE);
+    public static final int BLOCK_MODE =
+            PRIVATE_BASE - (0x0FFFFFFF & KeymasterDefs.KM_TAG_BLOCK_MODE);
+    public static final int DIGEST = PRIVATE_BASE - (0x0FFFFFFF & KeymasterDefs.KM_TAG_DIGEST);
+    public static final int PADDING = PRIVATE_BASE - (0x0FFFFFFF & KeymasterDefs.KM_TAG_PADDING);
+    public static final int CALLER_NONCE =
+            PRIVATE_BASE - (0x0FFFFFFF & KeymasterDefs.KM_TAG_CALLER_NONCE);
+    public static final int MIN_MAC_LENGTH =
+            PRIVATE_BASE - (0x0FFFFFFF & KeymasterDefs.KM_TAG_MIN_MAC_LENGTH);
+    public static final int KDF = PRIVATE_BASE - 9;
+
+    public static final int EC_CURVE = PRIVATE_BASE - 10;
+    public static final int EAT_EC_CURVE_P_224 = 0;
+    public static final int EAT_EC_CURVE_P_256 = 1;
+    public static final int EAT_EC_CURVE_P_384 = 2;
+    public static final int EAT_EC_CURVE_P_521 = 3;
+
+    public static final int RSA_PUBLIC_EXPONENT =
+            PRIVATE_BASE - (0x0FFFFFFF & KeymasterDefs.KM_TAG_RSA_PUBLIC_EXPONENT);
+
+    public static final int ROLLBACK_RESISTANCE = PRIVATE_BASE - 303;
+    public static final int EARLY_BOOT_ONLY = PRIVATE_BASE - 305;
+
+    public static final int ACTIVE_DATETIME =
+            PRIVATE_BASE - (0x0FFFFFFF & KeymasterDefs.KM_TAG_ACTIVE_DATETIME);
+    public static final int ORIGINATION_EXPIRE_DATETIME =
+            PRIVATE_BASE - (0x0FFFFFFF & KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME);
+    public static final int USAGE_EXPIRE_DATETIME =
+            PRIVATE_BASE - (0x0FFFFFFF & KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME);
+
+    public static final int NO_AUTH_REQUIRED =
+            PRIVATE_BASE - (0x0FFFFFFF & KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
+    public static final int USER_AUTH_TYPE =
+            PRIVATE_BASE - (0x0FFFFFFF & KeymasterDefs.KM_TAG_USER_AUTH_TYPE);
+    public static final int AUTH_TIMEOUT =
+            PRIVATE_BASE - (0x0FFFFFFF & KeymasterDefs.KM_TAG_AUTH_TIMEOUT);
+    public static final int ALLOW_WHILE_ON_BODY =
+            PRIVATE_BASE - (0x0FFFFFFF & KeymasterDefs.KM_TAG_ALLOW_WHILE_ON_BODY);
+    public static final int USER_PRESENCE_REQUIRED = PRIVATE_BASE - 507;
+    public static final int TRUSTED_CONFIRMATION_REQUIRED =
+            PRIVATE_BASE - (0x0FFFFFFF & KeymasterDefs.KM_TAG_TRUSTED_CONFIRMATION_REQUIRED);
+    public static final int UNLOCKED_DEVICE_REQUIRED =
+            PRIVATE_BASE - (0x0FFFFFFF & KeymasterDefs.KM_TAG_UNLOCKED_DEVICE_REQUIRED);
+
+    public static final int APPLICATION_ID =
+            PRIVATE_BASE - (0x0FFFFFFF & KeymasterDefs.KM_TAG_APPLICATION_ID);
+
+    public static final int ORIGIN = PRIVATE_BASE - (0x0FFFFFFF & KeymasterDefs.KM_TAG_ORIGIN);
+    // TODO: hardcoded while KeymasterDefs uses the same value for
+    // ROLLBACK_RESISTANCE and ROLLBACK_RESISTANT
+    public static final int ROLLBACK_RESISTANT = PRIVATE_BASE - 703;
+    public static final int OS_VERSION = PRIVATE_BASE - 705;
+    public static final int OS_PATCHLEVEL = PRIVATE_BASE - 706;
+    public static final int ATTESTATION_APPLICATION_ID = PRIVATE_BASE - 709;
+    public static final int ATTESTATION_ID_BRAND =
+            PRIVATE_BASE - (0x0FFFFFFF & KeymasterDefs.KM_TAG_ATTESTATION_ID_BRAND);
+    public static final int ATTESTATION_ID_DEVICE =
+            PRIVATE_BASE - (0x0FFFFFFF & KeymasterDefs.KM_TAG_ATTESTATION_ID_DEVICE);
+    public static final int ATTESTATION_ID_PRODUCT =
+            PRIVATE_BASE - (0x0FFFFFFF & KeymasterDefs.KM_TAG_ATTESTATION_ID_PRODUCT);
+    public static final int ATTESTATION_ID_SERIAL =
+            PRIVATE_BASE - (0x0FFFFFFF & KeymasterDefs.KM_TAG_ATTESTATION_ID_SERIAL);
+    public static final int ATTESTATION_ID_MEID =
+            PRIVATE_BASE - (0x0FFFFFFF & KeymasterDefs.KM_TAG_ATTESTATION_ID_MEID);
+    public static final int ATTESTATION_ID_MANUFACTURER =
+            PRIVATE_BASE - (0x0FFFFFFF & KeymasterDefs.KM_TAG_ATTESTATION_ID_MANUFACTURER);
+    public static final int ATTESTATION_ID_MODEL =
+            PRIVATE_BASE - (0x0FFFFFFF & KeymasterDefs.KM_TAG_ATTESTATION_ID_MODEL);
+    public static final int VENDOR_PATCHLEVEL =
+            PRIVATE_BASE - (0x0FFFFFFF & KeymasterDefs.KM_TAG_VENDOR_PATCHLEVEL);
+    public static final int BOOT_PATCHLEVEL =
+            PRIVATE_BASE - (0x0FFFFFFF & KeymasterDefs.KM_TAG_BOOT_PATCHLEVEL);
+    public static final int DEVICE_UNIQUE_ATTESTATION =
+            PRIVATE_BASE - (0x0FFFFFFF & KeymasterDefs.KM_TAG_DEVICE_UNIQUE_ATTESTATION);
+    public static final int IDENTITY_CREDENTIAL_KEY = PRIVATE_BASE - 721;
+
+    private static final int NON_KM_BASE = PRIVATE_BASE - 2000;
+
+    public static final int VERIFIED_BOOT_KEY = NON_KM_BASE - 1;
+    public static final int DEVICE_LOCKED = NON_KM_BASE - 2;
+    public static final int VERIFIED_BOOT_HASH = NON_KM_BASE - 3;
+    public static final int ATTESTATION_VERSION = NON_KM_BASE - 4;
+    public static final int KEYMASTER_VERSION = NON_KM_BASE - 5;
+    public static final int OFFICIAL_BUILD = NON_KM_BASE - 6;
+
+    public static final String SUBMOD_SOFTWARE = "software";
+    public static final String SUBMOD_TEE = "tee";
+}
diff --git a/tests/security/src/android/keystore/cts/RootOfTrust.java b/tests/security/src/android/keystore/cts/RootOfTrust.java
index 9957702..a115874 100644
--- a/tests/security/src/android/keystore/cts/RootOfTrust.java
+++ b/tests/security/src/android/keystore/cts/RootOfTrust.java
@@ -51,6 +51,13 @@
                 Asn1Utils.getIntegerFromAsn1(sequence.getObjectAt(VERIFIED_BOOT_STATE_INDEX));
     }
 
+
+    RootOfTrust(byte[] verifiedBootKey, boolean deviceLocked, int verifiedBootState) {
+        this.verifiedBootKey = verifiedBootKey;
+        this.deviceLocked = deviceLocked;
+        this.verifiedBootState = verifiedBootState;
+    }
+
     public static String verifiedBootStateToString(int verifiedBootState) {
         switch (verifiedBootState) {
             case KM_VERIFIED_BOOT_VERIFIED:
@@ -82,11 +89,35 @@
     public String toString() {
         return new StringBuilder()
                 .append("\nVerified boot Key: ")
-                .append(BaseEncoding.base64().encode(verifiedBootKey))
+                .append(verifiedBootKey != null ?
+                            BaseEncoding.base64().encode(verifiedBootKey) :
+                            "null")
                 .append("\nDevice locked: ")
                 .append(deviceLocked)
                 .append("\nVerified boot state: ")
                 .append(verifiedBootStateToString(verifiedBootState))
                 .toString();
     }
+
+    public static class Builder {
+        private byte[] verifiedBootKey;
+        private boolean deviceLocked = false;
+        private int verifiedBootState = -1;
+
+        public Builder setVerifiedBootKey(byte[] verifiedBootKey) {
+            this.verifiedBootKey = verifiedBootKey;
+            return this;
+        }
+        public Builder setDeviceLocked(boolean deviceLocked) {
+            this.deviceLocked = deviceLocked;
+            return this;
+        }
+        public Builder setVerifiedBootState(int verifiedBootState) {
+            this.verifiedBootState = verifiedBootState;
+            return this;
+        }
+        public RootOfTrust build() {
+            return new RootOfTrust(verifiedBootKey, deviceLocked, verifiedBootState);
+        }
+    }
 }
diff --git a/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java b/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
index d457995..1af904f 100644
--- a/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
@@ -234,7 +234,8 @@
                 assertEquals(1, certificates.length);
 
                 X509Certificate attestationCert = (X509Certificate) certificates[0];
-                assertNull(attestationCert.getExtensionValue(Attestation.KEY_DESCRIPTION_OID));
+                assertNull(attestationCert.getExtensionValue(Attestation.ASN1_OID));
+                assertNull(attestationCert.getExtensionValue(Attestation.EAT_OID));
             } finally {
                 keyStore.deleteEntry(keystoreAlias);
             }
@@ -272,7 +273,7 @@
             verifyCertificateChain(certificates, TestUtils.hasStrongBox(getContext()));
 
             X509Certificate attestationCert = (X509Certificate) certificates[0];
-            checkDeviceLocked(new Attestation(attestationCert));
+            checkDeviceLocked(Attestation.loadFromCertificate(attestationCert));
         } finally {
             keyStore.deleteEntry(keystoreAlias);
         }
@@ -402,7 +403,7 @@
                 assertEquals(1, certificates.length);
 
                 X509Certificate attestationCert = (X509Certificate) certificates[0];
-                assertNull(attestationCert.getExtensionValue(Attestation.KEY_DESCRIPTION_OID));
+                assertNull(attestationCert.getExtensionValue(Attestation.ASN1_OID));
             } finally {
                 keyStore.deleteEntry(keystoreAlias);
             }
@@ -441,7 +442,7 @@
             verifyCertificateChain(certificates, TestUtils.hasStrongBox(getContext()));
 
             X509Certificate attestationCert = (X509Certificate) certificates[0];
-            checkDeviceLocked(new Attestation(attestationCert));
+            checkDeviceLocked(Attestation.loadFromCertificate(attestationCert));
         } finally {
             keyStore.deleteEntry(keystoreAlias);
         }
@@ -559,12 +560,13 @@
             verifyCertificateChain(certificates, false /* expectStrongBox */);
 
             X509Certificate attestationCert = (X509Certificate) certificates[0];
-            Attestation attestation = new Attestation(attestationCert);
+            Attestation attestation = Attestation.loadFromCertificate(attestationCert);
 
-            checkRsaKeyDetails(attestation, keySize, purposes, ImmutableSet.copyOf(paddingModes));
+            checkRsaKeyDetails(attestation, keySize, purposes,
+                ImmutableSet.copyOf(paddingModes));
             checkKeyUsage(attestationCert, purposes);
-            checkKeyIndependentAttestationInfo(challenge, purposes, startTime, includeValidityDates,
-                    devicePropertiesAttestation, attestation);
+            checkKeyIndependentAttestationInfo(challenge, purposes, startTime,
+                includeValidityDates, devicePropertiesAttestation, attestation);
         } finally {
             keyStore.deleteEntry(keystoreAlias);
         }
@@ -618,12 +620,12 @@
             verifyCertificateChain(certificates, false /* expectStrongBox */);
 
             X509Certificate attestationCert = (X509Certificate) certificates[0];
-            Attestation attestation = new Attestation(attestationCert);
+            Attestation attestation = Attestation.loadFromCertificate(attestationCert);
 
             checkEcKeyDetails(attestation, ecCurve, keySize);
             checkKeyUsage(attestationCert, purposes);
-            checkKeyIndependentAttestationInfo(challenge, purposes, startTime, includeValidityDates,
-                    devicePropertiesAttestation, attestation);
+            checkKeyIndependentAttestationInfo(challenge, purposes, startTime,
+                includeValidityDates, devicePropertiesAttestation, attestation);
         } finally {
             keyStore.deleteEntry(keystoreAlias);
         }
@@ -635,6 +637,7 @@
         int kmVersion = attestation.getKeymasterVersion();
         assertNull(attestation.getTeeEnforced().getAttestationApplicationId());
         aaid = attestation.getSoftwareEnforced().getAttestationApplicationId();
+
         if (kmVersion >= 3) {
             // must be present and correct
             assertNotNull(aaid);
@@ -695,9 +698,11 @@
         checkUnexpectedOids(attestation);
         checkAttestationSecurityLevelDependentParams(attestation);
         assertNotNull(attestation.getAttestationChallenge());
-        assertTrue(Arrays.equals(challenge, attestation.getAttestationChallenge()));
-        assertNotNull(attestation.getUniqueId());
-        assertEquals(0, attestation.getUniqueId().length);
+        assertThat(attestation.getAttestationChallenge(), is(challenge));
+        // In EAT, this is null if not filled in. In ASN.1, this is an array with length 0.
+        if (attestation.getUniqueId() != null) {
+            assertEquals(0, attestation.getUniqueId().length);
+        }
         checkPurposes(attestation, purposes);
         checkDigests(attestation,
                 ImmutableSet.of(KM_DIGEST_NONE, KM_DIGEST_SHA_2_256, KM_DIGEST_SHA_2_512));
@@ -919,8 +924,8 @@
 
     @SuppressWarnings("unchecked")
     private void checkAttestationSecurityLevelDependentParams(Attestation attestation) {
-        assertThat("Attestation version must be 1, 2, 3, or 4", attestation.getAttestationVersion(),
-               either(is(1)).or(is(2)).or(is(3)).or(is(4)));
+        assertThat("Attestation version must be 1, 2, 3, 4 or 5", attestation.getAttestationVersion(),
+               either(is(1)).or(is(2)).or(is(3)).or(is(4)).or(is(5)));
 
         AuthorizationList teeEnforced = attestation.getTeeEnforced();
         AuthorizationList softwareEnforced = attestation.getSoftwareEnforced();
@@ -997,7 +1002,7 @@
     }
 
     private void checkRootOfTrust(Attestation attestation, boolean requireLocked) {
-        RootOfTrust rootOfTrust = attestation.getTeeEnforced().getRootOfTrust();
+        RootOfTrust rootOfTrust = attestation.getRootOfTrust();
         assertNotNull(rootOfTrust);
         assertNotNull(rootOfTrust.getVerifiedBootKey());
         assertTrue("Verified boot key is only " + rootOfTrust.getVerifiedBootKey().length +
diff --git a/tests/tests/permission2/res/raw/android_manifest.xml b/tests/tests/permission2/res/raw/android_manifest.xml
index a89ba85..cbcaf24 100644
--- a/tests/tests/permission2/res/raw/android_manifest.xml
+++ b/tests/tests/permission2/res/raw/android_manifest.xml
@@ -3011,6 +3011,12 @@
     <permission android:name="android.permission.DUMP"
         android:protectionLevel="signature|privileged|development" />
 
+    <!-- Allows an application to start tracing for InputMethod and WindowManager.
+    <p>Not for use by third-party applications.
+    @hide -->
+    <permission android:name="android.permission.CONTROL_UI_TRACING"
+        android:protectionLevel="signature|privileged|development" />
+
     <!-- Allows an application to read the low-level system log files.
     <p>Not for use by third-party applications, because
     Log entries can contain the user's private information. -->