diff options
author | Treehugger Robot <android-test-infra-autosubmit@system.gserviceaccount.com> | 2024-05-17 20:43:30 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2024-05-17 20:43:30 +0000 |
commit | ab80b933f51d662c2e811cb19ebaa9f60d31739a (patch) | |
tree | 95e5ed440f9e95ffa47bb3266b20c2cf86d6fdcc | |
parent | 6ccb9739db74be2e3ecd23f9b5af99e8fdd25c0f (diff) | |
parent | 2dfde193e378e97a953402c5e9bf40d75d65b26f (diff) | |
download | cts-android14-tests-dev.tar.gz |
Merge changes from topic "cherrypicker-L97700030003622837:N07800030057406848" into android14-tests-devandroid14-tests-dev
* changes:
RESTRICT AUTOMERGE Updated KM-Version check against FEATURE_HARDWARE_KEYSTORE based on VSR API level.
RESTRICT AUTOMERGE Updated the logic to determine the VSR API level for Keystore CTS tests.
RESTRICT AUTOMERGE Skipping test on GSI + first_api_level < U
3 files changed, 159 insertions, 14 deletions
diff --git a/tests/tests/keystore/src/android/keystore/cts/DeviceOwnerKeyManagementTest.java b/tests/tests/keystore/src/android/keystore/cts/DeviceOwnerKeyManagementTest.java index c57b7dde2d3..e5244005534 100644 --- a/tests/tests/keystore/src/android/keystore/cts/DeviceOwnerKeyManagementTest.java +++ b/tests/tests/keystore/src/android/keystore/cts/DeviceOwnerKeyManagementTest.java @@ -20,17 +20,35 @@ import static android.app.admin.DevicePolicyManager.ID_TYPE_BASE_INFO; import static android.app.admin.DevicePolicyManager.ID_TYPE_IMEI; import static android.app.admin.DevicePolicyManager.ID_TYPE_MEID; import static android.app.admin.DevicePolicyManager.ID_TYPE_SERIAL; +import static android.keystore.cts.Attestation.KM_SECURITY_LEVEL_SOFTWARE; +import static android.keystore.cts.Attestation.KM_SECURITY_LEVEL_STRONG_BOX; +import static android.keystore.cts.Attestation.KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT; +import static android.keystore.cts.KeyAttestationTest.verifyCertificateChain; +import static android.keystore.cts.RootOfTrust.KM_VERIFIED_BOOT_VERIFIED; +import static android.security.keystore.KeyProperties.DIGEST_NONE; +import static android.security.keystore.KeyProperties.DIGEST_SHA256; +import static android.security.keystore.KeyProperties.DIGEST_SHA512; +import static android.security.keystore.KeyProperties.KEY_ALGORITHM_EC; +import static android.security.keystore.KeyProperties.PURPOSE_SIGN; + import static com.google.android.attestation.ParsedAttestationRecord.createParsedAttestationRecord; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.greaterThanOrEqualTo; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.junit.Assume.assumeFalse; + import android.app.admin.DevicePolicyManager; import android.content.ComponentName; import android.content.Context; import android.content.pm.PackageManager; import android.keystore.cts.util.TestUtils; import android.os.Build; -import android.os.SystemProperties; import android.security.AttestedKeyPair; import android.security.keystore.KeyGenParameterSpec; import android.security.keystore.KeyProperties; @@ -44,6 +62,7 @@ import com.android.bedstead.harrier.annotations.RequireFeature; import com.android.bedstead.harrier.annotations.RequireRunOnSystemUser; import com.android.bedstead.nene.TestApis; import com.android.bedstead.nene.devicepolicy.DeviceOwner; +import com.android.bedstead.nene.exceptions.NeneException; import com.android.bedstead.nene.permissions.PermissionContext; import com.android.compatibility.common.util.ApiTest; @@ -56,12 +75,15 @@ import org.junit.Test; import java.io.IOException; import java.security.GeneralSecurityException; import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.KeyStore; import java.security.PrivateKey; import java.security.PublicKey; import java.security.Signature; import java.security.cert.Certificate; import java.security.cert.CertificateParsingException; import java.security.cert.X509Certificate; +import java.security.spec.ECGenParameterSpec; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -120,10 +142,6 @@ public class DeviceOwnerKeyManagementTest { signDataWithKey(algoIdentifier, keyPair.getPrivate())); } - int getDeviceFirstSdkLevel() { - return SystemProperties.getInt("ro.board.first_api_level", 0); - } - private void validateDeviceIdAttestationData(Certificate[] certs, String expectedSerial, String expectedImei, @@ -200,7 +218,7 @@ public class DeviceOwnerKeyManagementTest { TestUtils.getFeatureVersionKeystore(sContext, useStrongbox) >= 300; final boolean emptySecondImei = TextUtils.isEmpty(expectedSecondImei); final boolean deviceShippedWithKeyMint3 = - getDeviceFirstSdkLevel() >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE; + TestUtils.getVendorApiLevel() >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE; if (!isKeyMintV3) { // Earlier versions of KeyMint must not attest to second IMEI values as they are not @@ -510,6 +528,68 @@ public class DeviceOwnerKeyManagementTest { } } + private boolean checkRootOfTrustForLockedDevice(Attestation attestation) { + RootOfTrust rootOfTrust = attestation.getRootOfTrust(); + assertNotNull(rootOfTrust); + assertNotNull(rootOfTrust.getVerifiedBootKey()); + assertTrue("Verified boot key is only " + rootOfTrust.getVerifiedBootKey().length + + " bytes long", rootOfTrust.getVerifiedBootKey().length >= 32); + return (rootOfTrust.isDeviceLocked() + && (rootOfTrust.getVerifiedBootState() == KM_VERIFIED_BOOT_VERIFIED)); + } + + private boolean isDeviceLockedAccordingToAttestation(Attestation attestation) { + assertThat("Attestation version must be >= 1", + attestation.getAttestationVersion(), greaterThanOrEqualTo(1)); + + int attestationSecurityLevel = attestation.getAttestationSecurityLevel(); + switch (attestationSecurityLevel) { + case KM_SECURITY_LEVEL_STRONG_BOX: + case KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT: + assertThat("Attestation security level doesn't match keymaster security level", + attestation.getKeymasterSecurityLevel(), is(attestationSecurityLevel)); + assertThat("Keymaster version should be greater than or equal to 2.", + attestation.getKeymasterVersion(), greaterThanOrEqualTo(2)); + + return checkRootOfTrustForLockedDevice(attestation); + case KM_SECURITY_LEVEL_SOFTWARE: + default: + // TEE attestation has been required since Android 7.0. + fail("Unexpected attestation security level: " + + attestation.securityLevelToString(attestationSecurityLevel)); + break; + } + return false; + } + + private boolean checkDeviceLocked() throws Exception { + String keystoreAlias = "check_device_state"; + KeyGenParameterSpec.Builder builder = + new KeyGenParameterSpec.Builder(keystoreAlias, PURPOSE_SIGN) + .setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1")) + .setAttestationChallenge(new byte[128]) + .setDigests(DIGEST_NONE, DIGEST_SHA256, DIGEST_SHA512); + + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM_EC, + "AndroidKeyStore"); + keyPairGenerator.initialize(builder.build()); + keyPairGenerator.generateKeyPair(); + KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); + keyStore.load(null); + + try { + Certificate []certificates = keyStore.getCertificateChain(keystoreAlias); + verifyCertificateChain(certificates, false); + + X509Certificate attestationCert = (X509Certificate) certificates[0]; + + return isDeviceLockedAccordingToAttestation( + Attestation.loadFromCertificate(attestationCert)); + } finally { + keyStore.deleteEntry(keystoreAlias); + } + } + @Test @ApiTest(apis = {"android.app.admin.DevicePolicyManager#generateKeyPair", "android.app.admin.DevicePolicyManager#ID_TYPE_IMEI", @@ -519,8 +599,25 @@ public class DeviceOwnerKeyManagementTest { @RequireFeature(PackageManager.FEATURE_DEVICE_ADMIN) @RequireFeature(PackageManager.FEATURE_DEVICE_ID_ATTESTATION) public void testAllVariationsOfDeviceIdAttestation() throws Exception { + // b/298586194, there are some devices launched with Android T, and they will be receiving + // only system update and not vendor update, newly added attestation properties + // (ro.product.*_for_attestation) reading logic would not be available for such devices + // hence skipping this test for such scenario. + assumeFalse("This test is not applicable for device running GSI image and" + + " first_api_level < 14", TestUtils.isGsiImage() + && TestUtils.getVendorApiLevel() < Build.VERSION_CODES.UPSIDE_DOWN_CAKE); + + final boolean isDeviceLocked = checkDeviceLocked(); try (DeviceOwner o = TestApis.devicePolicy().setDeviceOwner(DEVICE_ADMIN_COMPONENT_NAME)) { assertAllVariantsOfDeviceIdAttestation(false /* useStrongBox */); + } catch (NeneException e) { + // b/291069162, some devices do not allow to set DeviceOwner in an unlocked state. And + // unlocked state is allowed while testing on GSI image. If this condition not match + // throw the exception. + assumeFalse("It is acceptable to fail setting DeviceOwner on GSI build running on" + + " unlocked devices.", (e.getMessage().contains("Error setting device owner") + && TestUtils.isGsiImage() && !isDeviceLocked)); + throw new Exception(e); } } @@ -533,8 +630,25 @@ public class DeviceOwnerKeyManagementTest { @RequireFeature(PackageManager.FEATURE_DEVICE_ADMIN) @RequireFeature(PackageManager.FEATURE_DEVICE_ID_ATTESTATION) public void testAllVariationsOfDeviceIdAttestationUsingStrongBox() throws Exception { + // b/298586194, there are some devices launched with Android T, and they will be receiving + // only system update and not vendor update, newly added attestation properties + // (ro.product.*_for_attestation) reading logic would not be available for such devices + // hence skipping this test for such scenario. + assumeFalse("This test is not applicable for device running GSI image and" + + " first_api_level < 14", TestUtils.isGsiImage() + && TestUtils.getVendorApiLevel() < Build.VERSION_CODES.UPSIDE_DOWN_CAKE); + + final boolean isDeviceLocked = checkDeviceLocked(); try (DeviceOwner o = TestApis.devicePolicy().setDeviceOwner(DEVICE_ADMIN_COMPONENT_NAME)) { assertAllVariantsOfDeviceIdAttestation(true /* useStrongBox */); + } catch (NeneException e) { + // b/291069162, some devices do not allow to set DeviceOwner in an unlocked state. And + // unlocked state is allowed while testing on GSI image. If this condition not match + // throw the exception. + assumeFalse("It is acceptable to fail setting DeviceOwner on GSI build running on" + + " unlocked devices.", (e.getMessage().contains("Error setting device owner") + && TestUtils.isGsiImage() && !isDeviceLocked)); + throw new Exception(e); } } diff --git a/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java b/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java index 5135c9f5ab1..80fa6385e1f 100644 --- a/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java +++ b/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java @@ -94,7 +94,6 @@ import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder; import org.junit.Test; import org.junit.runner.RunWith; -import java.io.File; import java.security.GeneralSecurityException; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; @@ -420,7 +419,7 @@ public class KeyAttestationTest { // Feature Version is required on devices launching with Android 12 (API Level // 31) but may be reported on devices launching with an earlier version. If it's // present, it must match what is reported in attestation. - if (PropertyUtil.getFirstApiLevel() >= 31) { + if (TestUtils.getVendorApiLevel() >= 31) { assertNotEquals(0, keyStoreFeatureVersion); } if (keyStoreFeatureVersion != 0) { @@ -1276,13 +1275,8 @@ public class KeyAttestationTest { return expectedPurposes; } - private boolean isGsiImage() { - final File initGsiRc= new File("/system/system_ext/etc/init/init.gsi.rc"); - return initGsiRc.exists(); - } - private void checkSystemPatchLevel(int teeOsPatchLevel, int systemPatchLevel) { - if (isGsiImage()) { + if (TestUtils.isGsiImage()) { // b/168663786: When using a GSI image, the system patch level might be // greater than or equal to the OS patch level reported from TEE. assertThat("For GSI image TEE os patch level should be less than or equal to system" diff --git a/tests/tests/keystore/src/android/keystore/cts/util/TestUtils.java b/tests/tests/keystore/src/android/keystore/cts/util/TestUtils.java index 890ab61b4d7..1b928500084 100644 --- a/tests/tests/keystore/src/android/keystore/cts/util/TestUtils.java +++ b/tests/tests/keystore/src/android/keystore/cts/util/TestUtils.java @@ -42,6 +42,7 @@ import com.android.internal.util.HexDump; import org.junit.Assert; import java.io.ByteArrayOutputStream; +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.math.BigInteger; @@ -207,6 +208,33 @@ public class TestUtils { } /** + * Returns VSR API level. + */ + public static int getVendorApiLevel() { + int vendorApiLevel = SystemProperties.getInt("ro.vendor.api_level", -1); + if (vendorApiLevel != -1) { + return vendorApiLevel; + } + + // Android S and older devices do not define ro.vendor.api_level + vendorApiLevel = SystemProperties.getInt("ro.board.api_level", -1); + if (vendorApiLevel == -1) { + vendorApiLevel = SystemProperties.getInt("ro.board.first_api_level", -1); + } + + int productApiLevel = SystemProperties.getInt("ro.product.first_api_level", -1); + if (productApiLevel == -1) { + productApiLevel = Build.VERSION.SDK_INT; + } + + // VSR API level is the minimum of vendorApiLevel and productApiLevel. + if (vendorApiLevel == -1 || vendorApiLevel > productApiLevel) { + return productApiLevel; + } + return vendorApiLevel; + } + + /** * Returns whether the device has a StrongBox backed KeyStore. */ public static boolean hasStrongBox(Context context) { @@ -1182,4 +1210,13 @@ public class TestUtils { PackageManager pm = context.getPackageManager(); return (pm != null && pm.hasSystemFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)); } + + /** + * Determines whether running build is GSI or not. + * @return true if running build is GSI, false otherwise. + */ + public static boolean isGsiImage() { + final File initGsiRc = new File("/system/system_ext/etc/init/init.gsi.rc"); + return initGsiRc.exists(); + } } |