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