• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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