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