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