Home | History | Annotate | Download | only in deviceandprofileowner
      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