1 /* 2 * Copyright (C) 2014 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 package com.android.cts.deviceandprofileowner; 17 18 import static android.app.admin.DevicePolicyManager.ID_TYPE_BASE_INFO; 19 import static android.app.admin.DevicePolicyManager.ID_TYPE_IMEI; 20 import static android.app.admin.DevicePolicyManager.ID_TYPE_INDIVIDUAL_ATTESTATION; 21 import static android.app.admin.DevicePolicyManager.ID_TYPE_MEID; 22 import static android.app.admin.DevicePolicyManager.ID_TYPE_SERIAL; 23 import static android.content.pm.PackageManager.FEATURE_TELEPHONY_CDMA; 24 import static android.keystore.cts.CertificateUtils.createCertificate; 25 26 import static com.google.common.truth.Truth.assertThat; 27 import static com.google.common.truth.Truth.assertWithMessage; 28 29 import static java.util.Collections.singleton; 30 31 import android.content.ComponentName; 32 import android.content.Context; 33 import android.content.pm.PackageManager; 34 import android.content.res.AssetManager; 35 import android.keystore.cts.Attestation; 36 import android.keystore.cts.AuthorizationList; 37 import android.keystore.cts.util.TestUtils; 38 import android.net.Uri; 39 import android.os.Build; 40 import android.os.Process; 41 import android.security.AttestedKeyPair; 42 import android.security.KeyChain; 43 import android.security.KeyChainAliasCallback; 44 import android.security.KeyChainException; 45 import android.security.keystore.KeyGenParameterSpec; 46 import android.security.keystore.KeyProperties; 47 import android.security.keystore.StrongBoxUnavailableException; 48 import android.telephony.TelephonyManager; 49 50 import androidx.test.uiautomator.UiDevice; 51 52 import com.android.compatibility.common.util.FakeKeys.FAKE_RSA_1; 53 54 import java.io.ByteArrayInputStream; 55 import java.io.ByteArrayOutputStream; 56 import java.io.IOException; 57 import java.io.InputStream; 58 import java.io.UnsupportedEncodingException; 59 import java.net.URLEncoder; 60 import java.security.GeneralSecurityException; 61 import java.security.KeyFactory; 62 import java.security.KeyPair; 63 import java.security.NoSuchAlgorithmException; 64 import java.security.PrivateKey; 65 import java.security.PublicKey; 66 import java.security.Signature; 67 import java.security.cert.Certificate; 68 import java.security.cert.CertificateException; 69 import java.security.cert.CertificateFactory; 70 import java.security.cert.CertificateParsingException; 71 import java.security.cert.X509Certificate; 72 import java.security.spec.InvalidKeySpecException; 73 import java.security.spec.PKCS8EncodedKeySpec; 74 import java.util.ArrayList; 75 import java.util.Collection; 76 import java.util.List; 77 import java.util.Map; 78 import java.util.Set; 79 import java.util.concurrent.CountDownLatch; 80 import java.util.concurrent.TimeUnit; 81 82 import javax.security.auth.x500.X500Principal; 83 84 public class KeyManagementTest extends BaseDeviceAdminTest { 85 private static final long KEYCHAIN_TIMEOUT_MINS = 6; 86 87 private static final String TEST_ALIAS = "KeyManagementTest-keypair"; 88 89 private static final String SHARED_UID_APP1_PKG = "com.android.cts.testapps.shareduidapp1"; 90 private static final String SHARED_UID_APP2_PKG = "com.android.cts.testapps.shareduidapp2"; 91 92 private PrivateKey mFakePrivKey; 93 private Certificate mFakeCert; 94 95 private static class SupportedKeyAlgorithm { 96 public final String keyAlgorithm; 97 public final String signatureAlgorithm; 98 public final String[] signaturePaddingSchemes; 99 SupportedKeyAlgorithm( String keyAlgorithm, String signatureAlgorithm, String[] signaturePaddingSchemes)100 public SupportedKeyAlgorithm( 101 String keyAlgorithm, String signatureAlgorithm, 102 String[] signaturePaddingSchemes) { 103 this.keyAlgorithm = keyAlgorithm; 104 this.signatureAlgorithm = signatureAlgorithm; 105 this.signaturePaddingSchemes = signaturePaddingSchemes; 106 } 107 } 108 109 private final SupportedKeyAlgorithm[] SUPPORTED_KEY_ALGORITHMS = new SupportedKeyAlgorithm[] { 110 new SupportedKeyAlgorithm(KeyProperties.KEY_ALGORITHM_RSA, "SHA256withRSA", 111 new String[] {KeyProperties.SIGNATURE_PADDING_RSA_PSS, 112 KeyProperties.SIGNATURE_PADDING_RSA_PKCS1}), 113 new SupportedKeyAlgorithm(KeyProperties.KEY_ALGORITHM_EC, "SHA256withECDSA", null) 114 }; 115 116 private KeyManagementActivity mActivity; 117 118 @Override setUp()119 public void setUp() throws Exception { 120 super.setUp(); 121 122 mFakePrivKey = getPrivateKey(FAKE_RSA_1.privateKey, "RSA"); 123 mFakeCert = getCertificate(FAKE_RSA_1.caCertificate); 124 125 final UiDevice device = UiDevice.getInstance(getInstrumentation()); 126 mActivity = launchActivity(getInstrumentation().getTargetContext().getPackageName(), 127 KeyManagementActivity.class, null); 128 device.waitForIdle(); 129 } 130 131 @Override tearDown()132 public void tearDown() throws Exception { 133 mActivity.finish(); 134 mDevicePolicyManager.removeKeyPair(getWho(), TEST_ALIAS); 135 super.tearDown(); 136 } 137 138 // TODO(b/204544463): Remove when installKeyPair_withAutomatedAccess_aliasIsGranted is enabled testCanInstallWithAutomaticAccess()139 public void testCanInstallWithAutomaticAccess() throws Exception { 140 final String grant = "com.android.test.autogrant-key-1"; 141 142 // Install keypair. 143 assertThat( 144 mDevicePolicyManager.installKeyPair( 145 getWho(), mFakePrivKey, new Certificate[] {mFakeCert}, grant, true)) 146 .isTrue(); 147 try { 148 // Verify the requested key was actually granted. 149 assertGranted(grant, true); 150 151 // Verify the granted key is actually obtainable in PrivateKey form. 152 assertThat(KeyChain.getPrivateKey(mActivity, grant).getAlgorithm()).isEqualTo("RSA"); 153 } finally { 154 // Delete the keypair. 155 assertThat(mDevicePolicyManager.removeKeyPair(getWho(), grant)).isTrue(); 156 } 157 // Verify it's actually gone. 158 assertGranted(grant, false); 159 } 160 loadCertificateChain(String assetName)161 private List<Certificate> loadCertificateChain(String assetName) throws Exception { 162 final Collection<Certificate> certs = loadCertificatesFromAsset(assetName); 163 final ArrayList<Certificate> certChain = new ArrayList(certs); 164 // Check the cert chain 165 assertThat(certs.size()).isGreaterThan(1); 166 for (int i = 1; i < certChain.size(); i++) { 167 certChain.get(i - 1).verify(certChain.get(i).getPublicKey()); 168 } 169 return certChain; 170 } 171 signDataWithKey(String algoIdentifier, PrivateKey privateKey)172 byte[] signDataWithKey(String algoIdentifier, PrivateKey privateKey) throws Exception { 173 byte[] data = new String("hello").getBytes(); 174 Signature sign = Signature.getInstance(algoIdentifier); 175 sign.initSign(privateKey); 176 sign.update(data); 177 return sign.sign(); 178 } 179 verifySignature(String algoIdentifier, PublicKey publicKey, byte[] signature)180 void verifySignature(String algoIdentifier, PublicKey publicKey, byte[] signature) 181 throws Exception { 182 byte[] data = new String("hello").getBytes(); 183 Signature verify = Signature.getInstance(algoIdentifier); 184 verify.initVerify(publicKey); 185 verify.update(data); 186 assertThat(verify.verify(signature)).isTrue(); 187 } 188 verifySignatureOverData(String algoIdentifier, KeyPair keyPair)189 void verifySignatureOverData(String algoIdentifier, KeyPair keyPair) throws Exception { 190 verifySignature(algoIdentifier, keyPair.getPublic(), 191 signDataWithKey(algoIdentifier, keyPair.getPrivate())); 192 } 193 buildRsaKeySpec(String alias, boolean useStrongBox)194 private KeyGenParameterSpec buildRsaKeySpec(String alias, boolean useStrongBox) { 195 return new KeyGenParameterSpec.Builder( 196 alias, 197 KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY) 198 .setKeySize(2048) 199 .setDigests(KeyProperties.DIGEST_SHA256) 200 .setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PSS, 201 KeyProperties.SIGNATURE_PADDING_RSA_PKCS1) 202 .setIsStrongBoxBacked(useStrongBox) 203 .build(); 204 } 205 testCanGenerateRSAKeyPair()206 public void testCanGenerateRSAKeyPair() throws Exception { 207 final String alias = "com.android.test.generated-rsa-1"; 208 try { 209 AttestedKeyPair generated = mDevicePolicyManager.generateKeyPair( 210 getWho(), "RSA", buildRsaKeySpec(alias, false /* useStrongBox */), 0); 211 assertThat(generated).isNotNull(); 212 verifySignatureOverData("SHA256withRSA", generated.getKeyPair()); 213 } finally { 214 assertThat(mDevicePolicyManager.removeKeyPair(getWho(), alias)).isTrue(); 215 } 216 } 217 218 // TODO(b/198408853): Migrate testCanGenerateRSAKeyPairUsingStrongBox()219 public void testCanGenerateRSAKeyPairUsingStrongBox() throws Exception { 220 final String alias = "com.android.test.generated-rsa-sb-1"; 221 try { 222 AttestedKeyPair generated = mDevicePolicyManager.generateKeyPair( 223 getWho(), "RSA", buildRsaKeySpec(alias, true /* useStrongBox */), 0); 224 verifySignatureOverData("SHA256withRSA", generated.getKeyPair()); 225 assertThat(mDevicePolicyManager.removeKeyPair(getWho(), alias)).isTrue(); 226 } catch (StrongBoxUnavailableException expected) { 227 assertThat(hasStrongBox()).isFalse(); 228 } 229 } 230 buildEcKeySpec(String alias, boolean useStrongBox)231 private KeyGenParameterSpec buildEcKeySpec(String alias, boolean useStrongBox) { 232 return new KeyGenParameterSpec.Builder( 233 alias, 234 KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY) 235 .setDigests(KeyProperties.DIGEST_SHA256) 236 .setIsStrongBoxBacked(useStrongBox) 237 .build(); 238 } 239 240 // TODO(b/198408853): Migrate testCanGenerateECKeyPair()241 public void testCanGenerateECKeyPair() throws Exception { 242 final String alias = "com.android.test.generated-ec-1"; 243 try { 244 AttestedKeyPair generated = mDevicePolicyManager.generateKeyPair( 245 getWho(), "EC", buildEcKeySpec(alias, false /* useStrongBox */), 0); 246 assertThat(generated).isNotNull(); 247 verifySignatureOverData("SHA256withECDSA", generated.getKeyPair()); 248 } finally { 249 assertThat(mDevicePolicyManager.removeKeyPair(getWho(), alias)).isTrue(); 250 } 251 } 252 253 // TODO(b/198408853): Migrate testCanGenerateECKeyPairUsingStrongBox()254 public void testCanGenerateECKeyPairUsingStrongBox() throws Exception { 255 final String alias = "com.android.test.generated-ec-sb-1"; 256 try { 257 AttestedKeyPair generated = mDevicePolicyManager.generateKeyPair( 258 getWho(), "EC", buildEcKeySpec(alias, true /* useStrongBox */), 0); 259 verifySignatureOverData("SHA256withECDSA", generated.getKeyPair()); 260 assertThat(mDevicePolicyManager.removeKeyPair(getWho(), alias)).isTrue(); 261 } catch (StrongBoxUnavailableException expected) { 262 assertThat(hasStrongBox()).isFalse(); 263 } 264 } 265 validateDeviceIdAttestationData(Certificate leaf, String expectedSerial, String expectedImei, String expectedMeid)266 private void validateDeviceIdAttestationData(Certificate leaf, 267 String expectedSerial, String expectedImei, String expectedMeid) 268 throws CertificateParsingException { 269 Attestation attestationRecord = Attestation.loadFromCertificate((X509Certificate) leaf); 270 AuthorizationList teeAttestation = attestationRecord.getTeeEnforced(); 271 assertThat(teeAttestation).isNotNull(); 272 final String platformReportedBrand = 273 TestUtils.isPropertyEmptyOrUnknown(Build.BRAND_FOR_ATTESTATION) 274 ? Build.BRAND : Build.BRAND_FOR_ATTESTATION; 275 assertThat(teeAttestation.getBrand()).isEqualTo(platformReportedBrand); 276 final String platformReportedDevice = 277 TestUtils.isPropertyEmptyOrUnknown(Build.DEVICE_FOR_ATTESTATION) 278 ? Build.DEVICE : Build.DEVICE_FOR_ATTESTATION; 279 assertThat(teeAttestation.getDevice()).isEqualTo(platformReportedDevice); 280 final String platformReportedProduct = 281 TestUtils.isPropertyEmptyOrUnknown(Build.PRODUCT_FOR_ATTESTATION) 282 ? Build.PRODUCT : Build.PRODUCT_FOR_ATTESTATION; 283 assertThat(teeAttestation.getProduct()).isEqualTo(platformReportedProduct); 284 final String platformReportedManufacturer = 285 TestUtils.isPropertyEmptyOrUnknown(Build.MANUFACTURER_FOR_ATTESTATION) 286 ? Build.MANUFACTURER : Build.MANUFACTURER_FOR_ATTESTATION; 287 assertThat(teeAttestation.getManufacturer()).isEqualTo(platformReportedManufacturer); 288 final String platformReportedModel = 289 TestUtils.isPropertyEmptyOrUnknown(Build.MODEL_FOR_ATTESTATION) 290 ? Build.MODEL : Build.MODEL_FOR_ATTESTATION; 291 assertThat(teeAttestation.getModel()).isEqualTo(platformReportedModel); 292 assertThat(teeAttestation.getSerialNumber()).isEqualTo(expectedSerial); 293 assertThat(teeAttestation.getImei()).isEqualTo(expectedImei); 294 assertThat(teeAttestation.getMeid()).isEqualTo(expectedMeid); 295 } 296 validateAttestationRecord(List<Certificate> attestation, byte[] providedChallenge)297 private void validateAttestationRecord(List<Certificate> attestation, byte[] providedChallenge) 298 throws CertificateParsingException { 299 assertThat(attestation).isNotNull(); 300 assertThat(attestation.size()).isGreaterThan(1); 301 X509Certificate leaf = (X509Certificate) attestation.get(0); 302 Attestation attestationRecord = Attestation.loadFromCertificate(leaf); 303 assertThat(attestationRecord.getAttestationChallenge()).isEqualTo(providedChallenge); 304 } 305 validateSignatureChain(List<Certificate> chain, PublicKey leafKey)306 private void validateSignatureChain(List<Certificate> chain, PublicKey leafKey) 307 throws GeneralSecurityException { 308 X509Certificate leaf = (X509Certificate) chain.get(0); 309 PublicKey keyFromCert = leaf.getPublicKey(); 310 assertThat(keyFromCert.getEncoded()).isEqualTo(leafKey.getEncoded()); 311 // Check that the certificate chain is valid. 312 for (int i = 1; i < chain.size(); i++) { 313 X509Certificate intermediate = (X509Certificate) chain.get(i); 314 PublicKey intermediateKey = intermediate.getPublicKey(); 315 leaf.verify(intermediateKey); 316 leaf = intermediate; 317 } 318 319 // leaf is now the root, verify the root is self-signed. 320 PublicKey rootKey = leaf.getPublicKey(); 321 leaf.verify(rootKey); 322 } 323 isDeviceIdAttestationSupported()324 private boolean isDeviceIdAttestationSupported() { 325 return mDevicePolicyManager.isDeviceIdAttestationSupported(); 326 } 327 isDeviceIdAttestationRequested(int deviceIdAttestationFlags)328 private boolean isDeviceIdAttestationRequested(int deviceIdAttestationFlags) { 329 return deviceIdAttestationFlags != 0; 330 } 331 332 /** 333 * Generates a key using DevicePolicyManager.generateKeyPair using the given key algorithm, 334 * then test signing and verifying using generated key. 335 * If {@code signaturePaddings} is not null, it is added to the key parameters specification. 336 * Returns the Attestation leaf certificate. 337 */ generateKeyAndCheckAttestation( String keyAlgorithm, String signatureAlgorithm, String[] signaturePaddings, boolean useStrongBox, int deviceIdAttestationFlags)338 private Certificate generateKeyAndCheckAttestation( 339 String keyAlgorithm, String signatureAlgorithm, 340 String[] signaturePaddings, boolean useStrongBox, 341 int deviceIdAttestationFlags) throws Exception { 342 final String alias = 343 String.format("com.android.test.attested-%s", keyAlgorithm.toLowerCase()); 344 byte[] attestationChallenge = new byte[] {0x01, 0x02, 0x03}; 345 try { 346 KeyGenParameterSpec.Builder specBuilder = new KeyGenParameterSpec.Builder( 347 alias, 348 KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY) 349 .setDigests(KeyProperties.DIGEST_SHA256) 350 .setAttestationChallenge(attestationChallenge) 351 .setIsStrongBoxBacked(useStrongBox); 352 if (signaturePaddings != null) { 353 specBuilder.setSignaturePaddings(signaturePaddings); 354 } 355 356 KeyGenParameterSpec spec = specBuilder.build(); 357 AttestedKeyPair generated = mDevicePolicyManager.generateKeyPair( 358 getWho(), keyAlgorithm, spec, deviceIdAttestationFlags); 359 // If Device ID attestation was requested, check it succeeded if and only if device ID 360 // attestation is supported. 361 if (isDeviceIdAttestationRequested(deviceIdAttestationFlags)) { 362 if (generated == null) { 363 assertWithMessage( 364 String.format( 365 "Failed getting Device ID attestation for key algorithm" 366 + " %s, with flags %s, despite device declaring" 367 + " support.", 368 keyAlgorithm, deviceIdAttestationFlags)) 369 .that(isDeviceIdAttestationSupported()) 370 .isFalse(); 371 return null; 372 } else { 373 assertWithMessage( 374 String.format( 375 "Device ID attestation for key " 376 + "algorithm %s, with flags %d should not have succeeded.", 377 keyAlgorithm, deviceIdAttestationFlags)) 378 .that(isDeviceIdAttestationSupported()) 379 .isTrue(); 380 } 381 } else { 382 assertWithMessage( 383 String.format( 384 "Key generation (of type %s) must succeed when Device ID " 385 + "attestation was not requested.", 386 keyAlgorithm)) 387 .that(generated) 388 .isNotNull(); 389 } 390 final KeyPair keyPair = generated.getKeyPair(); 391 verifySignatureOverData(signatureAlgorithm, keyPair); 392 List<Certificate> attestation = generated.getAttestationRecord(); 393 validateAttestationRecord(attestation, attestationChallenge); 394 validateSignatureChain(attestation, keyPair.getPublic()); 395 return attestation.get(0); 396 } catch (UnsupportedOperationException ex) { 397 assertWithMessage( 398 String.format( 399 "Unexpected failure while generating key %s with ID flags %d: %s", 400 keyAlgorithm, deviceIdAttestationFlags, ex)) 401 .that( 402 isDeviceIdAttestationRequested(deviceIdAttestationFlags) 403 && !isDeviceIdAttestationSupported()) 404 .isTrue(); 405 return null; 406 } finally { 407 assertThat(mDevicePolicyManager.removeKeyPair(getWho(), alias)).isTrue(); 408 } 409 } 410 411 /** 412 * Test key generation, including requesting Key Attestation, for all supported key 413 * algorithms. 414 */ 415 // TODO(b/198408853): Migrate testCanGenerateKeyPairWithKeyAttestation()416 public void testCanGenerateKeyPairWithKeyAttestation() throws Exception { 417 if (!isAttestationSupported()) { 418 return; 419 } 420 for (SupportedKeyAlgorithm supportedKey : SUPPORTED_KEY_ALGORITHMS) { 421 assertThat( 422 generateKeyAndCheckAttestation( 423 supportedKey.keyAlgorithm, 424 supportedKey.signatureAlgorithm, 425 supportedKey.signaturePaddingSchemes, 426 false /* useStrongBox */, 427 0)) 428 .isNotNull(); 429 } 430 } 431 432 // TODO(b/198408853): Migrate testCanGenerateKeyPairWithKeyAttestationUsingStrongBox()433 public void testCanGenerateKeyPairWithKeyAttestationUsingStrongBox() throws Exception { 434 try { 435 for (SupportedKeyAlgorithm supportedKey : SUPPORTED_KEY_ALGORITHMS) { 436 assertThat( 437 generateKeyAndCheckAttestation( 438 supportedKey.keyAlgorithm, 439 supportedKey.signatureAlgorithm, 440 supportedKey.signaturePaddingSchemes, 441 true /* useStrongBox */, 442 0)) 443 .isNotNull(); 444 } 445 } catch (StrongBoxUnavailableException expected) { 446 assertThat(hasStrongBox()).isFalse(); 447 } 448 } 449 assertAllVariantsOfDeviceIdAttestation(boolean useStrongBox)450 public void assertAllVariantsOfDeviceIdAttestation(boolean useStrongBox) throws Exception { 451 List<Integer> modesToTest = new ArrayList<Integer>(); 452 String imei = null; 453 String meid = null; 454 // All devices must support at least basic device information attestation as well as serial 455 // number attestation. Although attestation of unique device ids are only callable by device 456 // owner. 457 modesToTest.add(ID_TYPE_BASE_INFO); 458 if (isDeviceOwner()) { 459 modesToTest.add(ID_TYPE_SERIAL); 460 // Get IMEI and MEID of the device. 461 TelephonyManager telephonyService = 462 (TelephonyManager) mActivity.getSystemService(Context.TELEPHONY_SERVICE); 463 assertWithMessage("Need to be able to read device identifiers") 464 .that(telephonyService) 465 .isNotNull(); 466 imei = telephonyService.getImei(0); 467 // If the device has a valid IMEI it must support attestation for it. 468 if (imei != null) { 469 modesToTest.add(ID_TYPE_IMEI); 470 } 471 if (mContext.getPackageManager().hasSystemFeature(FEATURE_TELEPHONY_CDMA)) { 472 meid = telephonyService.getMeid(0); 473 // Same for MEID 474 if (meid != null) { 475 modesToTest.add(ID_TYPE_MEID); 476 } 477 } 478 } 479 int numCombinations = 1 << modesToTest.size(); 480 for (int i = 1; i < numCombinations; i++) { 481 // Set the bits in devIdOpt to be passed into generateKeyPair according to the 482 // current modes tested. 483 int devIdOpt = 0; 484 for (int j = 0; j < modesToTest.size(); j++) { 485 if ((i & (1 << j)) != 0) { 486 devIdOpt = devIdOpt | modesToTest.get(j); 487 } 488 } 489 try { 490 // Now run the test with all supported key algorithms 491 for (SupportedKeyAlgorithm supportedKey: SUPPORTED_KEY_ALGORITHMS) { 492 Certificate attestation = generateKeyAndCheckAttestation( 493 supportedKey.keyAlgorithm, supportedKey.signatureAlgorithm, 494 supportedKey.signaturePaddingSchemes, useStrongBox, devIdOpt); 495 // generateKeyAndCheckAttestation should return null if device ID attestation 496 // is not supported. Simply continue to test the next combination. 497 if (attestation == null && !isDeviceIdAttestationSupported()) { 498 continue; 499 } 500 assertWithMessage( 501 String.format( 502 "Attestation should be valid for key %s with" 503 + " attestation modes %s", 504 supportedKey.keyAlgorithm, devIdOpt)) 505 .that(attestation) 506 .isNotNull(); 507 // Set the expected values for serial, IMEI and MEID depending on whether 508 // attestation for them was requested. 509 String expectedSerial = null; 510 if ((devIdOpt & ID_TYPE_SERIAL) != 0) { 511 expectedSerial = Build.getSerial(); 512 } 513 String expectedImei = null; 514 if ((devIdOpt & ID_TYPE_IMEI) != 0) { 515 expectedImei = imei; 516 } 517 String expectedMeid = null; 518 if ((devIdOpt & ID_TYPE_MEID) != 0) { 519 expectedMeid = meid; 520 } 521 validateDeviceIdAttestationData(attestation, expectedSerial, expectedImei, 522 expectedMeid); 523 } 524 } catch (UnsupportedOperationException expected) { 525 // Make sure the test only fails if the device is not meant to support Device 526 // ID attestation. 527 assertThat(isDeviceIdAttestationSupported()).isFalse(); 528 } catch (StrongBoxUnavailableException expected) { 529 // This exception must only be thrown if StrongBox attestation was requested. 530 assertThat(useStrongBox && !hasStrongBox()).isTrue(); 531 } 532 } 533 } 534 testAllVariationsOfDeviceIdAttestation()535 public void testAllVariationsOfDeviceIdAttestation() throws Exception { 536 assertAllVariantsOfDeviceIdAttestation(false /* useStrongBox */); 537 } 538 testAllVariationsOfDeviceIdAttestationUsingStrongBox()539 public void testAllVariationsOfDeviceIdAttestationUsingStrongBox() throws Exception { 540 assertAllVariantsOfDeviceIdAttestation(true /* useStrongBox */); 541 } 542 543 // TODO(b/198408853): Migrate testProfileOwnerCannotAttestDeviceUniqueIds()544 public void testProfileOwnerCannotAttestDeviceUniqueIds() throws Exception { 545 if (isDeviceOwner()) { 546 return; 547 } 548 int[] forbiddenModes = new int[] {ID_TYPE_SERIAL, ID_TYPE_IMEI, ID_TYPE_MEID}; 549 for (int i = 0; i < forbiddenModes.length; i++) { 550 try { 551 for (SupportedKeyAlgorithm supportedKey : SUPPORTED_KEY_ALGORITHMS) { 552 generateKeyAndCheckAttestation( 553 supportedKey.keyAlgorithm, 554 supportedKey.signatureAlgorithm, 555 supportedKey.signaturePaddingSchemes, 556 false /* useStrongBox */, 557 forbiddenModes[i]); 558 fail("Attestation of device UID (" + forbiddenModes[i] + ") should not be " 559 + "possible from profile owner"); 560 } 561 } catch (SecurityException e) { 562 assertThat(e.getMessage()).contains( 563 "Calling identity is not authorized"); 564 } 565 } 566 } 567 568 // TODO(b/198408853): Migrate testUniqueDeviceAttestationUsingDifferentAttestationCert()569 public void testUniqueDeviceAttestationUsingDifferentAttestationCert() throws Exception { 570 // This test is only applicable in modes where Device ID attestation can be performed 571 // _and_ the device has StrongBox, which is provisioned with individual attestation 572 // certificates. 573 // The functionality tested should equally work for when the Profile Owner can perform 574 // Device ID attestation, but since the underlying StrongBox implementation cannot 575 // differentiate between PO and DO modes, for simplicity, it is only tested in DO mode. 576 if (!isDeviceOwner() || !hasStrongBox() || !isUniqueDeviceAttestationSupported()) { 577 return; 578 } 579 final String no_id_alias = "com.android.test.key_attested"; 580 final String dev_unique_alias = "com.android.test.individual_dev_attested"; 581 582 byte[] attestationChallenge = new byte[] {0x01, 0x02, 0x03}; 583 try { 584 KeyGenParameterSpec specKeyAttOnly = new KeyGenParameterSpec.Builder( 585 no_id_alias, 586 KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY) 587 .setDigests(KeyProperties.DIGEST_SHA256) 588 .setAttestationChallenge(attestationChallenge) 589 .setIsStrongBoxBacked(true) 590 .build(); 591 592 AttestedKeyPair attestedKeyPair = mDevicePolicyManager.generateKeyPair( 593 getWho(), KeyProperties.KEY_ALGORITHM_EC, specKeyAttOnly, 594 0 /* device id attestation flags */); 595 assertWithMessage( 596 String.format("Failed generating a key with attestation in StrongBox.")) 597 .that(attestedKeyPair) 598 .isNotNull(); 599 600 KeyGenParameterSpec specIndividualAtt = new KeyGenParameterSpec.Builder( 601 dev_unique_alias, 602 KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY) 603 .setDigests(KeyProperties.DIGEST_SHA256) 604 .setAttestationChallenge(attestationChallenge) 605 .setIsStrongBoxBacked(true) 606 .build(); 607 608 AttestedKeyPair individuallyAttestedPair = mDevicePolicyManager.generateKeyPair( 609 getWho(), KeyProperties.KEY_ALGORITHM_EC, specIndividualAtt, 610 ID_TYPE_INDIVIDUAL_ATTESTATION /* device id attestation flags */); 611 assertWithMessage( 612 String.format("Failed generating a key for unique attestation in StrongBox.")) 613 .that(individuallyAttestedPair) 614 .isNotNull(); 615 616 X509Certificate keyAttestationIntermediate = (X509Certificate) 617 attestedKeyPair.getAttestationRecord().get(1); 618 X509Certificate devUniqueAttestationIntermediate = (X509Certificate) 619 individuallyAttestedPair.getAttestationRecord().get(1); 620 assertWithMessage("Device unique attestation intermediate certificate" 621 + " should be different to the key attestation certificate.") 622 .that(devUniqueAttestationIntermediate.getEncoded()) 623 .isNotEqualTo(keyAttestationIntermediate.getEncoded()); 624 } finally { 625 mDevicePolicyManager.removeKeyPair(getWho(), no_id_alias); 626 mDevicePolicyManager.removeKeyPair(getWho(), dev_unique_alias); 627 } 628 } 629 630 // TODO(b/198408853): Migrate testUniqueDeviceAttestationFailsWhenUnsupported()631 public void testUniqueDeviceAttestationFailsWhenUnsupported() { 632 if (!isDeviceOwner() || !hasStrongBox()) { 633 return; 634 } 635 636 if (isUniqueDeviceAttestationSupported()) { 637 // testUniqueDeviceAttestationUsingDifferentAttestationCert is the positive test case. 638 return; 639 } 640 641 byte[] attestationChallenge = new byte[] {0x01, 0x02, 0x03}; 642 final String someAlias = "com.android.test.should_not_exist"; 643 try { 644 KeyGenParameterSpec specIndividualAtt = new KeyGenParameterSpec.Builder( 645 someAlias, 646 KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY) 647 .setDigests(KeyProperties.DIGEST_SHA256) 648 .setAttestationChallenge(attestationChallenge) 649 .setIsStrongBoxBacked(true) 650 .build(); 651 652 AttestedKeyPair individuallyAttestedPair = mDevicePolicyManager.generateKeyPair( 653 getWho(), KeyProperties.KEY_ALGORITHM_EC, specIndividualAtt, 654 ID_TYPE_INDIVIDUAL_ATTESTATION /* device id attestation flags */); 655 fail("When unique attestation is not supported, key generation should fail."); 656 }catch (UnsupportedOperationException expected) { 657 } finally { 658 mDevicePolicyManager.removeKeyPair(getWho(), someAlias); 659 } 660 } 661 662 // TODO(b/198408853): Migrate testCanSetKeyPairCert()663 public void testCanSetKeyPairCert() throws Exception { 664 final String alias = "com.android.test.set-ec-1"; 665 try { 666 KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder( 667 alias, 668 KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY) 669 .setDigests(KeyProperties.DIGEST_SHA256) 670 .build(); 671 672 AttestedKeyPair generated = 673 mDevicePolicyManager.generateKeyPair(getWho(), "EC", spec, 0); 674 assertThat(generated).isNotNull(); 675 // Create a self-signed cert to go with it. 676 X500Principal issuer = new X500Principal("CN=SelfSigned, O=Android, C=US"); 677 X500Principal subject = new X500Principal("CN=Subject, O=Android, C=US"); 678 X509Certificate cert = createCertificate(generated.getKeyPair(), subject, issuer); 679 // Set the certificate chain 680 List<Certificate> certs = new ArrayList<Certificate>(); 681 certs.add(cert); 682 mDevicePolicyManager.setKeyPairCertificate(getWho(), alias, certs, true); 683 // Make sure that the alias can now be obtained. 684 assertThat(new KeyChainAliasFuture(alias).get()).isEqualTo(alias); 685 // And can be retrieved from KeyChain 686 X509Certificate[] fetchedCerts = KeyChain.getCertificateChain(mActivity, alias); 687 assertThat(fetchedCerts.length).isEqualTo(certs.size()); 688 assertThat(fetchedCerts[0].getEncoded()).isEqualTo(certs.get(0).getEncoded()); 689 } finally { 690 assertThat(mDevicePolicyManager.removeKeyPair(getWho(), alias)).isTrue(); 691 } 692 } 693 694 // TODO(b/198408853): Migrate testCanSetKeyPairCertChain()695 public void testCanSetKeyPairCertChain() throws Exception { 696 final String alias = "com.android.test.set-ec-2"; 697 try { 698 KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder( 699 alias, 700 KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY) 701 .setDigests(KeyProperties.DIGEST_SHA256) 702 .build(); 703 704 AttestedKeyPair generated = 705 mDevicePolicyManager.generateKeyPair(getWho(), "EC", spec, 0); 706 assertThat(generated).isNotNull(); 707 List<Certificate> chain = loadCertificateChain("user-cert-chain.crt"); 708 mDevicePolicyManager.setKeyPairCertificate(getWho(), alias, chain, true); 709 // Make sure that the alias can now be obtained. 710 assertThat(new KeyChainAliasFuture(alias).get()).isEqualTo(alias); 711 // And can be retrieved from KeyChain 712 X509Certificate[] fetchedCerts = KeyChain.getCertificateChain(mActivity, alias); 713 assertThat(fetchedCerts.length).isEqualTo(chain.size()); 714 for (int i = 0; i < chain.size(); i++) { 715 assertThat(fetchedCerts[i].getEncoded()).isEqualTo(chain.get(i).getEncoded()); 716 } 717 } finally { 718 assertThat(mDevicePolicyManager.removeKeyPair(getWho(), alias)).isTrue(); 719 } 720 } 721 testGetKeyPairGrants_SharedUid()722 public void testGetKeyPairGrants_SharedUid() throws Exception { 723 mDevicePolicyManager.installKeyPair(getWho(), mFakePrivKey, new Certificate[]{mFakeCert}, 724 TEST_ALIAS, /* requestAccess= */ false); 725 mDevicePolicyManager.grantKeyPairToApp(getWho(), TEST_ALIAS, SHARED_UID_APP1_PKG); 726 final int sharedUid = mContext.getPackageManager() 727 .getApplicationInfo(SHARED_UID_APP1_PKG, 0).uid; 728 729 assertThat(mDevicePolicyManager.getKeyPairGrants(TEST_ALIAS)) 730 .isEqualTo(Map.of(sharedUid, Set.of(SHARED_UID_APP1_PKG, SHARED_UID_APP2_PKG))); 731 } 732 testGetKeyPairGrants_DifferentUids()733 public void testGetKeyPairGrants_DifferentUids() throws Exception { 734 mDevicePolicyManager.installKeyPair(getWho(), mFakePrivKey, new Certificate[]{mFakeCert}, 735 TEST_ALIAS, /* requestAccess= */ true); 736 mDevicePolicyManager.grantKeyPairToApp(getWho(), TEST_ALIAS, SHARED_UID_APP1_PKG); 737 final int sharedUid = mContext.getPackageManager() 738 .getApplicationInfo(SHARED_UID_APP1_PKG, 0).uid; 739 740 assertThat(mDevicePolicyManager.getKeyPairGrants(TEST_ALIAS)).isEqualTo(Map.of( 741 Process.myUid(), singleton(getWho().getPackageName()), 742 sharedUid, Set.of(SHARED_UID_APP1_PKG, SHARED_UID_APP2_PKG))); 743 } 744 assertGranted(String alias, boolean expected)745 private void assertGranted(String alias, boolean expected) 746 throws InterruptedException, KeyChainException { 747 boolean granted = (KeyChain.getPrivateKey(mActivity, alias) != null); 748 assertWithMessage("Grant for alias: \"" + alias + "\"").that(granted).isEqualTo(expected); 749 } 750 getPrivateKey(final byte[] key, String type)751 private static PrivateKey getPrivateKey(final byte[] key, String type) 752 throws NoSuchAlgorithmException, InvalidKeySpecException { 753 return KeyFactory.getInstance(type).generatePrivate( 754 new PKCS8EncodedKeySpec(key)); 755 } 756 getCertificate(byte[] cert)757 private static Certificate getCertificate(byte[] cert) throws CertificateException { 758 return CertificateFactory.getInstance("X.509").generateCertificate( 759 new ByteArrayInputStream(cert)); 760 } 761 loadCertificatesFromAsset(String assetName)762 private Collection<Certificate> loadCertificatesFromAsset(String assetName) { 763 try { 764 final CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); 765 AssetManager am = mActivity.getAssets(); 766 InputStream is = am.open(assetName); 767 return (Collection<Certificate>) certFactory.generateCertificates(is); 768 } catch (IOException | CertificateException e) { 769 e.printStackTrace(); 770 } 771 return null; 772 } 773 loadPrivateKeyFromAsset(String assetName)774 private PrivateKey loadPrivateKeyFromAsset(String assetName) { 775 try { 776 AssetManager am = mActivity.getAssets(); 777 InputStream is = am.open(assetName); 778 ByteArrayOutputStream output = new ByteArrayOutputStream(); 779 int length; 780 byte[] buffer = new byte[4096]; 781 while ((length = is.read(buffer, 0, buffer.length)) != -1) { 782 output.write(buffer, 0, length); 783 } 784 return getPrivateKey(output.toByteArray(), "RSA"); 785 } catch (IOException | NoSuchAlgorithmException | InvalidKeySpecException e) { 786 e.printStackTrace(); 787 } 788 return null; 789 } 790 791 private class KeyChainAliasFuture implements KeyChainAliasCallback { 792 private final CountDownLatch mLatch = new CountDownLatch(1); 793 private String mChosenAlias = null; 794 795 @Override alias(final String chosenAlias)796 public void alias(final String chosenAlias) { 797 mChosenAlias = chosenAlias; 798 mLatch.countDown(); 799 } 800 KeyChainAliasFuture(String alias)801 public KeyChainAliasFuture(String alias) 802 throws UnsupportedEncodingException { 803 /* Pass the alias as a GET to an imaginary server instead of explicitly asking for it, 804 * to make sure the DPC actually has to do some work to grant the cert. 805 */ 806 final Uri uri = 807 Uri.parse("https://example.org/?alias=" + URLEncoder.encode(alias, "UTF-8")); 808 KeyChain.choosePrivateKeyAlias(mActivity, this, 809 null /* keyTypes */, null /* issuers */, uri, null /* alias */); 810 } 811 get()812 public String get() throws InterruptedException { 813 assertWithMessage("Chooser timeout") 814 .that(mLatch.await(KEYCHAIN_TIMEOUT_MINS, TimeUnit.MINUTES)) 815 .isTrue(); 816 return mChosenAlias; 817 } 818 } 819 getWho()820 private ComponentName getWho() { 821 return ADMIN_RECEIVER_COMPONENT; 822 } 823 hasStrongBox()824 boolean hasStrongBox() { 825 return mActivity.getPackageManager() 826 .hasSystemFeature(PackageManager.FEATURE_STRONGBOX_KEYSTORE); 827 } 828 isUniqueDeviceAttestationSupported()829 boolean isUniqueDeviceAttestationSupported() { 830 return mDevicePolicyManager.isUniqueDeviceAttestationSupported(); 831 } 832 isAttestationSupported()833 private boolean isAttestationSupported() { 834 return Build.VERSION.DEVICE_INITIAL_SDK_INT >= Build.VERSION_CODES.O; 835 } 836 } 837