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