1 /* 2 * Copyright (C) 2022 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.app.admin.DevicePolicyManager.ID_TYPE_BASE_INFO; 20 import static android.app.admin.DevicePolicyManager.ID_TYPE_IMEI; 21 import static android.app.admin.DevicePolicyManager.ID_TYPE_MEID; 22 import static android.app.admin.DevicePolicyManager.ID_TYPE_SERIAL; 23 import static android.keystore.cts.Attestation.KM_SECURITY_LEVEL_SOFTWARE; 24 import static android.keystore.cts.Attestation.KM_SECURITY_LEVEL_STRONG_BOX; 25 import static android.keystore.cts.Attestation.KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT; 26 import static android.keystore.cts.KeyAttestationTest.verifyCertificateChain; 27 import static android.keystore.cts.RootOfTrust.KM_VERIFIED_BOOT_VERIFIED; 28 import static android.security.keystore.KeyProperties.DIGEST_NONE; 29 import static android.security.keystore.KeyProperties.DIGEST_SHA256; 30 import static android.security.keystore.KeyProperties.DIGEST_SHA512; 31 import static android.security.keystore.KeyProperties.KEY_ALGORITHM_EC; 32 import static android.security.keystore.KeyProperties.PURPOSE_SIGN; 33 34 import static com.google.android.attestation.ParsedAttestationRecord.createParsedAttestationRecord; 35 import static com.google.common.truth.Truth.assertThat; 36 import static com.google.common.truth.Truth.assertWithMessage; 37 38 import static org.hamcrest.CoreMatchers.is; 39 import static org.hamcrest.MatcherAssert.assertThat; 40 import static org.hamcrest.Matchers.greaterThanOrEqualTo; 41 import static org.junit.Assert.assertNotNull; 42 import static org.junit.Assert.assertTrue; 43 import static org.junit.Assert.fail; 44 import static org.junit.Assume.assumeFalse; 45 46 import android.app.admin.DevicePolicyManager; 47 import android.content.ComponentName; 48 import android.content.Context; 49 import android.content.pm.PackageManager; 50 import android.keystore.cts.util.TestUtils; 51 import android.os.Build; 52 import android.security.AttestedKeyPair; 53 import android.security.keystore.KeyGenParameterSpec; 54 import android.security.keystore.KeyProperties; 55 import android.security.keystore.StrongBoxUnavailableException; 56 import android.telephony.TelephonyManager; 57 import android.text.TextUtils; 58 59 import com.android.bedstead.deviceadminapp.DeviceAdminApp; 60 import com.android.bedstead.harrier.DeviceState; 61 import com.android.bedstead.harrier.annotations.RequireFeature; 62 import com.android.bedstead.harrier.annotations.RequireRunOnSystemUser; 63 import com.android.bedstead.nene.TestApis; 64 import com.android.bedstead.nene.devicepolicy.DeviceOwner; 65 import com.android.bedstead.nene.exceptions.NeneException; 66 import com.android.bedstead.permissions.PermissionContext; 67 import com.android.compatibility.common.util.ApiTest; 68 69 import com.google.android.attestation.ParsedAttestationRecord; 70 71 import org.junit.ClassRule; 72 import org.junit.Rule; 73 import org.junit.Test; 74 75 import java.io.IOException; 76 import java.security.GeneralSecurityException; 77 import java.security.KeyPair; 78 import java.security.KeyPairGenerator; 79 import java.security.KeyStore; 80 import java.security.PrivateKey; 81 import java.security.PublicKey; 82 import java.security.Signature; 83 import java.security.cert.Certificate; 84 import java.security.cert.CertificateParsingException; 85 import java.security.cert.X509Certificate; 86 import java.security.spec.ECGenParameterSpec; 87 import java.util.ArrayList; 88 import java.util.Arrays; 89 import java.util.List; 90 import java.util.stream.Collectors; 91 92 public class DeviceOwnerKeyManagementTest { 93 private static final Context sContext = TestApis.context().instrumentedContext(); 94 private static final DevicePolicyManager sDevicePolicyManager = 95 sContext.getSystemService(DevicePolicyManager.class); 96 private static final ComponentName DEVICE_ADMIN_COMPONENT_NAME = 97 DeviceAdminApp.deviceAdminComponentName(sContext); 98 99 @ClassRule @Rule 100 public static final DeviceState sDeviceState = new DeviceState(); 101 102 private static class SupportedKeyAlgorithm { 103 public final String keyAlgorithm; 104 public final String signatureAlgorithm; 105 public final String[] signaturePaddingSchemes; 106 SupportedKeyAlgorithm( String keyAlgorithm, String signatureAlgorithm, String[] signaturePaddingSchemes)107 SupportedKeyAlgorithm( 108 String keyAlgorithm, String signatureAlgorithm, 109 String[] signaturePaddingSchemes) { 110 this.keyAlgorithm = keyAlgorithm; 111 this.signatureAlgorithm = signatureAlgorithm; 112 this.signaturePaddingSchemes = signaturePaddingSchemes; 113 } 114 } 115 116 private static final SupportedKeyAlgorithm[] SUPPORTED_KEY_ALGORITHMS = 117 new SupportedKeyAlgorithm[] { 118 new SupportedKeyAlgorithm(KeyProperties.KEY_ALGORITHM_RSA, "SHA256withRSA", 119 new String[] {KeyProperties.SIGNATURE_PADDING_RSA_PSS, 120 KeyProperties.SIGNATURE_PADDING_RSA_PKCS1}), 121 new SupportedKeyAlgorithm(KeyProperties.KEY_ALGORITHM_EC, "SHA256withECDSA", null) 122 }; 123 signDataWithKey(String algoIdentifier, PrivateKey privateKey)124 byte[] signDataWithKey(String algoIdentifier, PrivateKey privateKey) throws Exception { 125 byte[] data = new String("hello").getBytes(); 126 Signature sign = Signature.getInstance(algoIdentifier); 127 sign.initSign(privateKey); 128 sign.update(data); 129 return sign.sign(); 130 } 131 verifySignature(String algoIdentifier, PublicKey publicKey, byte[] signature)132 void verifySignature(String algoIdentifier, PublicKey publicKey, byte[] signature) 133 throws Exception { 134 byte[] data = new String("hello").getBytes(); 135 Signature verify = Signature.getInstance(algoIdentifier); 136 verify.initVerify(publicKey); 137 verify.update(data); 138 assertThat(verify.verify(signature)).isTrue(); 139 } 140 verifySignatureOverData(String algoIdentifier, KeyPair keyPair)141 void verifySignatureOverData(String algoIdentifier, KeyPair keyPair) throws Exception { 142 verifySignature(algoIdentifier, keyPair.getPublic(), 143 signDataWithKey(algoIdentifier, keyPair.getPrivate())); 144 } 145 validateDeviceIdAttestationData(Certificate[] certs, String expectedSerial, String expectedImei, String expectedMeid, String expectedSecondImei, boolean useStrongbox)146 private void validateDeviceIdAttestationData(Certificate[] certs, 147 String expectedSerial, 148 String expectedImei, 149 String expectedMeid, 150 String expectedSecondImei, 151 boolean useStrongbox) 152 throws CertificateParsingException { 153 154 // find the first cert nearest to root contains the attestation data 155 Attestation attestationRecord = null; 156 for (int i = certs.length - 1; i >= 0; i--) { 157 try { 158 Attestation record; 159 record = Attestation.loadFromCertificate((X509Certificate) certs[i]); 160 161 // if there is no attestation data in the cert, loadFromCertificat will throw 162 // CertificateParsingException, if we got to here, then we have valid attestation 163 // data 164 165 // attestation data should not be in the root cert 166 assertThat(i).isNotEqualTo(certs.length - 1); 167 assertWithMessage("Duplicated attesaation data found in cert chain.") 168 .that(attestationRecord) 169 .isNull(); 170 attestationRecord = record; 171 } catch (CertificateParsingException expected) { 172 continue; 173 } 174 } 175 if (attestationRecord == null) { 176 throw new CertificateParsingException("No attestation data found."); 177 } 178 AuthorizationList teeAttestation = attestationRecord.getTeeEnforced(); 179 assertThat(teeAttestation).isNotNull(); 180 final String platformReportedBrand = 181 TestUtils.isPropertyEmptyOrUnknown(Build.BRAND_FOR_ATTESTATION) 182 ? Build.BRAND : Build.BRAND_FOR_ATTESTATION; 183 assertThat(teeAttestation.getBrand()).isEqualTo(platformReportedBrand); 184 final String platformReportedDevice = 185 TestUtils.isPropertyEmptyOrUnknown(Build.DEVICE_FOR_ATTESTATION) 186 ? Build.DEVICE : Build.DEVICE_FOR_ATTESTATION; 187 assertThat(teeAttestation.getDevice()).isEqualTo(platformReportedDevice); 188 final String platformReportedProduct = 189 TestUtils.isPropertyEmptyOrUnknown(Build.PRODUCT_FOR_ATTESTATION) 190 ? Build.PRODUCT : Build.PRODUCT_FOR_ATTESTATION; 191 assertThat(teeAttestation.getProduct()).isEqualTo(platformReportedProduct); 192 final String platformReportedManufacturer = 193 TestUtils.isPropertyEmptyOrUnknown(Build.MANUFACTURER_FOR_ATTESTATION) 194 ? Build.MANUFACTURER : Build.MANUFACTURER_FOR_ATTESTATION; 195 assertThat(teeAttestation.getManufacturer()).isEqualTo(platformReportedManufacturer); 196 final String platformReportedModel = 197 TestUtils.isPropertyEmptyOrUnknown(Build.MODEL_FOR_ATTESTATION) 198 ? Build.MODEL : Build.MODEL_FOR_ATTESTATION; 199 assertThat(teeAttestation.getModel()).isEqualTo(platformReportedModel); 200 assertThat(teeAttestation.getSerialNumber()).isEqualTo(expectedSerial); 201 assertThat(teeAttestation.getImei()).isEqualTo(expectedImei); 202 assertThat(teeAttestation.getMeid()).isEqualTo(expectedMeid); 203 204 validateSecondImei(teeAttestation.getSecondImei(), expectedSecondImei, useStrongbox); 205 } 206 validateSecondImei(String attestedSecondImei, String expectedSecondImei, boolean useStrongbox)207 private void validateSecondImei(String attestedSecondImei, String expectedSecondImei, 208 boolean useStrongbox) { 209 /** 210 * Test attestation support for 2nd IMEI: 211 * * Attestation of 2nd IMEI (if present on the device) is required for devices shipping 212 * with VSR-U (device's first SDK level U and above). 213 * * KeyMint v3 implementations on devices that shipped with earlier VSR, MAY support 214 * attesting to the 2nd IMEI. In that case, if the 2nd IMEI tag is included in the 215 * attestation record, it must match what the platform provided. 216 * * Other KeyMint implementations must not include anything in this tag. 217 */ 218 final boolean isKeyMintV3 = 219 TestUtils.hasKeystoreVersion(useStrongbox, Attestation.KM_VERSION_KEYMINT_3); 220 final boolean emptySecondImei = TextUtils.isEmpty(expectedSecondImei); 221 final boolean deviceShippedWithKeyMint3 = 222 TestUtils.getVendorApiLevel() >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE; 223 224 if (!isKeyMintV3) { 225 // Earlier versions of KeyMint must not attest to second IMEI values as they are not 226 // allowed to emit an attestation extension version that includes it. 227 assertThat(attestedSecondImei).isNull(); 228 } else if (emptySecondImei) { 229 // Device doesn't have a second IMEI, so none should be included in the attestation 230 // extension. 231 assertThat(attestedSecondImei).isNull(); 232 } else if (deviceShippedWithKeyMint3) { 233 // The device has a second IMEI and should attest to it. 234 assertThat(attestedSecondImei).isEqualTo(expectedSecondImei); 235 } else { 236 // Device has KeyMint 3, but originally shipped with an earlier KeyMint and 237 // may not have provisioned the second IMEI as an attestation ID. 238 // It does not have to support attesting to the second IMEI, but if there is something 239 // in the attestation record, it must match the platform-provided second IMEI. 240 if (!TextUtils.isEmpty(attestedSecondImei)) { 241 assertThat(attestedSecondImei).isEqualTo(expectedSecondImei); 242 } 243 } 244 } 245 validateDeviceIdAttestationDataUsingExtLib(Certificate[] certs, String expectedSerial, String expectedImei, String expectedMeid, String expectedSecondImei, boolean useStrongbox)246 private void validateDeviceIdAttestationDataUsingExtLib(Certificate[] certs, 247 String expectedSerial, 248 String expectedImei, 249 String expectedMeid, 250 String expectedSecondImei, 251 boolean useStrongbox) 252 throws CertificateParsingException, IOException { 253 ParsedAttestationRecord parsedAttestationRecord = 254 createParsedAttestationRecord( 255 Arrays.stream(certs) 256 .map(certificate -> (X509Certificate) certificate) 257 .collect(Collectors.toList())); 258 259 com.google.android.attestation.AuthorizationList teeAttestation = 260 parsedAttestationRecord.teeEnforced; 261 262 assertThat(teeAttestation).isNotNull(); 263 final String platformReportedBrand = 264 TestUtils.isPropertyEmptyOrUnknown(Build.BRAND_FOR_ATTESTATION) 265 ? Build.BRAND : Build.BRAND_FOR_ATTESTATION; 266 assertThat(new String(teeAttestation.attestationIdBrand.get())) 267 .isEqualTo(platformReportedBrand); 268 final String platformReportedDevice = 269 TestUtils.isPropertyEmptyOrUnknown(Build.DEVICE_FOR_ATTESTATION) 270 ? Build.DEVICE : Build.DEVICE_FOR_ATTESTATION; 271 assertThat(new String(teeAttestation.attestationIdDevice.get())) 272 .isEqualTo(platformReportedDevice); 273 final String platformReportedProduct = 274 TestUtils.isPropertyEmptyOrUnknown(Build.PRODUCT_FOR_ATTESTATION) 275 ? Build.PRODUCT : Build.PRODUCT_FOR_ATTESTATION; 276 assertThat(new String(teeAttestation.attestationIdProduct.get())) 277 .isEqualTo(platformReportedProduct); 278 final String platformReportedManufacturer = 279 TestUtils.isPropertyEmptyOrUnknown(Build.MANUFACTURER_FOR_ATTESTATION) 280 ? Build.MANUFACTURER : Build.MANUFACTURER_FOR_ATTESTATION; 281 assertThat(new String(teeAttestation.attestationIdManufacturer.get())) 282 .isEqualTo(platformReportedManufacturer); 283 final String platformReportedModel = 284 TestUtils.isPropertyEmptyOrUnknown(Build.MODEL_FOR_ATTESTATION) 285 ? Build.MODEL : Build.MODEL_FOR_ATTESTATION; 286 assertThat(new String(teeAttestation.attestationIdModel.get())) 287 .isEqualTo(platformReportedModel); 288 289 assertThat(!TextUtils.isEmpty(expectedSerial)) 290 .isEqualTo(teeAttestation.attestationIdSerial.isPresent()); 291 if (!TextUtils.isEmpty(expectedSerial)) { 292 assertThat(new String(teeAttestation.attestationIdSerial.get())) 293 .isEqualTo(expectedSerial); 294 } 295 assertThat(!TextUtils.isEmpty(expectedImei)) 296 .isEqualTo(teeAttestation.attestationIdImei.isPresent()); 297 if (!TextUtils.isEmpty(expectedImei)) { 298 assertThat(new String(teeAttestation.attestationIdImei.get())) 299 .isEqualTo(expectedImei); 300 } 301 assertThat(!TextUtils.isEmpty(expectedMeid)) 302 .isEqualTo(teeAttestation.attestationIdMeid.isPresent()); 303 if (!TextUtils.isEmpty(expectedMeid)) { 304 assertThat(new String(teeAttestation.attestationIdMeid.get())) 305 .isEqualTo(expectedMeid); 306 } 307 // We only need to read the second imei from the attestation record if it's available. 308 // All other validations are performed in validateSecondImei function. 309 String attestationIdSecondImei = null; 310 if (teeAttestation.attestationIdSecondImei.isPresent()) { 311 attestationIdSecondImei = new String(teeAttestation.attestationIdSecondImei.get()); 312 } 313 validateSecondImei(attestationIdSecondImei, expectedSecondImei, useStrongbox); 314 } 315 validateAttestationRecord(List<Certificate> attestation, byte[] providedChallenge)316 private void validateAttestationRecord(List<Certificate> attestation, byte[] providedChallenge) 317 throws CertificateParsingException { 318 assertThat(attestation).isNotNull(); 319 assertThat(attestation.size()).isGreaterThan(1); 320 X509Certificate leaf = (X509Certificate) attestation.get(0); 321 Attestation attestationRecord = Attestation.loadFromCertificate(leaf); 322 assertThat(attestationRecord.getAttestationChallenge()).isEqualTo(providedChallenge); 323 } 324 validateSignatureChain(List<Certificate> chain, PublicKey leafKey)325 private void validateSignatureChain(List<Certificate> chain, PublicKey leafKey) 326 throws GeneralSecurityException { 327 X509Certificate leaf = (X509Certificate) chain.get(0); 328 PublicKey keyFromCert = leaf.getPublicKey(); 329 assertThat(keyFromCert.getEncoded()).isEqualTo(leafKey.getEncoded()); 330 // Check that the certificate chain is valid. 331 for (int i = 1; i < chain.size(); i++) { 332 X509Certificate intermediate = (X509Certificate) chain.get(i); 333 PublicKey intermediateKey = intermediate.getPublicKey(); 334 leaf.verify(intermediateKey); 335 leaf = intermediate; 336 } 337 338 // leaf is now the root, verify the root is self-signed. 339 PublicKey rootKey = leaf.getPublicKey(); 340 leaf.verify(rootKey); 341 } 342 isDeviceIdAttestationSupported()343 private boolean isDeviceIdAttestationSupported() { 344 return sDevicePolicyManager.isDeviceIdAttestationSupported(); 345 } 346 isDeviceIdAttestationRequested(int deviceIdAttestationFlags)347 private boolean isDeviceIdAttestationRequested(int deviceIdAttestationFlags) { 348 return deviceIdAttestationFlags != 0; 349 } 350 351 /** 352 * Generates a key using DevicePolicyManager.generateKeyPair using the given key algorithm, 353 * then test signing and verifying using generated key. 354 * If {@code signaturePaddings} is not null, it is added to the key parameters specification. 355 * Returns the Attestation leaf certificate. 356 */ generateKeyAndCheckAttestation( String keyAlgorithm, String signatureAlgorithm, String[] signaturePaddings, boolean useStrongBox, int deviceIdAttestationFlags)357 private Certificate[] generateKeyAndCheckAttestation( 358 String keyAlgorithm, String signatureAlgorithm, 359 String[] signaturePaddings, boolean useStrongBox, 360 int deviceIdAttestationFlags) throws Exception { 361 final String alias = 362 String.format("com.android.test.attested-%s", keyAlgorithm.toLowerCase()); 363 byte[] attestationChallenge = new byte[] {0x01, 0x02, 0x03}; 364 try { 365 KeyGenParameterSpec.Builder specBuilder = new KeyGenParameterSpec.Builder( 366 alias, 367 KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY) 368 .setDigests(KeyProperties.DIGEST_SHA256) 369 .setAttestationChallenge(attestationChallenge) 370 .setIsStrongBoxBacked(useStrongBox); 371 if (signaturePaddings != null) { 372 specBuilder.setSignaturePaddings(signaturePaddings); 373 } 374 375 KeyGenParameterSpec spec = specBuilder.build(); 376 AttestedKeyPair generated = sDevicePolicyManager.generateKeyPair( 377 DEVICE_ADMIN_COMPONENT_NAME, keyAlgorithm, spec, deviceIdAttestationFlags); 378 // If Device ID attestation was requested, check it succeeded if and only if device ID 379 // attestation is supported. 380 if (isDeviceIdAttestationRequested(deviceIdAttestationFlags)) { 381 if (generated == null) { 382 assertWithMessage( 383 String.format( 384 "The device failed ID attestation, despite declaring it support for" 385 + "the feature. This is a hardware-related failure that should be " 386 + "analyzed by the OEM. The failure was for algorithm %s with flags" 387 + " %s.", 388 keyAlgorithm, deviceIdAttestationFlags)) 389 .that(isDeviceIdAttestationSupported()) 390 .isFalse(); 391 return null; 392 } else { 393 assertWithMessage( 394 String.format( 395 "The device declares it does not support ID attestation yet it " 396 + "produced a valid ID attestation record (for algorithm %s, flags" 397 + " %s). This is a device configuration issue that should be " 398 + "analyzed by the OEM first", 399 keyAlgorithm, deviceIdAttestationFlags)) 400 .that(isDeviceIdAttestationSupported()) 401 .isTrue(); 402 } 403 } else { 404 assertWithMessage( 405 String.format( 406 "The device failed to generate a key with attestation that does not" 407 + " include Device ID attestation. This is a hardware failure that " 408 + "is usually caused by attestation keys not being provisioned on " 409 + "the device, and the OEM needs to analyze the underlying cause." 410 + " Algorithm used: %s", 411 keyAlgorithm)) 412 .that(generated) 413 .isNotNull(); 414 } 415 final KeyPair keyPair = generated.getKeyPair(); 416 verifySignatureOverData(signatureAlgorithm, keyPair); 417 List<Certificate> attestation = generated.getAttestationRecord(); 418 validateAttestationRecord(attestation, attestationChallenge); 419 validateSignatureChain(attestation, keyPair.getPublic()); 420 return attestation.toArray(new Certificate[0]); 421 } catch (UnsupportedOperationException ex) { 422 assertWithMessage( 423 String.format( 424 "Unexpected failure while generating key %s with ID flags %d: %s" 425 + "\nIn case of AOSP/GSI builds, system provided properties could be" 426 + " different from provisioned properties in KeyMaster/KeyMint. In" 427 + " such cases, make sure attestation specific properties" 428 + " (Build.*_FOR_ATTESTATION) are configured correctly.", 429 keyAlgorithm, deviceIdAttestationFlags, ex)) 430 .that( 431 isDeviceIdAttestationRequested(deviceIdAttestationFlags) 432 && !isDeviceIdAttestationSupported()) 433 .isTrue(); 434 return null; 435 } finally { 436 assertThat(sDevicePolicyManager.removeKeyPair(DEVICE_ADMIN_COMPONENT_NAME, alias)) 437 .isTrue(); 438 } 439 } 440 assertAllVariantsOfDeviceIdAttestation(boolean useStrongBox)441 public void assertAllVariantsOfDeviceIdAttestation(boolean useStrongBox) throws Exception { 442 List<Integer> modesToTest = new ArrayList<Integer>(); 443 String imei = null; 444 String secondImei = null; 445 String meid = null; 446 // All devices must support at least basic device information attestation as well as serial 447 // number attestation. Although attestation of unique device ids are only callable by 448 // device owner. 449 modesToTest.add(ID_TYPE_BASE_INFO); 450 modesToTest.add(ID_TYPE_SERIAL); 451 // Get IMEI and MEID of the device. 452 try (PermissionContext c = TestApis.permissions().withPermission( 453 "android.permission.READ_PHONE_STATE")) { 454 TelephonyManager telephonyService = sContext.getSystemService(TelephonyManager.class); 455 assertWithMessage("Need to be able to read device identifiers") 456 .that(telephonyService) 457 .isNotNull(); 458 imei = telephonyService.getImei(0); 459 secondImei = telephonyService.getImei(1); 460 if (sContext.getPackageManager() 461 .hasSystemFeature(PackageManager.FEATURE_TELEPHONY_CDMA)) { 462 meid = telephonyService.getMeid(0); 463 } 464 // If the device has a valid IMEI it must support attestation for it. 465 if (imei != null) { 466 modesToTest.add(ID_TYPE_IMEI); 467 } 468 // Same for MEID 469 if (meid != null) { 470 modesToTest.add(ID_TYPE_MEID); 471 } 472 int numCombinations = 1 << modesToTest.size(); 473 for (int i = 1; i < numCombinations; i++) { 474 // Set the bits in devIdOpt to be passed into generateKeyPair according to the 475 // current modes tested. 476 int devIdOpt = 0; 477 for (int j = 0; j < modesToTest.size(); j++) { 478 if ((i & (1 << j)) != 0) { 479 devIdOpt = devIdOpt | modesToTest.get(j); 480 } 481 } 482 try { 483 // Now run the test with all supported key algorithms 484 for (SupportedKeyAlgorithm supportedKey: SUPPORTED_KEY_ALGORITHMS) { 485 Certificate[] attestation = generateKeyAndCheckAttestation( 486 supportedKey.keyAlgorithm, supportedKey.signatureAlgorithm, 487 supportedKey.signaturePaddingSchemes, useStrongBox, devIdOpt); 488 // generateKeyAndCheckAttestation should return null if device ID 489 // attestation is not supported. Simply continue to test the next 490 // combination. 491 if (attestation == null && !isDeviceIdAttestationSupported()) { 492 continue; 493 } 494 assertWithMessage( 495 String.format( 496 "Attestation should be valid for key %s with attestation" 497 + " modes %s", 498 supportedKey.keyAlgorithm, devIdOpt)) 499 .that(attestation) 500 .isNotNull(); 501 // Set the expected values for serial, IMEI and MEID depending on whether 502 // attestation for them was requested. 503 String expectedSerial = null; 504 if ((devIdOpt & ID_TYPE_SERIAL) != 0) { 505 expectedSerial = Build.getSerial(); 506 } 507 String expectedImei = null; 508 if ((devIdOpt & ID_TYPE_IMEI) != 0) { 509 expectedImei = imei; 510 } 511 String expectedMeid = null; 512 if ((devIdOpt & ID_TYPE_MEID) != 0) { 513 expectedMeid = meid; 514 } 515 String expectedSecondImei = null; 516 if ((devIdOpt & ID_TYPE_IMEI) != 0) { 517 expectedSecondImei = secondImei; 518 } 519 validateDeviceIdAttestationData(attestation, expectedSerial, 520 expectedImei, expectedMeid, expectedSecondImei, useStrongBox); 521 // Validate attestation record using external library. As above validation 522 // is successful external library validation should also pass. 523 validateDeviceIdAttestationDataUsingExtLib(attestation, expectedSerial, 524 expectedImei, expectedMeid, expectedSecondImei, useStrongBox); 525 } 526 } catch (UnsupportedOperationException expected) { 527 // Make sure the test only fails if the device is not meant to support Device 528 // ID attestation. 529 assertThat(isDeviceIdAttestationSupported()).isFalse(); 530 } catch (StrongBoxUnavailableException expected) { 531 // This exception must only be thrown if StrongBox attestation was requested. 532 assertThat(useStrongBox && !TestUtils.hasStrongBox(sContext)).isTrue(); 533 } 534 } 535 } 536 } 537 checkRootOfTrustForLockedDevice(Attestation attestation)538 private boolean checkRootOfTrustForLockedDevice(Attestation attestation) { 539 RootOfTrust rootOfTrust = attestation.getRootOfTrust(); 540 assertNotNull(rootOfTrust); 541 assertNotNull(rootOfTrust.getVerifiedBootKey()); 542 assertTrue("Verified boot key is only " + rootOfTrust.getVerifiedBootKey().length 543 + " bytes long", rootOfTrust.getVerifiedBootKey().length >= 32); 544 return (rootOfTrust.isDeviceLocked() 545 && (rootOfTrust.getVerifiedBootState() == KM_VERIFIED_BOOT_VERIFIED)); 546 } 547 isDeviceLockedAccordingToAttestation(Attestation attestation)548 private boolean isDeviceLockedAccordingToAttestation(Attestation attestation) { 549 assertThat("Attestation version must be >= 1", 550 attestation.getAttestationVersion(), greaterThanOrEqualTo(1)); 551 552 int attestationSecurityLevel = attestation.getAttestationSecurityLevel(); 553 switch (attestationSecurityLevel) { 554 case KM_SECURITY_LEVEL_STRONG_BOX: 555 case KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT: 556 assertThat("Attestation security level doesn't match keymaster security level", 557 attestation.getKeymasterSecurityLevel(), is(attestationSecurityLevel)); 558 assertThat("Keymaster version should be greater than or equal to 2.", 559 attestation.getKeymasterVersion(), greaterThanOrEqualTo(2)); 560 561 return checkRootOfTrustForLockedDevice(attestation); 562 case KM_SECURITY_LEVEL_SOFTWARE: 563 default: 564 // TEE attestation has been required since Android 7.0. 565 fail("Unexpected attestation security level: " 566 + attestation.securityLevelToString(attestationSecurityLevel)); 567 break; 568 } 569 return false; 570 } 571 checkDeviceLocked()572 private boolean checkDeviceLocked() throws Exception { 573 String keystoreAlias = "check_device_state"; 574 KeyGenParameterSpec.Builder builder = 575 new KeyGenParameterSpec.Builder(keystoreAlias, PURPOSE_SIGN) 576 .setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1")) 577 .setAttestationChallenge(new byte[128]) 578 .setDigests(DIGEST_NONE, DIGEST_SHA256, DIGEST_SHA512); 579 580 KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM_EC, 581 "AndroidKeyStore"); 582 keyPairGenerator.initialize(builder.build()); 583 keyPairGenerator.generateKeyPair(); 584 KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); 585 keyStore.load(null); 586 587 try { 588 Certificate []certificates = keyStore.getCertificateChain(keystoreAlias); 589 verifyCertificateChain(certificates, false); 590 591 X509Certificate attestationCert = (X509Certificate) certificates[0]; 592 593 return isDeviceLockedAccordingToAttestation( 594 Attestation.loadFromCertificate(attestationCert)); 595 } finally { 596 keyStore.deleteEntry(keystoreAlias); 597 } 598 } 599 600 @Test 601 @ApiTest(apis = {"android.app.admin.DevicePolicyManager#generateKeyPair", 602 "android.app.admin.DevicePolicyManager#ID_TYPE_IMEI", 603 "android.app.admin.DevicePolicyManager#ID_TYPE_MEID", 604 "android.app.admin.DevicePolicyManager#ID_TYPE_SERIAL"}) 605 @RequireRunOnSystemUser 606 @RequireFeature(PackageManager.FEATURE_DEVICE_ADMIN) 607 @RequireFeature(PackageManager.FEATURE_DEVICE_ID_ATTESTATION) testAllVariationsOfDeviceIdAttestation()608 public void testAllVariationsOfDeviceIdAttestation() throws Exception { 609 // b/298586194, there are some devices launched with Android T, and they will be receiving 610 // only system update and not vendor update, newly added attestation properties 611 // (ro.product.*_for_attestation) reading logic would not be available for such devices 612 // hence skipping this test for such scenario. 613 assumeFalse("This test is not applicable for device running GSI image and" 614 + " first_api_level < 14", TestUtils.isGsiImage() 615 && TestUtils.getVendorApiLevel() < Build.VERSION_CODES.UPSIDE_DOWN_CAKE); 616 617 final boolean isDeviceLocked = checkDeviceLocked(); 618 try (DeviceOwner o = TestApis.devicePolicy().setDeviceOwner(DEVICE_ADMIN_COMPONENT_NAME)) { 619 assertAllVariantsOfDeviceIdAttestation(false /* useStrongBox */); 620 } catch (NeneException e) { 621 // b/291069162, some devices do not allow to set DeviceOwner in an unlocked state. And 622 // unlocked state is allowed while testing on GSI image. If this condition not match 623 // throw the exception. 624 assumeFalse("It is acceptable to fail setting DeviceOwner on GSI build running on" 625 + " unlocked devices.", (e.getMessage().contains("Error setting device owner") 626 && TestUtils.isGsiImage() && !isDeviceLocked)); 627 throw new Exception(e); 628 } 629 } 630 631 @Test 632 @ApiTest(apis = {"android.app.admin.DevicePolicyManager#generateKeyPair", 633 "android.app.admin.DevicePolicyManager#ID_TYPE_IMEI", 634 "android.app.admin.DevicePolicyManager#ID_TYPE_MEID", 635 "android.app.admin.DevicePolicyManager#ID_TYPE_SERIAL"}) 636 @RequireRunOnSystemUser 637 @RequireFeature(PackageManager.FEATURE_DEVICE_ADMIN) 638 @RequireFeature(PackageManager.FEATURE_DEVICE_ID_ATTESTATION) testAllVariationsOfDeviceIdAttestationUsingStrongBox()639 public void testAllVariationsOfDeviceIdAttestationUsingStrongBox() throws Exception { 640 // b/298586194, there are some devices launched with Android T, and they will be receiving 641 // only system update and not vendor update, newly added attestation properties 642 // (ro.product.*_for_attestation) reading logic would not be available for such devices 643 // hence skipping this test for such scenario. 644 assumeFalse("This test is not applicable for device running GSI image and" 645 + " first_api_level < 14", TestUtils.isGsiImage() 646 && TestUtils.getVendorApiLevel() < Build.VERSION_CODES.UPSIDE_DOWN_CAKE); 647 648 final boolean isDeviceLocked = checkDeviceLocked(); 649 try (DeviceOwner o = TestApis.devicePolicy().setDeviceOwner(DEVICE_ADMIN_COMPONENT_NAME)) { 650 assertAllVariantsOfDeviceIdAttestation(true /* useStrongBox */); 651 } catch (NeneException e) { 652 // b/291069162, some devices do not allow to set DeviceOwner in an unlocked state. And 653 // unlocked state is allowed while testing on GSI image. If this condition not match 654 // throw the exception. 655 assumeFalse("It is acceptable to fail setting DeviceOwner on GSI build running on" 656 + " unlocked devices.", (e.getMessage().contains("Error setting device owner") 657 && TestUtils.isGsiImage() && !isDeviceLocked)); 658 throw new Exception(e); 659 } 660 } 661 } 662