1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.keystore.cts; 18 19 import static android.keystore.cts.Attestation.KM_SECURITY_LEVEL_SOFTWARE; 20 import static android.keystore.cts.Attestation.KM_SECURITY_LEVEL_STRONG_BOX; 21 import static android.keystore.cts.Attestation.KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT; 22 import static android.keystore.cts.AuthorizationList.KM_ALGORITHM_EC; 23 import static android.keystore.cts.AuthorizationList.KM_ALGORITHM_RSA; 24 import static android.keystore.cts.AuthorizationList.KM_DIGEST_NONE; 25 import static android.keystore.cts.AuthorizationList.KM_DIGEST_SHA_2_256; 26 import static android.keystore.cts.AuthorizationList.KM_DIGEST_SHA_2_512; 27 import static android.keystore.cts.AuthorizationList.KM_ORIGIN_GENERATED; 28 import static android.keystore.cts.AuthorizationList.KM_ORIGIN_UNKNOWN; 29 import static android.keystore.cts.RootOfTrust.KM_VERIFIED_BOOT_UNVERIFIED; 30 import static android.keystore.cts.RootOfTrust.KM_VERIFIED_BOOT_VERIFIED; 31 import static android.keystore.cts.util.TestUtils.assumeLockScreenSupport; 32 import static android.security.keymaster.KeymasterDefs.KM_PURPOSE_AGREE_KEY; 33 import static android.security.keymaster.KeymasterDefs.KM_PURPOSE_DECRYPT; 34 import static android.security.keymaster.KeymasterDefs.KM_PURPOSE_ENCRYPT; 35 import static android.security.keymaster.KeymasterDefs.KM_PURPOSE_SIGN; 36 import static android.security.keymaster.KeymasterDefs.KM_PURPOSE_VERIFY; 37 import static android.security.keystore.KeyProperties.DIGEST_SHA256; 38 import static android.security.keystore.KeyProperties.ENCRYPTION_PADDING_NONE; 39 import static android.security.keystore.KeyProperties.ENCRYPTION_PADDING_RSA_OAEP; 40 import static android.security.keystore.KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1; 41 import static android.security.keystore.KeyProperties.KEY_ALGORITHM_EC; 42 import static android.security.keystore.KeyProperties.KEY_ALGORITHM_RSA; 43 import static android.security.keystore.KeyProperties.PURPOSE_AGREE_KEY; 44 import static android.security.keystore.KeyProperties.PURPOSE_DECRYPT; 45 import static android.security.keystore.KeyProperties.PURPOSE_ENCRYPT; 46 import static android.security.keystore.KeyProperties.PURPOSE_SIGN; 47 import static android.security.keystore.KeyProperties.PURPOSE_VERIFY; 48 import static android.security.keystore.KeyProperties.SIGNATURE_PADDING_RSA_PKCS1; 49 import static android.security.keystore.KeyProperties.SIGNATURE_PADDING_RSA_PSS; 50 51 import static com.google.common.truth.Truth.assertThat; 52 53 import static org.hamcrest.CoreMatchers.is; 54 import static org.hamcrest.MatcherAssert.assertThat; 55 import static org.hamcrest.Matchers.either; 56 import static org.hamcrest.Matchers.everyItem; 57 import static org.hamcrest.Matchers.greaterThanOrEqualTo; 58 import static org.hamcrest.Matchers.hasItems; 59 import static org.hamcrest.Matchers.isIn; 60 import static org.hamcrest.Matchers.lessThanOrEqualTo; 61 import static org.junit.Assert.assertArrayEquals; 62 import static org.junit.Assert.assertEquals; 63 import static org.junit.Assert.assertFalse; 64 import static org.junit.Assert.assertNotEquals; 65 import static org.junit.Assert.assertNotNull; 66 import static org.junit.Assert.assertNull; 67 import static org.junit.Assert.assertTrue; 68 import static org.junit.Assert.fail; 69 import static org.junit.Assume.assumeTrue; 70 71 import android.content.Context; 72 import android.content.pm.PackageManager; 73 import android.content.pm.PackageManager.NameNotFoundException; 74 import android.keystore.cts.util.TestUtils; 75 import android.os.Build; 76 import android.os.SystemProperties; 77 import android.platform.test.annotations.RequiresFlagsEnabled; 78 import android.platform.test.annotations.RestrictedBuildTest; 79 import android.platform.test.flag.junit.CheckFlagsRule; 80 import android.platform.test.flag.junit.DeviceFlagsValueProvider; 81 import android.security.KeyStoreException; 82 import android.security.keystore.AttestationUtils; 83 import android.security.keystore.DeviceIdAttestationException; 84 import android.security.keystore.KeyGenParameterSpec; 85 import android.security.keystore.KeyProperties; 86 import android.security.keystore.KeyStoreManager; 87 import android.security.keystore2.Flags; 88 import android.util.ArraySet; 89 import android.util.Log; 90 91 import androidx.test.InstrumentationRegistry; 92 import androidx.test.filters.RequiresDevice; 93 import androidx.test.runner.AndroidJUnit4; 94 95 import com.android.bedstead.nene.TestApis; 96 import com.android.bedstead.permissions.PermissionContext; 97 import com.android.compatibility.common.util.CddTest; 98 import com.android.compatibility.common.util.PropertyUtil; 99 100 import com.google.common.collect.ImmutableSet; 101 102 import org.bouncycastle.asn1.x500.X500Name; 103 import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder; 104 import org.junit.Rule; 105 import org.junit.Test; 106 import org.junit.runner.RunWith; 107 108 import java.security.GeneralSecurityException; 109 import java.security.InvalidAlgorithmParameterException; 110 import java.security.InvalidKeyException; 111 import java.security.KeyPairGenerator; 112 import java.security.KeyStore; 113 import java.security.MessageDigest; 114 import java.security.NoSuchAlgorithmException; 115 import java.security.NoSuchProviderException; 116 import java.security.ProviderException; 117 import java.security.PublicKey; 118 import java.security.SignatureException; 119 import java.security.cert.Certificate; 120 import java.security.cert.CertificateException; 121 import java.security.cert.CertificateParsingException; 122 import java.security.cert.X509Certificate; 123 import java.security.interfaces.ECPublicKey; 124 import java.security.interfaces.RSAPublicKey; 125 import java.security.spec.ECGenParameterSpec; 126 import java.security.spec.ECParameterSpec; 127 import java.util.ArrayList; 128 import java.util.Arrays; 129 import java.util.Date; 130 import java.util.HashMap; 131 import java.util.HashSet; 132 import java.util.List; 133 import java.util.Map; 134 import java.util.Set; 135 import java.util.regex.Matcher; 136 import java.util.regex.Pattern; 137 138 import javax.crypto.KeyGenerator; 139 140 /** 141 * Tests for Android Keystore attestation. 142 */ 143 @RunWith(AndroidJUnit4.class) 144 public class KeyAttestationTest { 145 146 @Rule 147 public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); 148 149 private static final String TAG = AndroidKeyStoreTest.class.getSimpleName(); 150 151 private static final int ORIGINATION_TIME_OFFSET = 1000000; 152 private static final int CONSUMPTION_TIME_OFFSET = 2000000; 153 154 private static final int KEY_USAGE_BITSTRING_LENGTH = 9; 155 private static final int KEY_USAGE_DIGITAL_SIGNATURE_BIT_OFFSET = 0; 156 private static final int KEY_USAGE_KEY_ENCIPHERMENT_BIT_OFFSET = 2; 157 private static final int KEY_USAGE_DATA_ENCIPHERMENT_BIT_OFFSET = 3; 158 private static final int KEY_USAGE_KEY_AGREE_BIT_OFFSET = 4; 159 160 private static final int OS_MAJOR_VERSION_MATCH_GROUP_NAME = 1; 161 private static final int OS_MINOR_VERSION_MATCH_GROUP_NAME = 2; 162 private static final int OS_SUBMINOR_VERSION_MATCH_GROUP_NAME = 3; 163 private static final Pattern OS_VERSION_STRING_PATTERN = Pattern 164 .compile("([0-9]{1,2})(?:\\.([0-9]{1,2}))?(?:\\.([0-9]{1,2}))?(?:[^0-9.]+.*)?"); 165 166 private static final int OS_PATCH_LEVEL_YEAR_GROUP_NAME = 1; 167 private static final int OS_PATCH_LEVEL_MONTH_GROUP_NAME = 2; 168 private static final Pattern OS_PATCH_LEVEL_STRING_PATTERN = Pattern 169 .compile("([0-9]{4})-([0-9]{2})-[0-9]{2}"); 170 171 private static final int KM_ERROR_CANNOT_ATTEST_IDS = -66; 172 private static final int KM_ERROR_INVALID_INPUT_LENGTH = -21; 173 private static final int KM_ERROR_UNKNOWN_ERROR = -1000; 174 private static final int KM_ERROR_PERMISSION_DENIED = 6; 175 176 private static final int KS_ERROR_INVALID_ARGUMENT = 20; 177 178 private static final Map<String, Integer> sECKeySizes = new HashMap<>(); 179 180 static { 181 sECKeySizes.put("secp224r1", 224); 182 sECKeySizes.put("secp256r1", 256); 183 sECKeySizes.put("secp384r1", 384); 184 sECKeySizes.put("secp521r1", 521); 185 sECKeySizes.put("CURVE_25519", 256); 186 } 187 getContext()188 private Context getContext() { 189 return InstrumentationRegistry.getInstrumentation().getTargetContext(); 190 } 191 192 @Test 193 @RequiresFlagsEnabled(android.security.keystore2.Flags.FLAG_ATTEST_MODULES) testSupplementaryAttestationInfoAbsence()194 public void testSupplementaryAttestationInfoAbsence() throws Exception { 195 // Valid tag IDs that have no supplementary info. 196 checkAbsentSupplementaryInfo(805307074); // OS_PATCHLEVEL = TagType.UINT | 706 197 checkAbsentSupplementaryInfo(-1879047488); // ROOT_OF_TRUST = TagType.BYTES | 704 198 199 // Invalid tag value 200 checkAbsentSupplementaryInfo(9999); 201 } 202 checkAbsentSupplementaryInfo(int tag)203 private void checkAbsentSupplementaryInfo(int tag) throws Exception { 204 KeyStoreManager manager = KeyStoreManager.getInstance(); 205 try { 206 byte[] data = manager.getSupplementaryAttestationInfo(tag); 207 fail( 208 "Call to getSupplementaryAttestationInfo() for tag " 209 + tag 210 + " should throw a KeyStoreException; instead got: " 211 + HexEncoding.encode(data)); 212 } catch (KeyStoreException e) { 213 assertTrue(e.getErrorCode() == KS_ERROR_INVALID_ARGUMENT); 214 } 215 } 216 217 @Test testVersionParser()218 public void testVersionParser() throws Exception { 219 // Non-numerics/empty give version 0 220 assertEquals(0, parseSystemOsVersion("")); 221 assertEquals(0, parseSystemOsVersion("N")); 222 223 // Should support one, two or three version number values. 224 assertEquals(10000, parseSystemOsVersion("1")); 225 assertEquals(10200, parseSystemOsVersion("1.2")); 226 assertEquals(10203, parseSystemOsVersion("1.2.3")); 227 228 // It's fine to append other stuff to the dotted numeric version. 229 assertEquals(10000, parseSystemOsVersion("1stuff")); 230 assertEquals(10200, parseSystemOsVersion("1.2garbage.32")); 231 assertEquals(10203, parseSystemOsVersion("1.2.3-stuff")); 232 233 // Two digits per version field are supported 234 assertEquals(152536, parseSystemOsVersion("15.25.36")); 235 assertEquals(999999, parseSystemOsVersion("99.99.99")); 236 assertEquals(0, parseSystemOsVersion("100.99.99")); 237 assertEquals(0, parseSystemOsVersion("99.100.99")); 238 assertEquals(0, parseSystemOsVersion("99.99.100")); 239 } 240 241 @RequiresDevice 242 @Test testEcAttestation()243 public void testEcAttestation() throws Exception { 244 testEcAttestation(false); 245 } 246 247 @RequiresDevice 248 @Test testEcAttestation_StrongBox()249 public void testEcAttestation_StrongBox() throws Exception { 250 assumeTrue("This test is only applicable to devices with StrongBox", 251 TestUtils.hasStrongBox(getContext())); 252 // Exempt older versions due to increased coverage of this test beyond VTS, 253 // requiring exceptions for implementations frozen to an older VSR. 254 assumeTrue(TestUtils.hasKeystoreVersion(true /*isStrongBoxBased*/, 255 Attestation.KM_VERSION_KEYMINT_3)); 256 testEcAttestation(true); 257 } 258 testEcAttestation(boolean isStrongBox)259 private void testEcAttestation(boolean isStrongBox) throws Exception { 260 if (!TestUtils.isAttestationSupported()) { 261 return; 262 } 263 264 if (getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_PC)) { 265 return; 266 } 267 268 final @KeyProperties.PurposeEnum int[] purposes = { 269 PURPOSE_SIGN, PURPOSE_VERIFY, PURPOSE_SIGN | PURPOSE_VERIFY 270 }; 271 final boolean[] devicePropertiesAttestationValues = {true, false}; 272 final boolean[] includeValidityDatesValues = {true, false}; 273 final String[] curves; 274 final int[] keySizes; 275 final byte[][] challenges; 276 277 if (isStrongBox) { 278 // StrongBox only supports secp256r1 keys. 279 curves = new String[]{"secp256r1"}; 280 keySizes = new int[]{256}; 281 challenges = new byte[][]{ 282 // Empty challange is not accepted by StrongBox. 283 "challenge".getBytes(), // short challenge 284 new byte[128], // long challenge 285 }; 286 } else { 287 curves = new String[]{ 288 "secp224r1", "secp256r1", "secp384r1", "secp521r1" 289 }; 290 keySizes = new int[]{ 291 224, 256, 384, 521 292 }; 293 challenges = new byte[][]{ 294 new byte[0], // empty challenge 295 "challenge".getBytes(), // short challenge 296 new byte[128], // long challenge 297 }; 298 } 299 300 for (int curveIndex = 0; curveIndex < curves.length; ++curveIndex) { 301 for (int challengeIndex = 0; challengeIndex < challenges.length; ++challengeIndex) { 302 for (int purposeIndex = 0; purposeIndex < purposes.length; ++purposeIndex) { 303 for (boolean includeValidityDates : includeValidityDatesValues) { 304 for (boolean devicePropertiesAttestation : 305 devicePropertiesAttestationValues) { 306 try { 307 testEcAttestation(challenges[challengeIndex], includeValidityDates, 308 curves[curveIndex], keySizes[curveIndex], 309 purposes[purposeIndex], devicePropertiesAttestation, 310 isStrongBox); 311 } catch (Throwable e) { 312 if (devicePropertiesAttestation 313 && isIgnorableIdAttestationFailure(e)) { 314 continue; 315 } 316 throw new Exception("Failed on curve " + curveIndex + 317 " challenge " + challengeIndex + " purpose " + 318 purposeIndex + " includeValidityDates " + 319 includeValidityDates + " and devicePropertiesAttestation " + 320 devicePropertiesAttestation, e); 321 } 322 } 323 } 324 } 325 } 326 } 327 } 328 assertAttestationKeyMintError(KeyStoreException keyStoreException, boolean devicePropertiesAttestation)329 private void assertAttestationKeyMintError(KeyStoreException keyStoreException, 330 boolean devicePropertiesAttestation) { 331 int errorCode = keyStoreException.getErrorCode(); 332 List<Integer> expectedErrs = new ArrayList<Integer>(); 333 expectedErrs.add(KM_ERROR_INVALID_INPUT_LENGTH); 334 if (devicePropertiesAttestation) { 335 expectedErrs.add(KM_ERROR_CANNOT_ATTEST_IDS); 336 } 337 if (TestUtils.getVendorApiLevel() < 35) { 338 // b/337427860, some devices returns UNKNOWN_ERROR if large challenge is 339 // passed. So allow an extra error code for earlier devices. 340 expectedErrs.add(KM_ERROR_UNKNOWN_ERROR); 341 } 342 String assertMessage = String.format( 343 "The KeyMint implementation may only return INVALID_INPUT_LENGTH or " 344 + "CANNOT_ATTEST_IDSs as errors when the attestation challenge is " 345 + "too large (error code was %d, attestation properties %b)", 346 errorCode, devicePropertiesAttestation); 347 assertTrue(assertMessage, expectedErrs.contains(errorCode)); 348 } 349 assertPublicAttestationError(KeyStoreException keyStoreException, boolean devicePropertiesAttestation)350 private void assertPublicAttestationError(KeyStoreException keyStoreException, 351 boolean devicePropertiesAttestation) { 352 // Assert public failure information. 353 int errorCode = keyStoreException.getNumericErrorCode(); 354 List<Integer> expectedErrs = new ArrayList<Integer>(); 355 expectedErrs.add(KeyStoreException.ERROR_INCORRECT_USAGE); 356 if (devicePropertiesAttestation) { 357 expectedErrs.add(KeyStoreException.ERROR_ID_ATTESTATION_FAILURE); 358 } 359 if (TestUtils.getVendorApiLevel() < 35) { 360 // b/337427860, some devices returns UNKNOWN_ERROR if large challenge is 361 // passed. So allow an extra error code for earlier devices. 362 expectedErrs.add(KeyStoreException.ERROR_KEYMINT_FAILURE); 363 } 364 String assertMessage = String.format( 365 "Error code was %d, device properties attestation? %b", 366 errorCode, devicePropertiesAttestation); 367 assertTrue(assertMessage, expectedErrs.contains(errorCode)); 368 assertFalse("Unexpected transient failure.", keyStoreException.isTransientFailure()); 369 } 370 371 @Test testEcAttestation_TooLargeChallenge()372 public void testEcAttestation_TooLargeChallenge() throws Exception { 373 testEcAttestation_TooLargeChallenge(false); 374 } 375 376 @Test testEcAttestation_TooLargeChallenge_StrongBox()377 public void testEcAttestation_TooLargeChallenge_StrongBox() throws Exception { 378 assumeTrue("This test is only applicable to devices with StrongBox", 379 TestUtils.hasStrongBox(getContext())); 380 testEcAttestation_TooLargeChallenge(true); 381 } 382 testEcAttestation_TooLargeChallenge(boolean isStrongBox)383 private void testEcAttestation_TooLargeChallenge(boolean isStrongBox) throws Exception { 384 if (!TestUtils.isAttestationSupported()) { 385 return; 386 } 387 388 boolean[] devicePropertiesAttestationValues = {true, false}; 389 for (boolean devicePropertiesAttestation : devicePropertiesAttestationValues) { 390 try { 391 testEcAttestation( 392 new byte[129], 393 true /* includeValidityDates */, 394 "secp256r1", 395 256, 396 PURPOSE_SIGN, 397 devicePropertiesAttestation, 398 isStrongBox); 399 fail("Attestation challenges larger than 128 bytes should be rejected"); 400 } catch (ProviderException e) { 401 KeyStoreException cause = (KeyStoreException) e.getCause(); 402 assertAttestationKeyMintError(cause, devicePropertiesAttestation); 403 assertPublicAttestationError(cause, devicePropertiesAttestation); 404 } 405 } 406 } 407 408 @Test testEcAttestation_NoChallenge()409 public void testEcAttestation_NoChallenge() throws Exception { 410 testEcAttestation_NoChallenge(false); 411 } 412 413 @Test testEcAttestation_NoChallenge_StrongBox()414 public void testEcAttestation_NoChallenge_StrongBox() throws Exception { 415 assumeTrue("This test is only applicable to devices with StrongBox", 416 TestUtils.hasStrongBox(getContext())); 417 testEcAttestation_NoChallenge(true); 418 } 419 testEcAttestation_NoChallenge(boolean isStrongBox)420 public void testEcAttestation_NoChallenge(boolean isStrongBox) throws Exception { 421 boolean[] devicePropertiesAttestationValues = {true, false}; 422 for (boolean devicePropertiesAttestation : devicePropertiesAttestationValues) { 423 String keystoreAlias = "test_key"; 424 Date now = new Date(); 425 Date originationEnd = new Date(now.getTime() + ORIGINATION_TIME_OFFSET); 426 Date consumptionEnd = new Date(now.getTime() + CONSUMPTION_TIME_OFFSET); 427 KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(keystoreAlias, PURPOSE_SIGN) 428 .setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1")) 429 .setDigests(TestUtils.getDigestsForKeyMintImplementation(isStrongBox)) 430 .setAttestationChallenge(null) 431 .setKeyValidityStart(now) 432 .setKeyValidityForOriginationEnd(originationEnd) 433 .setKeyValidityForConsumptionEnd(consumptionEnd) 434 .setDevicePropertiesAttestationIncluded(devicePropertiesAttestation) 435 .setIsStrongBoxBacked(isStrongBox) 436 .build(); 437 438 generateKeyPair(KEY_ALGORITHM_EC, spec); 439 440 KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); 441 keyStore.load(null); 442 443 try { 444 Certificate[] certificates = keyStore.getCertificateChain(keystoreAlias); 445 assertEquals(1, certificates.length); 446 447 X509Certificate attestationCert = (X509Certificate) certificates[0]; 448 assertNull(attestationCert.getExtensionValue(Attestation.ASN1_OID)); 449 assertNull(attestationCert.getExtensionValue(Attestation.EAT_OID)); 450 } finally { 451 keyStore.deleteEntry(keystoreAlias); 452 } 453 } 454 } 455 testEcAttestation_DeviceLocked(Boolean expectStrongBox)456 private void testEcAttestation_DeviceLocked(Boolean expectStrongBox) throws Exception { 457 if (!TestUtils.isAttestationSupported()) { 458 return; 459 } 460 461 if (getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_PC)) { 462 return; 463 } 464 465 String keystoreAlias = "test_key"; 466 Date now = new Date(); 467 Date originationEnd = new Date(now.getTime() + ORIGINATION_TIME_OFFSET); 468 Date consumptionEnd = new Date(now.getTime() + CONSUMPTION_TIME_OFFSET); 469 KeyGenParameterSpec.Builder builder = 470 new KeyGenParameterSpec.Builder(keystoreAlias, PURPOSE_SIGN) 471 .setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1")) 472 .setDigests(TestUtils.getDigestsForKeyMintImplementation(expectStrongBox)) 473 .setAttestationChallenge(new byte[128]) 474 .setKeyValidityStart(now) 475 .setKeyValidityForOriginationEnd(originationEnd) 476 .setKeyValidityForConsumptionEnd(consumptionEnd) 477 .setIsStrongBoxBacked(expectStrongBox); 478 479 generateKeyPair(KEY_ALGORITHM_EC, builder.build()); 480 481 KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); 482 keyStore.load(null); 483 484 try { 485 Certificate[] certificates = keyStore.getCertificateChain(keystoreAlias); 486 verifyCertificateChain(certificates, expectStrongBox); 487 488 X509Certificate attestationCert = (X509Certificate) certificates[0]; 489 checkDeviceLocked(Attestation.loadFromCertificate(attestationCert)); 490 } finally { 491 keyStore.deleteEntry(keystoreAlias); 492 } 493 } 494 495 @RestrictedBuildTest 496 @RequiresDevice 497 @Test 498 @CddTest(requirements = {"9.10/C-0-1", "9.10/C-1-3"}) testEcAttestation_DeviceLocked()499 public void testEcAttestation_DeviceLocked() throws Exception { 500 testEcAttestation_DeviceLocked(false /* expectStrongBox */); 501 } 502 503 @RestrictedBuildTest 504 @RequiresDevice 505 @Test 506 @CddTest(requirements = {"9.10/C-0-1", "9.10/C-1-3"}) testEcAttestation_DeviceLockedStrongbox()507 public void testEcAttestation_DeviceLockedStrongbox() throws Exception { 508 if (!TestUtils.hasStrongBox(getContext())) { 509 return; 510 } 511 testEcAttestation_DeviceLocked(true /* expectStrongBox */); 512 } 513 514 @Test testAttestationKmVersionMatchesFeatureVersion()515 public void testAttestationKmVersionMatchesFeatureVersion() throws Exception { 516 if (getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_PC)) { 517 return; 518 } 519 assumeTrue("Device does not support attestation", TestUtils.isAttestationSupported()); 520 521 testAttestationKmVersionMatchesFeatureVersion(false); 522 } 523 524 @Test testAttestationKmVersionMatchesFeatureVersionStrongBox()525 public void testAttestationKmVersionMatchesFeatureVersionStrongBox() throws Exception { 526 if (getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_PC)) { 527 return; 528 } 529 assumeTrue("Device does not support attestation", TestUtils.isAttestationSupported()); 530 531 int keyStoreFeatureVersionStrongBox = 532 TestUtils.getFeatureVersionKeystoreStrongBox(getContext()); 533 534 if (!TestUtils.hasStrongBox(getContext())) { 535 // If there's no StrongBox, ensure there's no feature version for it. 536 assertEquals(0, keyStoreFeatureVersionStrongBox); 537 return; 538 } 539 540 testAttestationKmVersionMatchesFeatureVersion(true); 541 } 542 testAttestationKmVersionMatchesFeatureVersion(boolean isStrongBox)543 private void testAttestationKmVersionMatchesFeatureVersion(boolean isStrongBox) 544 throws Exception { 545 assumeTrue("Device does not support attestation", TestUtils.isAttestationSupported()); 546 547 String keystoreAlias = "test_key"; 548 Date now = new Date(); 549 Date originationEnd = new Date(now.getTime() + ORIGINATION_TIME_OFFSET); 550 Date consumptionEnd = new Date(now.getTime() + CONSUMPTION_TIME_OFFSET); 551 KeyGenParameterSpec.Builder builder = 552 new KeyGenParameterSpec.Builder(keystoreAlias, PURPOSE_SIGN) 553 .setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1")) 554 .setAttestationChallenge(new byte[128]) 555 .setKeyValidityStart(now) 556 .setKeyValidityForOriginationEnd(originationEnd) 557 .setKeyValidityForConsumptionEnd(consumptionEnd) 558 .setIsStrongBoxBacked(isStrongBox); 559 560 generateKeyPair(KEY_ALGORITHM_EC, builder.build()); 561 562 KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); 563 keyStore.load(null); 564 565 try { 566 Certificate[] certificates = keyStore.getCertificateChain(keystoreAlias); 567 verifyCertificateChain(certificates, isStrongBox /* expectStrongBox */); 568 X509Certificate attestationCert = (X509Certificate) certificates[0]; 569 Attestation attestation = Attestation.loadFromCertificate(attestationCert); 570 int kmVersionFromAttestation = attestation.keymasterVersion; 571 int keyStoreFeatureVersion; 572 573 if (isStrongBox) { 574 keyStoreFeatureVersion = 575 TestUtils.getFeatureVersionKeystoreStrongBox(getContext()); 576 } else { 577 keyStoreFeatureVersion = 578 TestUtils.getFeatureVersionKeystore(getContext()); 579 } 580 // Feature Version is required on devices launching with Android 12 (API Level 581 // 31) but may be reported on devices launching with an earlier version. If it's 582 // present, it must match what is reported in attestation. 583 if (TestUtils.getVendorApiLevel() >= 31) { 584 assertNotEquals(0, keyStoreFeatureVersion); 585 } 586 if (keyStoreFeatureVersion != 0) { 587 assertEquals(kmVersionFromAttestation, keyStoreFeatureVersion); 588 } 589 } finally { 590 keyStore.deleteEntry(keystoreAlias); 591 } 592 } 593 594 @Test testEcAttestation_KeyStoreExceptionWhenRequestingUniqueId()595 public void testEcAttestation_KeyStoreExceptionWhenRequestingUniqueId() throws Exception { 596 testEcAttestation_KeyStoreExceptionWhenRequestingUniqueId(false); 597 } 598 599 @Test testEcAttestation_KeyStoreExceptionWhenRequestingUniqueId_StrongBox()600 public void testEcAttestation_KeyStoreExceptionWhenRequestingUniqueId_StrongBox() 601 throws Exception { 602 assumeTrue("This test is only applicable to devices with StrongBox", 603 TestUtils.hasStrongBox(getContext())); 604 testEcAttestation_KeyStoreExceptionWhenRequestingUniqueId(true); 605 } 606 testEcAttestation_KeyStoreExceptionWhenRequestingUniqueId( boolean isStrongBox)607 private void testEcAttestation_KeyStoreExceptionWhenRequestingUniqueId( 608 boolean isStrongBox) throws Exception { 609 String keystoreAlias = "test_key"; 610 KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(keystoreAlias, PURPOSE_SIGN) 611 .setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1")) 612 .setDigests(TestUtils.getDigestsForKeyMintImplementation(isStrongBox)) 613 .setAttestationChallenge(new byte[128]) 614 .setUniqueIdIncluded(true) 615 .setIsStrongBoxBacked(isStrongBox) 616 .build(); 617 618 try { 619 generateKeyPair(KEY_ALGORITHM_EC, spec); 620 fail("Attestation should have failed."); 621 } catch (ProviderException e) { 622 // Attestation is expected to fail because of lack of permissions. 623 KeyStoreException cause = (KeyStoreException) e.getCause(); 624 assertEquals(KM_ERROR_PERMISSION_DENIED, cause.getErrorCode()); 625 // Assert public failure information. 626 assertEquals(KeyStoreException.ERROR_PERMISSION_DENIED, cause.getNumericErrorCode()); 627 assertFalse("Unexpected transient failure in generate key.", 628 cause.isTransientFailure()); 629 } finally { 630 KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); 631 keyStore.load(null); 632 keyStore.deleteEntry(keystoreAlias); 633 } 634 } 635 636 @Test testEcAttestation_UniqueIdWorksWithCorrectPermission()637 public void testEcAttestation_UniqueIdWorksWithCorrectPermission() throws Exception { 638 testEcAttestation_UniqueIdWorksWithCorrectPermission(false); 639 } 640 641 @Test testEcAttestation_UniqueIdWorksWithCorrectPermission_StrongBox()642 public void testEcAttestation_UniqueIdWorksWithCorrectPermission_StrongBox() 643 throws Exception { 644 assumeTrue("This test is only applicable to devices with StrongBox", 645 TestUtils.hasStrongBox(getContext())); 646 testEcAttestation_UniqueIdWorksWithCorrectPermission(true); 647 } 648 testEcAttestation_UniqueIdWorksWithCorrectPermission(boolean isStrongBox)649 private void testEcAttestation_UniqueIdWorksWithCorrectPermission(boolean isStrongBox) 650 throws Exception { 651 assumeLockScreenSupport(); 652 assumeTrue("Device does not support attestation", TestUtils.isAttestationSupported()); 653 654 String keystoreAlias = "test_key"; 655 KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(keystoreAlias, PURPOSE_SIGN) 656 .setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1")) 657 .setDigests(TestUtils.getDigestsForKeyMintImplementation(isStrongBox)) 658 .setAttestationChallenge(new byte[128]) 659 .setUniqueIdIncluded(true) 660 .setIsStrongBoxBacked(isStrongBox) 661 .build(); 662 663 KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); 664 keyStore.load(null); 665 666 try (PermissionContext c = TestApis.permissions().withPermission( 667 "android.permission.REQUEST_UNIQUE_ID_ATTESTATION")) { 668 generateKeyPair(KEY_ALGORITHM_EC, spec); 669 Certificate[] certificates = keyStore.getCertificateChain(keystoreAlias); 670 Attestation attestation = Attestation.loadFromCertificate( 671 (X509Certificate) certificates[0]); 672 byte[] firstUniqueId = attestation.getUniqueId(); 673 assertTrue("UniqueId must not be empty", firstUniqueId.length > 0); 674 675 // The unique id rotates (30 days in the default implementation), and it's possible to 676 // get a spurious failure if the test runs exactly when the rotation occurs. Allow a 677 // single retry, just in case. 678 byte[] secondUniqueId = null; 679 for (int i = 0; i < 2; ++i) { 680 keyStore.deleteEntry(keystoreAlias); 681 682 generateKeyPair(KEY_ALGORITHM_EC, spec); 683 certificates = keyStore.getCertificateChain(keystoreAlias); 684 attestation = Attestation.loadFromCertificate((X509Certificate) certificates[0]); 685 secondUniqueId = attestation.getUniqueId(); 686 687 if (Arrays.equals(firstUniqueId, secondUniqueId)) { 688 break; 689 } else { 690 firstUniqueId = secondUniqueId; 691 secondUniqueId = null; 692 } 693 } 694 assertArrayEquals("UniqueIds must be consistent", firstUniqueId, secondUniqueId); 695 696 } finally { 697 keyStore.deleteEntry(keystoreAlias); 698 } 699 } 700 701 @RequiresDevice 702 @Test testRsaAttestation()703 public void testRsaAttestation() throws Exception { 704 testRsaAttestation(false); 705 } 706 707 @RequiresDevice 708 @Test testRsaAttestation_StrongBox()709 public void testRsaAttestation_StrongBox() throws Exception { 710 assumeTrue("This test is only applicable to devices with StrongBox", 711 TestUtils.hasStrongBox(getContext())); 712 // Exempt older versions due to increased coverage of this test beyond VTS, 713 // requiring exceptions for implementations frozen to an older VSR. 714 assumeTrue(TestUtils.hasKeystoreVersion(true /*isStrongBoxBased*/, 715 Attestation.KM_VERSION_KEYMINT_3)); 716 testRsaAttestation(true); 717 } 718 testRsaAttestation(boolean isStrongBox)719 private void testRsaAttestation(boolean isStrongBox) throws Exception { 720 if (!TestUtils.isAttestationSupported()) { 721 return; 722 } 723 724 if (getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_PC)) { 725 return; 726 } 727 728 final @KeyProperties.PurposeEnum int[] purposes = { 729 PURPOSE_SIGN | PURPOSE_VERIFY, 730 PURPOSE_ENCRYPT | PURPOSE_DECRYPT, 731 }; 732 final String[][] signaturePaddingModes = { 733 { 734 SIGNATURE_PADDING_RSA_PKCS1, 735 }, 736 { 737 SIGNATURE_PADDING_RSA_PSS, 738 }, 739 { 740 SIGNATURE_PADDING_RSA_PKCS1, 741 SIGNATURE_PADDING_RSA_PSS, 742 }, 743 }; 744 final boolean[] devicePropertiesAttestationValues = {true, false}; 745 final int[] keySizes; 746 final byte[][] challenges; 747 final String[][] encryptionPaddingModes; 748 749 if (isStrongBox) { 750 // StrongBox has to support 2048 bit key. 751 keySizes = new int[]{2048}; 752 challenges = new byte[][]{ 753 "challenge".getBytes(), // short challenge 754 new byte[128] // long challenge 755 }; 756 encryptionPaddingModes = new String[][]{ 757 { 758 ENCRYPTION_PADDING_RSA_OAEP, 759 }, 760 { 761 ENCRYPTION_PADDING_RSA_PKCS1, 762 }, 763 { 764 ENCRYPTION_PADDING_RSA_OAEP, 765 ENCRYPTION_PADDING_RSA_PKCS1, 766 }, 767 }; 768 } else { 769 keySizes = new int[]{ // Smallish sizes to keep test runtimes down. 770 512, 768, 1024 771 }; 772 challenges = new byte[][]{ 773 new byte[0], // empty challenge 774 "challenge".getBytes(), // short challenge 775 new byte[128] // long challenge 776 }; 777 encryptionPaddingModes = new String[][]{ 778 { 779 ENCRYPTION_PADDING_NONE 780 }, 781 { 782 ENCRYPTION_PADDING_RSA_OAEP, 783 }, 784 { 785 ENCRYPTION_PADDING_RSA_PKCS1, 786 }, 787 { 788 ENCRYPTION_PADDING_RSA_OAEP, 789 ENCRYPTION_PADDING_RSA_PKCS1, 790 }, 791 }; 792 } 793 794 for (boolean devicePropertiesAttestation : devicePropertiesAttestationValues) { 795 for (int keySize : keySizes) { 796 for (byte[] challenge : challenges) { 797 for (@KeyProperties.PurposeEnum int purpose : purposes) { 798 if (isEncryptionPurpose(purpose)) { 799 testRsaAttestations(keySize, challenge, purpose, encryptionPaddingModes, 800 devicePropertiesAttestation, isStrongBox); 801 } else { 802 testRsaAttestations(keySize, challenge, purpose, signaturePaddingModes, 803 devicePropertiesAttestation, isStrongBox); 804 } 805 } 806 } 807 } 808 } 809 } 810 811 @Test testRsaAttestation_TooLargeChallenge()812 public void testRsaAttestation_TooLargeChallenge() throws Exception { 813 testRsaAttestation_TooLargeChallenge(512, false); 814 } 815 816 @Test testRsaAttestation_TooLargeChallenge_StrongBox()817 public void testRsaAttestation_TooLargeChallenge_StrongBox() throws Exception { 818 assumeTrue("This test is only applicable to devices with StrongBox", 819 TestUtils.hasStrongBox(getContext())); 820 testRsaAttestation_TooLargeChallenge(2048, true); 821 } 822 testRsaAttestation_TooLargeChallenge(int keySize, boolean isStrongBox)823 private void testRsaAttestation_TooLargeChallenge(int keySize, boolean isStrongBox) 824 throws Exception { 825 if (!TestUtils.isAttestationSupported()) { 826 return; 827 } 828 829 boolean[] devicePropertiesAttestationValues = {true, false}; 830 for (boolean devicePropertiesAttestation : devicePropertiesAttestationValues) { 831 try { 832 testRsaAttestation(new byte[129], true /* includeValidityDates */, keySize, 833 PURPOSE_SIGN, 834 null /* paddingModes; may be empty because we'll never test them */, 835 devicePropertiesAttestation, isStrongBox); 836 fail("Attestation challenges larger than 128 bytes should be rejected"); 837 } catch (ProviderException e) { 838 KeyStoreException cause = (KeyStoreException) e.getCause(); 839 assertAttestationKeyMintError(cause, devicePropertiesAttestation); 840 assertPublicAttestationError(cause, devicePropertiesAttestation); 841 } 842 } 843 } 844 845 @Test testRsaAttestation_NoChallenge()846 public void testRsaAttestation_NoChallenge() throws Exception { 847 testRsaAttestation_NoChallenge(false); 848 } 849 850 @Test testRsaAttestation_NoChallenge_StrongBox()851 public void testRsaAttestation_NoChallenge_StrongBox() throws Exception { 852 assumeTrue("This test is only applicable to devices with StrongBox", 853 TestUtils.hasStrongBox(getContext())); 854 testRsaAttestation_NoChallenge(true); 855 } 856 testRsaAttestation_NoChallenge(boolean isStrongBox)857 private void testRsaAttestation_NoChallenge(boolean isStrongBox) throws Exception { 858 boolean[] devicePropertiesAttestationValues = {true, false}; 859 for (boolean devicePropertiesAttestation : devicePropertiesAttestationValues) { 860 String keystoreAlias = "test_key"; 861 Date now = new Date(); 862 Date originationEnd = new Date(now.getTime() + ORIGINATION_TIME_OFFSET); 863 Date consumptionEnd = new Date(now.getTime() + CONSUMPTION_TIME_OFFSET); 864 KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(keystoreAlias, PURPOSE_SIGN) 865 .setDigests(TestUtils.getDigestsForKeyMintImplementation(isStrongBox)) 866 .setAttestationChallenge(null) 867 .setKeyValidityStart(now) 868 .setKeyValidityForOriginationEnd(originationEnd) 869 .setKeyValidityForConsumptionEnd(consumptionEnd) 870 .setDevicePropertiesAttestationIncluded(devicePropertiesAttestation) 871 .setIsStrongBoxBacked(isStrongBox) 872 .build(); 873 874 generateKeyPair(KEY_ALGORITHM_RSA, spec); 875 876 KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); 877 keyStore.load(null); 878 879 try { 880 Certificate[] certificates = keyStore.getCertificateChain(keystoreAlias); 881 assertEquals(1, certificates.length); 882 883 X509Certificate attestationCert = (X509Certificate) certificates[0]; 884 assertNull(attestationCert.getExtensionValue(Attestation.ASN1_OID)); 885 } finally { 886 keyStore.deleteEntry(keystoreAlias); 887 } 888 } 889 } 890 testRsaAttestation_DeviceLocked(Boolean expectStrongBox)891 private void testRsaAttestation_DeviceLocked(Boolean expectStrongBox) throws Exception { 892 if (!TestUtils.isAttestationSupported()) { 893 return; 894 } 895 896 if (getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_PC)) { 897 return; 898 } 899 900 String keystoreAlias = "test_key"; 901 Date now = new Date(); 902 Date originationEnd = new Date(now.getTime() + ORIGINATION_TIME_OFFSET); 903 Date consumptionEnd = new Date(now.getTime() + CONSUMPTION_TIME_OFFSET); 904 KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(keystoreAlias, PURPOSE_SIGN) 905 .setDigests(TestUtils.getDigestsForKeyMintImplementation(expectStrongBox)) 906 .setAttestationChallenge("challenge".getBytes()) 907 .setKeyValidityStart(now) 908 .setKeyValidityForOriginationEnd(originationEnd) 909 .setKeyValidityForConsumptionEnd(consumptionEnd) 910 .setIsStrongBoxBacked(expectStrongBox) 911 .build(); 912 913 generateKeyPair(KEY_ALGORITHM_RSA, spec); 914 915 KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); 916 keyStore.load(null); 917 918 try { 919 Certificate[] certificates = keyStore.getCertificateChain(keystoreAlias); 920 verifyCertificateChain(certificates, expectStrongBox); 921 922 X509Certificate attestationCert = (X509Certificate) certificates[0]; 923 checkDeviceLocked(Attestation.loadFromCertificate(attestationCert)); 924 } finally { 925 keyStore.deleteEntry(keystoreAlias); 926 } 927 } 928 929 @RestrictedBuildTest 930 @RequiresDevice // Emulators have no place to store the needed key 931 @Test 932 @CddTest(requirements = {"9.10/C-0-1", "9.10/C-1-3"}) testRsaAttestation_DeviceLocked()933 public void testRsaAttestation_DeviceLocked() throws Exception { 934 testRsaAttestation_DeviceLocked(false /* expectStrongbox */); 935 } 936 937 @RestrictedBuildTest 938 @RequiresDevice // Emulators have no place to store the needed key 939 @Test 940 @CddTest(requirements = {"9.10/C-0-1", "9.10/C-1-3"}) testRsaAttestation_DeviceLockedStrongbox()941 public void testRsaAttestation_DeviceLockedStrongbox() throws Exception { 942 if (!TestUtils.hasStrongBox(getContext())) { 943 return; 944 } 945 946 testRsaAttestation_DeviceLocked(true /* expectStrongbox */); 947 } 948 949 @Test testAesAttestation()950 public void testAesAttestation() throws Exception { 951 testAesAttestation(false); 952 } 953 954 @Test testAesAttestation_StrongBox()955 public void testAesAttestation_StrongBox() throws Exception { 956 assumeTrue("This test is only applicable to devices with StrongBox", 957 TestUtils.hasStrongBox(getContext())); 958 testAesAttestation(true); 959 } 960 testAesAttestation(boolean isStrongBox)961 private void testAesAttestation(boolean isStrongBox) throws Exception { 962 boolean[] devicePropertiesAttestationValues = {true, false}; 963 for (boolean devicePropertiesAttestation : devicePropertiesAttestationValues) { 964 String keystoreAlias = "test_key"; 965 KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(keystoreAlias, 966 PURPOSE_ENCRYPT) 967 .setBlockModes(KeyProperties.BLOCK_MODE_GCM) 968 .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) 969 .setAttestationChallenge(new byte[0]) 970 .setDevicePropertiesAttestationIncluded(devicePropertiesAttestation) 971 .setIsStrongBoxBacked(isStrongBox) 972 .build(); 973 generateKey(spec, KeyProperties.KEY_ALGORITHM_AES); 974 975 KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); 976 keyStore.load(null); 977 try { 978 assertNull(keyStore.getCertificateChain(keystoreAlias)); 979 } finally { 980 keyStore.deleteEntry(keystoreAlias); 981 } 982 } 983 } 984 985 @Test testHmacAttestation()986 public void testHmacAttestation() throws Exception { 987 testHmacAttestation(false); 988 } 989 990 @Test testHmacAttestation_StrongBox()991 public void testHmacAttestation_StrongBox() throws Exception { 992 assumeTrue("This test is only applicable to devices with StrongBox", 993 TestUtils.hasStrongBox(getContext())); 994 testHmacAttestation(true); 995 } 996 testHmacAttestation(boolean isStrongBox)997 private void testHmacAttestation(boolean isStrongBox) throws Exception { 998 boolean[] devicePropertiesAttestationValues = {true, false}; 999 for (boolean devicePropertiesAttestation : devicePropertiesAttestationValues) { 1000 String keystoreAlias = "test_key"; 1001 KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(keystoreAlias, PURPOSE_SIGN) 1002 .setDevicePropertiesAttestationIncluded(devicePropertiesAttestation) 1003 .setIsStrongBoxBacked(isStrongBox) 1004 .build(); 1005 1006 generateKey(spec, KeyProperties.KEY_ALGORITHM_HMAC_SHA256); 1007 1008 KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); 1009 keyStore.load(null); 1010 try { 1011 assertNull(keyStore.getCertificateChain(keystoreAlias)); 1012 } finally { 1013 keyStore.deleteEntry(keystoreAlias); 1014 } 1015 } 1016 } 1017 testRsaAttestations(int keySize, byte[] challenge, @KeyProperties.PurposeEnum int purpose, String[][] paddingModes, boolean devicePropertiesAttestation, boolean isStrongBox)1018 private void testRsaAttestations(int keySize, byte[] challenge, 1019 @KeyProperties.PurposeEnum int purpose, String[][] paddingModes, 1020 boolean devicePropertiesAttestation, boolean isStrongBox) throws Exception { 1021 for (String[] paddings : paddingModes) { 1022 try { 1023 testRsaAttestation(challenge, true /* includeValidityDates */, keySize, purpose, 1024 paddings, devicePropertiesAttestation, isStrongBox); 1025 testRsaAttestation(challenge, false /* includeValidityDates */, keySize, purpose, 1026 paddings, devicePropertiesAttestation, isStrongBox); 1027 } catch (Throwable e) { 1028 if (devicePropertiesAttestation && isIgnorableIdAttestationFailure(e)) { 1029 continue; 1030 } 1031 throw new Exception("Failed on key size " + keySize + " challenge [" + 1032 new String(challenge) + "], purposes " + 1033 buildPurposeSet(purpose) + " paddings " + 1034 ImmutableSet.copyOf(paddings) + " and devicePropertiesAttestation " 1035 + devicePropertiesAttestation, 1036 e); 1037 } 1038 } 1039 } 1040 1041 @Test testDeviceIdAttestation()1042 public void testDeviceIdAttestation() throws Exception { 1043 testDeviceIdAttestationFailure(AttestationUtils.ID_TYPE_SERIAL, null); 1044 testDeviceIdAttestationFailure(AttestationUtils.ID_TYPE_IMEI, "Unable to retrieve IMEI"); 1045 testDeviceIdAttestationFailure(AttestationUtils.ID_TYPE_MEID, "Unable to retrieve MEID"); 1046 } 1047 1048 @Test testAttestedRoTAcrossKeymints()1049 public void testAttestedRoTAcrossKeymints() throws Exception { 1050 assumeTrue("This test requires a device supporting key attestation", 1051 TestUtils.isAttestationSupported()); 1052 assumeTrue("This test is not applicable for PC", 1053 !getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_PC)); 1054 assumeTrue("This test is only applicable to devices with StrongBox", 1055 TestUtils.hasStrongBox(getContext())); 1056 1057 RootOfTrust teeRootOfTrust = generateAttestationAndExtractRoT("tee_test_key", false); 1058 RootOfTrust sbRootOfTrust = generateAttestationAndExtractRoT("sb_test_key", true); 1059 1060 assertNotNull("RootOfTrust should not be null for TEE", teeRootOfTrust); 1061 assertNotNull("RootOfTrust should not be null for StrongBox", sbRootOfTrust); 1062 assertArrayEquals("Verified boot hash in TEE and StrongBox issued certificates must be" 1063 + " same.", teeRootOfTrust.getVerifiedBootHash(), 1064 sbRootOfTrust.getVerifiedBootHash()); 1065 assertArrayEquals("Verified boot key in TEE and StrongBox issued certificates must be" 1066 + " same.", teeRootOfTrust.getVerifiedBootKey(), 1067 sbRootOfTrust.getVerifiedBootKey()); 1068 assertEquals("Verified boot state in TEE and StrongBox issued certificates must be same.", 1069 teeRootOfTrust.getVerifiedBootState(), 1070 sbRootOfTrust.getVerifiedBootState()); 1071 assertEquals("Device locked state in TEE and StrongBox issued certificates must be same.", 1072 teeRootOfTrust.isDeviceLocked(), sbRootOfTrust.isDeviceLocked()); 1073 } 1074 generateAttestationAndExtractRoT(String alias, boolean isStrongBox)1075 private RootOfTrust generateAttestationAndExtractRoT(String alias, boolean isStrongBox) 1076 throws Exception { 1077 KeyGenParameterSpec.Builder specBuilder = 1078 new KeyGenParameterSpec.Builder(alias, PURPOSE_SIGN | PURPOSE_VERIFY) 1079 .setIsStrongBoxBacked(isStrongBox) 1080 .setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1")) 1081 .setDigests(DIGEST_SHA256) 1082 .setAttestationChallenge("challenge".getBytes()); 1083 generateKeyPair(KEY_ALGORITHM_EC, specBuilder.build()); 1084 1085 KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); 1086 keyStore.load(null); 1087 1088 Certificate[] certificates = keyStore.getCertificateChain(alias); 1089 verifyCertificateChain(certificates, isStrongBox); 1090 1091 Attestation attestation = 1092 Attestation.loadFromCertificate((X509Certificate) certificates[0]); 1093 return attestation.getRootOfTrust(); 1094 } 1095 1096 @RequiresDevice 1097 @Test testCurve25519Attestation()1098 public void testCurve25519Attestation() throws Exception { 1099 if (!TestUtils.isAttestationSupported()) { 1100 return; 1101 } 1102 assumeTrue("Curve25519 Key attestation supported from KeyMint v2 and above.", 1103 TestUtils.hasKeystoreVersion(false /*isStrongBoxBased*/, 1104 Attestation.KM_VERSION_KEYMINT_2)); 1105 1106 if (getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_PC)) { 1107 return; 1108 } 1109 1110 byte[][] challenges = { 1111 new byte[0], // empty challenge 1112 "challenge".getBytes(), // short challenge 1113 new byte[128] // long challenge 1114 }; 1115 boolean[] devicePropertiesAttestationValues = {true, false}; 1116 1117 for (boolean devicePropertiesAttestation : devicePropertiesAttestationValues) { 1118 for (byte[] challenge : challenges) { 1119 testCurve25519Attestations("ed25519", challenge, PURPOSE_SIGN | PURPOSE_VERIFY, 1120 devicePropertiesAttestation); 1121 testCurve25519Attestations("x25519", challenge, PURPOSE_AGREE_KEY, 1122 devicePropertiesAttestation); 1123 } 1124 } 1125 } 1126 1127 @SuppressWarnings("deprecation") testCurve25519Attestations(String curve, byte[] challenge, @KeyProperties.PurposeEnum int purpose, boolean devicePropertiesAttestation)1128 private void testCurve25519Attestations(String curve, byte[] challenge, 1129 @KeyProperties.PurposeEnum int purpose, boolean devicePropertiesAttestation) 1130 throws Exception { 1131 Log.i(TAG, curve + " curve key attestation with: " 1132 + " / challenge " + Arrays.toString(challenge) 1133 + " / purposes " + purpose 1134 + " / devicePropertiesAttestation " + devicePropertiesAttestation); 1135 String keystoreAlias = "test_key"; 1136 Date startTime = new Date(); 1137 KeyGenParameterSpec.Builder builder = 1138 new KeyGenParameterSpec.Builder(keystoreAlias, purpose) 1139 .setAlgorithmParameterSpec(new ECGenParameterSpec(curve)) 1140 .setDigests(KeyProperties.DIGEST_NONE) 1141 .setAttestationChallenge(challenge) 1142 .setDevicePropertiesAttestationIncluded(devicePropertiesAttestation); 1143 1144 try { 1145 generateKeyPair(KEY_ALGORITHM_EC, builder.build()); 1146 } catch (Throwable e) { 1147 if (devicePropertiesAttestation && isIgnorableIdAttestationFailure(e)) { 1148 return; 1149 } 1150 throw new Exception("Failed on curve " + curve + " challenge [" 1151 + new String(challenge) + "], purposes " 1152 + buildPurposeSet(purpose) + " and devicePropertiesAttestation " 1153 + devicePropertiesAttestation, 1154 e); 1155 1156 } 1157 1158 KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); 1159 keyStore.load(null); 1160 1161 try { 1162 Certificate[] certificates = keyStore.getCertificateChain(keystoreAlias); 1163 verifyCertificateChain(certificates, false /* expectStrongBox */); 1164 1165 X509Certificate attestationCert = (X509Certificate) certificates[0]; 1166 Attestation attestation = Attestation.loadFromCertificate(attestationCert); 1167 1168 checkEcKeyDetails(attestationCert, attestation, "CURVE_25519", 256); 1169 checkKeyUsage(attestationCert, purpose, /* isStrongBox= */ false); 1170 checkKeyIndependentAttestationInfo(challenge, purpose, 1171 ImmutableSet.of(KM_DIGEST_NONE), startTime, false, 1172 devicePropertiesAttestation, attestation); 1173 } finally { 1174 keyStore.deleteEntry(keystoreAlias); 1175 } 1176 } 1177 1178 @SuppressWarnings("deprecation") testRsaAttestation(byte[] challenge, boolean includeValidityDates, int keySize, @KeyProperties.PurposeEnum int purposes, String[] paddingModes, boolean devicePropertiesAttestation, boolean isStrongBox)1179 private void testRsaAttestation(byte[] challenge, boolean includeValidityDates, int keySize, 1180 @KeyProperties.PurposeEnum int purposes, String[] paddingModes, 1181 boolean devicePropertiesAttestation, boolean isStrongBox) throws Exception { 1182 Log.i(TAG, "RSA key attestation with: challenge " + Arrays.toString(challenge) + 1183 " / includeValidityDates " + includeValidityDates + " / keySize " + keySize + 1184 " / purposes " + purposes + " / paddingModes " + Arrays.toString(paddingModes) + 1185 " / devicePropertiesAttestation " + devicePropertiesAttestation); 1186 1187 String keystoreAlias = "test_key"; 1188 Date startTime = new Date(); 1189 Date originationEnd = new Date(startTime.getTime() + ORIGINATION_TIME_OFFSET); 1190 Date consumptionEnd = new Date(startTime.getTime() + CONSUMPTION_TIME_OFFSET); 1191 KeyGenParameterSpec.Builder builder = 1192 new KeyGenParameterSpec.Builder(keystoreAlias, purposes) 1193 .setKeySize(keySize) 1194 .setDigests(TestUtils.getDigestsForKeyMintImplementation(isStrongBox)) 1195 .setAttestationChallenge(challenge) 1196 .setDevicePropertiesAttestationIncluded(devicePropertiesAttestation) 1197 .setIsStrongBoxBacked(isStrongBox); 1198 1199 if (includeValidityDates) { 1200 builder.setKeyValidityStart(startTime) 1201 .setKeyValidityForOriginationEnd(originationEnd) 1202 .setKeyValidityForConsumptionEnd(consumptionEnd); 1203 } 1204 if (isEncryptionPurpose(purposes)) { 1205 builder.setEncryptionPaddings(paddingModes); 1206 // Because we sometimes set "no padding", allow non-randomized encryption. 1207 builder.setRandomizedEncryptionRequired(false); 1208 } 1209 if (isSignaturePurpose(purposes) || isVerifyPurpose(purposes)) { 1210 builder.setSignaturePaddings(paddingModes); 1211 } 1212 1213 generateKeyPair(KEY_ALGORITHM_RSA, builder.build()); 1214 1215 KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); 1216 keyStore.load(null); 1217 1218 try { 1219 Certificate[] certificates = keyStore.getCertificateChain(keystoreAlias); 1220 verifyCertificateChain(certificates, isStrongBox /* expectStrongBox */); 1221 1222 X509Certificate attestationCert = (X509Certificate) certificates[0]; 1223 Attestation attestation = Attestation.loadFromCertificate(attestationCert); 1224 1225 checkRsaKeyDetails(attestationCert, attestation, keySize, 1226 (paddingModes == null) 1227 ? new HashSet<String>() : ImmutableSet.copyOf(paddingModes)); 1228 checkKeyUsage(attestationCert, purposes, isStrongBox); 1229 checkKeyIndependentAttestationInfo(challenge, purposes, startTime, 1230 includeValidityDates, devicePropertiesAttestation, attestation); 1231 } finally { 1232 keyStore.deleteEntry(keystoreAlias); 1233 } 1234 } 1235 checkKeyUsage( X509Certificate attestationCert, @KeyProperties.PurposeEnum int purposes, boolean isStrongBox)1236 private void checkKeyUsage( 1237 X509Certificate attestationCert, 1238 @KeyProperties.PurposeEnum int purposes, 1239 boolean isStrongBox) { 1240 boolean[] actualKeyUsage = attestationCert.getKeyUsage(); 1241 if (purposes == PURPOSE_VERIFY && actualKeyUsage == null) { 1242 // A key with *just* verify purpose might have no `KeyUsage` extension, 1243 // because the private key can't be used by KeyMint. 1244 return; 1245 } 1246 1247 // Key attestation tests for StrongBox were only enabled from Android 15, 1248 // so allow more lax `KeyUsage` bits for earlier StrongBox implementations. 1249 boolean laxChecks = (isStrongBox && TestUtils.getVendorApiLevel() <= 34); 1250 1251 boolean[] requiredKeyUsage = new boolean[KEY_USAGE_BITSTRING_LENGTH]; 1252 boolean[] allowedKeyUsage = new boolean[KEY_USAGE_BITSTRING_LENGTH]; 1253 if (isVerifyPurpose(purposes)) { 1254 // A PURPOSE_VERIFY key might have the digital signature bit set. 1255 allowedKeyUsage[KEY_USAGE_DIGITAL_SIGNATURE_BIT_OFFSET] = true; 1256 } 1257 if (isSignaturePurpose(purposes)) { 1258 // A PURPOSE_SIGN key must have the digital signature bit set. 1259 requiredKeyUsage[KEY_USAGE_DIGITAL_SIGNATURE_BIT_OFFSET] = true; 1260 } 1261 if (isEncryptionPurpose(purposes)) { 1262 requiredKeyUsage[KEY_USAGE_DATA_ENCIPHERMENT_BIT_OFFSET] = true; 1263 if (laxChecks) { 1264 // Allow the key encipherment bit to be missing on older StrongBox impls. 1265 allowedKeyUsage[KEY_USAGE_KEY_ENCIPHERMENT_BIT_OFFSET] = true; 1266 } else { 1267 requiredKeyUsage[KEY_USAGE_KEY_ENCIPHERMENT_BIT_OFFSET] = true; 1268 } 1269 } 1270 if (isAgreeKeyPurpose(purposes)) { 1271 requiredKeyUsage[KEY_USAGE_KEY_AGREE_BIT_OFFSET] = true; 1272 } 1273 1274 // Any bit that's required is also allowed. 1275 for (int bit = 0; bit < KEY_USAGE_BITSTRING_LENGTH; bit++) { 1276 if (requiredKeyUsage[bit]) { 1277 allowedKeyUsage[bit] = true; 1278 } 1279 } 1280 1281 assertTrue( 1282 "Attested certificate missing a required key usage bit.\n Required : " 1283 + Arrays.toString(requiredKeyUsage) 1284 + "\n Actual : " 1285 + Arrays.toString(actualKeyUsage), 1286 checkSubset(requiredKeyUsage, actualKeyUsage)); 1287 assertTrue( 1288 "Attested certificate has an unexpected key usage bit set.\n Actual : " 1289 + Arrays.toString(actualKeyUsage) 1290 + "\n Allowed : " 1291 + Arrays.toString(allowedKeyUsage), 1292 checkSubset(actualKeyUsage, allowedKeyUsage)); 1293 } 1294 1295 // Indicate whether the first argument is a subset of the second. 1296 // Both arguments must be the same length. checkSubset(boolean[] subset, boolean[] superset)1297 private boolean checkSubset(boolean[] subset, boolean[] superset) { 1298 assertThat(superset.length).isEqualTo(subset.length); 1299 for (int i = 0; i < subset.length; i++) { 1300 if (subset[i] && !superset[i]) { 1301 // Value i is set in `subset` but not in the putative superset 1302 return false; 1303 } 1304 } 1305 return true; 1306 } 1307 1308 @SuppressWarnings("deprecation") testEcAttestation(byte[] challenge, boolean includeValidityDates, String ecCurve, int keySize, @KeyProperties.PurposeEnum int purposes, boolean devicePropertiesAttestation, boolean isStrongBox)1309 private void testEcAttestation(byte[] challenge, boolean includeValidityDates, String ecCurve, 1310 int keySize, @KeyProperties.PurposeEnum int purposes, 1311 boolean devicePropertiesAttestation, boolean isStrongBox) throws Exception { 1312 Log.i(TAG, "EC key attestation with: challenge " + Arrays.toString(challenge) + 1313 " / includeValidityDates " + includeValidityDates + " / ecCurve " + ecCurve + 1314 " / keySize " + keySize + " / purposes " + purposes + 1315 " / devicePropertiesAttestation " + devicePropertiesAttestation); 1316 1317 String keystoreAlias = "test_key"; 1318 Date startTime = new Date(); 1319 Date originationEnd = new Date(startTime.getTime() + ORIGINATION_TIME_OFFSET); 1320 Date consumptionEnd = new Date(startTime.getTime() + CONSUMPTION_TIME_OFFSET); 1321 KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(keystoreAlias, 1322 purposes) 1323 .setAlgorithmParameterSpec(new ECGenParameterSpec(ecCurve)) 1324 .setDigests(TestUtils.getDigestsForKeyMintImplementation(isStrongBox)) 1325 .setAttestationChallenge(challenge) 1326 .setDevicePropertiesAttestationIncluded(devicePropertiesAttestation) 1327 .setIsStrongBoxBacked(isStrongBox); 1328 1329 if (includeValidityDates) { 1330 builder.setKeyValidityStart(startTime) 1331 .setKeyValidityForOriginationEnd(originationEnd) 1332 .setKeyValidityForConsumptionEnd(consumptionEnd); 1333 } 1334 1335 generateKeyPair(KEY_ALGORITHM_EC, builder.build()); 1336 1337 KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); 1338 keyStore.load(null); 1339 1340 try { 1341 Certificate[] certificates = keyStore.getCertificateChain(keystoreAlias); 1342 verifyCertificateChain(certificates, isStrongBox /* expectStrongBox */); 1343 1344 X509Certificate attestationCert = (X509Certificate) certificates[0]; 1345 Attestation attestation = Attestation.loadFromCertificate(attestationCert); 1346 1347 checkEcKeyDetails(attestationCert, attestation, ecCurve, keySize); 1348 checkKeyUsage(attestationCert, purposes, isStrongBox); 1349 checkKeyIndependentAttestationInfo(challenge, purposes, startTime, 1350 includeValidityDates, devicePropertiesAttestation, attestation); 1351 } finally { 1352 keyStore.deleteEntry(keystoreAlias); 1353 } 1354 } 1355 checkAttestationApplicationId(Attestation attestation)1356 private void checkAttestationApplicationId(Attestation attestation) 1357 throws NoSuchAlgorithmException, NameNotFoundException { 1358 AttestationApplicationId aaid = null; 1359 int kmVersion = attestation.getKeymasterVersion(); 1360 assertNull(attestation.getTeeEnforced().getAttestationApplicationId()); 1361 aaid = attestation.getSoftwareEnforced().getAttestationApplicationId(); 1362 1363 if (kmVersion >= 3) { 1364 // must be present and correct 1365 assertNotNull(aaid); 1366 assertEquals(new AttestationApplicationId(getContext()), aaid); 1367 } else { 1368 // may be present and 1369 // must be correct if present 1370 if (aaid != null) { 1371 assertEquals(new AttestationApplicationId(getContext()), aaid); 1372 } 1373 } 1374 } 1375 checkAttestationDeviceProperties(boolean devicePropertiesAttestation, Attestation attestation)1376 private void checkAttestationDeviceProperties(boolean devicePropertiesAttestation, 1377 Attestation attestation) { 1378 final AuthorizationList keyDetailsList; 1379 final AuthorizationList nonKeyDetailsList; 1380 if (attestation.getKeymasterSecurityLevel() == KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT 1381 || attestation.getKeymasterSecurityLevel() == KM_SECURITY_LEVEL_STRONG_BOX) { 1382 keyDetailsList = attestation.getTeeEnforced(); 1383 nonKeyDetailsList = attestation.getSoftwareEnforced(); 1384 } else { 1385 keyDetailsList = attestation.getSoftwareEnforced(); 1386 nonKeyDetailsList = attestation.getTeeEnforced(); 1387 } 1388 1389 if (devicePropertiesAttestation) { 1390 final String platformReportedBrand = 1391 TestUtils.isPropertyEmptyOrUnknown(Build.BRAND_FOR_ATTESTATION) 1392 ? Build.BRAND : Build.BRAND_FOR_ATTESTATION; 1393 assertThat(keyDetailsList.getBrand()).isEqualTo(platformReportedBrand); 1394 final String platformReportedDevice = 1395 TestUtils.isPropertyEmptyOrUnknown(Build.DEVICE_FOR_ATTESTATION) 1396 ? Build.DEVICE : Build.DEVICE_FOR_ATTESTATION; 1397 assertThat(keyDetailsList.getDevice()).isEqualTo(platformReportedDevice); 1398 final String platformReportedProduct = 1399 TestUtils.isPropertyEmptyOrUnknown(Build.PRODUCT_FOR_ATTESTATION) 1400 ? Build.PRODUCT : Build.PRODUCT_FOR_ATTESTATION; 1401 assertThat(keyDetailsList.getProduct()).isEqualTo(platformReportedProduct); 1402 final String platformReportedManufacturer = 1403 TestUtils.isPropertyEmptyOrUnknown(Build.MANUFACTURER_FOR_ATTESTATION) 1404 ? Build.MANUFACTURER : Build.MANUFACTURER_FOR_ATTESTATION; 1405 assertThat(keyDetailsList.getManufacturer()).isEqualTo(platformReportedManufacturer); 1406 final String platformReportedModel = 1407 TestUtils.isPropertyEmptyOrUnknown(Build.MODEL_FOR_ATTESTATION) 1408 ? Build.MODEL : Build.MODEL_FOR_ATTESTATION; 1409 assertThat(keyDetailsList.getModel()).isEqualTo(platformReportedModel); 1410 } else { 1411 assertNull(keyDetailsList.getBrand()); 1412 assertNull(keyDetailsList.getDevice()); 1413 assertNull(keyDetailsList.getProduct()); 1414 assertNull(keyDetailsList.getManufacturer()); 1415 assertNull(keyDetailsList.getModel()); 1416 } 1417 assertNull(nonKeyDetailsList.getBrand()); 1418 assertNull(nonKeyDetailsList.getDevice()); 1419 assertNull(nonKeyDetailsList.getProduct()); 1420 assertNull(nonKeyDetailsList.getManufacturer()); 1421 assertNull(nonKeyDetailsList.getModel()); 1422 } 1423 checkAttestationNoUniqueIds(Attestation attestation)1424 private void checkAttestationNoUniqueIds(Attestation attestation) { 1425 assertNull(attestation.getTeeEnforced().getImei()); 1426 assertNull(attestation.getTeeEnforced().getMeid()); 1427 assertNull(attestation.getTeeEnforced().getSerialNumber()); 1428 assertNull(attestation.getSoftwareEnforced().getImei()); 1429 assertNull(attestation.getSoftwareEnforced().getMeid()); 1430 assertNull(attestation.getSoftwareEnforced().getSerialNumber()); 1431 } 1432 checkKeyIndependentAttestationInfo(byte[] challenge, @KeyProperties.PurposeEnum int purposes, Date startTime, boolean includesValidityDates, boolean devicePropertiesAttestation, Attestation attestation)1433 private void checkKeyIndependentAttestationInfo(byte[] challenge, 1434 @KeyProperties.PurposeEnum int purposes, 1435 Date startTime, boolean includesValidityDates, 1436 boolean devicePropertiesAttestation, Attestation attestation) 1437 throws NoSuchAlgorithmException, NameNotFoundException { 1438 Set digests = ImmutableSet.of(KM_DIGEST_NONE, KM_DIGEST_SHA_2_256, KM_DIGEST_SHA_2_512); 1439 if (attestation.getAttestationSecurityLevel() == KM_SECURITY_LEVEL_STRONG_BOX) { 1440 digests = ImmutableSet.of(KM_DIGEST_NONE, KM_DIGEST_SHA_2_256); 1441 } 1442 checkKeyIndependentAttestationInfo(challenge, purposes, digests, 1443 startTime, includesValidityDates, 1444 devicePropertiesAttestation, attestation); 1445 } 1446 checkKeyIndependentAttestationInfo(byte[] challenge, @KeyProperties.PurposeEnum int purposes, Set digests, Date startTime, boolean includesValidityDates, boolean devicePropertiesAttestation, Attestation attestation)1447 private void checkKeyIndependentAttestationInfo(byte[] challenge, 1448 @KeyProperties.PurposeEnum int purposes, Set digests, Date startTime, 1449 boolean includesValidityDates, boolean devicePropertiesAttestation, 1450 Attestation attestation) throws NoSuchAlgorithmException, NameNotFoundException { 1451 checkUnexpectedOids(attestation); 1452 checkAttestationSecurityLevelDependentParams(attestation); 1453 assertNotNull("Attestation challenge must not be null.", 1454 attestation.getAttestationChallenge()); 1455 assertThat("Attestation challenge not matching with provided challenge.", 1456 attestation.getAttestationChallenge(), is(challenge)); 1457 // In EAT, this is null if not filled in. In ASN.1, this is an array with length 0. 1458 if (attestation.getUniqueId() != null) { 1459 assertEquals("Unique ID must not be empty if present.", 1460 0, attestation.getUniqueId().length); 1461 } 1462 checkPurposes(attestation, purposes); 1463 checkDigests(attestation, digests); 1464 checkValidityPeriod(attestation, startTime, includesValidityDates); 1465 checkFlags(attestation); 1466 checkOrigin(attestation); 1467 checkAttestationApplicationId(attestation); 1468 checkAttestationDeviceProperties(devicePropertiesAttestation, attestation); 1469 checkAttestationNoUniqueIds(attestation); 1470 } 1471 checkUnexpectedOids(Attestation attestation)1472 private void checkUnexpectedOids(Attestation attestation) { 1473 // Key attestation tests for StrongBox were only enabled from Android 15, 1474 // so allow more lax checks for earlier StrongBox implementations. 1475 boolean laxChecks = 1476 (attestation.getAttestationSecurityLevel() == KM_SECURITY_LEVEL_STRONG_BOX 1477 && TestUtils.getVendorApiLevel() <= 34); 1478 1479 // Retrieve the extension OIDs that are not expected. This includes OIDs whose critical 1480 // bit is incorrect. 1481 Set<String> unexpectedOids = attestation.getUnexpectedExtensionOids(); 1482 1483 ImmutableSet.Builder<String> allowedOids = new ImmutableSet.Builder<String>(); 1484 if (laxChecks) { 1485 // Allow for a KeyUsage extension that is incorrectly marked as non-critical 1486 allowedOids.add(Attestation.KEY_USAGE_OID); 1487 } 1488 assertThat( 1489 "Attestations must not contain additional extensions", 1490 unexpectedOids, 1491 everyItem(isIn(allowedOids.build()))); 1492 } 1493 getSystemPatchLevel()1494 private int getSystemPatchLevel() { 1495 Matcher matcher = OS_PATCH_LEVEL_STRING_PATTERN.matcher(Build.VERSION.SECURITY_PATCH); 1496 String invalidPatternMessage = "Invalid pattern for security path level string " 1497 + Build.VERSION.SECURITY_PATCH; 1498 assertTrue(invalidPatternMessage, matcher.matches()); 1499 String year_string = matcher.group(OS_PATCH_LEVEL_YEAR_GROUP_NAME); 1500 String month_string = matcher.group(OS_PATCH_LEVEL_MONTH_GROUP_NAME); 1501 int patch_level = Integer.parseInt(year_string) * 100 + Integer.parseInt(month_string); 1502 return patch_level; 1503 } 1504 getSystemOsVersion()1505 private int getSystemOsVersion() { 1506 return parseSystemOsVersion(Build.VERSION.RELEASE); 1507 } 1508 parseSystemOsVersion(String versionString)1509 private int parseSystemOsVersion(String versionString) { 1510 Matcher matcher = OS_VERSION_STRING_PATTERN.matcher(versionString); 1511 if (!matcher.matches()) { 1512 return 0; 1513 } 1514 1515 int version = 0; 1516 String major_string = matcher.group(OS_MAJOR_VERSION_MATCH_GROUP_NAME); 1517 String minor_string = matcher.group(OS_MINOR_VERSION_MATCH_GROUP_NAME); 1518 String subminor_string = matcher.group(OS_SUBMINOR_VERSION_MATCH_GROUP_NAME); 1519 if (major_string != null) { 1520 version += Integer.parseInt(major_string) * 10000; 1521 } 1522 if (minor_string != null) { 1523 version += Integer.parseInt(minor_string) * 100; 1524 } 1525 if (subminor_string != null) { 1526 version += Integer.parseInt(subminor_string); 1527 } 1528 return version; 1529 } 1530 checkOrigin(Attestation attestation)1531 private void checkOrigin(Attestation attestation) { 1532 assertTrue("Origin must be defined", 1533 attestation.getSoftwareEnforced().getOrigin() != null || 1534 attestation.getTeeEnforced().getOrigin() != null); 1535 if (attestation.getKeymasterVersion() != 0) { 1536 assertTrue("Origin may not be defined in both SW and TEE, except on keymaster0", 1537 attestation.getSoftwareEnforced().getOrigin() == null || 1538 attestation.getTeeEnforced().getOrigin() == null); 1539 } 1540 1541 if (attestation.getKeymasterSecurityLevel() == KM_SECURITY_LEVEL_SOFTWARE) { 1542 assertThat("For security level software," 1543 + " SoftwareEnforced origin must be " + KM_ORIGIN_GENERATED, 1544 attestation.getSoftwareEnforced().getOrigin(), is(KM_ORIGIN_GENERATED)); 1545 } else if (attestation.getKeymasterVersion() == 0) { 1546 assertThat("For KeyMaster version 0," 1547 + "TeeEnforced origin must be " + KM_ORIGIN_UNKNOWN, 1548 attestation.getTeeEnforced().getOrigin(), is(KM_ORIGIN_UNKNOWN)); 1549 } else { 1550 assertThat("TeeEnforced origin must be " + KM_ORIGIN_GENERATED, 1551 attestation.getTeeEnforced().getOrigin(), is(KM_ORIGIN_GENERATED)); 1552 } 1553 } 1554 checkFlags(Attestation attestation)1555 private void checkFlags(Attestation attestation) { 1556 assertFalse("All applications was not requested", 1557 attestation.getSoftwareEnforced().isAllApplications()); 1558 assertFalse("All applications was not requested", 1559 attestation.getTeeEnforced().isAllApplications()); 1560 assertFalse("Allow while on body was not requested", 1561 attestation.getSoftwareEnforced().isAllowWhileOnBody()); 1562 assertFalse("Allow while on body was not requested", 1563 attestation.getTeeEnforced().isAllowWhileOnBody()); 1564 assertNull("Auth binding was not requiested", 1565 attestation.getSoftwareEnforced().getUserAuthType()); 1566 assertNull("Auth binding was not requiested", 1567 attestation.getTeeEnforced().getUserAuthType()); 1568 assertTrue("noAuthRequired must be true", 1569 attestation.getSoftwareEnforced().isNoAuthRequired() 1570 || attestation.getTeeEnforced().isNoAuthRequired()); 1571 assertFalse("auth is either software or TEE", 1572 attestation.getSoftwareEnforced().isNoAuthRequired() 1573 && attestation.getTeeEnforced().isNoAuthRequired()); 1574 assertFalse("Software cannot implement rollback resistance", 1575 attestation.getSoftwareEnforced().isRollbackResistant()); 1576 } 1577 checkValidityPeriod(Attestation attestation, Date startTime, boolean includesValidityDates)1578 private void checkValidityPeriod(Attestation attestation, Date startTime, 1579 boolean includesValidityDates) { 1580 AuthorizationList validityPeriodList = attestation.getSoftwareEnforced(); 1581 AuthorizationList nonValidityPeriodList = attestation.getTeeEnforced(); 1582 1583 // A bug in Android S leads Android S devices with KeyMint1 not to add a creationDateTime. 1584 boolean creationDateTimeBroken = 1585 Build.VERSION.SDK_INT == Build.VERSION_CODES.S 1586 && attestation.getKeymasterVersion() == Attestation.KM_VERSION_KEYMINT_1; 1587 1588 if (!creationDateTimeBroken) { 1589 assertNull(nonValidityPeriodList.getCreationDateTime()); 1590 1591 Date creationDateTime = validityPeriodList.getCreationDateTime(); 1592 1593 boolean requireCreationDateTime = 1594 attestation.getKeymasterVersion() >= Attestation.KM_VERSION_KEYMINT_1; 1595 1596 if (requireCreationDateTime || creationDateTime != null) { 1597 assertNotNull(creationDateTime); 1598 1599 assertTrue("Test start time (" + startTime.getTime() + ") and key creation time (" + 1600 creationDateTime.getTime() + ") should be close", 1601 Math.abs(creationDateTime.getTime() - startTime.getTime()) <= 2000); 1602 1603 // Allow 1 second leeway in case of nearest-second rounding. 1604 Date now = new Date(); 1605 assertTrue("Key creation time (" + creationDateTime.getTime() + ") must be now (" + 1606 now.getTime() + ") or earlier.", 1607 now.getTime() >= (creationDateTime.getTime() - 1000)); 1608 } 1609 } 1610 1611 if (includesValidityDates) { 1612 Date activeDateTime = validityPeriodList.getActiveDateTime(); 1613 Date originationExpirationDateTime = validityPeriodList.getOriginationExpireDateTime(); 1614 Date usageExpirationDateTime = validityPeriodList.getUsageExpireDateTime(); 1615 1616 assertNotNull("Active date time should not be null in SoftwareEnforced" 1617 + " authorization list.", activeDateTime); 1618 assertNotNull("Origination expiration date time should not be null in" 1619 + " SoftwareEnforced authorization list.", 1620 originationExpirationDateTime); 1621 assertNotNull("Usage expiration date time should not be null in SoftwareEnforced" 1622 + " authorization list.", usageExpirationDateTime); 1623 1624 assertNull("Active date time must not be included in TeeEnforced authorization list.", 1625 nonValidityPeriodList.getActiveDateTime()); 1626 assertNull("Origination date time must not be included in TeeEnforced authorization" 1627 + "list.", nonValidityPeriodList.getOriginationExpireDateTime()); 1628 assertNull("Usage expiration date time must not be included in TeeEnforced" 1629 + " authorization list.", 1630 nonValidityPeriodList.getUsageExpireDateTime()); 1631 1632 assertThat("Origination expiration date time must match with provided expiration" 1633 + " date time.", originationExpirationDateTime.getTime(), 1634 is(startTime.getTime() + ORIGINATION_TIME_OFFSET)); 1635 assertThat("Usage (consumption) expiration date time must match with provided" 1636 + " expiration date time.", usageExpirationDateTime.getTime(), 1637 is(startTime.getTime() + CONSUMPTION_TIME_OFFSET)); 1638 } 1639 } 1640 checkDigests(Attestation attestation, Set<Integer> expectedDigests)1641 private void checkDigests(Attestation attestation, Set<Integer> expectedDigests) { 1642 Set<Integer> softwareEnforcedDigests = attestation.getSoftwareEnforced().getDigests(); 1643 Set<Integer> teeEnforcedDigests = attestation.getTeeEnforced().getDigests(); 1644 1645 if (softwareEnforcedDigests == null) { 1646 softwareEnforcedDigests = ImmutableSet.of(); 1647 } 1648 if (teeEnforcedDigests == null) { 1649 teeEnforcedDigests = ImmutableSet.of(); 1650 } 1651 1652 Set<Integer> allDigests = ImmutableSet.<Integer>builder() 1653 .addAll(softwareEnforcedDigests) 1654 .addAll(teeEnforcedDigests) 1655 .build(); 1656 Set<Integer> intersection = new ArraySet<>(); 1657 intersection.addAll(softwareEnforcedDigests); 1658 intersection.retainAll(teeEnforcedDigests); 1659 1660 assertThat("Set of digests from software enforced and Tee enforced must match" 1661 + " with expected digests set.", allDigests, is(expectedDigests)); 1662 assertTrue("Digest sets must be disjoint", intersection.isEmpty()); 1663 1664 if (attestation.getKeymasterSecurityLevel() == KM_SECURITY_LEVEL_SOFTWARE 1665 || attestation.getKeymasterVersion() == 0) { 1666 assertThat("Digests in software-enforced", 1667 softwareEnforcedDigests, is(expectedDigests)); 1668 } else { 1669 if (attestation.getKeymasterVersion() == 1) { 1670 // KM1 implementations may not support SHA512 in the TEE 1671 assertTrue("KeyMaster version 1 may not support SHA256, in which case it must be" 1672 + " software-emulated.", 1673 softwareEnforcedDigests.contains(KM_DIGEST_SHA_2_512) 1674 || teeEnforcedDigests.contains(KM_DIGEST_SHA_2_512)); 1675 1676 assertThat("Tee enforced digests should have digests {none and SHA2-256}", 1677 teeEnforcedDigests, hasItems(KM_DIGEST_NONE, KM_DIGEST_SHA_2_256)); 1678 } else { 1679 assertThat("Tee enforced digests should have all expected digests.", 1680 teeEnforcedDigests, is(expectedDigests)); 1681 } 1682 } 1683 } 1684 checkPurposes(Attestation attestation, @KeyProperties.PurposeEnum int purposes)1685 private Set<Integer> checkPurposes(Attestation attestation, 1686 @KeyProperties.PurposeEnum int purposes) { 1687 Set<Integer> expectedPurposes = buildPurposeSet(purposes); 1688 if (attestation.getKeymasterSecurityLevel() == KM_SECURITY_LEVEL_SOFTWARE 1689 || attestation.getKeymasterVersion() == 0) { 1690 assertThat("Purposes in software-enforced should match expected set", 1691 attestation.getSoftwareEnforced().getPurposes(), is(expectedPurposes)); 1692 assertNull("Should be no purposes in TEE-enforced", 1693 attestation.getTeeEnforced().getPurposes()); 1694 } else { 1695 assertThat("Purposes in TEE-enforced should match expected set", 1696 attestation.getTeeEnforced().getPurposes(), is(expectedPurposes)); 1697 assertNull("No purposes in software-enforced", 1698 attestation.getSoftwareEnforced().getPurposes()); 1699 } 1700 return expectedPurposes; 1701 } 1702 checkSystemPatchLevel(int teeOsPatchLevel, int systemPatchLevel)1703 private void checkSystemPatchLevel(int teeOsPatchLevel, int systemPatchLevel) { 1704 if (TestUtils.isGsiImage()) { 1705 // b/168663786: When using a GSI image, the system patch level might be 1706 // greater than or equal to the OS patch level reported from TEE. 1707 assertThat("For GSI image TEE os patch level should be less than or equal to system" 1708 + " patch level.", teeOsPatchLevel, lessThanOrEqualTo(systemPatchLevel)); 1709 } else { 1710 assertThat("TEE os patch level must be equal to system patch level.", 1711 teeOsPatchLevel, is(systemPatchLevel)); 1712 } 1713 } 1714 1715 @SuppressWarnings("unchecked") checkAttestationSecurityLevelDependentParams(Attestation attestation)1716 private void checkAttestationSecurityLevelDependentParams(Attestation attestation) { 1717 assertThat("Attestation version must be one of: {1, 2, 3, 4, 100, 200, 300, 400}", 1718 attestation.getAttestationVersion(), 1719 either(is(1)).or(is(2)).or(is(3)).or(is(4)) 1720 .or(is(100)).or(is(200)).or(is(300)).or(is(400))); 1721 1722 AuthorizationList teeEnforced = attestation.getTeeEnforced(); 1723 AuthorizationList softwareEnforced = attestation.getSoftwareEnforced(); 1724 1725 int systemOsVersion = getSystemOsVersion(); 1726 int systemPatchLevel = getSystemPatchLevel(); 1727 1728 switch (attestation.getAttestationSecurityLevel()) { 1729 case KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT: 1730 assertThat("TEE attestation can only come from TEE keymaster", 1731 attestation.getKeymasterSecurityLevel(), 1732 is(KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT)); 1733 assertThat("KeyMaster version is not valid.", attestation.getKeymasterVersion(), 1734 either(is(2)).or(is(3)).or(is(4)).or(is(41)) 1735 .or(is(100)).or(is(200)).or(is(300)).or(is(400))); 1736 1737 checkRootOfTrust(attestation, false /* requireLocked */); 1738 checkModuleHash(attestation); 1739 assertThat( 1740 "TEE enforced OS version and system OS version must be same.", 1741 teeEnforced.getOsVersion(), 1742 is(systemOsVersion)); 1743 checkSystemPatchLevel(teeEnforced.getOsPatchLevel(), systemPatchLevel); 1744 break; 1745 1746 case KM_SECURITY_LEVEL_STRONG_BOX: 1747 assertThat("StrongBox attestation can only come from StrongBox keymaster", 1748 attestation.getKeymasterSecurityLevel(), 1749 is(KM_SECURITY_LEVEL_STRONG_BOX)); 1750 assertThat("KeyMaster version is not valid.", attestation.getKeymasterVersion(), 1751 either(is(2)).or(is(3)).or(is(4)).or(is(41)) 1752 .or(is(100)).or(is(200)).or(is(300)).or(is(400))); 1753 1754 checkRootOfTrust(attestation, false /* requireLocked */); 1755 checkModuleHash(attestation); 1756 assertThat( 1757 "StrongBox enforced OS version and system OS version must be same.", 1758 teeEnforced.getOsVersion(), 1759 is(systemOsVersion)); 1760 checkSystemPatchLevel(teeEnforced.getOsPatchLevel(), systemPatchLevel); 1761 break; 1762 1763 case KM_SECURITY_LEVEL_SOFTWARE: 1764 if (attestation 1765 .getKeymasterSecurityLevel() == KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT) { 1766 assertThat("TEE KM version must be 0 or 1 with software attestation", 1767 attestation.getKeymasterVersion(), either(is(0)).or(is(1))); 1768 } else { 1769 assertThat("Software KM is version 3", attestation.getKeymasterVersion(), 1770 is(3)); 1771 assertThat("Software enforced OS version and System OS version must be same.", 1772 softwareEnforced.getOsVersion(), is(systemOsVersion)); 1773 checkSystemPatchLevel(softwareEnforced.getOsPatchLevel(), systemPatchLevel); 1774 } 1775 1776 assertNull("Software attestation cannot provide root of trust", 1777 teeEnforced.getRootOfTrust()); 1778 1779 break; 1780 1781 default: 1782 fail("Invalid attestation security level: " 1783 + attestation.getAttestationSecurityLevel()); 1784 break; 1785 } 1786 } 1787 checkDeviceLocked(Attestation attestation)1788 private void checkDeviceLocked(Attestation attestation) { 1789 assertThat("Attestation version must be >= 1", 1790 attestation.getAttestationVersion(), greaterThanOrEqualTo(1)); 1791 1792 int attestationSecurityLevel = attestation.getAttestationSecurityLevel(); 1793 switch (attestationSecurityLevel) { 1794 case KM_SECURITY_LEVEL_STRONG_BOX: 1795 case KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT: 1796 assertThat("Attestation security level doesn't match keymaster security level", 1797 attestation.getKeymasterSecurityLevel(), is(attestationSecurityLevel)); 1798 assertThat("Keymaster version should be greater than or equal to 2.", 1799 attestation.getKeymasterVersion(), greaterThanOrEqualTo(2)); 1800 1801 // Devices launched in Android 10.0 (API level 29) and after should run CTS 1802 // in LOCKED state. 1803 boolean requireLocked = PropertyUtil.getFirstApiLevel() >= 29; 1804 checkRootOfTrust(attestation, requireLocked); 1805 break; 1806 1807 case KM_SECURITY_LEVEL_SOFTWARE: 1808 default: 1809 // TEE attestation has been required since Android 7.0. 1810 fail("Unexpected attestation security level: " + 1811 Attestation.securityLevelToString(attestationSecurityLevel)); 1812 break; 1813 } 1814 } 1815 checkVerifiedBootHash(byte[] verifiedBootHash)1816 private void checkVerifiedBootHash(byte[] verifiedBootHash) { 1817 assertNotNull(verifiedBootHash); 1818 assertEquals(32, verifiedBootHash.length); 1819 checkEntropy(verifiedBootHash, "rootOfTrust.verifiedBootHash" /* dataName */); 1820 1821 String bootVbMetaDigest = SystemProperties.get("ro.boot.vbmeta.digest", ""); 1822 assertEquals( 1823 "VerifiedBootHash field of RootOfTrust section does not match with" 1824 + "system property ro.boot.vbmeta.digest", 1825 bootVbMetaDigest, 1826 HexEncoding.encode(verifiedBootHash)); 1827 } 1828 checkVerifiedBootKey(byte[] verifiedBootKey, boolean isLocked)1829 private void checkVerifiedBootKey(byte[] verifiedBootKey, boolean isLocked) { 1830 assertNotNull(verifiedBootKey); 1831 if (isLocked) { 1832 checkEntropy(verifiedBootKey, "rootOfTrust.verifiedBootKey" /* dataName */); 1833 } 1834 1835 String unexpectedLengthMessagePrefix = 1836 "rootOfTrust.verifiedBootKey has an unexpected length: " + verifiedBootKey.length; 1837 1838 if (TestUtils.getVendorApiLevel() >= 202504) { 1839 assertEquals( 1840 unexpectedLengthMessagePrefix + " (expected 32)", 32, verifiedBootKey.length); 1841 if (isLocked) { 1842 String systemProperty = 1843 SystemProperties.get("ro.boot.vbmeta.public_key_digest", ""); 1844 assertEquals( 1845 "rootOfTrust.verifiedBootKey does not match the " 1846 + "ro.boot.vbmeta.public_key_digest system property", 1847 systemProperty, 1848 HexEncoding.encode(verifiedBootKey)); 1849 } else { 1850 byte[] emptyVerifiedBootKey = new byte[32]; 1851 assertArrayEquals( 1852 "Bootloader is unlocked, so rootOfTrust.verifiedBootKey should contain 32 " 1853 + " bytes of zeroes", 1854 emptyVerifiedBootKey, 1855 verifiedBootKey); 1856 } 1857 } else { 1858 assertTrue( 1859 unexpectedLengthMessagePrefix + " (expected >= 32)", 1860 verifiedBootKey.length >= 32); 1861 } 1862 } 1863 checkRootOfTrust(Attestation attestation, boolean requireLocked)1864 private void checkRootOfTrust(Attestation attestation, boolean requireLocked) { 1865 RootOfTrust rootOfTrust = attestation.getRootOfTrust(); 1866 assertNotNull(rootOfTrust); 1867 1868 if (requireLocked) { 1869 final String unlockedDeviceMessage = "The device's bootloader must be locked. This may " 1870 + "not be the default for pre-production devices."; 1871 assertTrue(unlockedDeviceMessage, rootOfTrust.isDeviceLocked()); 1872 checkVerifiedBootKey(rootOfTrust.getVerifiedBootKey(), true /* isLocked */); 1873 assertEquals(KM_VERIFIED_BOOT_VERIFIED, rootOfTrust.getVerifiedBootState()); 1874 1875 if (PropertyUtil.getFirstApiLevel() >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { 1876 // The Verified Boot hash was not previously checked in CTS, so set an API level 1877 // check to avoid running into waiver issues. 1878 checkVerifiedBootHash(rootOfTrust.getVerifiedBootHash()); 1879 } 1880 } else { 1881 // We expect exactly one of these combinations of values because either: 1882 // 1) CTS is running on a signed bootloader-locked build verified with the OEM's root 1883 // of trust, so the Verified Boot state is KM_VERIFIED_BOOT_VERIFIED. 1884 // OR 1885 // 2) CTS is running on a signed GSI. This requires an unlocked bootloader and 1886 // therefore a chain of trust cannot be established, so the Verified Boot state is 1887 // KM_VERIFIED_BOOT_UNVERIFIED. 1888 // Builds with a custom root of trust (which would result in 1889 // KM_VERIFIED_BOOT_SELF_SIGNED and could have a locked or unlocked bootloader) 1890 // shouldn't pass CTS since they don't comply with CDD requirement 9.10 [C-1-3] which 1891 // requires use of the fused OEM root of trust for Verified Boot. 1892 boolean isLocked = rootOfTrust.getVerifiedBootState() == KM_VERIFIED_BOOT_VERIFIED 1893 && rootOfTrust.isDeviceLocked(); 1894 boolean isUnlocked = rootOfTrust.getVerifiedBootState() == KM_VERIFIED_BOOT_UNVERIFIED 1895 && !rootOfTrust.isDeviceLocked(); 1896 assertTrue("Unexpected combination of device locked state and Verified Boot " 1897 + "state.", isLocked || isUnlocked); 1898 checkVerifiedBootKey(rootOfTrust.getVerifiedBootKey(), isLocked); 1899 } 1900 } 1901 checkModuleHash(Attestation attestation)1902 private void checkModuleHash(Attestation attestation) { 1903 if (attestation.getKeymasterVersion() < Attestation.KM_VERSION_KEYMINT_4) { 1904 // Module hash will only be populated if the underlying device is KeyMint v4 or later. 1905 return; 1906 } 1907 if (!Flags.attestModules()) { 1908 // Module hash will only be populated if the flag is on. 1909 return; 1910 } 1911 1912 // The KeyMint device should have populated a module hash value in the software-enforced 1913 // list. 1914 byte[] moduleHash = attestation.softwareEnforced.getModuleHash(); 1915 assertTrue(moduleHash != null); 1916 assertTrue(moduleHash.length > 0); 1917 1918 // The value in the attestation should match the hash of what Keystore reports as the module 1919 // hash input data. 1920 byte[] inputData; 1921 try { 1922 KeyStoreManager manager = KeyStoreManager.getInstance(); 1923 inputData = manager.getSupplementaryAttestationInfo(KeyStoreManager.MODULE_HASH); 1924 } catch (KeyStoreException e) { 1925 fail("Could not retrieve expected module hash value: " + e); 1926 return; 1927 } 1928 byte[] expectedHash; 1929 try { 1930 MessageDigest digest = MessageDigest.getInstance("SHA-256"); 1931 expectedHash = digest.digest(inputData); 1932 } catch (NoSuchAlgorithmException e) { 1933 fail("No SHA-256 available: " + e); 1934 return; 1935 } 1936 assertEquals(HexEncoding.encode(expectedHash), HexEncoding.encode(moduleHash)); 1937 1938 // The `inputData` should also parse as a DER encoding of the schema described in 1939 // KeyCreationResult.aidl in the KeyMint HAL definition. 1940 try { 1941 Modules unusedModules = new Modules(inputData); 1942 } catch (CertificateParsingException e) { 1943 fail("failed to parse module data: " + e); 1944 } 1945 } 1946 checkEntropy(byte[] data, String dataName)1947 private void checkEntropy(byte[] data, String dataName) { 1948 byte[] dataCopy = Arrays.copyOf(data, data.length); 1949 assertTrue("Failed Shannon entropy check", checkShannonEntropy(dataCopy, dataName)); 1950 assertTrue("Failed BiEntropy check", checkTresBiEntropy(dataCopy, dataName)); 1951 } 1952 checkShannonEntropy(byte[] data, String dataName)1953 private boolean checkShannonEntropy(byte[] data, String dataName) { 1954 double probabilityOfSetBit = countSetBits(data) / (double) (data.length * 8); 1955 return calculateShannonEntropy(probabilityOfSetBit, dataName) > 0.8; 1956 } 1957 calculateShannonEntropy(double probabilityOfSetBit, String dataName)1958 private double calculateShannonEntropy(double probabilityOfSetBit, String dataName) { 1959 if (probabilityOfSetBit <= 0.001 || probabilityOfSetBit >= .999) return 0; 1960 double entropy = (-probabilityOfSetBit * logTwo(probabilityOfSetBit)) - 1961 ((1 - probabilityOfSetBit) * logTwo(1 - probabilityOfSetBit)); 1962 Log.i(TAG, "Shannon entropy of " + dataName + ": " + entropy); 1963 return entropy; 1964 } 1965 1966 /** 1967 * Note: This method modifies the input parameter while performing bit entropy check. 1968 */ checkTresBiEntropy(byte[] data, String dataName)1969 private boolean checkTresBiEntropy(byte[] data, String dataName) { 1970 double weightingFactor = 0; 1971 double weightedEntropy = 0; 1972 double probabilityOfSetBit = 0; 1973 int length = data.length * 8; 1974 for (int i = 0; i < (data.length * 8) - 2; i++) { 1975 probabilityOfSetBit = countSetBits(data) / (double) length; 1976 weightingFactor += logTwo(i + 2); 1977 weightedEntropy += calculateShannonEntropy(probabilityOfSetBit, dataName) 1978 * logTwo(i + 2); 1979 deriveBitString(data, length); 1980 length -= 1; 1981 } 1982 double tresBiEntropy = (1 / weightingFactor) * weightedEntropy; 1983 Log.i(TAG, "BiEntropy of " + dataName + ": " + tresBiEntropy); 1984 return tresBiEntropy > 0.9; 1985 } 1986 1987 /** 1988 * Note: This method modifies the input parameter - bitString. 1989 */ deriveBitString(byte[] bitString, int activeLength)1990 private void deriveBitString(byte[] bitString, int activeLength) { 1991 int length = activeLength / 8; 1992 if (activeLength % 8 != 0) { 1993 length += 1; 1994 } 1995 1996 byte mask = (byte) ((byte) 0x80 >>> ((activeLength + 6) % 8)); 1997 if (activeLength % 8 == 1) { 1998 mask = (byte) ~mask; 1999 } 2000 2001 for (int i = 0; i < length; i++) { 2002 if (i == length - 1) { 2003 bitString[i] ^= ((bitString[i] & 0xFF) << 1); 2004 bitString[i] &= mask; 2005 } else { 2006 bitString[i] ^= ((bitString[i] & 0xFF) << 1) | ((bitString[i + 1] & 0xFF) >>> 7); 2007 } 2008 } 2009 } 2010 logTwo(double value)2011 private double logTwo(double value) { 2012 return Math.log(value) / Math.log(2); 2013 } 2014 countSetBits(byte[] toCount)2015 private int countSetBits(byte[] toCount) { 2016 int setBitCount = 0; 2017 for (int i = 0; i < toCount.length; i++) { 2018 setBitCount += countSetBits(toCount[i]); 2019 } 2020 return setBitCount; 2021 } 2022 countSetBits(byte toCount)2023 private int countSetBits(byte toCount) { 2024 int setBitCounter = 0; 2025 while (toCount != 0) { 2026 toCount &= (toCount - 1); 2027 setBitCounter++; 2028 } 2029 return setBitCounter; 2030 } 2031 checkRsaKeyDetails(X509Certificate attestationCert, Attestation attestation, int keySize, Set<String> expectedPaddingModes)2032 private void checkRsaKeyDetails(X509Certificate attestationCert, Attestation attestation, 2033 int keySize, Set<String> expectedPaddingModes) 2034 throws CertificateParsingException { 2035 AuthorizationList keyDetailsList; 2036 AuthorizationList nonKeyDetailsList; 2037 if (attestation.getKeymasterSecurityLevel() == KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT 2038 || attestation.getKeymasterSecurityLevel() == KM_SECURITY_LEVEL_STRONG_BOX) { 2039 keyDetailsList = attestation.getTeeEnforced(); 2040 nonKeyDetailsList = attestation.getSoftwareEnforced(); 2041 } else { 2042 keyDetailsList = attestation.getSoftwareEnforced(); 2043 nonKeyDetailsList = attestation.getTeeEnforced(); 2044 } 2045 assertEquals(keySize, keyDetailsList.getKeySize().intValue()); 2046 assertNull(nonKeyDetailsList.getKeySize()); 2047 assertEquals(keySize, 2048 ((RSAPublicKey) attestationCert.getPublicKey()).getModulus().bitLength()); 2049 2050 assertEquals(KM_ALGORITHM_RSA, keyDetailsList.getAlgorithm().intValue()); 2051 assertNull(nonKeyDetailsList.getAlgorithm()); 2052 assertEquals(KEY_ALGORITHM_RSA, attestationCert.getPublicKey().getAlgorithm()); 2053 2054 assertNull(keyDetailsList.getEcCurve()); 2055 assertNull(nonKeyDetailsList.getEcCurve()); 2056 2057 assertEquals(65537, keyDetailsList.getRsaPublicExponent().longValue()); 2058 assertNull(nonKeyDetailsList.getRsaPublicExponent()); 2059 assertEquals(65537, 2060 ((RSAPublicKey) attestationCert.getPublicKey()).getPublicExponent().intValue()); 2061 2062 Set<String> paddingModes; 2063 if (attestation.getKeymasterVersion() == 0) { 2064 // KM0 implementations don't support padding info, so it's always in the 2065 // software-enforced list. 2066 paddingModes = attestation.getSoftwareEnforced().getPaddingModesAsStrings(); 2067 assertNull(attestation.getTeeEnforced().getPaddingModes()); 2068 } else { 2069 paddingModes = keyDetailsList.getPaddingModesAsStrings(); 2070 assertNull(nonKeyDetailsList.getPaddingModes()); 2071 } 2072 2073 // KM1 implementations may add ENCRYPTION_PADDING_NONE to the list of paddings. 2074 Set<String> km1PossiblePaddingModes = expectedPaddingModes; 2075 if (attestation.getKeymasterVersion() == 1 && 2076 attestation.getKeymasterSecurityLevel() == KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT) { 2077 ImmutableSet.Builder<String> builder = ImmutableSet.builder(); 2078 builder.addAll(expectedPaddingModes); 2079 builder.add(ENCRYPTION_PADDING_NONE); 2080 km1PossiblePaddingModes = builder.build(); 2081 } 2082 2083 assertThat("Attested padding mode does not matched with expected modes.", 2084 paddingModes, either(is(expectedPaddingModes)).or(is(km1PossiblePaddingModes))); 2085 } 2086 checkEcKeyDetails(X509Certificate attestationCert, Attestation attestation, String ecCurve, int keySize)2087 private void checkEcKeyDetails(X509Certificate attestationCert, Attestation attestation, 2088 String ecCurve, int keySize) { 2089 AuthorizationList keyDetailsList; 2090 AuthorizationList nonKeyDetailsList; 2091 if (attestation.getKeymasterSecurityLevel() == KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT 2092 || attestation.getKeymasterSecurityLevel() == KM_SECURITY_LEVEL_STRONG_BOX) { 2093 keyDetailsList = attestation.getTeeEnforced(); 2094 nonKeyDetailsList = attestation.getSoftwareEnforced(); 2095 } else { 2096 keyDetailsList = attestation.getSoftwareEnforced(); 2097 nonKeyDetailsList = attestation.getTeeEnforced(); 2098 } 2099 assertEquals(keySize, keyDetailsList.getKeySize().intValue()); 2100 assertEquals(keySize, sECKeySizes.get(ecCurve).intValue()); 2101 assertNull(nonKeyDetailsList.getKeySize()); 2102 assertEquals(KM_ALGORITHM_EC, keyDetailsList.getAlgorithm().intValue()); 2103 // Curve25519 Public key returns OID string for getAlgorithm 2104 if (!ecCurve.equals("CURVE_25519")) { 2105 assertEquals(KEY_ALGORITHM_EC, attestationCert.getPublicKey().getAlgorithm()); 2106 } else { 2107 assertThat(attestationCert.getPublicKey().getAlgorithm(), 2108 /*Signing key algorithm "1.3.101.112" & Agreement Key algorithm "XDH"*/ 2109 either(is("1.3.101.112")).or(is("XDH"))); 2110 } 2111 assertNull(nonKeyDetailsList.getAlgorithm()); 2112 assertEquals(ecCurve, keyDetailsList.ecCurveAsString()); 2113 // Curve25519 Public key(X509PublicKey) cannot be cast to ECPublicKey and hence could not 2114 // determine EC key parameters such as FieldFp, a, b, gx, gy, order and cofactor 2115 if (!ecCurve.equals("CURVE_25519")) { 2116 TestUtils.assertECParameterSpecEqualsIgnoreSeedIfNotPresent( 2117 getECParameterSpecFor(ecCurve), 2118 ((ECPublicKey) attestationCert.getPublicKey()).getParams()); 2119 } 2120 assertNull(nonKeyDetailsList.getEcCurve()); 2121 assertNull(keyDetailsList.getRsaPublicExponent()); 2122 assertNull(nonKeyDetailsList.getRsaPublicExponent()); 2123 assertNull(keyDetailsList.getPaddingModes()); 2124 assertNull(nonKeyDetailsList.getPaddingModes()); 2125 } 2126 getECParameterSpecFor(String ecCurve)2127 private ECParameterSpec getECParameterSpecFor(String ecCurve) { 2128 if (ecCurve.equals("secp224r1")) { 2129 return ECCurves.NIST_P_224_SPEC; 2130 } else if (ecCurve.equals("secp256r1")) { 2131 return ECCurves.NIST_P_256_SPEC; 2132 } else if (ecCurve.equals("secp384r1")) { 2133 return ECCurves.NIST_P_384_SPEC; 2134 } else if (ecCurve.equals("secp521r1")) { 2135 return ECCurves.NIST_P_521_SPEC; 2136 } 2137 return null; 2138 } 2139 isEncryptionPurpose(@eyProperties.PurposeEnum int purposes)2140 private boolean isEncryptionPurpose(@KeyProperties.PurposeEnum int purposes) { 2141 return (purposes & PURPOSE_DECRYPT) != 0 || (purposes & PURPOSE_ENCRYPT) != 0; 2142 } 2143 isSignaturePurpose(@eyProperties.PurposeEnum int purposes)2144 private boolean isSignaturePurpose(@KeyProperties.PurposeEnum int purposes) { 2145 return (purposes & PURPOSE_SIGN) != 0; 2146 } 2147 isVerifyPurpose(@eyProperties.PurposeEnum int purposes)2148 private boolean isVerifyPurpose(@KeyProperties.PurposeEnum int purposes) { 2149 return (purposes & PURPOSE_VERIFY) != 0; 2150 } 2151 isAgreeKeyPurpose(@eyProperties.PurposeEnum int purposes)2152 private boolean isAgreeKeyPurpose(@KeyProperties.PurposeEnum int purposes) { 2153 return (purposes & PURPOSE_AGREE_KEY) != 0; 2154 } 2155 buildPurposeSet(@eyProperties.PurposeEnum int purposes)2156 private ImmutableSet<Integer> buildPurposeSet(@KeyProperties.PurposeEnum int purposes) { 2157 ImmutableSet.Builder<Integer> builder = ImmutableSet.builder(); 2158 if ((purposes & PURPOSE_SIGN) != 0) { 2159 builder.add(KM_PURPOSE_SIGN); 2160 } 2161 if ((purposes & PURPOSE_VERIFY) != 0) { 2162 builder.add(KM_PURPOSE_VERIFY); 2163 } 2164 if ((purposes & PURPOSE_ENCRYPT) != 0) { 2165 builder.add(KM_PURPOSE_ENCRYPT); 2166 } 2167 if ((purposes & PURPOSE_DECRYPT) != 0) { 2168 builder.add(KM_PURPOSE_DECRYPT); 2169 } 2170 if ((purposes & PURPOSE_AGREE_KEY) != 0) { 2171 builder.add(KM_PURPOSE_AGREE_KEY); 2172 } 2173 return builder.build(); 2174 } 2175 generateKey(KeyGenParameterSpec spec, String algorithm)2176 private void generateKey(KeyGenParameterSpec spec, String algorithm) 2177 throws NoSuchAlgorithmException, NoSuchProviderException, 2178 InvalidAlgorithmParameterException { 2179 KeyGenerator keyGenerator = KeyGenerator.getInstance(algorithm, "AndroidKeyStore"); 2180 keyGenerator.init(spec); 2181 keyGenerator.generateKey(); 2182 } 2183 generateKeyPair(String algorithm, KeyGenParameterSpec spec)2184 private void generateKeyPair(String algorithm, KeyGenParameterSpec spec) 2185 throws NoSuchAlgorithmException, NoSuchProviderException, 2186 InvalidAlgorithmParameterException { 2187 KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm, 2188 "AndroidKeyStore"); 2189 keyPairGenerator.initialize(spec); 2190 keyPairGenerator.generateKeyPair(); 2191 } 2192 verifyCertificateChain(Certificate[] certChain, boolean expectStrongBox)2193 public static void verifyCertificateChain(Certificate[] certChain, boolean expectStrongBox) 2194 throws GeneralSecurityException { 2195 assertNotNull(certChain); 2196 boolean strongBoxSubjectFound = false; 2197 for (int i = 1; i < certChain.length; ++i) { 2198 try { 2199 PublicKey pubKey = certChain[i].getPublicKey(); 2200 certChain[i - 1].verify(pubKey); 2201 if (i == certChain.length - 1) { 2202 // Last cert should be self-signed. 2203 certChain[i].verify(pubKey); 2204 } 2205 2206 // Check that issuer in the signed cert matches subject in the signing cert. 2207 X509Certificate x509CurrCert = (X509Certificate) certChain[i]; 2208 X509Certificate x509PrevCert = (X509Certificate) certChain[i - 1]; 2209 X500Name signingCertSubject = 2210 new JcaX509CertificateHolder(x509CurrCert).getSubject(); 2211 X500Name signedCertIssuer = 2212 new JcaX509CertificateHolder(x509PrevCert).getIssuer(); 2213 // Use .toASN1Object().equals() rather than .equals() because .equals() is case 2214 // insensitive, and we want to verify an exact match. 2215 assertTrue(String.format("Certificate Issuer (%s) is not matching with parent" 2216 + " certificate's Subject (%s).", 2217 signedCertIssuer.toString(), signingCertSubject.toString()), 2218 signedCertIssuer.toASN1Object().equals(signingCertSubject.toASN1Object())); 2219 2220 X500Name signedCertSubject = 2221 new JcaX509CertificateHolder(x509PrevCert).getSubject(); 2222 if (i == 1) { 2223 // First cert should have subject "CN=Android Keystore Key". 2224 assertEquals(signedCertSubject, new X500Name("CN=Android Keystore Key")); 2225 } else if (signedCertSubject.toString().toLowerCase().contains("strongbox")) { 2226 strongBoxSubjectFound = true; 2227 } 2228 } catch (InvalidKeyException | CertificateException | NoSuchAlgorithmException 2229 | NoSuchProviderException | SignatureException e) { 2230 throw new GeneralSecurityException("Using StrongBox: " + expectStrongBox + "\n" 2231 + "Failed to verify certificate " + certChain[i - 1] 2232 + " with public key " + certChain[i].getPublicKey(), 2233 e); 2234 } 2235 } 2236 // At least one intermediate in a StrongBox chain must have "strongbox" in the subject. 2237 assertEquals(expectStrongBox, strongBoxSubjectFound); 2238 } 2239 testDeviceIdAttestationFailure(int idType, String acceptableDeviceIdAttestationFailureMessage)2240 private void testDeviceIdAttestationFailure(int idType, 2241 String acceptableDeviceIdAttestationFailureMessage) throws Exception { 2242 try { 2243 AttestationUtils.attestDeviceIds(getContext(), new int[]{idType}, "123".getBytes()); 2244 fail("Attestation should have failed."); 2245 } catch (SecurityException e) { 2246 // Attestation is expected to fail. If the device has the device ID type we are trying 2247 // to attest, it should fail with a SecurityException as we do not hold 2248 // READ_PRIVILEGED_PHONE_STATE permission. 2249 } catch (DeviceIdAttestationException e) { 2250 // Attestation is expected to fail. If the device does not have the device ID type we 2251 // are trying to attest (e.g. no IMEI on devices without a radio), it should fail with 2252 // a corresponding DeviceIdAttestationException. 2253 if (acceptableDeviceIdAttestationFailureMessage == null || 2254 !acceptableDeviceIdAttestationFailureMessage.equals(e.getMessage())) { 2255 throw e; 2256 } 2257 } 2258 } 2259 2260 // Indicate whether the provided exception indicates an ID attestation failure that 2261 // can be ignored (because the device doesn't support ID attestation). This method 2262 // will throw a new exception if the error is an ID attestation failure that can't 2263 // be ignored. isIgnorableIdAttestationFailure(Throwable e)2264 private boolean isIgnorableIdAttestationFailure(Throwable e) throws Exception { 2265 if (!(e.getCause() instanceof KeyStoreException)) { 2266 return false; 2267 } 2268 if (((KeyStoreException) e.getCause()).getNumericErrorCode() 2269 != KeyStoreException.ERROR_ID_ATTESTATION_FAILURE) { 2270 return false; 2271 } 2272 if (!getContext().getPackageManager().hasSystemFeature( 2273 PackageManager.FEATURE_DEVICE_ID_ATTESTATION)) { 2274 Log.i(TAG, "key attestation with device IDs not supported; test skipped"); 2275 return true; 2276 } 2277 2278 if (TestUtils.isGsiImage()) { 2279 // When running under GSI, the ro.product.<device-id> values may be replaced with values 2280 // that don't match the vendor code. However, any ro.product.vendor.<device-id> values 2281 // will not be replaced by GSI (and the frameworks code will use them in preference to 2282 // the ro.product.<device-id> values). 2283 if (TestUtils.getVendorApiLevel() < Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { 2284 // On older releases just skip the failure. 2285 Log.i(TAG, "device ID attestation on older device under GSI; test skipped"); 2286 return true; 2287 } else { 2288 // On more current devices, raise a different Exception to give out a hint 2289 // that the vendor properties should be set. 2290 throw new Exception("Failed to generate key with device ID attestation under GSI. " 2291 + "Check that the relevant ro.product.vendor.<device-id> field is " 2292 + "set correctly in the vendor image.", 2293 e); 2294 } 2295 } 2296 return false; 2297 } 2298 } 2299