xref: /aosp_15_r20/cts/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java (revision b7c941bb3fa97aba169d73cee0bed2de8ac964bf)
1 /*
2  * Copyright (C) 2016 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.keystore.cts.Attestation.KM_SECURITY_LEVEL_SOFTWARE;
20 import static android.keystore.cts.Attestation.KM_SECURITY_LEVEL_STRONG_BOX;
21 import static android.keystore.cts.Attestation.KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT;
22 import static android.keystore.cts.AuthorizationList.KM_ALGORITHM_EC;
23 import static android.keystore.cts.AuthorizationList.KM_ALGORITHM_RSA;
24 import static android.keystore.cts.AuthorizationList.KM_DIGEST_NONE;
25 import static android.keystore.cts.AuthorizationList.KM_DIGEST_SHA_2_256;
26 import static android.keystore.cts.AuthorizationList.KM_DIGEST_SHA_2_512;
27 import static android.keystore.cts.AuthorizationList.KM_ORIGIN_GENERATED;
28 import static android.keystore.cts.AuthorizationList.KM_ORIGIN_UNKNOWN;
29 import static android.keystore.cts.AuthorizationList.KM_PURPOSE_DECRYPT;
30 import static android.keystore.cts.AuthorizationList.KM_PURPOSE_ENCRYPT;
31 import static android.keystore.cts.AuthorizationList.KM_PURPOSE_SIGN;
32 import static android.keystore.cts.AuthorizationList.KM_PURPOSE_VERIFY;
33 import static android.keystore.cts.RootOfTrust.KM_VERIFIED_BOOT_UNVERIFIED;
34 import static android.keystore.cts.RootOfTrust.KM_VERIFIED_BOOT_VERIFIED;
35 import static android.security.keymaster.KeymasterDefs.KM_PURPOSE_AGREE_KEY;
36 import static android.security.keystore.KeyProperties.DIGEST_SHA256;
37 import static android.security.keystore.KeyProperties.ENCRYPTION_PADDING_NONE;
38 import static android.security.keystore.KeyProperties.ENCRYPTION_PADDING_RSA_OAEP;
39 import static android.security.keystore.KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1;
40 import static android.security.keystore.KeyProperties.KEY_ALGORITHM_EC;
41 import static android.security.keystore.KeyProperties.KEY_ALGORITHM_RSA;
42 import static android.security.keystore.KeyProperties.PURPOSE_AGREE_KEY;
43 import static android.security.keystore.KeyProperties.PURPOSE_DECRYPT;
44 import static android.security.keystore.KeyProperties.PURPOSE_ENCRYPT;
45 import static android.security.keystore.KeyProperties.PURPOSE_SIGN;
46 import static android.security.keystore.KeyProperties.PURPOSE_VERIFY;
47 import static android.security.keystore.KeyProperties.SIGNATURE_PADDING_RSA_PKCS1;
48 import static android.security.keystore.KeyProperties.SIGNATURE_PADDING_RSA_PSS;
49 
50 import static com.google.common.truth.Truth.assertThat;
51 
52 import static org.hamcrest.CoreMatchers.is;
53 import static org.hamcrest.MatcherAssert.assertThat;
54 import static org.hamcrest.Matchers.either;
55 import static org.hamcrest.Matchers.empty;
56 import static org.hamcrest.Matchers.greaterThanOrEqualTo;
57 import static org.hamcrest.Matchers.hasItems;
58 import static org.hamcrest.Matchers.lessThanOrEqualTo;
59 import static org.junit.Assert.assertArrayEquals;
60 import static org.junit.Assert.assertEquals;
61 import static org.junit.Assert.assertFalse;
62 import static org.junit.Assert.assertNotEquals;
63 import static org.junit.Assert.assertNotNull;
64 import static org.junit.Assert.assertNull;
65 import static org.junit.Assert.assertTrue;
66 import static org.junit.Assert.fail;
67 import static org.junit.Assume.assumeTrue;
68 
69 import android.content.Context;
70 import android.content.pm.PackageManager;
71 import android.content.pm.PackageManager.NameNotFoundException;
72 import android.keystore.cts.util.TestUtils;
73 import android.os.Build;
74 import android.os.SystemProperties;
75 import android.platform.test.annotations.RestrictedBuildTest;
76 import android.security.KeyStoreException;
77 import android.security.keystore.AttestationUtils;
78 import android.security.keystore.DeviceIdAttestationException;
79 import android.security.keystore.KeyGenParameterSpec;
80 import android.security.keystore.KeyProperties;
81 import android.util.ArraySet;
82 import android.util.Log;
83 
84 import androidx.test.InstrumentationRegistry;
85 import androidx.test.filters.RequiresDevice;
86 import androidx.test.runner.AndroidJUnit4;
87 
88 import com.android.bedstead.nene.TestApis;
89 import com.android.bedstead.permissions.PermissionContext;
90 import com.android.compatibility.common.util.CddTest;
91 import com.android.compatibility.common.util.PropertyUtil;
92 
93 import com.google.common.collect.ImmutableSet;
94 
95 import org.bouncycastle.asn1.x500.X500Name;
96 import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
97 import org.junit.Test;
98 import org.junit.runner.RunWith;
99 
100 import java.security.GeneralSecurityException;
101 import java.security.InvalidAlgorithmParameterException;
102 import java.security.InvalidKeyException;
103 import java.security.KeyPairGenerator;
104 import java.security.KeyStore;
105 import java.security.NoSuchAlgorithmException;
106 import java.security.NoSuchProviderException;
107 import java.security.ProviderException;
108 import java.security.PublicKey;
109 import java.security.SignatureException;
110 import java.security.cert.Certificate;
111 import java.security.cert.CertificateException;
112 import java.security.cert.CertificateParsingException;
113 import java.security.cert.X509Certificate;
114 import java.security.interfaces.ECPublicKey;
115 import java.security.interfaces.RSAPublicKey;
116 import java.security.spec.ECGenParameterSpec;
117 import java.security.spec.ECParameterSpec;
118 import java.util.ArrayList;
119 import java.util.Arrays;
120 import java.util.Date;
121 import java.util.HashMap;
122 import java.util.HashSet;
123 import java.util.List;
124 import java.util.Map;
125 import java.util.Set;
126 import java.util.regex.Matcher;
127 import java.util.regex.Pattern;
128 
129 import javax.crypto.KeyGenerator;
130 
131 /**
132  * Tests for Android Keystore attestation.
133  */
134 @RunWith(AndroidJUnit4.class)
135 public class KeyAttestationTest {
136 
137     private static final String TAG = AndroidKeyStoreTest.class.getSimpleName();
138 
139     private static final int ORIGINATION_TIME_OFFSET = 1000000;
140     private static final int CONSUMPTION_TIME_OFFSET = 2000000;
141 
142     private static final int KEY_USAGE_BITSTRING_LENGTH = 9;
143     private static final int KEY_USAGE_DIGITAL_SIGNATURE_BIT_OFFSET = 0;
144     private static final int KEY_USAGE_KEY_ENCIPHERMENT_BIT_OFFSET = 2;
145     private static final int KEY_USAGE_DATA_ENCIPHERMENT_BIT_OFFSET = 3;
146     private static final int KEY_USAGE_KEY_AGREE_BIT_OFFSET = 4;
147 
148     private static final int OS_MAJOR_VERSION_MATCH_GROUP_NAME = 1;
149     private static final int OS_MINOR_VERSION_MATCH_GROUP_NAME = 2;
150     private static final int OS_SUBMINOR_VERSION_MATCH_GROUP_NAME = 3;
151     private static final Pattern OS_VERSION_STRING_PATTERN = Pattern
152             .compile("([0-9]{1,2})(?:\\.([0-9]{1,2}))?(?:\\.([0-9]{1,2}))?(?:[^0-9.]+.*)?");
153 
154     private static final int OS_PATCH_LEVEL_YEAR_GROUP_NAME = 1;
155     private static final int OS_PATCH_LEVEL_MONTH_GROUP_NAME = 2;
156     private static final Pattern OS_PATCH_LEVEL_STRING_PATTERN = Pattern
157             .compile("([0-9]{4})-([0-9]{2})-[0-9]{2}");
158 
159     private static final int KM_ERROR_CANNOT_ATTEST_IDS = -66;
160     private static final int KM_ERROR_INVALID_INPUT_LENGTH = -21;
161     private static final int KM_ERROR_UNKNOWN_ERROR = -1000;
162     private static final int KM_ERROR_PERMISSION_DENIED = 6;
163 
164     private static final Map<String, Integer> sECKeySizes = new HashMap<>();
165 
166     static {
167         sECKeySizes.put("secp224r1", 224);
168         sECKeySizes.put("secp256r1", 256);
169         sECKeySizes.put("secp384r1", 384);
170         sECKeySizes.put("secp521r1", 521);
171         sECKeySizes.put("CURVE_25519", 256);
172     }
173 
getContext()174     private Context getContext() {
175         return InstrumentationRegistry.getInstrumentation().getTargetContext();
176     }
177 
178     @Test
testVersionParser()179     public void testVersionParser() throws Exception {
180         // Non-numerics/empty give version 0
181         assertEquals(0, parseSystemOsVersion(""));
182         assertEquals(0, parseSystemOsVersion("N"));
183 
184         // Should support one, two or three version number values.
185         assertEquals(10000, parseSystemOsVersion("1"));
186         assertEquals(10200, parseSystemOsVersion("1.2"));
187         assertEquals(10203, parseSystemOsVersion("1.2.3"));
188 
189         // It's fine to append other stuff to the dotted numeric version.
190         assertEquals(10000, parseSystemOsVersion("1stuff"));
191         assertEquals(10200, parseSystemOsVersion("1.2garbage.32"));
192         assertEquals(10203, parseSystemOsVersion("1.2.3-stuff"));
193 
194         // Two digits per version field are supported
195         assertEquals(152536, parseSystemOsVersion("15.25.36"));
196         assertEquals(999999, parseSystemOsVersion("99.99.99"));
197         assertEquals(0, parseSystemOsVersion("100.99.99"));
198         assertEquals(0, parseSystemOsVersion("99.100.99"));
199         assertEquals(0, parseSystemOsVersion("99.99.100"));
200     }
201 
202     @RequiresDevice
203     @Test
testEcAttestation()204     public void testEcAttestation() throws Exception {
205         testEcAttestation(false);
206     }
207 
208     @RequiresDevice
209     @Test
testEcAttestation_StrongBox()210     public void testEcAttestation_StrongBox() throws Exception {
211         assumeTrue("This test is only applicable to devices with StrongBox",
212                 TestUtils.hasStrongBox(getContext()));
213         // Exempt older versions due to increased coverage of this test beyond VTS,
214         // requiring exceptions for implementations frozen to an older VSR.
215         assumeTrue(TestUtils.hasKeystoreVersion(true /*isStrongBoxBased*/,
216                 Attestation.KM_VERSION_KEYMINT_3));
217         testEcAttestation(true);
218     }
219 
testEcAttestation(boolean isStrongBox)220     private void testEcAttestation(boolean isStrongBox) throws Exception {
221         if (!TestUtils.isAttestationSupported()) {
222             return;
223         }
224 
225         if (getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_PC)) {
226             return;
227         }
228 
229         final int[] purposes = {
230                 KM_PURPOSE_SIGN, KM_PURPOSE_VERIFY, KM_PURPOSE_SIGN | KM_PURPOSE_VERIFY
231         };
232         final boolean[] devicePropertiesAttestationValues = {true, false};
233         final boolean[] includeValidityDatesValues = {true, false};
234         final String[] curves;
235         final int[] keySizes;
236         final byte[][] challenges;
237 
238         if (isStrongBox) {
239             // StrongBox only supports secp256r1 keys.
240             curves = new String[]{"secp256r1"};
241             keySizes = new int[]{256};
242             challenges = new byte[][]{
243                     // Empty challange is not accepted by StrongBox.
244                     "challenge".getBytes(), // short challenge
245                     new byte[128], // long challenge
246             };
247         } else {
248             curves = new String[]{
249                     "secp224r1", "secp256r1", "secp384r1", "secp521r1"
250             };
251             keySizes = new int[]{
252                     224, 256, 384, 521
253             };
254             challenges = new byte[][]{
255                     new byte[0], // empty challenge
256                     "challenge".getBytes(), // short challenge
257                     new byte[128], // long challenge
258             };
259         }
260 
261         for (int curveIndex = 0; curveIndex < curves.length; ++curveIndex) {
262             for (int challengeIndex = 0; challengeIndex < challenges.length; ++challengeIndex) {
263                 for (int purposeIndex = 0; purposeIndex < purposes.length; ++purposeIndex) {
264                     for (boolean includeValidityDates : includeValidityDatesValues) {
265                         for (boolean devicePropertiesAttestation :
266                                 devicePropertiesAttestationValues) {
267                             try {
268                                 testEcAttestation(challenges[challengeIndex], includeValidityDates,
269                                         curves[curveIndex], keySizes[curveIndex],
270                                         purposes[purposeIndex], devicePropertiesAttestation,
271                                         isStrongBox);
272                             } catch (Throwable e) {
273                                 if (devicePropertiesAttestation
274                                         && isIgnorableIdAttestationFailure(e)) {
275                                     continue;
276                                 }
277                                 throw new Exception("Failed on curve " + curveIndex +
278                                         " challenge " + challengeIndex + " purpose " +
279                                         purposeIndex + " includeValidityDates " +
280                                         includeValidityDates + " and devicePropertiesAttestation " +
281                                         devicePropertiesAttestation, e);
282                             }
283                         }
284                     }
285                 }
286             }
287         }
288     }
289 
assertAttestationKeyMintError(KeyStoreException keyStoreException, boolean devicePropertiesAttestation)290     private void assertAttestationKeyMintError(KeyStoreException keyStoreException,
291             boolean devicePropertiesAttestation) {
292         int errorCode = keyStoreException.getErrorCode();
293         List<Integer> expectedErrs = new ArrayList<Integer>();
294         expectedErrs.add(KM_ERROR_INVALID_INPUT_LENGTH);
295         if (devicePropertiesAttestation) {
296             expectedErrs.add(KM_ERROR_CANNOT_ATTEST_IDS);
297         }
298         if (TestUtils.getVendorApiLevel() < 35) {
299             // b/337427860, some devices returns UNKNOWN_ERROR if large challenge is
300             // passed. So allow an extra error code for earlier devices.
301             expectedErrs.add(KM_ERROR_UNKNOWN_ERROR);
302         }
303         String assertMessage = String.format(
304                 "The KeyMint implementation may only return INVALID_INPUT_LENGTH or "
305                         + "CANNOT_ATTEST_IDSs as errors when the attestation challenge is "
306                         + "too large (error code was %d, attestation properties %b)",
307                 errorCode, devicePropertiesAttestation);
308         assertTrue(assertMessage, expectedErrs.contains(errorCode));
309     }
310 
assertPublicAttestationError(KeyStoreException keyStoreException, boolean devicePropertiesAttestation)311     private void assertPublicAttestationError(KeyStoreException keyStoreException,
312             boolean devicePropertiesAttestation) {
313         // Assert public failure information.
314         int errorCode = keyStoreException.getNumericErrorCode();
315         List<Integer> expectedErrs = new ArrayList<Integer>();
316         expectedErrs.add(KeyStoreException.ERROR_INCORRECT_USAGE);
317         if (devicePropertiesAttestation) {
318             expectedErrs.add(KeyStoreException.ERROR_ID_ATTESTATION_FAILURE);
319         }
320         if (TestUtils.getVendorApiLevel() < 35) {
321             // b/337427860, some devices returns UNKNOWN_ERROR if large challenge is
322             // passed. So allow an extra error code for earlier devices.
323             expectedErrs.add(KeyStoreException.ERROR_KEYMINT_FAILURE);
324         }
325         String assertMessage = String.format(
326                 "Error code was %d, device properties attestation? %b",
327                 errorCode, devicePropertiesAttestation);
328         assertTrue(assertMessage, expectedErrs.contains(errorCode));
329         assertFalse("Unexpected transient failure.", keyStoreException.isTransientFailure());
330     }
331 
332     @Test
testEcAttestation_TooLargeChallenge()333     public void testEcAttestation_TooLargeChallenge() throws Exception {
334         testEcAttestation_TooLargeChallenge(false);
335     }
336 
337     @Test
testEcAttestation_TooLargeChallenge_StrongBox()338     public void testEcAttestation_TooLargeChallenge_StrongBox() throws Exception {
339         assumeTrue("This test is only applicable to devices with StrongBox",
340                 TestUtils.hasStrongBox(getContext()));
341         testEcAttestation_TooLargeChallenge(true);
342     }
343 
testEcAttestation_TooLargeChallenge(boolean isStrongBox)344     private void testEcAttestation_TooLargeChallenge(boolean isStrongBox) throws Exception {
345         if (!TestUtils.isAttestationSupported()) {
346             return;
347         }
348 
349         boolean[] devicePropertiesAttestationValues = {true, false};
350         for (boolean devicePropertiesAttestation : devicePropertiesAttestationValues) {
351             try {
352                 testEcAttestation(new byte[129], true /* includeValidityDates */, "secp256r1", 256,
353                         KM_PURPOSE_SIGN, devicePropertiesAttestation, isStrongBox);
354                 fail("Attestation challenges larger than 128 bytes should be rejected");
355             } catch (ProviderException e) {
356                 KeyStoreException cause = (KeyStoreException) e.getCause();
357                 assertAttestationKeyMintError(cause, devicePropertiesAttestation);
358                 assertPublicAttestationError(cause, devicePropertiesAttestation);
359             }
360         }
361     }
362 
363     @Test
testEcAttestation_NoChallenge()364     public void testEcAttestation_NoChallenge() throws Exception {
365         testEcAttestation_NoChallenge(false);
366     }
367 
368     @Test
testEcAttestation_NoChallenge_StrongBox()369     public void testEcAttestation_NoChallenge_StrongBox() throws Exception {
370         assumeTrue("This test is only applicable to devices with StrongBox",
371                 TestUtils.hasStrongBox(getContext()));
372         testEcAttestation_NoChallenge(true);
373     }
374 
testEcAttestation_NoChallenge(boolean isStrongBox)375     public void testEcAttestation_NoChallenge(boolean isStrongBox) throws Exception {
376         boolean[] devicePropertiesAttestationValues = {true, false};
377         for (boolean devicePropertiesAttestation : devicePropertiesAttestationValues) {
378             String keystoreAlias = "test_key";
379             Date now = new Date();
380             Date originationEnd = new Date(now.getTime() + ORIGINATION_TIME_OFFSET);
381             Date consumptionEnd = new Date(now.getTime() + CONSUMPTION_TIME_OFFSET);
382             KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(keystoreAlias, PURPOSE_SIGN)
383                     .setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1"))
384                     .setDigests(TestUtils.getDigestsForKeyMintImplementation(isStrongBox))
385                     .setAttestationChallenge(null)
386                     .setKeyValidityStart(now)
387                     .setKeyValidityForOriginationEnd(originationEnd)
388                     .setKeyValidityForConsumptionEnd(consumptionEnd)
389                     .setDevicePropertiesAttestationIncluded(devicePropertiesAttestation)
390                     .setIsStrongBoxBacked(isStrongBox)
391                     .build();
392 
393             generateKeyPair(KEY_ALGORITHM_EC, spec);
394 
395             KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
396             keyStore.load(null);
397 
398             try {
399                 Certificate[] certificates = keyStore.getCertificateChain(keystoreAlias);
400                 assertEquals(1, certificates.length);
401 
402                 X509Certificate attestationCert = (X509Certificate) certificates[0];
403                 assertNull(attestationCert.getExtensionValue(Attestation.ASN1_OID));
404                 assertNull(attestationCert.getExtensionValue(Attestation.EAT_OID));
405             } finally {
406                 keyStore.deleteEntry(keystoreAlias);
407             }
408         }
409     }
410 
testEcAttestation_DeviceLocked(Boolean expectStrongBox)411     private void testEcAttestation_DeviceLocked(Boolean expectStrongBox) throws Exception {
412         if (!TestUtils.isAttestationSupported()) {
413             return;
414         }
415 
416         if (getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_PC)) {
417             return;
418         }
419 
420         String keystoreAlias = "test_key";
421         Date now = new Date();
422         Date originationEnd = new Date(now.getTime() + ORIGINATION_TIME_OFFSET);
423         Date consumptionEnd = new Date(now.getTime() + CONSUMPTION_TIME_OFFSET);
424         KeyGenParameterSpec.Builder builder =
425                 new KeyGenParameterSpec.Builder(keystoreAlias, PURPOSE_SIGN)
426                         .setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1"))
427                         .setDigests(TestUtils.getDigestsForKeyMintImplementation(expectStrongBox))
428                         .setAttestationChallenge(new byte[128])
429                         .setKeyValidityStart(now)
430                         .setKeyValidityForOriginationEnd(originationEnd)
431                         .setKeyValidityForConsumptionEnd(consumptionEnd)
432                         .setIsStrongBoxBacked(expectStrongBox);
433 
434         generateKeyPair(KEY_ALGORITHM_EC, builder.build());
435 
436         KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
437         keyStore.load(null);
438 
439         try {
440             Certificate[] certificates = keyStore.getCertificateChain(keystoreAlias);
441             verifyCertificateChain(certificates, expectStrongBox);
442 
443             X509Certificate attestationCert = (X509Certificate) certificates[0];
444             checkDeviceLocked(Attestation.loadFromCertificate(attestationCert));
445         } finally {
446             keyStore.deleteEntry(keystoreAlias);
447         }
448     }
449 
450     @RestrictedBuildTest
451     @RequiresDevice
452     @Test
453     @CddTest(requirements = {"9.10/C-0-1", "9.10/C-1-3"})
testEcAttestation_DeviceLocked()454     public void testEcAttestation_DeviceLocked() throws Exception {
455         testEcAttestation_DeviceLocked(false /* expectStrongBox */);
456     }
457 
458     @RestrictedBuildTest
459     @RequiresDevice
460     @Test
461     @CddTest(requirements = {"9.10/C-0-1", "9.10/C-1-3"})
testEcAttestation_DeviceLockedStrongbox()462     public void testEcAttestation_DeviceLockedStrongbox() throws Exception {
463         if (!TestUtils.hasStrongBox(getContext())) {
464             return;
465         }
466         testEcAttestation_DeviceLocked(true /* expectStrongBox */);
467     }
468 
469     @Test
testAttestationKmVersionMatchesFeatureVersion()470     public void testAttestationKmVersionMatchesFeatureVersion() throws Exception {
471         if (getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_PC)) {
472             return;
473         }
474         assumeTrue("Device does not support attestation", TestUtils.isAttestationSupported());
475 
476         testAttestationKmVersionMatchesFeatureVersion(false);
477     }
478 
479     @Test
testAttestationKmVersionMatchesFeatureVersionStrongBox()480     public void testAttestationKmVersionMatchesFeatureVersionStrongBox() throws Exception {
481         if (getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_PC)) {
482             return;
483         }
484         assumeTrue("Device does not support attestation", TestUtils.isAttestationSupported());
485 
486         int keyStoreFeatureVersionStrongBox =
487                 TestUtils.getFeatureVersionKeystoreStrongBox(getContext());
488 
489         if (!TestUtils.hasStrongBox(getContext())) {
490             // If there's no StrongBox, ensure there's no feature version for it.
491             assertEquals(0, keyStoreFeatureVersionStrongBox);
492             return;
493         }
494 
495         testAttestationKmVersionMatchesFeatureVersion(true);
496     }
497 
testAttestationKmVersionMatchesFeatureVersion(boolean isStrongBox)498     private void testAttestationKmVersionMatchesFeatureVersion(boolean isStrongBox)
499             throws Exception {
500         assumeTrue("Device does not support attestation", TestUtils.isAttestationSupported());
501 
502         String keystoreAlias = "test_key";
503         Date now = new Date();
504         Date originationEnd = new Date(now.getTime() + ORIGINATION_TIME_OFFSET);
505         Date consumptionEnd = new Date(now.getTime() + CONSUMPTION_TIME_OFFSET);
506         KeyGenParameterSpec.Builder builder =
507                 new KeyGenParameterSpec.Builder(keystoreAlias, PURPOSE_SIGN)
508                         .setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1"))
509                         .setAttestationChallenge(new byte[128])
510                         .setKeyValidityStart(now)
511                         .setKeyValidityForOriginationEnd(originationEnd)
512                         .setKeyValidityForConsumptionEnd(consumptionEnd)
513                         .setIsStrongBoxBacked(isStrongBox);
514 
515         generateKeyPair(KEY_ALGORITHM_EC, builder.build());
516 
517         KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
518         keyStore.load(null);
519 
520         try {
521             Certificate[] certificates = keyStore.getCertificateChain(keystoreAlias);
522             verifyCertificateChain(certificates, isStrongBox /* expectStrongBox */);
523             X509Certificate attestationCert = (X509Certificate) certificates[0];
524             Attestation attestation = Attestation.loadFromCertificate(attestationCert);
525             int kmVersionFromAttestation = attestation.keymasterVersion;
526             int keyStoreFeatureVersion;
527 
528             if (isStrongBox) {
529                 keyStoreFeatureVersion =
530                         TestUtils.getFeatureVersionKeystoreStrongBox(getContext());
531             } else {
532                 keyStoreFeatureVersion =
533                         TestUtils.getFeatureVersionKeystore(getContext());
534             }
535             // Feature Version is required on devices launching with Android 12 (API Level
536             // 31) but may be reported on devices launching with an earlier version. If it's
537             // present, it must match what is reported in attestation.
538             if (TestUtils.getVendorApiLevel() >= 31) {
539                 assertNotEquals(0, keyStoreFeatureVersion);
540             }
541             if (keyStoreFeatureVersion != 0) {
542                 assertEquals(kmVersionFromAttestation, keyStoreFeatureVersion);
543             }
544         } finally {
545             keyStore.deleteEntry(keystoreAlias);
546         }
547     }
548 
549     @Test
testEcAttestation_KeyStoreExceptionWhenRequestingUniqueId()550     public void testEcAttestation_KeyStoreExceptionWhenRequestingUniqueId() throws Exception {
551         testEcAttestation_KeyStoreExceptionWhenRequestingUniqueId(false);
552     }
553 
554     @Test
testEcAttestation_KeyStoreExceptionWhenRequestingUniqueId_StrongBox()555     public void testEcAttestation_KeyStoreExceptionWhenRequestingUniqueId_StrongBox()
556             throws Exception {
557         assumeTrue("This test is only applicable to devices with StrongBox",
558                 TestUtils.hasStrongBox(getContext()));
559         testEcAttestation_KeyStoreExceptionWhenRequestingUniqueId(true);
560     }
561 
testEcAttestation_KeyStoreExceptionWhenRequestingUniqueId( boolean isStrongBox)562     private void testEcAttestation_KeyStoreExceptionWhenRequestingUniqueId(
563             boolean isStrongBox) throws Exception {
564         String keystoreAlias = "test_key";
565         KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(keystoreAlias, PURPOSE_SIGN)
566                 .setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1"))
567                 .setDigests(TestUtils.getDigestsForKeyMintImplementation(isStrongBox))
568                 .setAttestationChallenge(new byte[128])
569                 .setUniqueIdIncluded(true)
570                 .setIsStrongBoxBacked(isStrongBox)
571                 .build();
572 
573         try {
574             generateKeyPair(KEY_ALGORITHM_EC, spec);
575             fail("Attestation should have failed.");
576         } catch (ProviderException e) {
577             // Attestation is expected to fail because of lack of permissions.
578             KeyStoreException cause = (KeyStoreException) e.getCause();
579             assertEquals(KM_ERROR_PERMISSION_DENIED, cause.getErrorCode());
580             // Assert public failure information.
581             assertEquals(KeyStoreException.ERROR_PERMISSION_DENIED, cause.getNumericErrorCode());
582             assertFalse("Unexpected transient failure in generate key.",
583                     cause.isTransientFailure());
584         } finally {
585             KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
586             keyStore.load(null);
587             keyStore.deleteEntry(keystoreAlias);
588         }
589     }
590 
591     @Test
testEcAttestation_UniqueIdWorksWithCorrectPermission()592     public void testEcAttestation_UniqueIdWorksWithCorrectPermission() throws Exception {
593         testEcAttestation_UniqueIdWorksWithCorrectPermission(false);
594     }
595 
596     @Test
testEcAttestation_UniqueIdWorksWithCorrectPermission_StrongBox()597     public void testEcAttestation_UniqueIdWorksWithCorrectPermission_StrongBox()
598             throws Exception {
599         assumeTrue("This test is only applicable to devices with StrongBox",
600                 TestUtils.hasStrongBox(getContext()));
601         testEcAttestation_UniqueIdWorksWithCorrectPermission(true);
602     }
603 
testEcAttestation_UniqueIdWorksWithCorrectPermission(boolean isStrongBox)604     private void testEcAttestation_UniqueIdWorksWithCorrectPermission(boolean isStrongBox)
605             throws Exception {
606         assumeTrue("Device doesn't have secure lock screen",
607                 TestUtils.hasSecureLockScreen(getContext()));
608         assumeTrue("Device does not support attestation", TestUtils.isAttestationSupported());
609 
610         String keystoreAlias = "test_key";
611         KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(keystoreAlias, PURPOSE_SIGN)
612                 .setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1"))
613                 .setDigests(TestUtils.getDigestsForKeyMintImplementation(isStrongBox))
614                 .setAttestationChallenge(new byte[128])
615                 .setUniqueIdIncluded(true)
616                 .setIsStrongBoxBacked(isStrongBox)
617                 .build();
618 
619         KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
620         keyStore.load(null);
621 
622         try (PermissionContext c = TestApis.permissions().withPermission(
623                 "android.permission.REQUEST_UNIQUE_ID_ATTESTATION")) {
624             generateKeyPair(KEY_ALGORITHM_EC, spec);
625             Certificate[] certificates = keyStore.getCertificateChain(keystoreAlias);
626             Attestation attestation = Attestation.loadFromCertificate(
627                     (X509Certificate) certificates[0]);
628             byte[] firstUniqueId = attestation.getUniqueId();
629             assertTrue("UniqueId must not be empty", firstUniqueId.length > 0);
630 
631             // The unique id rotates (30 days in the default implementation), and it's possible to
632             // get a spurious failure if the test runs exactly when the rotation occurs. Allow a
633             // single retry, just in case.
634             byte[] secondUniqueId = null;
635             for (int i = 0; i < 2; ++i) {
636                 keyStore.deleteEntry(keystoreAlias);
637 
638                 generateKeyPair(KEY_ALGORITHM_EC, spec);
639                 certificates = keyStore.getCertificateChain(keystoreAlias);
640                 attestation = Attestation.loadFromCertificate((X509Certificate) certificates[0]);
641                 secondUniqueId = attestation.getUniqueId();
642 
643                 if (Arrays.equals(firstUniqueId, secondUniqueId)) {
644                     break;
645                 } else {
646                     firstUniqueId = secondUniqueId;
647                     secondUniqueId = null;
648                 }
649             }
650             assertArrayEquals("UniqueIds must be consistent", firstUniqueId, secondUniqueId);
651 
652         } finally {
653             keyStore.deleteEntry(keystoreAlias);
654         }
655     }
656 
657     @RequiresDevice
658     @Test
testRsaAttestation()659     public void testRsaAttestation() throws Exception {
660         testRsaAttestation(false);
661     }
662 
663     @RequiresDevice
664     @Test
testRsaAttestation_StrongBox()665     public void testRsaAttestation_StrongBox() throws Exception {
666         assumeTrue("This test is only applicable to devices with StrongBox",
667                 TestUtils.hasStrongBox(getContext()));
668         // Exempt older versions due to increased coverage of this test beyond VTS,
669         // requiring exceptions for implementations frozen to an older VSR.
670         assumeTrue(TestUtils.hasKeystoreVersion(true /*isStrongBoxBased*/,
671                 Attestation.KM_VERSION_KEYMINT_3));
672         testRsaAttestation(true);
673     }
674 
testRsaAttestation(boolean isStrongBox)675     private void testRsaAttestation(boolean isStrongBox) throws Exception {
676         if (!TestUtils.isAttestationSupported()) {
677             return;
678         }
679 
680         if (getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_PC)) {
681             return;
682         }
683 
684         final int[] purposes = {
685                 PURPOSE_SIGN | PURPOSE_VERIFY,
686                 PURPOSE_ENCRYPT | PURPOSE_DECRYPT,
687         };
688         final String[][] signaturePaddingModes = {
689                 {
690                         SIGNATURE_PADDING_RSA_PKCS1,
691                 },
692                 {
693                         SIGNATURE_PADDING_RSA_PSS,
694                 },
695                 {
696                         SIGNATURE_PADDING_RSA_PKCS1,
697                         SIGNATURE_PADDING_RSA_PSS,
698                 },
699         };
700         final boolean[] devicePropertiesAttestationValues = {true, false};
701         final int[] keySizes;
702         final byte[][] challenges;
703         final String[][] encryptionPaddingModes;
704 
705         if (isStrongBox) {
706             // StrongBox has to support 2048 bit key.
707             keySizes = new int[]{2048};
708             challenges = new byte[][]{
709                     "challenge".getBytes(), // short challenge
710                     new byte[128] // long challenge
711             };
712             encryptionPaddingModes = new String[][]{
713                     {
714                             ENCRYPTION_PADDING_RSA_OAEP,
715                     },
716                     {
717                             ENCRYPTION_PADDING_RSA_PKCS1,
718                     },
719                     {
720                             ENCRYPTION_PADDING_RSA_OAEP,
721                             ENCRYPTION_PADDING_RSA_PKCS1,
722                     },
723             };
724         } else {
725             keySizes = new int[]{ // Smallish sizes to keep test runtimes down.
726                     512, 768, 1024
727             };
728             challenges = new byte[][]{
729                     new byte[0], // empty challenge
730                     "challenge".getBytes(), // short challenge
731                     new byte[128] // long challenge
732             };
733             encryptionPaddingModes = new String[][]{
734                     {
735                             ENCRYPTION_PADDING_NONE
736                     },
737                     {
738                             ENCRYPTION_PADDING_RSA_OAEP,
739                     },
740                     {
741                             ENCRYPTION_PADDING_RSA_PKCS1,
742                     },
743                     {
744                             ENCRYPTION_PADDING_RSA_OAEP,
745                             ENCRYPTION_PADDING_RSA_PKCS1,
746                     },
747             };
748         }
749 
750         for (boolean devicePropertiesAttestation : devicePropertiesAttestationValues) {
751             for (int keySize : keySizes) {
752                 for (byte[] challenge : challenges) {
753                     for (int purpose : purposes) {
754                         if (isEncryptionPurpose(purpose)) {
755                             testRsaAttestations(keySize, challenge, purpose, encryptionPaddingModes,
756                                     devicePropertiesAttestation, isStrongBox);
757                         } else {
758                             testRsaAttestations(keySize, challenge, purpose, signaturePaddingModes,
759                                     devicePropertiesAttestation, isStrongBox);
760                         }
761                     }
762                 }
763             }
764         }
765     }
766 
767     @Test
testRsaAttestation_TooLargeChallenge()768     public void testRsaAttestation_TooLargeChallenge() throws Exception {
769         testRsaAttestation_TooLargeChallenge(512, false);
770     }
771 
772     @Test
testRsaAttestation_TooLargeChallenge_StrongBox()773     public void testRsaAttestation_TooLargeChallenge_StrongBox() throws Exception {
774         assumeTrue("This test is only applicable to devices with StrongBox",
775                 TestUtils.hasStrongBox(getContext()));
776         testRsaAttestation_TooLargeChallenge(2048, true);
777     }
778 
testRsaAttestation_TooLargeChallenge(int keySize, boolean isStrongBox)779     private void testRsaAttestation_TooLargeChallenge(int keySize, boolean isStrongBox)
780             throws Exception {
781         if (!TestUtils.isAttestationSupported()) {
782             return;
783         }
784 
785         boolean[] devicePropertiesAttestationValues = {true, false};
786         for (boolean devicePropertiesAttestation : devicePropertiesAttestationValues) {
787             try {
788                 testRsaAttestation(new byte[129], true /* includeValidityDates */, keySize,
789                         PURPOSE_SIGN,
790                         null /* paddingModes; may be empty because we'll never test them */,
791                         devicePropertiesAttestation, isStrongBox);
792                 fail("Attestation challenges larger than 128 bytes should be rejected");
793             } catch (ProviderException e) {
794                 KeyStoreException cause = (KeyStoreException) e.getCause();
795                 assertAttestationKeyMintError(cause, devicePropertiesAttestation);
796                 assertPublicAttestationError(cause, devicePropertiesAttestation);
797             }
798         }
799     }
800 
801     @Test
testRsaAttestation_NoChallenge()802     public void testRsaAttestation_NoChallenge() throws Exception {
803         testRsaAttestation_NoChallenge(false);
804     }
805 
806     @Test
testRsaAttestation_NoChallenge_StrongBox()807     public void testRsaAttestation_NoChallenge_StrongBox() throws Exception {
808         assumeTrue("This test is only applicable to devices with StrongBox",
809                 TestUtils.hasStrongBox(getContext()));
810         testRsaAttestation_NoChallenge(true);
811     }
812 
testRsaAttestation_NoChallenge(boolean isStrongBox)813     private void testRsaAttestation_NoChallenge(boolean isStrongBox) throws Exception {
814         boolean[] devicePropertiesAttestationValues = {true, false};
815         for (boolean devicePropertiesAttestation : devicePropertiesAttestationValues) {
816             String keystoreAlias = "test_key";
817             Date now = new Date();
818             Date originationEnd = new Date(now.getTime() + ORIGINATION_TIME_OFFSET);
819             Date consumptionEnd = new Date(now.getTime() + CONSUMPTION_TIME_OFFSET);
820             KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(keystoreAlias, PURPOSE_SIGN)
821                     .setDigests(TestUtils.getDigestsForKeyMintImplementation(isStrongBox))
822                     .setAttestationChallenge(null)
823                     .setKeyValidityStart(now)
824                     .setKeyValidityForOriginationEnd(originationEnd)
825                     .setKeyValidityForConsumptionEnd(consumptionEnd)
826                     .setDevicePropertiesAttestationIncluded(devicePropertiesAttestation)
827                     .setIsStrongBoxBacked(isStrongBox)
828                     .build();
829 
830             generateKeyPair(KEY_ALGORITHM_RSA, spec);
831 
832             KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
833             keyStore.load(null);
834 
835             try {
836                 Certificate[] certificates = keyStore.getCertificateChain(keystoreAlias);
837                 assertEquals(1, certificates.length);
838 
839                 X509Certificate attestationCert = (X509Certificate) certificates[0];
840                 assertNull(attestationCert.getExtensionValue(Attestation.ASN1_OID));
841             } finally {
842                 keyStore.deleteEntry(keystoreAlias);
843             }
844         }
845     }
846 
testRsaAttestation_DeviceLocked(Boolean expectStrongBox)847     private void testRsaAttestation_DeviceLocked(Boolean expectStrongBox) throws Exception {
848         if (!TestUtils.isAttestationSupported()) {
849             return;
850         }
851 
852         if (getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_PC)) {
853             return;
854         }
855 
856         String keystoreAlias = "test_key";
857         Date now = new Date();
858         Date originationEnd = new Date(now.getTime() + ORIGINATION_TIME_OFFSET);
859         Date consumptionEnd = new Date(now.getTime() + CONSUMPTION_TIME_OFFSET);
860         KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(keystoreAlias, PURPOSE_SIGN)
861                 .setDigests(TestUtils.getDigestsForKeyMintImplementation(expectStrongBox))
862                 .setAttestationChallenge("challenge".getBytes())
863                 .setKeyValidityStart(now)
864                 .setKeyValidityForOriginationEnd(originationEnd)
865                 .setKeyValidityForConsumptionEnd(consumptionEnd)
866                 .setIsStrongBoxBacked(expectStrongBox)
867                 .build();
868 
869         generateKeyPair(KEY_ALGORITHM_RSA, spec);
870 
871         KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
872         keyStore.load(null);
873 
874         try {
875             Certificate[] certificates = keyStore.getCertificateChain(keystoreAlias);
876             verifyCertificateChain(certificates, expectStrongBox);
877 
878             X509Certificate attestationCert = (X509Certificate) certificates[0];
879             checkDeviceLocked(Attestation.loadFromCertificate(attestationCert));
880         } finally {
881             keyStore.deleteEntry(keystoreAlias);
882         }
883     }
884 
885     @RestrictedBuildTest
886     @RequiresDevice  // Emulators have no place to store the needed key
887     @Test
888     @CddTest(requirements = {"9.10/C-0-1", "9.10/C-1-3"})
testRsaAttestation_DeviceLocked()889     public void testRsaAttestation_DeviceLocked() throws Exception {
890         testRsaAttestation_DeviceLocked(false /* expectStrongbox */);
891     }
892 
893     @RestrictedBuildTest
894     @RequiresDevice  // Emulators have no place to store the needed key
895     @Test
896     @CddTest(requirements = {"9.10/C-0-1", "9.10/C-1-3"})
testRsaAttestation_DeviceLockedStrongbox()897     public void testRsaAttestation_DeviceLockedStrongbox() throws Exception {
898         if (!TestUtils.hasStrongBox(getContext())) {
899             return;
900         }
901 
902         testRsaAttestation_DeviceLocked(true /* expectStrongbox */);
903     }
904 
905     @Test
testAesAttestation()906     public void testAesAttestation() throws Exception {
907         testAesAttestation(false);
908     }
909 
910     @Test
testAesAttestation_StrongBox()911     public void testAesAttestation_StrongBox() throws Exception {
912         assumeTrue("This test is only applicable to devices with StrongBox",
913                 TestUtils.hasStrongBox(getContext()));
914         testAesAttestation(true);
915     }
916 
testAesAttestation(boolean isStrongBox)917     private void testAesAttestation(boolean isStrongBox) throws Exception {
918         boolean[] devicePropertiesAttestationValues = {true, false};
919         for (boolean devicePropertiesAttestation : devicePropertiesAttestationValues) {
920             String keystoreAlias = "test_key";
921             KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(keystoreAlias,
922                     PURPOSE_ENCRYPT)
923                     .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
924                     .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
925                     .setAttestationChallenge(new byte[0])
926                     .setDevicePropertiesAttestationIncluded(devicePropertiesAttestation)
927                     .setIsStrongBoxBacked(isStrongBox)
928                     .build();
929             generateKey(spec, KeyProperties.KEY_ALGORITHM_AES);
930 
931             KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
932             keyStore.load(null);
933             try {
934                 assertNull(keyStore.getCertificateChain(keystoreAlias));
935             } finally {
936                 keyStore.deleteEntry(keystoreAlias);
937             }
938         }
939     }
940 
941     @Test
testHmacAttestation()942     public void testHmacAttestation() throws Exception {
943         testHmacAttestation(false);
944     }
945 
946     @Test
testHmacAttestation_StrongBox()947     public void testHmacAttestation_StrongBox() throws Exception {
948         assumeTrue("This test is only applicable to devices with StrongBox",
949                 TestUtils.hasStrongBox(getContext()));
950         testHmacAttestation(true);
951     }
952 
testHmacAttestation(boolean isStrongBox)953     private void testHmacAttestation(boolean isStrongBox) throws Exception {
954         boolean[] devicePropertiesAttestationValues = {true, false};
955         for (boolean devicePropertiesAttestation : devicePropertiesAttestationValues) {
956             String keystoreAlias = "test_key";
957             KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(keystoreAlias, PURPOSE_SIGN)
958                     .setDevicePropertiesAttestationIncluded(devicePropertiesAttestation)
959                     .setIsStrongBoxBacked(isStrongBox)
960                     .build();
961 
962             generateKey(spec, KeyProperties.KEY_ALGORITHM_HMAC_SHA256);
963 
964             KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
965             keyStore.load(null);
966             try {
967                 assertNull(keyStore.getCertificateChain(keystoreAlias));
968             } finally {
969                 keyStore.deleteEntry(keystoreAlias);
970             }
971         }
972     }
973 
testRsaAttestations(int keySize, byte[] challenge, int purpose, String[][] paddingModes, boolean devicePropertiesAttestation, boolean isStrongBox)974     private void testRsaAttestations(int keySize, byte[] challenge, int purpose,
975             String[][] paddingModes, boolean devicePropertiesAttestation,
976             boolean isStrongBox) throws Exception {
977         for (String[] paddings : paddingModes) {
978             try {
979                 testRsaAttestation(challenge, true /* includeValidityDates */, keySize, purpose,
980                         paddings, devicePropertiesAttestation, isStrongBox);
981                 testRsaAttestation(challenge, false /* includeValidityDates */, keySize, purpose,
982                         paddings, devicePropertiesAttestation, isStrongBox);
983             } catch (Throwable e) {
984                 if (devicePropertiesAttestation && isIgnorableIdAttestationFailure(e)) {
985                     continue;
986                 }
987                 throw new Exception("Failed on key size " + keySize + " challenge [" +
988                         new String(challenge) + "], purposes " +
989                         buildPurposeSet(purpose) + " paddings " +
990                         ImmutableSet.copyOf(paddings) + " and devicePropertiesAttestation "
991                         + devicePropertiesAttestation,
992                         e);
993             }
994         }
995     }
996 
997     @Test
testDeviceIdAttestation()998     public void testDeviceIdAttestation() throws Exception {
999         testDeviceIdAttestationFailure(AttestationUtils.ID_TYPE_SERIAL, null);
1000         testDeviceIdAttestationFailure(AttestationUtils.ID_TYPE_IMEI, "Unable to retrieve IMEI");
1001         testDeviceIdAttestationFailure(AttestationUtils.ID_TYPE_MEID, "Unable to retrieve MEID");
1002     }
1003 
1004     @Test
testAttestedRoTAcrossKeymints()1005     public void testAttestedRoTAcrossKeymints() throws Exception {
1006         assumeTrue("This test requires a device supporting key attestation",
1007                 TestUtils.isAttestationSupported());
1008         assumeTrue("This test is not applicable for PC",
1009                 !getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_PC));
1010         assumeTrue("This test is only applicable to devices with StrongBox",
1011                 TestUtils.hasStrongBox(getContext()));
1012 
1013         RootOfTrust teeRootOfTrust = generateAttestationAndExtractRoT("tee_test_key", false);
1014         RootOfTrust sbRootOfTrust = generateAttestationAndExtractRoT("sb_test_key", true);
1015 
1016         assertNotNull("RootOfTrust should not be null for TEE", teeRootOfTrust);
1017         assertNotNull("RootOfTrust should not be null for StrongBox", sbRootOfTrust);
1018         assertArrayEquals("Verified boot hash in TEE and StrongBox issued certificates must be"
1019                         + " same.", teeRootOfTrust.getVerifiedBootHash(),
1020                 sbRootOfTrust.getVerifiedBootHash());
1021         assertArrayEquals("Verified boot key in TEE and StrongBox issued certificates must be"
1022                         + " same.", teeRootOfTrust.getVerifiedBootKey(),
1023                 sbRootOfTrust.getVerifiedBootKey());
1024         assertEquals("Verified boot state in TEE and StrongBox issued certificates must be same.",
1025                 teeRootOfTrust.getVerifiedBootState(),
1026                 sbRootOfTrust.getVerifiedBootState());
1027         assertEquals("Device locked state in TEE and StrongBox issued certificates must be same.",
1028                 teeRootOfTrust.isDeviceLocked(), sbRootOfTrust.isDeviceLocked());
1029     }
1030 
generateAttestationAndExtractRoT(String alias, boolean isStrongBox)1031     private RootOfTrust generateAttestationAndExtractRoT(String alias, boolean isStrongBox)
1032             throws Exception {
1033         KeyGenParameterSpec.Builder specBuilder =
1034                 new KeyGenParameterSpec.Builder(alias, PURPOSE_SIGN | PURPOSE_VERIFY)
1035                         .setIsStrongBoxBacked(isStrongBox)
1036                         .setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1"))
1037                         .setDigests(DIGEST_SHA256)
1038                         .setAttestationChallenge("challenge".getBytes());
1039         generateKeyPair(KEY_ALGORITHM_EC, specBuilder.build());
1040 
1041         KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
1042         keyStore.load(null);
1043 
1044         Certificate[] certificates = keyStore.getCertificateChain(alias);
1045         verifyCertificateChain(certificates, isStrongBox);
1046 
1047         Attestation attestation =
1048                 Attestation.loadFromCertificate((X509Certificate) certificates[0]);
1049         return attestation.getRootOfTrust();
1050     }
1051 
1052     @RequiresDevice
1053     @Test
testCurve25519Attestation()1054     public void testCurve25519Attestation() throws Exception {
1055         if (!TestUtils.isAttestationSupported()) {
1056             return;
1057         }
1058         assumeTrue("Curve25519 Key attestation supported from KeyMint v2 and above.",
1059                 TestUtils.hasKeystoreVersion(false /*isStrongBoxBased*/,
1060                         Attestation.KM_VERSION_KEYMINT_2));
1061 
1062         if (getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_PC)) {
1063             return;
1064         }
1065 
1066         byte[][] challenges = {
1067                 new byte[0], // empty challenge
1068                 "challenge".getBytes(), // short challenge
1069                 new byte[128] // long challenge
1070         };
1071         boolean[] devicePropertiesAttestationValues = {true, false};
1072 
1073         for (boolean devicePropertiesAttestation : devicePropertiesAttestationValues) {
1074             for (byte[] challenge : challenges) {
1075                 testCurve25519Attestations("ed25519", challenge, PURPOSE_SIGN | PURPOSE_VERIFY,
1076                         devicePropertiesAttestation);
1077                 testCurve25519Attestations("x25519", challenge, PURPOSE_AGREE_KEY,
1078                         devicePropertiesAttestation);
1079             }
1080         }
1081     }
1082 
1083     @SuppressWarnings("deprecation")
testCurve25519Attestations(String curve, byte[] challenge, int purpose, boolean devicePropertiesAttestation)1084     private void testCurve25519Attestations(String curve, byte[] challenge,
1085             int purpose, boolean devicePropertiesAttestation)
1086             throws Exception {
1087         Log.i(TAG, curve + " curve key attestation with: "
1088                 + " / challenge " + Arrays.toString(challenge)
1089                 + " / purposes " + purpose
1090                 + " / devicePropertiesAttestation " + devicePropertiesAttestation);
1091         String keystoreAlias = "test_key";
1092         Date startTime = new Date();
1093         KeyGenParameterSpec.Builder builder =
1094                 new KeyGenParameterSpec.Builder(keystoreAlias, purpose)
1095                         .setAlgorithmParameterSpec(new ECGenParameterSpec(curve))
1096                         .setDigests(KeyProperties.DIGEST_NONE)
1097                         .setAttestationChallenge(challenge)
1098                         .setDevicePropertiesAttestationIncluded(devicePropertiesAttestation);
1099 
1100         try {
1101             generateKeyPair(KEY_ALGORITHM_EC, builder.build());
1102         } catch (Throwable e) {
1103             if (devicePropertiesAttestation && isIgnorableIdAttestationFailure(e)) {
1104                 return;
1105             }
1106             throw new Exception("Failed on curve " + curve + " challenge ["
1107                     + new String(challenge) + "], purposes "
1108                     + buildPurposeSet(purpose) + " and devicePropertiesAttestation "
1109                     + devicePropertiesAttestation,
1110                     e);
1111 
1112         }
1113 
1114         KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
1115         keyStore.load(null);
1116 
1117         try {
1118             Certificate[] certificates = keyStore.getCertificateChain(keystoreAlias);
1119             verifyCertificateChain(certificates, false /* expectStrongBox */);
1120 
1121             X509Certificate attestationCert = (X509Certificate) certificates[0];
1122             Attestation attestation = Attestation.loadFromCertificate(attestationCert);
1123 
1124             checkEcKeyDetails(attestationCert, attestation, "CURVE_25519", 256);
1125             checkKeyUsage(attestationCert, purpose);
1126             checkKeyIndependentAttestationInfo(challenge, purpose,
1127                     ImmutableSet.of(KM_DIGEST_NONE), startTime, false,
1128                     devicePropertiesAttestation, attestation);
1129         } finally {
1130             keyStore.deleteEntry(keystoreAlias);
1131         }
1132     }
1133 
1134     @SuppressWarnings("deprecation")
testRsaAttestation(byte[] challenge, boolean includeValidityDates, int keySize, int purposes, String[] paddingModes, boolean devicePropertiesAttestation, boolean isStrongBox)1135     private void testRsaAttestation(byte[] challenge, boolean includeValidityDates, int keySize,
1136             int purposes, String[] paddingModes, boolean devicePropertiesAttestation,
1137             boolean isStrongBox) throws Exception {
1138         Log.i(TAG, "RSA key attestation with: challenge " + Arrays.toString(challenge) +
1139                 " / includeValidityDates " + includeValidityDates + " / keySize " + keySize +
1140                 " / purposes " + purposes + " / paddingModes " + Arrays.toString(paddingModes) +
1141                 " / devicePropertiesAttestation " + devicePropertiesAttestation);
1142 
1143         String keystoreAlias = "test_key";
1144         Date startTime = new Date();
1145         Date originationEnd = new Date(startTime.getTime() + ORIGINATION_TIME_OFFSET);
1146         Date consumptionEnd = new Date(startTime.getTime() + CONSUMPTION_TIME_OFFSET);
1147         KeyGenParameterSpec.Builder builder =
1148                 new KeyGenParameterSpec.Builder(keystoreAlias, purposes)
1149                         .setKeySize(keySize)
1150                         .setDigests(TestUtils.getDigestsForKeyMintImplementation(isStrongBox))
1151                         .setAttestationChallenge(challenge)
1152                         .setDevicePropertiesAttestationIncluded(devicePropertiesAttestation)
1153                         .setIsStrongBoxBacked(isStrongBox);
1154 
1155         if (includeValidityDates) {
1156             builder.setKeyValidityStart(startTime)
1157                     .setKeyValidityForOriginationEnd(originationEnd)
1158                     .setKeyValidityForConsumptionEnd(consumptionEnd);
1159         }
1160         if (isEncryptionPurpose(purposes)) {
1161             builder.setEncryptionPaddings(paddingModes);
1162             // Because we sometimes set "no padding", allow non-randomized encryption.
1163             builder.setRandomizedEncryptionRequired(false);
1164         }
1165         if (isSignaturePurpose(purposes)) {
1166             builder.setSignaturePaddings(paddingModes);
1167         }
1168 
1169         generateKeyPair(KEY_ALGORITHM_RSA, builder.build());
1170 
1171         KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
1172         keyStore.load(null);
1173 
1174         try {
1175             Certificate[] certificates = keyStore.getCertificateChain(keystoreAlias);
1176             verifyCertificateChain(certificates, isStrongBox /* expectStrongBox */);
1177 
1178             X509Certificate attestationCert = (X509Certificate) certificates[0];
1179             Attestation attestation = Attestation.loadFromCertificate(attestationCert);
1180 
1181             checkRsaKeyDetails(attestationCert, attestation, keySize,
1182                     (paddingModes == null)
1183                             ? new HashSet<String>() : ImmutableSet.copyOf(paddingModes));
1184             checkKeyUsage(attestationCert, purposes);
1185             checkKeyIndependentAttestationInfo(challenge, purposes, startTime,
1186                     includeValidityDates, devicePropertiesAttestation, attestation);
1187         } finally {
1188             keyStore.deleteEntry(keystoreAlias);
1189         }
1190     }
1191 
checkKeyUsage(X509Certificate attestationCert, int purposes)1192     private void checkKeyUsage(X509Certificate attestationCert, int purposes) {
1193 
1194         boolean[] expectedKeyUsage = new boolean[KEY_USAGE_BITSTRING_LENGTH];
1195         if (isSignaturePurpose(purposes)) {
1196             expectedKeyUsage[KEY_USAGE_DIGITAL_SIGNATURE_BIT_OFFSET] = true;
1197         }
1198         if (isEncryptionPurpose(purposes)) {
1199             expectedKeyUsage[KEY_USAGE_KEY_ENCIPHERMENT_BIT_OFFSET] = true;
1200             expectedKeyUsage[KEY_USAGE_DATA_ENCIPHERMENT_BIT_OFFSET] = true;
1201         }
1202         if (isAgreeKeyPurpose(purposes)) {
1203             expectedKeyUsage[KEY_USAGE_KEY_AGREE_BIT_OFFSET] = true;
1204         }
1205         assertThat("Attested certificate has unexpected key usage.",
1206                 attestationCert.getKeyUsage(), is(expectedKeyUsage));
1207     }
1208 
1209     @SuppressWarnings("deprecation")
testEcAttestation(byte[] challenge, boolean includeValidityDates, String ecCurve, int keySize, int purposes, boolean devicePropertiesAttestation, boolean isStrongBox)1210     private void testEcAttestation(byte[] challenge, boolean includeValidityDates, String ecCurve,
1211             int keySize, int purposes, boolean devicePropertiesAttestation,
1212             boolean isStrongBox) throws Exception {
1213         Log.i(TAG, "EC key attestation with: challenge " + Arrays.toString(challenge) +
1214                 " / includeValidityDates " + includeValidityDates + " / ecCurve " + ecCurve +
1215                 " / keySize " + keySize + " / purposes " + purposes +
1216                 " / devicePropertiesAttestation " + devicePropertiesAttestation);
1217 
1218         String keystoreAlias = "test_key";
1219         Date startTime = new Date();
1220         Date originationEnd = new Date(startTime.getTime() + ORIGINATION_TIME_OFFSET);
1221         Date consumptionEnd = new Date(startTime.getTime() + CONSUMPTION_TIME_OFFSET);
1222         KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(keystoreAlias,
1223                 purposes)
1224                 .setAlgorithmParameterSpec(new ECGenParameterSpec(ecCurve))
1225                 .setDigests(TestUtils.getDigestsForKeyMintImplementation(isStrongBox))
1226                 .setAttestationChallenge(challenge)
1227                 .setDevicePropertiesAttestationIncluded(devicePropertiesAttestation)
1228                 .setIsStrongBoxBacked(isStrongBox);
1229 
1230         if (includeValidityDates) {
1231             builder.setKeyValidityStart(startTime)
1232                     .setKeyValidityForOriginationEnd(originationEnd)
1233                     .setKeyValidityForConsumptionEnd(consumptionEnd);
1234         }
1235 
1236         generateKeyPair(KEY_ALGORITHM_EC, builder.build());
1237 
1238         KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
1239         keyStore.load(null);
1240 
1241         try {
1242             Certificate[] certificates = keyStore.getCertificateChain(keystoreAlias);
1243             verifyCertificateChain(certificates, isStrongBox /* expectStrongBox */);
1244 
1245             X509Certificate attestationCert = (X509Certificate) certificates[0];
1246             Attestation attestation = Attestation.loadFromCertificate(attestationCert);
1247 
1248             checkEcKeyDetails(attestationCert, attestation, ecCurve, keySize);
1249             checkKeyUsage(attestationCert, purposes);
1250             checkKeyIndependentAttestationInfo(challenge, purposes, startTime,
1251                     includeValidityDates, devicePropertiesAttestation, attestation);
1252         } finally {
1253             keyStore.deleteEntry(keystoreAlias);
1254         }
1255     }
1256 
checkAttestationApplicationId(Attestation attestation)1257     private void checkAttestationApplicationId(Attestation attestation)
1258             throws NoSuchAlgorithmException, NameNotFoundException {
1259         AttestationApplicationId aaid = null;
1260         int kmVersion = attestation.getKeymasterVersion();
1261         assertNull(attestation.getTeeEnforced().getAttestationApplicationId());
1262         aaid = attestation.getSoftwareEnforced().getAttestationApplicationId();
1263 
1264         if (kmVersion >= 3) {
1265             // must be present and correct
1266             assertNotNull(aaid);
1267             assertEquals(new AttestationApplicationId(getContext()), aaid);
1268         } else {
1269             // may be present and
1270             // must be correct if present
1271             if (aaid != null) {
1272                 assertEquals(new AttestationApplicationId(getContext()), aaid);
1273             }
1274         }
1275     }
1276 
checkAttestationDeviceProperties(boolean devicePropertiesAttestation, Attestation attestation)1277     private void checkAttestationDeviceProperties(boolean devicePropertiesAttestation,
1278             Attestation attestation) {
1279         final AuthorizationList keyDetailsList;
1280         final AuthorizationList nonKeyDetailsList;
1281         if (attestation.getKeymasterSecurityLevel() == KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT
1282                 || attestation.getKeymasterSecurityLevel() == KM_SECURITY_LEVEL_STRONG_BOX) {
1283             keyDetailsList = attestation.getTeeEnforced();
1284             nonKeyDetailsList = attestation.getSoftwareEnforced();
1285         } else {
1286             keyDetailsList = attestation.getSoftwareEnforced();
1287             nonKeyDetailsList = attestation.getTeeEnforced();
1288         }
1289 
1290         if (devicePropertiesAttestation) {
1291             final String platformReportedBrand =
1292                     TestUtils.isPropertyEmptyOrUnknown(Build.BRAND_FOR_ATTESTATION)
1293                             ? Build.BRAND : Build.BRAND_FOR_ATTESTATION;
1294             assertThat(keyDetailsList.getBrand()).isEqualTo(platformReportedBrand);
1295             final String platformReportedDevice =
1296                     TestUtils.isPropertyEmptyOrUnknown(Build.DEVICE_FOR_ATTESTATION)
1297                             ? Build.DEVICE : Build.DEVICE_FOR_ATTESTATION;
1298             assertThat(keyDetailsList.getDevice()).isEqualTo(platformReportedDevice);
1299             final String platformReportedProduct =
1300                     TestUtils.isPropertyEmptyOrUnknown(Build.PRODUCT_FOR_ATTESTATION)
1301                             ? Build.PRODUCT : Build.PRODUCT_FOR_ATTESTATION;
1302             assertThat(keyDetailsList.getProduct()).isEqualTo(platformReportedProduct);
1303             final String platformReportedManufacturer =
1304                     TestUtils.isPropertyEmptyOrUnknown(Build.MANUFACTURER_FOR_ATTESTATION)
1305                             ? Build.MANUFACTURER : Build.MANUFACTURER_FOR_ATTESTATION;
1306             assertThat(keyDetailsList.getManufacturer()).isEqualTo(platformReportedManufacturer);
1307             final String platformReportedModel =
1308                     TestUtils.isPropertyEmptyOrUnknown(Build.MODEL_FOR_ATTESTATION)
1309                             ? Build.MODEL : Build.MODEL_FOR_ATTESTATION;
1310             assertThat(keyDetailsList.getModel()).isEqualTo(platformReportedModel);
1311         } else {
1312             assertNull(keyDetailsList.getBrand());
1313             assertNull(keyDetailsList.getDevice());
1314             assertNull(keyDetailsList.getProduct());
1315             assertNull(keyDetailsList.getManufacturer());
1316             assertNull(keyDetailsList.getModel());
1317         }
1318         assertNull(nonKeyDetailsList.getBrand());
1319         assertNull(nonKeyDetailsList.getDevice());
1320         assertNull(nonKeyDetailsList.getProduct());
1321         assertNull(nonKeyDetailsList.getManufacturer());
1322         assertNull(nonKeyDetailsList.getModel());
1323     }
1324 
checkAttestationNoUniqueIds(Attestation attestation)1325     private void checkAttestationNoUniqueIds(Attestation attestation) {
1326         assertNull(attestation.getTeeEnforced().getImei());
1327         assertNull(attestation.getTeeEnforced().getMeid());
1328         assertNull(attestation.getTeeEnforced().getSerialNumber());
1329         assertNull(attestation.getSoftwareEnforced().getImei());
1330         assertNull(attestation.getSoftwareEnforced().getMeid());
1331         assertNull(attestation.getSoftwareEnforced().getSerialNumber());
1332     }
1333 
checkKeyIndependentAttestationInfo(byte[] challenge, int purposes, Date startTime, boolean includesValidityDates, boolean devicePropertiesAttestation, Attestation attestation)1334     private void checkKeyIndependentAttestationInfo(byte[] challenge, int purposes,
1335             Date startTime, boolean includesValidityDates,
1336             boolean devicePropertiesAttestation, Attestation attestation)
1337             throws NoSuchAlgorithmException, NameNotFoundException {
1338         Set digests = ImmutableSet.of(KM_DIGEST_NONE, KM_DIGEST_SHA_2_256, KM_DIGEST_SHA_2_512);
1339         if (attestation.getAttestationSecurityLevel() == KM_SECURITY_LEVEL_STRONG_BOX) {
1340             digests = ImmutableSet.of(KM_DIGEST_NONE, KM_DIGEST_SHA_2_256);
1341         }
1342         checkKeyIndependentAttestationInfo(challenge, purposes, digests,
1343                 startTime, includesValidityDates,
1344                 devicePropertiesAttestation, attestation);
1345     }
1346 
checkKeyIndependentAttestationInfo(byte[] challenge, int purposes, Set digests, Date startTime, boolean includesValidityDates, boolean devicePropertiesAttestation, Attestation attestation)1347     private void checkKeyIndependentAttestationInfo(byte[] challenge, int purposes,
1348             Set digests, Date startTime, boolean includesValidityDates,
1349             boolean devicePropertiesAttestation, Attestation attestation)
1350             throws NoSuchAlgorithmException, NameNotFoundException {
1351         checkUnexpectedOids(attestation);
1352         checkAttestationSecurityLevelDependentParams(attestation);
1353         assertNotNull("Attestation challenge must not be null.",
1354                 attestation.getAttestationChallenge());
1355         assertThat("Attestation challenge not matching with provided challenge.",
1356                 attestation.getAttestationChallenge(), is(challenge));
1357         // In EAT, this is null if not filled in. In ASN.1, this is an array with length 0.
1358         if (attestation.getUniqueId() != null) {
1359             assertEquals("Unique ID must not be empty if present.",
1360                     0, attestation.getUniqueId().length);
1361         }
1362         checkPurposes(attestation, purposes);
1363         checkDigests(attestation, digests);
1364         checkValidityPeriod(attestation, startTime, includesValidityDates);
1365         checkFlags(attestation);
1366         checkOrigin(attestation);
1367         checkAttestationApplicationId(attestation);
1368         checkAttestationDeviceProperties(devicePropertiesAttestation, attestation);
1369         checkAttestationNoUniqueIds(attestation);
1370     }
1371 
checkUnexpectedOids(Attestation attestation)1372     private void checkUnexpectedOids(Attestation attestation) {
1373         assertThat("Attestations must not contain any extra data",
1374                 attestation.getUnexpectedExtensionOids(), is(empty()));
1375     }
1376 
getSystemPatchLevel()1377     private int getSystemPatchLevel() {
1378         Matcher matcher = OS_PATCH_LEVEL_STRING_PATTERN.matcher(Build.VERSION.SECURITY_PATCH);
1379         String invalidPatternMessage = "Invalid pattern for security path level string "
1380                 + Build.VERSION.SECURITY_PATCH;
1381         assertTrue(invalidPatternMessage, matcher.matches());
1382         String year_string = matcher.group(OS_PATCH_LEVEL_YEAR_GROUP_NAME);
1383         String month_string = matcher.group(OS_PATCH_LEVEL_MONTH_GROUP_NAME);
1384         int patch_level = Integer.parseInt(year_string) * 100 + Integer.parseInt(month_string);
1385         return patch_level;
1386     }
1387 
getSystemOsVersion()1388     private int getSystemOsVersion() {
1389         return parseSystemOsVersion(Build.VERSION.RELEASE);
1390     }
1391 
parseSystemOsVersion(String versionString)1392     private int parseSystemOsVersion(String versionString) {
1393         Matcher matcher = OS_VERSION_STRING_PATTERN.matcher(versionString);
1394         if (!matcher.matches()) {
1395             return 0;
1396         }
1397 
1398         int version = 0;
1399         String major_string = matcher.group(OS_MAJOR_VERSION_MATCH_GROUP_NAME);
1400         String minor_string = matcher.group(OS_MINOR_VERSION_MATCH_GROUP_NAME);
1401         String subminor_string = matcher.group(OS_SUBMINOR_VERSION_MATCH_GROUP_NAME);
1402         if (major_string != null) {
1403             version += Integer.parseInt(major_string) * 10000;
1404         }
1405         if (minor_string != null) {
1406             version += Integer.parseInt(minor_string) * 100;
1407         }
1408         if (subminor_string != null) {
1409             version += Integer.parseInt(subminor_string);
1410         }
1411         return version;
1412     }
1413 
checkOrigin(Attestation attestation)1414     private void checkOrigin(Attestation attestation) {
1415         assertTrue("Origin must be defined",
1416                 attestation.getSoftwareEnforced().getOrigin() != null ||
1417                         attestation.getTeeEnforced().getOrigin() != null);
1418         if (attestation.getKeymasterVersion() != 0) {
1419             assertTrue("Origin may not be defined in both SW and TEE, except on keymaster0",
1420                     attestation.getSoftwareEnforced().getOrigin() == null ||
1421                             attestation.getTeeEnforced().getOrigin() == null);
1422         }
1423 
1424         if (attestation.getKeymasterSecurityLevel() == KM_SECURITY_LEVEL_SOFTWARE) {
1425             assertThat("For security level software,"
1426                             + " SoftwareEnforced origin must be " + KM_ORIGIN_GENERATED,
1427                     attestation.getSoftwareEnforced().getOrigin(), is(KM_ORIGIN_GENERATED));
1428         } else if (attestation.getKeymasterVersion() == 0) {
1429             assertThat("For KeyMaster version 0,"
1430                             + "TeeEnforced origin must be " + KM_ORIGIN_UNKNOWN,
1431                     attestation.getTeeEnforced().getOrigin(), is(KM_ORIGIN_UNKNOWN));
1432         } else {
1433             assertThat("TeeEnforced origin must be " + KM_ORIGIN_GENERATED,
1434                     attestation.getTeeEnforced().getOrigin(), is(KM_ORIGIN_GENERATED));
1435         }
1436     }
1437 
checkFlags(Attestation attestation)1438     private void checkFlags(Attestation attestation) {
1439         assertFalse("All applications was not requested",
1440                 attestation.getSoftwareEnforced().isAllApplications());
1441         assertFalse("All applications was not requested",
1442                 attestation.getTeeEnforced().isAllApplications());
1443         assertFalse("Allow while on body was not requested",
1444                 attestation.getSoftwareEnforced().isAllowWhileOnBody());
1445         assertFalse("Allow while on body was not requested",
1446                 attestation.getTeeEnforced().isAllowWhileOnBody());
1447         assertNull("Auth binding was not requiested",
1448                 attestation.getSoftwareEnforced().getUserAuthType());
1449         assertNull("Auth binding was not requiested",
1450                 attestation.getTeeEnforced().getUserAuthType());
1451         assertTrue("noAuthRequired must be true",
1452                 attestation.getSoftwareEnforced().isNoAuthRequired()
1453                         || attestation.getTeeEnforced().isNoAuthRequired());
1454         assertFalse("auth is either software or TEE",
1455                 attestation.getSoftwareEnforced().isNoAuthRequired()
1456                         && attestation.getTeeEnforced().isNoAuthRequired());
1457         assertFalse("Software cannot implement rollback resistance",
1458                 attestation.getSoftwareEnforced().isRollbackResistant());
1459     }
1460 
checkValidityPeriod(Attestation attestation, Date startTime, boolean includesValidityDates)1461     private void checkValidityPeriod(Attestation attestation, Date startTime,
1462             boolean includesValidityDates) {
1463         AuthorizationList validityPeriodList = attestation.getSoftwareEnforced();
1464         AuthorizationList nonValidityPeriodList = attestation.getTeeEnforced();
1465 
1466         // A bug in Android S leads Android S devices with KeyMint1 not to add a creationDateTime.
1467         boolean creationDateTimeBroken =
1468                 Build.VERSION.SDK_INT == Build.VERSION_CODES.S
1469                         && attestation.getKeymasterVersion() == Attestation.KM_VERSION_KEYMINT_1;
1470 
1471         if (!creationDateTimeBroken) {
1472             assertNull(nonValidityPeriodList.getCreationDateTime());
1473 
1474             Date creationDateTime = validityPeriodList.getCreationDateTime();
1475 
1476             boolean requireCreationDateTime =
1477                     attestation.getKeymasterVersion() >= Attestation.KM_VERSION_KEYMINT_1;
1478 
1479             if (requireCreationDateTime || creationDateTime != null) {
1480                 assertNotNull(creationDateTime);
1481 
1482                 assertTrue("Test start time (" + startTime.getTime() + ") and key creation time (" +
1483                                 creationDateTime.getTime() + ") should be close",
1484                         Math.abs(creationDateTime.getTime() - startTime.getTime()) <= 2000);
1485 
1486                 // Allow 1 second leeway in case of nearest-second rounding.
1487                 Date now = new Date();
1488                 assertTrue("Key creation time (" + creationDateTime.getTime() + ") must be now (" +
1489                                 now.getTime() + ") or earlier.",
1490                         now.getTime() >= (creationDateTime.getTime() - 1000));
1491             }
1492         }
1493 
1494         if (includesValidityDates) {
1495             Date activeDateTime = validityPeriodList.getActiveDateTime();
1496             Date originationExpirationDateTime = validityPeriodList.getOriginationExpireDateTime();
1497             Date usageExpirationDateTime = validityPeriodList.getUsageExpireDateTime();
1498 
1499             assertNotNull("Active date time should not be null in SoftwareEnforced"
1500                     + " authorization list.", activeDateTime);
1501             assertNotNull("Origination expiration date time should not be null in"
1502                             + " SoftwareEnforced authorization list.",
1503                     originationExpirationDateTime);
1504             assertNotNull("Usage expiration date time should not be null in SoftwareEnforced"
1505                     + " authorization list.", usageExpirationDateTime);
1506 
1507             assertNull("Active date time must not be included in TeeEnforced authorization list.",
1508                     nonValidityPeriodList.getActiveDateTime());
1509             assertNull("Origination date time must not be included in TeeEnforced authorization"
1510                     + "list.", nonValidityPeriodList.getOriginationExpireDateTime());
1511             assertNull("Usage expiration date time must not be included in TeeEnforced"
1512                             + " authorization list.",
1513                     nonValidityPeriodList.getUsageExpireDateTime());
1514 
1515             assertThat("Origination expiration date time must match with provided expiration"
1516                             + " date time.", originationExpirationDateTime.getTime(),
1517                     is(startTime.getTime() + ORIGINATION_TIME_OFFSET));
1518             assertThat("Usage (consumption) expiration date time must match with provided"
1519                             + " expiration date time.", usageExpirationDateTime.getTime(),
1520                     is(startTime.getTime() + CONSUMPTION_TIME_OFFSET));
1521         }
1522     }
1523 
checkDigests(Attestation attestation, Set<Integer> expectedDigests)1524     private void checkDigests(Attestation attestation, Set<Integer> expectedDigests) {
1525         Set<Integer> softwareEnforcedDigests = attestation.getSoftwareEnforced().getDigests();
1526         Set<Integer> teeEnforcedDigests = attestation.getTeeEnforced().getDigests();
1527 
1528         if (softwareEnforcedDigests == null) {
1529             softwareEnforcedDigests = ImmutableSet.of();
1530         }
1531         if (teeEnforcedDigests == null) {
1532             teeEnforcedDigests = ImmutableSet.of();
1533         }
1534 
1535         Set<Integer> allDigests = ImmutableSet.<Integer>builder()
1536                 .addAll(softwareEnforcedDigests)
1537                 .addAll(teeEnforcedDigests)
1538                 .build();
1539         Set<Integer> intersection = new ArraySet<>();
1540         intersection.addAll(softwareEnforcedDigests);
1541         intersection.retainAll(teeEnforcedDigests);
1542 
1543         assertThat("Set of digests from software enforced and Tee enforced must match"
1544                 + " with expected digests set.", allDigests, is(expectedDigests));
1545         assertTrue("Digest sets must be disjoint", intersection.isEmpty());
1546 
1547         if (attestation.getKeymasterSecurityLevel() == KM_SECURITY_LEVEL_SOFTWARE
1548                 || attestation.getKeymasterVersion() == 0) {
1549             assertThat("Digests in software-enforced",
1550                     softwareEnforcedDigests, is(expectedDigests));
1551         } else {
1552             if (attestation.getKeymasterVersion() == 1) {
1553                 // KM1 implementations may not support SHA512 in the TEE
1554                 assertTrue("KeyMaster version 1 may not support SHA256, in which case it must be"
1555                                 + " software-emulated.",
1556                         softwareEnforcedDigests.contains(KM_DIGEST_SHA_2_512)
1557                                 || teeEnforcedDigests.contains(KM_DIGEST_SHA_2_512));
1558 
1559                 assertThat("Tee enforced digests should have digests {none and SHA2-256}",
1560                         teeEnforcedDigests, hasItems(KM_DIGEST_NONE, KM_DIGEST_SHA_2_256));
1561             } else {
1562                 assertThat("Tee enforced digests should have all expected digests.",
1563                         teeEnforcedDigests, is(expectedDigests));
1564             }
1565         }
1566     }
1567 
checkPurposes(Attestation attestation, int purposes)1568     private Set<Integer> checkPurposes(Attestation attestation, int purposes) {
1569         Set<Integer> expectedPurposes = buildPurposeSet(purposes);
1570         if (attestation.getKeymasterSecurityLevel() == KM_SECURITY_LEVEL_SOFTWARE
1571                 || attestation.getKeymasterVersion() == 0) {
1572             assertThat("Purposes in software-enforced should match expected set",
1573                     attestation.getSoftwareEnforced().getPurposes(), is(expectedPurposes));
1574             assertNull("Should be no purposes in TEE-enforced",
1575                     attestation.getTeeEnforced().getPurposes());
1576         } else {
1577             assertThat("Purposes in TEE-enforced should match expected set",
1578                     attestation.getTeeEnforced().getPurposes(), is(expectedPurposes));
1579             assertNull("No purposes in software-enforced",
1580                     attestation.getSoftwareEnforced().getPurposes());
1581         }
1582         return expectedPurposes;
1583     }
1584 
checkSystemPatchLevel(int teeOsPatchLevel, int systemPatchLevel)1585     private void checkSystemPatchLevel(int teeOsPatchLevel, int systemPatchLevel) {
1586         if (TestUtils.isGsiImage()) {
1587             // b/168663786: When using a GSI image, the system patch level might be
1588             // greater than or equal to the OS patch level reported from TEE.
1589             assertThat("For GSI image TEE os patch level should be less than or equal to system"
1590                     + " patch level.", teeOsPatchLevel, lessThanOrEqualTo(systemPatchLevel));
1591         } else {
1592             assertThat("TEE os patch level must be equal to system patch level.",
1593                     teeOsPatchLevel, is(systemPatchLevel));
1594         }
1595     }
1596 
1597     @SuppressWarnings("unchecked")
checkAttestationSecurityLevelDependentParams(Attestation attestation)1598     private void checkAttestationSecurityLevelDependentParams(Attestation attestation) {
1599         assertThat("Attestation version must be one of: {1, 2, 3, 4, 100, 200, 300, 400}",
1600                 attestation.getAttestationVersion(),
1601                 either(is(1)).or(is(2)).or(is(3)).or(is(4))
1602                         .or(is(100)).or(is(200)).or(is(300)).or(is(400)));
1603 
1604         AuthorizationList teeEnforced = attestation.getTeeEnforced();
1605         AuthorizationList softwareEnforced = attestation.getSoftwareEnforced();
1606 
1607         int systemOsVersion = getSystemOsVersion();
1608         int systemPatchLevel = getSystemPatchLevel();
1609 
1610         switch (attestation.getAttestationSecurityLevel()) {
1611             case KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT:
1612                 assertThat("TEE attestation can only come from TEE keymaster",
1613                         attestation.getKeymasterSecurityLevel(),
1614                         is(KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT));
1615                 assertThat("KeyMaster version is not valid.", attestation.getKeymasterVersion(),
1616                         either(is(2)).or(is(3)).or(is(4)).or(is(41))
1617                                 .or(is(100)).or(is(200)).or(is(300)).or(is(400)));
1618 
1619                 checkRootOfTrust(attestation, false /* requireLocked */);
1620                 assertThat("TEE enforced OS version and system OS version must be same.",
1621                         teeEnforced.getOsVersion(), is(systemOsVersion));
1622                 checkSystemPatchLevel(teeEnforced.getOsPatchLevel(), systemPatchLevel);
1623                 break;
1624 
1625             case KM_SECURITY_LEVEL_STRONG_BOX:
1626                 assertThat("StrongBox attestation can only come from StrongBox keymaster",
1627                         attestation.getKeymasterSecurityLevel(),
1628                         is(KM_SECURITY_LEVEL_STRONG_BOX));
1629                 assertThat("KeyMaster version is not valid.", attestation.getKeymasterVersion(),
1630                         either(is(2)).or(is(3)).or(is(4)).or(is(41))
1631                                 .or(is(100)).or(is(200)).or(is(300)).or(is(400)));
1632 
1633                 checkRootOfTrust(attestation, false /* requireLocked */);
1634                 assertThat("StrongBox enforced OS version and system OS version must be same.",
1635                         teeEnforced.getOsVersion(), is(systemOsVersion));
1636                 checkSystemPatchLevel(teeEnforced.getOsPatchLevel(), systemPatchLevel);
1637                 break;
1638 
1639             case KM_SECURITY_LEVEL_SOFTWARE:
1640                 if (attestation
1641                         .getKeymasterSecurityLevel() == KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT) {
1642                     assertThat("TEE KM version must be 0 or 1 with software attestation",
1643                             attestation.getKeymasterVersion(), either(is(0)).or(is(1)));
1644                 } else {
1645                     assertThat("Software KM is version 3", attestation.getKeymasterVersion(),
1646                             is(3));
1647                     assertThat("Software enforced OS version and System OS version must be same.",
1648                             softwareEnforced.getOsVersion(), is(systemOsVersion));
1649                     checkSystemPatchLevel(softwareEnforced.getOsPatchLevel(), systemPatchLevel);
1650                 }
1651 
1652                 assertNull("Software attestation cannot provide root of trust",
1653                         teeEnforced.getRootOfTrust());
1654 
1655                 break;
1656 
1657             default:
1658                 fail("Invalid attestation security level: "
1659                         + attestation.getAttestationSecurityLevel());
1660                 break;
1661         }
1662     }
1663 
checkDeviceLocked(Attestation attestation)1664     private void checkDeviceLocked(Attestation attestation) {
1665         assertThat("Attestation version must be >= 1",
1666                 attestation.getAttestationVersion(), greaterThanOrEqualTo(1));
1667 
1668         int attestationSecurityLevel = attestation.getAttestationSecurityLevel();
1669         switch (attestationSecurityLevel) {
1670             case KM_SECURITY_LEVEL_STRONG_BOX:
1671             case KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT:
1672                 assertThat("Attestation security level doesn't match keymaster security level",
1673                         attestation.getKeymasterSecurityLevel(), is(attestationSecurityLevel));
1674                 assertThat("Keymaster version should be greater than or equal to 2.",
1675                         attestation.getKeymasterVersion(), greaterThanOrEqualTo(2));
1676 
1677                 // Devices launched in Android 10.0 (API level 29) and after should run CTS
1678                 // in LOCKED state.
1679                 boolean requireLocked = PropertyUtil.getFirstApiLevel() >= 29;
1680                 checkRootOfTrust(attestation, requireLocked);
1681                 break;
1682 
1683             case KM_SECURITY_LEVEL_SOFTWARE:
1684             default:
1685                 // TEE attestation has been required since Android 7.0.
1686                 fail("Unexpected attestation security level: " +
1687                         Attestation.securityLevelToString(attestationSecurityLevel));
1688                 break;
1689         }
1690     }
1691 
checkVerifiedBootHash(byte[] verifiedBootHash)1692     private void checkVerifiedBootHash(byte[] verifiedBootHash) {
1693         assertNotNull(verifiedBootHash);
1694         assertEquals(32, verifiedBootHash.length);
1695         checkEntropy(verifiedBootHash, "rootOfTrust.verifiedBootHash" /* dataName */);
1696 
1697         StringBuilder hexVerifiedBootHash = new StringBuilder(verifiedBootHash.length * 2);
1698         for (byte b : verifiedBootHash) {
1699             hexVerifiedBootHash.append(String.format("%02x", b));
1700         }
1701         String bootVbMetaDigest = SystemProperties.get("ro.boot.vbmeta.digest", "");
1702         assertEquals(
1703                 "VerifiedBootHash field of RootOfTrust section does not match with"
1704                         + "system property ro.boot.vbmeta.digest",
1705                 bootVbMetaDigest, hexVerifiedBootHash.toString());
1706     }
1707 
checkRootOfTrust(Attestation attestation, boolean requireLocked)1708     private void checkRootOfTrust(Attestation attestation, boolean requireLocked) {
1709         RootOfTrust rootOfTrust = attestation.getRootOfTrust();
1710         assertNotNull(rootOfTrust);
1711         assertNotNull(rootOfTrust.getVerifiedBootKey());
1712         assertTrue("Verified boot key is only " + rootOfTrust.getVerifiedBootKey().length +
1713                 " bytes long", rootOfTrust.getVerifiedBootKey().length >= 32);
1714         if (requireLocked) {
1715             final String unlockedDeviceMessage = "The device's bootloader must be locked. This may "
1716                     + "not be the default for pre-production devices.";
1717             assertTrue(unlockedDeviceMessage, rootOfTrust.isDeviceLocked());
1718             checkEntropy(rootOfTrust.getVerifiedBootKey(),
1719                     "rootOfTrust.verifiedBootKey" /* dataName */);
1720             assertEquals(KM_VERIFIED_BOOT_VERIFIED, rootOfTrust.getVerifiedBootState());
1721 
1722             if (PropertyUtil.getFirstApiLevel() >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
1723                 // The Verified Boot hash was not previously checked in CTS, so set an API level
1724                 // check to avoid running into waiver issues.
1725                 checkVerifiedBootHash(rootOfTrust.getVerifiedBootHash());
1726             }
1727         } else {
1728             // We expect exactly one of these combinations of values because either:
1729             //   1) CTS is running on a signed bootloader-locked build verified with the OEM's root
1730             //      of trust, so the Verified Boot state is KM_VERIFIED_BOOT_VERIFIED.
1731             //   OR
1732             //   2) CTS is running on a signed GSI. This requires an unlocked bootloader and
1733             //      therefore a chain of trust cannot be established, so the Verified Boot state is
1734             //      KM_VERIFIED_BOOT_UNVERIFIED.
1735             // Builds with a custom root of trust (which would result in
1736             // KM_VERIFIED_BOOT_SELF_SIGNED and could have a locked or unlocked bootloader)
1737             // shouldn't pass CTS since they don't comply with CDD requirement 9.10 [C-1-3] which
1738             // requires use of the fused OEM root of trust for Verified Boot.
1739             boolean isLocked = rootOfTrust.getVerifiedBootState() == KM_VERIFIED_BOOT_VERIFIED
1740                     && rootOfTrust.isDeviceLocked();
1741             boolean isUnlocked = rootOfTrust.getVerifiedBootState() == KM_VERIFIED_BOOT_UNVERIFIED
1742                     && !rootOfTrust.isDeviceLocked();
1743             assertTrue("Unexpected combination of device locked state and Verified Boot "
1744                     + "state.", isLocked || isUnlocked);
1745         }
1746     }
1747 
checkEntropy(byte[] data, String dataName)1748     private void checkEntropy(byte[] data, String dataName) {
1749         byte[] dataCopy = Arrays.copyOf(data, data.length);
1750         assertTrue("Failed Shannon entropy check", checkShannonEntropy(dataCopy, dataName));
1751         assertTrue("Failed BiEntropy check", checkTresBiEntropy(dataCopy, dataName));
1752     }
1753 
checkShannonEntropy(byte[] data, String dataName)1754     private boolean checkShannonEntropy(byte[] data, String dataName) {
1755         double probabilityOfSetBit = countSetBits(data) / (double) (data.length * 8);
1756         return calculateShannonEntropy(probabilityOfSetBit, dataName) > 0.8;
1757     }
1758 
calculateShannonEntropy(double probabilityOfSetBit, String dataName)1759     private double calculateShannonEntropy(double probabilityOfSetBit, String dataName) {
1760         if (probabilityOfSetBit <= 0.001 || probabilityOfSetBit >= .999) return 0;
1761         double entropy = (-probabilityOfSetBit * logTwo(probabilityOfSetBit)) -
1762                 ((1 - probabilityOfSetBit) * logTwo(1 - probabilityOfSetBit));
1763         Log.i(TAG, "Shannon entropy of " + dataName + ": " + entropy);
1764         return entropy;
1765     }
1766 
1767     /**
1768      * Note: This method modifies the input parameter while performing bit entropy check.
1769      */
checkTresBiEntropy(byte[] data, String dataName)1770     private boolean checkTresBiEntropy(byte[] data, String dataName) {
1771         double weightingFactor = 0;
1772         double weightedEntropy = 0;
1773         double probabilityOfSetBit = 0;
1774         int length = data.length * 8;
1775         for (int i = 0; i < (data.length * 8) - 2; i++) {
1776             probabilityOfSetBit = countSetBits(data) / (double) length;
1777             weightingFactor += logTwo(i + 2);
1778             weightedEntropy += calculateShannonEntropy(probabilityOfSetBit, dataName)
1779                     * logTwo(i + 2);
1780             deriveBitString(data, length);
1781             length -= 1;
1782         }
1783         double tresBiEntropy = (1 / weightingFactor) * weightedEntropy;
1784         Log.i(TAG, "BiEntropy of " + dataName + ": " + tresBiEntropy);
1785         return tresBiEntropy > 0.9;
1786     }
1787 
1788     /**
1789      * Note: This method modifies the input parameter - bitString.
1790      */
deriveBitString(byte[] bitString, int activeLength)1791     private void deriveBitString(byte[] bitString, int activeLength) {
1792         int length = activeLength / 8;
1793         if (activeLength % 8 != 0) {
1794             length += 1;
1795         }
1796 
1797         byte mask = (byte) ((byte) 0x80 >>> ((activeLength + 6) % 8));
1798         if (activeLength % 8 == 1) {
1799             mask = (byte) ~mask;
1800         }
1801 
1802         for (int i = 0; i < length; i++) {
1803             if (i == length - 1) {
1804                 bitString[i] ^= ((bitString[i] & 0xFF) << 1);
1805                 bitString[i] &= mask;
1806             } else {
1807                 bitString[i] ^= ((bitString[i] & 0xFF) << 1) | ((bitString[i + 1] & 0xFF) >>> 7);
1808             }
1809         }
1810     }
1811 
logTwo(double value)1812     private double logTwo(double value) {
1813         return Math.log(value) / Math.log(2);
1814     }
1815 
countSetBits(byte[] toCount)1816     private int countSetBits(byte[] toCount) {
1817         int setBitCount = 0;
1818         for (int i = 0; i < toCount.length; i++) {
1819             setBitCount += countSetBits(toCount[i]);
1820         }
1821         return setBitCount;
1822     }
1823 
countSetBits(byte toCount)1824     private int countSetBits(byte toCount) {
1825         int setBitCounter = 0;
1826         while (toCount != 0) {
1827             toCount &= (toCount - 1);
1828             setBitCounter++;
1829         }
1830         return setBitCounter;
1831     }
1832 
checkRsaKeyDetails(X509Certificate attestationCert, Attestation attestation, int keySize, Set<String> expectedPaddingModes)1833     private void checkRsaKeyDetails(X509Certificate attestationCert, Attestation attestation,
1834             int keySize, Set<String> expectedPaddingModes)
1835             throws CertificateParsingException {
1836         AuthorizationList keyDetailsList;
1837         AuthorizationList nonKeyDetailsList;
1838         if (attestation.getKeymasterSecurityLevel() == KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT
1839                 || attestation.getKeymasterSecurityLevel() == KM_SECURITY_LEVEL_STRONG_BOX) {
1840             keyDetailsList = attestation.getTeeEnforced();
1841             nonKeyDetailsList = attestation.getSoftwareEnforced();
1842         } else {
1843             keyDetailsList = attestation.getSoftwareEnforced();
1844             nonKeyDetailsList = attestation.getTeeEnforced();
1845         }
1846         assertEquals(keySize, keyDetailsList.getKeySize().intValue());
1847         assertNull(nonKeyDetailsList.getKeySize());
1848         assertEquals(keySize,
1849                 ((RSAPublicKey) attestationCert.getPublicKey()).getModulus().bitLength());
1850 
1851         assertEquals(KM_ALGORITHM_RSA, keyDetailsList.getAlgorithm().intValue());
1852         assertNull(nonKeyDetailsList.getAlgorithm());
1853         assertEquals(KEY_ALGORITHM_RSA, attestationCert.getPublicKey().getAlgorithm());
1854 
1855         assertNull(keyDetailsList.getEcCurve());
1856         assertNull(nonKeyDetailsList.getEcCurve());
1857 
1858         assertEquals(65537, keyDetailsList.getRsaPublicExponent().longValue());
1859         assertNull(nonKeyDetailsList.getRsaPublicExponent());
1860         assertEquals(65537,
1861                 ((RSAPublicKey) attestationCert.getPublicKey()).getPublicExponent().intValue());
1862 
1863         Set<String> paddingModes;
1864         if (attestation.getKeymasterVersion() == 0) {
1865             // KM0 implementations don't support padding info, so it's always in the
1866             // software-enforced list.
1867             paddingModes = attestation.getSoftwareEnforced().getPaddingModesAsStrings();
1868             assertNull(attestation.getTeeEnforced().getPaddingModes());
1869         } else {
1870             paddingModes = keyDetailsList.getPaddingModesAsStrings();
1871             assertNull(nonKeyDetailsList.getPaddingModes());
1872         }
1873 
1874         // KM1 implementations may add ENCRYPTION_PADDING_NONE to the list of paddings.
1875         Set<String> km1PossiblePaddingModes = expectedPaddingModes;
1876         if (attestation.getKeymasterVersion() == 1 &&
1877                 attestation.getKeymasterSecurityLevel() == KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT) {
1878             ImmutableSet.Builder<String> builder = ImmutableSet.builder();
1879             builder.addAll(expectedPaddingModes);
1880             builder.add(ENCRYPTION_PADDING_NONE);
1881             km1PossiblePaddingModes = builder.build();
1882         }
1883 
1884         assertThat("Attested padding mode does not matched with expected modes.",
1885                 paddingModes, either(is(expectedPaddingModes)).or(is(km1PossiblePaddingModes)));
1886     }
1887 
checkEcKeyDetails(X509Certificate attestationCert, Attestation attestation, String ecCurve, int keySize)1888     private void checkEcKeyDetails(X509Certificate attestationCert, Attestation attestation,
1889             String ecCurve, int keySize) {
1890         AuthorizationList keyDetailsList;
1891         AuthorizationList nonKeyDetailsList;
1892         if (attestation.getKeymasterSecurityLevel() == KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT
1893                 || attestation.getKeymasterSecurityLevel() == KM_SECURITY_LEVEL_STRONG_BOX) {
1894             keyDetailsList = attestation.getTeeEnforced();
1895             nonKeyDetailsList = attestation.getSoftwareEnforced();
1896         } else {
1897             keyDetailsList = attestation.getSoftwareEnforced();
1898             nonKeyDetailsList = attestation.getTeeEnforced();
1899         }
1900         assertEquals(keySize, keyDetailsList.getKeySize().intValue());
1901         assertEquals(keySize, sECKeySizes.get(ecCurve).intValue());
1902         assertNull(nonKeyDetailsList.getKeySize());
1903         assertEquals(KM_ALGORITHM_EC, keyDetailsList.getAlgorithm().intValue());
1904         // Curve25519 Public key returns OID string for getAlgorithm
1905         if (!ecCurve.equals("CURVE_25519")) {
1906             assertEquals(KEY_ALGORITHM_EC, attestationCert.getPublicKey().getAlgorithm());
1907         } else {
1908             assertThat(attestationCert.getPublicKey().getAlgorithm(),
1909                     /*Signing key algorithm "1.3.101.112" & Agreement Key algorithm "XDH"*/
1910                     either(is("1.3.101.112")).or(is("XDH")));
1911         }
1912         assertNull(nonKeyDetailsList.getAlgorithm());
1913         assertEquals(ecCurve, keyDetailsList.ecCurveAsString());
1914         // Curve25519 Public key(X509PublicKey) cannot be cast to ECPublicKey and hence could not
1915         // determine EC key parameters such as FieldFp, a, b, gx, gy, order and cofactor
1916         if (!ecCurve.equals("CURVE_25519")) {
1917             TestUtils.assertECParameterSpecEqualsIgnoreSeedIfNotPresent(
1918                     getECParameterSpecFor(ecCurve),
1919                     ((ECPublicKey) attestationCert.getPublicKey()).getParams());
1920         }
1921         assertNull(nonKeyDetailsList.getEcCurve());
1922         assertNull(keyDetailsList.getRsaPublicExponent());
1923         assertNull(nonKeyDetailsList.getRsaPublicExponent());
1924         assertNull(keyDetailsList.getPaddingModes());
1925         assertNull(nonKeyDetailsList.getPaddingModes());
1926     }
1927 
getECParameterSpecFor(String ecCurve)1928     private ECParameterSpec getECParameterSpecFor(String ecCurve) {
1929         if (ecCurve.equals("secp224r1")) {
1930             return ECCurves.NIST_P_224_SPEC;
1931         } else if (ecCurve.equals("secp256r1")) {
1932             return ECCurves.NIST_P_256_SPEC;
1933         } else if (ecCurve.equals("secp384r1")) {
1934             return ECCurves.NIST_P_384_SPEC;
1935         } else if (ecCurve.equals("secp521r1")) {
1936             return ECCurves.NIST_P_521_SPEC;
1937         }
1938         return null;
1939     }
1940 
isEncryptionPurpose(int purposes)1941     private boolean isEncryptionPurpose(int purposes) {
1942         return (purposes & PURPOSE_DECRYPT) != 0 || (purposes & PURPOSE_ENCRYPT) != 0;
1943     }
1944 
isSignaturePurpose(int purposes)1945     private boolean isSignaturePurpose(int purposes) {
1946         return (purposes & PURPOSE_SIGN) != 0 || (purposes & PURPOSE_VERIFY) != 0;
1947     }
1948 
isAgreeKeyPurpose(int purposes)1949     private boolean isAgreeKeyPurpose(int purposes) {
1950         return (purposes & PURPOSE_AGREE_KEY) != 0;
1951     }
1952 
buildPurposeSet(int purposes)1953     private ImmutableSet<Integer> buildPurposeSet(int purposes) {
1954         ImmutableSet.Builder<Integer> builder = ImmutableSet.builder();
1955         if ((purposes & PURPOSE_SIGN) != 0) {
1956             builder.add(KM_PURPOSE_SIGN);
1957         }
1958         if ((purposes & PURPOSE_VERIFY) != 0) {
1959             builder.add(KM_PURPOSE_VERIFY);
1960         }
1961         if ((purposes & PURPOSE_ENCRYPT) != 0) {
1962             builder.add(KM_PURPOSE_ENCRYPT);
1963         }
1964         if ((purposes & PURPOSE_DECRYPT) != 0) {
1965             builder.add(KM_PURPOSE_DECRYPT);
1966         }
1967         if ((purposes & PURPOSE_AGREE_KEY) != 0) {
1968             builder.add(KM_PURPOSE_AGREE_KEY);
1969         }
1970         return builder.build();
1971     }
1972 
generateKey(KeyGenParameterSpec spec, String algorithm)1973     private void generateKey(KeyGenParameterSpec spec, String algorithm)
1974             throws NoSuchAlgorithmException, NoSuchProviderException,
1975             InvalidAlgorithmParameterException {
1976         KeyGenerator keyGenerator = KeyGenerator.getInstance(algorithm, "AndroidKeyStore");
1977         keyGenerator.init(spec);
1978         keyGenerator.generateKey();
1979     }
1980 
generateKeyPair(String algorithm, KeyGenParameterSpec spec)1981     private void generateKeyPair(String algorithm, KeyGenParameterSpec spec)
1982             throws NoSuchAlgorithmException, NoSuchProviderException,
1983             InvalidAlgorithmParameterException {
1984         KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm,
1985                 "AndroidKeyStore");
1986         keyPairGenerator.initialize(spec);
1987         keyPairGenerator.generateKeyPair();
1988     }
1989 
verifyCertificateChain(Certificate[] certChain, boolean expectStrongBox)1990     public static void verifyCertificateChain(Certificate[] certChain, boolean expectStrongBox)
1991             throws GeneralSecurityException {
1992         assertNotNull(certChain);
1993         boolean strongBoxSubjectFound = false;
1994         for (int i = 1; i < certChain.length; ++i) {
1995             try {
1996                 PublicKey pubKey = certChain[i].getPublicKey();
1997                 certChain[i - 1].verify(pubKey);
1998                 if (i == certChain.length - 1) {
1999                     // Last cert should be self-signed.
2000                     certChain[i].verify(pubKey);
2001                 }
2002 
2003                 // Check that issuer in the signed cert matches subject in the signing cert.
2004                 X509Certificate x509CurrCert = (X509Certificate) certChain[i];
2005                 X509Certificate x509PrevCert = (X509Certificate) certChain[i - 1];
2006                 X500Name signingCertSubject =
2007                         new JcaX509CertificateHolder(x509CurrCert).getSubject();
2008                 X500Name signedCertIssuer =
2009                         new JcaX509CertificateHolder(x509PrevCert).getIssuer();
2010                 // Use .toASN1Object().equals() rather than .equals() because .equals() is case
2011                 // insensitive, and we want to verify an exact match.
2012                 assertTrue(String.format("Certificate Issuer (%s) is not matching with parent"
2013                                         + " certificate's Subject (%s).",
2014                                 signedCertIssuer.toString(), signingCertSubject.toString()),
2015                         signedCertIssuer.toASN1Object().equals(signingCertSubject.toASN1Object()));
2016 
2017                 X500Name signedCertSubject =
2018                         new JcaX509CertificateHolder(x509PrevCert).getSubject();
2019                 if (i == 1) {
2020                     // First cert should have subject "CN=Android Keystore Key".
2021                     assertEquals(signedCertSubject, new X500Name("CN=Android Keystore Key"));
2022                 } else if (signedCertSubject.toString().toLowerCase().contains("strongbox")) {
2023                     strongBoxSubjectFound = true;
2024                 }
2025             } catch (InvalidKeyException | CertificateException | NoSuchAlgorithmException
2026                      | NoSuchProviderException | SignatureException e) {
2027                 throw new GeneralSecurityException("Using StrongBox: " + expectStrongBox + "\n"
2028                         + "Failed to verify certificate " + certChain[i - 1]
2029                         + " with public key " + certChain[i].getPublicKey(),
2030                         e);
2031             }
2032         }
2033         // At least one intermediate in a StrongBox chain must have "strongbox" in the subject.
2034         assertEquals(expectStrongBox, strongBoxSubjectFound);
2035     }
2036 
testDeviceIdAttestationFailure(int idType, String acceptableDeviceIdAttestationFailureMessage)2037     private void testDeviceIdAttestationFailure(int idType,
2038             String acceptableDeviceIdAttestationFailureMessage) throws Exception {
2039         try {
2040             AttestationUtils.attestDeviceIds(getContext(), new int[]{idType}, "123".getBytes());
2041             fail("Attestation should have failed.");
2042         } catch (SecurityException e) {
2043             // Attestation is expected to fail. If the device has the device ID type we are trying
2044             // to attest, it should fail with a SecurityException as we do not hold
2045             // READ_PRIVILEGED_PHONE_STATE permission.
2046         } catch (DeviceIdAttestationException e) {
2047             // Attestation is expected to fail. If the device does not have the device ID type we
2048             // are trying to attest (e.g. no IMEI on devices without a radio), it should fail with
2049             // a corresponding DeviceIdAttestationException.
2050             if (acceptableDeviceIdAttestationFailureMessage == null ||
2051                     !acceptableDeviceIdAttestationFailureMessage.equals(e.getMessage())) {
2052                 throw e;
2053             }
2054         }
2055     }
2056 
2057     // Indicate whether the provided exception indicates an ID attestation failure that
2058     // can be ignored (because the device doesn't support ID attestation).  This method
2059     // will throw a new exception if the error is an ID attestation failure that can't
2060     // be ignored.
isIgnorableIdAttestationFailure(Throwable e)2061     private boolean isIgnorableIdAttestationFailure(Throwable e) throws Exception {
2062         if (!(e.getCause() instanceof KeyStoreException)) {
2063             return false;
2064         }
2065         if (((KeyStoreException) e.getCause()).getNumericErrorCode()
2066                 != KeyStoreException.ERROR_ID_ATTESTATION_FAILURE) {
2067             return false;
2068         }
2069         if (!getContext().getPackageManager().hasSystemFeature(
2070                 PackageManager.FEATURE_DEVICE_ID_ATTESTATION)) {
2071             Log.i(TAG, "key attestation with device IDs not supported; test skipped");
2072             return true;
2073         }
2074 
2075         if (TestUtils.isGsiImage()) {
2076             // When running under GSI, the ro.product.<device-id> values may be replaced with values
2077             // that don't match the vendor code. However, any ro.product.vendor.<device-id> values
2078             // will not be replaced by GSI (and the frameworks code will use them in preference to
2079             // the ro.product.<device-id> values).
2080             if (TestUtils.getVendorApiLevel() < Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
2081                 // On older releases just skip the failure.
2082                 Log.i(TAG, "device ID attestation on older device under GSI; test skipped");
2083                 return true;
2084             } else {
2085                 // On more current devices, raise a different Exception to give out a hint
2086                 // that the vendor properties should be set.
2087                 throw new Exception("Failed to generate key with device ID attestation under GSI.  "
2088                         + "Check that the relevant ro.product.vendor.<device-id> field is "
2089                         + "set correctly in the vendor image.",
2090                         e);
2091             }
2092         }
2093         return false;
2094     }
2095 }
2096