summaryrefslogtreecommitdiff
path: root/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
diff options
context:
space:
mode:
Diffstat (limited to 'tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java')
-rw-r--r--tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java142
1 files changed, 80 insertions, 62 deletions
diff --git a/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java b/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
index f27b44579eb..88857c3dba4 100644
--- a/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
@@ -32,9 +32,7 @@ import static android.keystore.cts.AuthorizationList.KM_PURPOSE_SIGN;
import static android.keystore.cts.AuthorizationList.KM_PURPOSE_VERIFY;
import static android.keystore.cts.RootOfTrust.KM_VERIFIED_BOOT_VERIFIED;
import static android.security.keymaster.KeymasterDefs.KM_PURPOSE_AGREE_KEY;
-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.ENCRYPTION_PADDING_NONE;
import static android.security.keystore.KeyProperties.ENCRYPTION_PADDING_RSA_OAEP;
import static android.security.keystore.KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1;
@@ -89,6 +87,7 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.bedstead.nene.TestApis;
import com.android.bedstead.nene.permissions.PermissionContext;
+import com.android.compatibility.common.util.CddTest;
import com.android.compatibility.common.util.PropertyUtil;
import com.google.common.collect.ImmutableSet;
@@ -113,9 +112,11 @@ import java.security.cert.CertificateException;
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.Date;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -152,6 +153,7 @@ public class KeyAttestationTest {
private static final int KM_ERROR_CANNOT_ATTEST_IDS = -66;
private static final int KM_ERROR_INVALID_INPUT_LENGTH = -21;
+ private static final int KM_ERROR_UNKNOWN_ERROR = -1000;
private static final int KM_ERROR_PERMISSION_DENIED = 6;
private Context getContext() {
@@ -281,16 +283,45 @@ public class KeyAttestationTest {
}
}
+ private void assertAttestationKeyMintError(KeyStoreException keyStoreException,
+ boolean devicePropertiesAttestation) {
+ int errorCode = keyStoreException.getErrorCode();
+ List<Integer> expectedErrs = new ArrayList<Integer>();
+ expectedErrs.add(KM_ERROR_INVALID_INPUT_LENGTH);
+ if (devicePropertiesAttestation) {
+ expectedErrs.add(KM_ERROR_CANNOT_ATTEST_IDS);
+ }
+ if (TestUtils.getVendorApiLevel() < 35) {
+ // b/337427860, some devices returns UNKNOWN_ERROR if large challenge is
+ // passed. So allow an extra error code for earlier devices.
+ expectedErrs.add(KM_ERROR_UNKNOWN_ERROR);
+ }
+ String assertMessage = String.format(
+ "The KeyMint implementation may only return INVALID_INPUT_LENGTH or "
+ + "CANNOT_ATTEST_IDSs as errors when the attestation challenge is "
+ + "too large (error code was %d, attestation properties %b)",
+ errorCode, devicePropertiesAttestation);
+ assertTrue(assertMessage, expectedErrs.contains(errorCode));
+ }
+
private void assertPublicAttestationError(KeyStoreException keyStoreException,
boolean devicePropertiesAttestation) {
// Assert public failure information.
int errorCode = keyStoreException.getNumericErrorCode();
+ List<Integer> expectedErrs = new ArrayList<Integer>();
+ expectedErrs.add(KeyStoreException.ERROR_INCORRECT_USAGE);
+ if (devicePropertiesAttestation) {
+ expectedErrs.add(KeyStoreException.ERROR_ID_ATTESTATION_FAILURE);
+ }
+ if (TestUtils.getVendorApiLevel() < 35) {
+ // b/337427860, some devices returns UNKNOWN_ERROR if large challenge is
+ // passed. So allow an extra error code for earlier devices.
+ expectedErrs.add(KeyStoreException.ERROR_KEYMINT_FAILURE);
+ }
String assertMessage = String.format(
"Error code was %d, device properties attestation? %b",
errorCode, devicePropertiesAttestation);
- assertTrue(assertMessage, KeyStoreException.ERROR_INCORRECT_USAGE == errorCode
- || (devicePropertiesAttestation
- && KeyStoreException.ERROR_ID_ATTESTATION_FAILURE == errorCode));
+ assertTrue(assertMessage, expectedErrs.contains(errorCode));
assertFalse("Unexpected transient failure.", keyStoreException.isTransientFailure());
}
@@ -319,16 +350,7 @@ public class KeyAttestationTest {
fail("Attestation challenges larger than 128 bytes should be rejected");
} catch (ProviderException e) {
KeyStoreException cause = (KeyStoreException) e.getCause();
- int errorCode = cause.getErrorCode();
- String assertMessage = String.format(
- "The KeyMint implementation may only return INVALID_INPUT_LENGTH or "
- + "CANNOT_ATTEST_IDSs as errors when the attestation challenge is "
- + "too large (error code was %d, attestation properties %b)",
- errorCode, devicePropertiesAttestation);
- assertTrue(assertMessage, KM_ERROR_INVALID_INPUT_LENGTH == cause.getErrorCode()
- || (devicePropertiesAttestation
- && KM_ERROR_CANNOT_ATTEST_IDS == cause.getErrorCode())
- );
+ assertAttestationKeyMintError(cause, devicePropertiesAttestation);
assertPublicAttestationError(cause, devicePropertiesAttestation);
}
}
@@ -355,7 +377,7 @@ public class KeyAttestationTest {
Date consumptionEnd = new Date(now.getTime() + CONSUMPTION_TIME_OFFSET);
KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(keystoreAlias, PURPOSE_SIGN)
.setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1"))
- .setDigests(DIGEST_NONE, DIGEST_SHA256, DIGEST_SHA512)
+ .setDigests(TestUtils.getDigestsForKeyMintImplementation(isStrongBox))
.setAttestationChallenge(null)
.setKeyValidityStart(now)
.setKeyValidityForOriginationEnd(originationEnd)
@@ -397,18 +419,13 @@ public class KeyAttestationTest {
KeyGenParameterSpec.Builder builder =
new KeyGenParameterSpec.Builder(keystoreAlias, PURPOSE_SIGN)
.setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1"))
+ .setDigests(TestUtils.getDigestsForKeyMintImplementation(expectStrongBox))
.setAttestationChallenge(new byte[128])
.setKeyValidityStart(now)
.setKeyValidityForOriginationEnd(originationEnd)
.setKeyValidityForConsumptionEnd(consumptionEnd)
.setIsStrongBoxBacked(expectStrongBox);
- if (expectStrongBox) {
- builder.setDigests(DIGEST_NONE, DIGEST_SHA256);
- } else {
- builder.setDigests(DIGEST_NONE, DIGEST_SHA256, DIGEST_SHA512);
- }
-
generateKeyPair(KEY_ALGORITHM_EC, builder.build());
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
@@ -428,6 +445,7 @@ public class KeyAttestationTest {
@RestrictedBuildTest
@RequiresDevice
@Test
+ @CddTest(requirements = {"9.10/C-0-1", "9.10/C-1-3"})
public void testEcAttestation_DeviceLocked() throws Exception {
testEcAttestation_DeviceLocked(false /* expectStrongBox */);
}
@@ -435,6 +453,7 @@ public class KeyAttestationTest {
@RestrictedBuildTest
@RequiresDevice
@Test
+ @CddTest(requirements = {"9.10/C-0-1", "9.10/C-1-3"})
public void testEcAttestation_DeviceLockedStrongbox() throws Exception {
if (!TestUtils.hasStrongBox(getContext()))
return;
@@ -445,6 +464,7 @@ public class KeyAttestationTest {
public void testAttestationKmVersionMatchesFeatureVersion() throws Exception {
if (getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_PC))
return;
+ assumeTrue("Device does not support attestation", TestUtils.isAttestationSupported());
testAttestationKmVersionMatchesFeatureVersion(false);
}
@@ -453,6 +473,7 @@ public class KeyAttestationTest {
public void testAttestationKmVersionMatchesFeatureVersionStrongBox() throws Exception {
if (getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_PC))
return;
+ assumeTrue("Device does not support attestation", TestUtils.isAttestationSupported());
int keyStoreFeatureVersionStrongBox =
TestUtils.getFeatureVersionKeystoreStrongBox(getContext());
@@ -467,7 +488,8 @@ public class KeyAttestationTest {
}
private void testAttestationKmVersionMatchesFeatureVersion(boolean isStrongBox)
- throws Exception {
+ throws Exception {
+ assumeTrue("Device does not support attestation", TestUtils.isAttestationSupported());
String keystoreAlias = "test_key";
Date now = new Date();
@@ -505,7 +527,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) {
@@ -534,7 +556,7 @@ public class KeyAttestationTest {
String keystoreAlias = "test_key";
KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(keystoreAlias, PURPOSE_SIGN)
.setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1"))
- .setDigests(DIGEST_NONE, DIGEST_SHA256, DIGEST_SHA512)
+ .setDigests(TestUtils.getDigestsForKeyMintImplementation(isStrongBox))
.setAttestationChallenge(new byte[128])
.setUniqueIdIncluded(true)
.setIsStrongBoxBacked(isStrongBox)
@@ -579,7 +601,7 @@ public class KeyAttestationTest {
String keystoreAlias = "test_key";
KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(keystoreAlias, PURPOSE_SIGN)
.setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1"))
- .setDigests(DIGEST_NONE, DIGEST_SHA256, DIGEST_SHA512)
+ .setDigests(TestUtils.getDigestsForKeyMintImplementation(isStrongBox))
.setAttestationChallenge(new byte[128])
.setUniqueIdIncluded(true)
.setIsStrongBoxBacked(isStrongBox)
@@ -756,16 +778,7 @@ public class KeyAttestationTest {
fail("Attestation challenges larger than 128 bytes should be rejected");
} catch(ProviderException e){
KeyStoreException cause = (KeyStoreException) e.getCause();
- int errorCode = cause.getErrorCode();
- String assertMessage = String.format(
- "The KeyMint implementation may only return INVALID_INPUT_LENGTH or "
- + "CANNOT_ATTEST_IDSs as errors when the attestation challenge is "
- + "too large (error code was %d, attestation properties %b)",
- errorCode, devicePropertiesAttestation);
- assertTrue(assertMessage, KM_ERROR_INVALID_INPUT_LENGTH == cause.getErrorCode()
- || (devicePropertiesAttestation
- && KM_ERROR_CANNOT_ATTEST_IDS == cause.getErrorCode())
- );
+ assertAttestationKeyMintError(cause, devicePropertiesAttestation);
assertPublicAttestationError(cause, devicePropertiesAttestation);
}
}
@@ -791,7 +804,7 @@ public class KeyAttestationTest {
Date originationEnd = new Date(now.getTime() + ORIGINATION_TIME_OFFSET);
Date consumptionEnd = new Date(now.getTime() + CONSUMPTION_TIME_OFFSET);
KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(keystoreAlias, PURPOSE_SIGN)
- .setDigests(DIGEST_NONE, DIGEST_SHA256, DIGEST_SHA512)
+ .setDigests(TestUtils.getDigestsForKeyMintImplementation(isStrongBox))
.setAttestationChallenge(null)
.setKeyValidityStart(now)
.setKeyValidityForOriginationEnd(originationEnd)
@@ -829,22 +842,16 @@ public class KeyAttestationTest {
Date now = new Date();
Date originationEnd = new Date(now.getTime() + ORIGINATION_TIME_OFFSET);
Date consumptionEnd = new Date(now.getTime() + CONSUMPTION_TIME_OFFSET);
- KeyGenParameterSpec.Builder builder =
- new KeyGenParameterSpec.Builder(keystoreAlias, PURPOSE_SIGN)
- .setDigests(DIGEST_NONE, DIGEST_SHA256, DIGEST_SHA512)
- .setAttestationChallenge("challenge".getBytes())
- .setKeyValidityStart(now)
- .setKeyValidityForOriginationEnd(originationEnd)
- .setKeyValidityForConsumptionEnd(consumptionEnd)
- .setIsStrongBoxBacked(expectStrongBox);
-
- if (expectStrongBox) {
- builder.setDigests(DIGEST_NONE, DIGEST_SHA256);
- } else {
- builder.setDigests(DIGEST_NONE, DIGEST_SHA256, DIGEST_SHA512);
- }
+ KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(keystoreAlias, PURPOSE_SIGN)
+ .setDigests(TestUtils.getDigestsForKeyMintImplementation(expectStrongBox))
+ .setAttestationChallenge("challenge".getBytes())
+ .setKeyValidityStart(now)
+ .setKeyValidityForOriginationEnd(originationEnd)
+ .setKeyValidityForConsumptionEnd(consumptionEnd)
+ .setIsStrongBoxBacked(expectStrongBox)
+ .build();
- generateKeyPair(KEY_ALGORITHM_RSA, builder.build());
+ generateKeyPair(KEY_ALGORITHM_RSA, spec);
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
@@ -863,11 +870,13 @@ public class KeyAttestationTest {
@RestrictedBuildTest
@RequiresDevice // Emulators have no place to store the needed key
@Test
+ @CddTest(requirements = {"9.10/C-0-1", "9.10/C-1-3"})
public void testRsaAttestation_DeviceLocked() throws Exception {
testRsaAttestation_DeviceLocked(false /* expectStrongbox */);
}
@RestrictedBuildTest
+ @CddTest(requirements = {"9.10/C-0-1", "9.10/C-1-3"})
@RequiresDevice // Emulators have no place to store the needed key
@Test
public void testRsaAttestation_DeviceLockedStrongbox() throws Exception {
@@ -1045,9 +1054,8 @@ public class KeyAttestationTest {
return;
}
assumeTrue("Curve25519 Key attestation supported from KeyMint v2 and above.",
- getContext().getPackageManager()
- .hasSystemFeature(PackageManager.FEATURE_HARDWARE_KEYSTORE,
- Attestation.KM_VERSION_KEYMINT_2));
+ TestUtils.hasKeystoreVersion(false /*isStrongBoxBased*/,
+ Attestation.KM_VERSION_KEYMINT_2));
if (getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_PC)) {
return;
@@ -1126,7 +1134,7 @@ public class KeyAttestationTest {
KeyGenParameterSpec.Builder builder =
new KeyGenParameterSpec.Builder(keystoreAlias, purposes)
.setKeySize(keySize)
- .setDigests(DIGEST_NONE, DIGEST_SHA256, DIGEST_SHA512)
+ .setDigests(TestUtils.getDigestsForKeyMintImplementation(isStrongBox))
.setAttestationChallenge(challenge)
.setDevicePropertiesAttestationIncluded(devicePropertiesAttestation)
.setIsStrongBoxBacked(isStrongBox);
@@ -1201,7 +1209,7 @@ public class KeyAttestationTest {
KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(keystoreAlias,
purposes)
.setAlgorithmParameterSpec(new ECGenParameterSpec(ecCurve))
- .setDigests(DIGEST_NONE, DIGEST_SHA256, DIGEST_SHA512)
+ .setDigests(TestUtils.getDigestsForKeyMintImplementation(isStrongBox))
.setAttestationChallenge(challenge)
.setDevicePropertiesAttestationIncluded(devicePropertiesAttestation)
.setIsStrongBoxBacked(isStrongBox);
@@ -1314,8 +1322,11 @@ public class KeyAttestationTest {
Date startTime, boolean includesValidityDates,
boolean devicePropertiesAttestation, Attestation attestation)
throws NoSuchAlgorithmException, NameNotFoundException {
- checkKeyIndependentAttestationInfo(challenge, purposes,
- ImmutableSet.of(KM_DIGEST_NONE, KM_DIGEST_SHA_2_256, KM_DIGEST_SHA_2_512),
+ Set digests = ImmutableSet.of(KM_DIGEST_NONE, KM_DIGEST_SHA_2_256, KM_DIGEST_SHA_2_512);
+ if (attestation.getAttestationSecurityLevel() == KM_SECURITY_LEVEL_STRONG_BOX) {
+ digests = ImmutableSet.of(KM_DIGEST_NONE, KM_DIGEST_SHA_2_256);
+ }
+ checkKeyIndependentAttestationInfo(challenge, purposes, digests,
startTime, includesValidityDates,
devicePropertiesAttestation, attestation);
}
@@ -1701,9 +1712,10 @@ public class KeyAttestationTest {
}
}
- private void checkEntropy(byte[] verifiedBootKey) {
- assertTrue("Failed Shannon entropy check", checkShannonEntropy(verifiedBootKey));
- assertTrue("Failed BiEntropy check", checkTresBiEntropy(verifiedBootKey));
+ private void checkEntropy(byte[] entropyData) {
+ byte[] entropyDataCopy = Arrays.copyOf(entropyData, entropyData.length);
+ assertTrue("Failed Shannon entropy check", checkShannonEntropy(entropyDataCopy));
+ assertTrue("Failed BiEntropy check", checkTresBiEntropy(entropyDataCopy));
}
private boolean checkShannonEntropy(byte[] verifiedBootKey) {
@@ -1719,6 +1731,9 @@ public class KeyAttestationTest {
return entropy;
}
+ /**
+ * Note: This method modifies the input parameter while performing bit entropy check.
+ */
private boolean checkTresBiEntropy(byte[] verifiedBootKey) {
double weightingFactor = 0;
double weightedEntropy = 0;
@@ -1736,6 +1751,9 @@ public class KeyAttestationTest {
return tresBiEntropy > 0.9;
}
+ /**
+ * Note: This method modifies the input parameter - bitString.
+ */
private void deriveBitString(byte[] bitString, int activeLength) {
int length = activeLength / 8;
if (activeLength % 8 != 0) {