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