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_TRUSTED_ENVIRONMENT;
21 import static android.keystore.cts.AuthorizationList.KM_ALGORITHM_EC;
22 import static android.keystore.cts.AuthorizationList.KM_ALGORITHM_RSA;
23 import static android.keystore.cts.AuthorizationList.KM_DIGEST_NONE;
24 import static android.keystore.cts.AuthorizationList.KM_DIGEST_SHA_2_256;
25 import static android.keystore.cts.AuthorizationList.KM_DIGEST_SHA_2_512;
26 import static android.keystore.cts.AuthorizationList.KM_ORIGIN_GENERATED;
27 import static android.keystore.cts.AuthorizationList.KM_ORIGIN_UNKNOWN;
28 import static android.keystore.cts.AuthorizationList.KM_PURPOSE_DECRYPT;
29 import static android.keystore.cts.AuthorizationList.KM_PURPOSE_ENCRYPT;
30 import static android.keystore.cts.AuthorizationList.KM_PURPOSE_SIGN;
31 import static android.keystore.cts.AuthorizationList.KM_PURPOSE_VERIFY;
32 import static android.keystore.cts.RootOfTrust.KM_VERIFIED_BOOT_VERIFIED;
33 import static android.security.keystore.KeyProperties.DIGEST_NONE;
34 import static android.security.keystore.KeyProperties.DIGEST_SHA256;
35 import static android.security.keystore.KeyProperties.DIGEST_SHA512;
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_DECRYPT;
42 import static android.security.keystore.KeyProperties.PURPOSE_ENCRYPT;
43 import static android.security.keystore.KeyProperties.PURPOSE_SIGN;
44 import static android.security.keystore.KeyProperties.PURPOSE_VERIFY;
45 import static android.security.keystore.KeyProperties.SIGNATURE_PADDING_RSA_PKCS1;
46 import static android.security.keystore.KeyProperties.SIGNATURE_PADDING_RSA_PSS;
47 import static org.hamcrest.CoreMatchers.is;
48 import static org.junit.Assert.assertThat;
49 import static org.junit.matchers.JUnitMatchers.either;
50 import static org.junit.matchers.JUnitMatchers.hasItems;
51 
52 import com.google.common.collect.ImmutableSet;
53 import android.content.pm.PackageManager.NameNotFoundException;
54 import android.content.Context;
55 import android.os.Build;
56 import android.os.SystemProperties;
57 import android.security.KeyStoreException;
58 import android.security.keystore.AttestationUtils;
59 import android.security.keystore.DeviceIdAttestationException;
60 import android.security.keystore.KeyGenParameterSpec;
61 import android.security.keystore.KeyProperties;
62 import android.test.AndroidTestCase;
63 import android.util.ArraySet;
64 
65 import com.android.org.bouncycastle.asn1.x500.X500Name;
66 import com.android.org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
67 
68 import java.security.GeneralSecurityException;
69 import java.security.InvalidAlgorithmParameterException;
70 import java.security.InvalidKeyException;
71 import java.security.KeyPairGenerator;
72 import java.security.KeyStore;
73 import java.security.NoSuchAlgorithmException;
74 import java.security.NoSuchProviderException;
75 import java.security.ProviderException;
76 import java.security.PublicKey;
77 import java.security.SignatureException;
78 import java.security.cert.Certificate;
79 import java.security.cert.CertificateException;
80 import java.security.cert.CertificateParsingException;
81 import java.security.cert.X509Certificate;
82 import java.security.spec.ECGenParameterSpec;
83 import java.util.Arrays;
84 import java.util.Date;
85 import java.util.Set;
86 import java.util.regex.Matcher;
87 import java.util.regex.Pattern;
88 
89 import javax.crypto.KeyGenerator;
90 
91 /**
92  * Tests for Android KeysStore attestation.
93  */
94 public class KeyAttestationTest extends AndroidTestCase {
95 
96     private static final int ORIGINATION_TIME_OFFSET = 1000000;
97     private static final int CONSUMPTION_TIME_OFFSET = 2000000;
98 
99     private static final int KEY_USAGE_BITSTRING_LENGTH = 9;
100     private static final int KEY_USAGE_DIGITAL_SIGNATURE_BIT_OFFSET = 0;
101     private static final int KEY_USAGE_KEY_ENCIPHERMENT_BIT_OFFSET = 2;
102     private static final int KEY_USAGE_DATA_ENCIPHERMENT_BIT_OFFSET = 3;
103 
104     private static final int OS_MAJOR_VERSION_MATCH_GROUP_NAME = 1;
105     private static final int OS_MINOR_VERSION_MATCH_GROUP_NAME = 2;
106     private static final int OS_SUBMINOR_VERSION_MATCH_GROUP_NAME = 3;
107     private static final Pattern OS_VERSION_STRING_PATTERN = Pattern
108             .compile("([0-9]{1,2})(?:\\.([0-9]{1,2}))?(?:\\.([0-9]{1,2}))?(?:[^0-9.]+.*)?");
109 
110     private static final int OS_PATCH_LEVEL_YEAR_GROUP_NAME = 1;
111     private static final int OS_PATCH_LEVEL_MONTH_GROUP_NAME = 2;
112     private static final Pattern OS_PATCH_LEVEL_STRING_PATTERN = Pattern
113             .compile("([0-9]{4})-([0-9]{2})-[0-9]{2}");
114 
115     private static final int KM_ERROR_INVALID_INPUT_LENGTH = -21;
116     private static final int KM_ERROR_PERMISSION_DENIED = 6;
117 
testVersionParser()118     public void testVersionParser() throws Exception {
119         // Non-numerics/empty give version 0
120         assertEquals(0, parseSystemOsVersion(""));
121         assertEquals(0, parseSystemOsVersion("N"));
122 
123         // Should support one, two or three version number values.
124         assertEquals(10000, parseSystemOsVersion("1"));
125         assertEquals(10200, parseSystemOsVersion("1.2"));
126         assertEquals(10203, parseSystemOsVersion("1.2.3"));
127 
128         // It's fine to append other stuff to the dotted numeric version.
129         assertEquals(10000, parseSystemOsVersion("1stuff"));
130         assertEquals(10200, parseSystemOsVersion("1.2garbage.32"));
131         assertEquals(10203, parseSystemOsVersion("1.2.3-stuff"));
132 
133         // Two digits per version field are supported
134         assertEquals(152536, parseSystemOsVersion("15.25.36"));
135         assertEquals(999999, parseSystemOsVersion("99.99.99"));
136         assertEquals(0, parseSystemOsVersion("100.99.99"));
137         assertEquals(0, parseSystemOsVersion("99.100.99"));
138         assertEquals(0, parseSystemOsVersion("99.99.100"));
139     }
140 
testEcAttestation()141     public void testEcAttestation() throws Exception {
142         // Note: Curve and key sizes arrays must correspond.
143         String[] curves = {
144                 "secp224r1", "secp256r1", "secp384r1", "secp521r1"
145         };
146         int[] keySizes = {
147                 224, 256, 384, 521
148         };
149         byte[][] challenges = {
150                 new byte[0], // empty challenge
151                 "challenge".getBytes(), // short challenge
152                 new byte[128], // long challenge
153         };
154         int[] purposes = {
155                 KM_PURPOSE_SIGN, KM_PURPOSE_VERIFY, KM_PURPOSE_SIGN | KM_PURPOSE_VERIFY
156         };
157 
158         for (int curveIndex = 0; curveIndex < curves.length; ++curveIndex) {
159             for (int challengeIndex = 0; challengeIndex < challenges.length; ++challengeIndex) {
160                 for (int purposeIndex = 0; purposeIndex < purposes.length; ++purposeIndex) {
161                     try {
162                         testEcAttestation(challenges[challengeIndex],
163                                 true /* includeValidityDates */,
164                                 curves[curveIndex], keySizes[curveIndex], purposes[purposeIndex]);
165                         testEcAttestation(challenges[challengeIndex],
166                                 false /* includeValidityDates */,
167                                 curves[curveIndex], keySizes[curveIndex], purposes[purposeIndex]);
168                     } catch (Throwable e) {
169                         throw new Exception(
170                                 "Failed on curve " + curveIndex + " and challege " + challengeIndex,
171                                 e);
172                     }
173                 }
174             }
175         }
176     }
177 
testEcAttestation_TooLargeChallenge()178     public void testEcAttestation_TooLargeChallenge() throws Exception {
179         try {
180             testEcAttestation(new byte[129], true /* includeValidityDates */, "secp256r1", 256,
181                     KM_PURPOSE_SIGN);
182             fail("Attestation challenges larger than 128 bytes should be rejected");
183         } catch (ProviderException e) {
184             KeyStoreException cause = (KeyStoreException) e.getCause();
185             assertEquals(KM_ERROR_INVALID_INPUT_LENGTH, cause.getErrorCode());
186         }
187     }
188 
testEcAttestation_NoChallenge()189     public void testEcAttestation_NoChallenge() throws Exception {
190         String keystoreAlias = "test_key";
191         Date now = new Date();
192         Date originationEnd = new Date(now.getTime() + ORIGINATION_TIME_OFFSET);
193         Date consumptionEnd = new Date(now.getTime() + CONSUMPTION_TIME_OFFSET);
194         KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(keystoreAlias, PURPOSE_SIGN)
195                 .setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1"))
196                 .setDigests(DIGEST_NONE, DIGEST_SHA256, DIGEST_SHA512)
197                 .setAttestationChallenge(null)
198                 .setKeyValidityStart(now)
199                 .setKeyValidityForOriginationEnd(originationEnd)
200                 .setKeyValidityForConsumptionEnd(consumptionEnd)
201                 .build();
202 
203         generateKeyPair(KEY_ALGORITHM_EC, spec);
204 
205         KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
206         keyStore.load(null);
207 
208         try {
209             Certificate certificates[] = keyStore.getCertificateChain(keystoreAlias);
210             assertEquals(1, certificates.length);
211 
212             X509Certificate attestationCert = (X509Certificate) certificates[0];
213             assertNull(attestationCert.getExtensionValue(Attestation.KEY_DESCRIPTION_OID));
214         } finally {
215             keyStore.deleteEntry(keystoreAlias);
216         }
217     }
218 
testEcAttestation_KeyStoreExceptionWhenRequestingUniqueId()219     public void testEcAttestation_KeyStoreExceptionWhenRequestingUniqueId() throws Exception {
220         String keystoreAlias = "test_key";
221         KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(keystoreAlias, PURPOSE_SIGN)
222                 .setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1"))
223                 .setDigests(DIGEST_NONE, DIGEST_SHA256, DIGEST_SHA512)
224                 .setAttestationChallenge(new byte[128])
225                 .setUniqueIdIncluded(true)
226                 .build();
227 
228         try {
229             generateKeyPair(KEY_ALGORITHM_EC, spec);
230             fail("Attestation should have failed.");
231         } catch (ProviderException e) {
232             // Attestation is expected to fail because of lack of permissions.
233             KeyStoreException cause = (KeyStoreException) e.getCause();
234             assertEquals(KM_ERROR_PERMISSION_DENIED, cause.getErrorCode());
235         } finally {
236             KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
237             keyStore.load(null);
238             keyStore.deleteEntry(keystoreAlias);
239         }
240     }
241 
testRsaAttestation()242     public void testRsaAttestation() throws Exception {
243         int[] keySizes = { // Smallish sizes to keep test runtimes down.
244                 512, 768, 1024
245         };
246         byte[][] challenges = {
247                 new byte[0], // empty challenge
248                 "challenge".getBytes(), // short challenge
249                 new byte[128] // long challenge
250         };
251         int[] purposes = {
252                 PURPOSE_SIGN | PURPOSE_VERIFY,
253                 PURPOSE_ENCRYPT | PURPOSE_DECRYPT,
254         };
255         String[][] encryptionPaddingModes = {
256                 {
257                         ENCRYPTION_PADDING_NONE
258                 },
259                 {
260                         ENCRYPTION_PADDING_RSA_OAEP,
261                 },
262                 {
263                         ENCRYPTION_PADDING_RSA_PKCS1,
264                 },
265                 {
266                         ENCRYPTION_PADDING_RSA_OAEP,
267                         ENCRYPTION_PADDING_RSA_PKCS1,
268                 },
269         };
270         String[][] signaturePaddingModes = {
271                 {
272                         SIGNATURE_PADDING_RSA_PKCS1,
273                 },
274                 {
275                         SIGNATURE_PADDING_RSA_PSS,
276                 },
277                 {
278                         SIGNATURE_PADDING_RSA_PKCS1,
279                         SIGNATURE_PADDING_RSA_PSS,
280                 },
281         };
282 
283         for (int keySize : keySizes) {
284             for (byte[] challenge : challenges) {
285                 for (int purpose : purposes) {
286                     if (isEncryptionPurpose(purpose)) {
287                         testRsaAttestations(keySize, challenge, purpose, encryptionPaddingModes);
288                     } else {
289                         testRsaAttestations(keySize, challenge, purpose, signaturePaddingModes);
290                     }
291                 }
292             }
293         }
294     }
295 
testRsaAttestation_TooLargeChallenge()296     public void testRsaAttestation_TooLargeChallenge() throws Exception {
297         try {
298             testRsaAttestation(new byte[129], true /* includeValidityDates */, 512, PURPOSE_SIGN,
299                     null /* paddingModes; may be empty because we'll never test them */);
300             fail("Attestation challenges larger than 128 bytes should be rejected");
301         } catch (ProviderException e) {
302             KeyStoreException cause = (KeyStoreException) e.getCause();
303             assertEquals(KM_ERROR_INVALID_INPUT_LENGTH, cause.getErrorCode());
304         }
305     }
306 
testRsaAttestation_NoChallenge()307     public void testRsaAttestation_NoChallenge() throws Exception {
308         String keystoreAlias = "test_key";
309         Date now = new Date();
310         Date originationEnd = new Date(now.getTime() + ORIGINATION_TIME_OFFSET);
311         Date consumptionEnd = new Date(now.getTime() + CONSUMPTION_TIME_OFFSET);
312         KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(keystoreAlias, PURPOSE_SIGN)
313                 .setDigests(DIGEST_NONE, DIGEST_SHA256, DIGEST_SHA512)
314                 .setAttestationChallenge(null)
315                 .setKeyValidityStart(now)
316                 .setKeyValidityForOriginationEnd(originationEnd)
317                 .setKeyValidityForConsumptionEnd(consumptionEnd)
318                 .build();
319 
320         generateKeyPair(KEY_ALGORITHM_RSA, spec);
321 
322         KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
323         keyStore.load(null);
324 
325         try {
326             Certificate certificates[] = keyStore.getCertificateChain(keystoreAlias);
327             assertEquals(1, certificates.length);
328 
329             X509Certificate attestationCert = (X509Certificate) certificates[0];
330             assertNull(attestationCert.getExtensionValue(Attestation.KEY_DESCRIPTION_OID));
331         } finally {
332             keyStore.deleteEntry(keystoreAlias);
333         }
334     }
335 
testAesAttestation()336     public void testAesAttestation() throws Exception {
337         String keystoreAlias = "test_key";
338         KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(keystoreAlias, PURPOSE_ENCRYPT)
339                 .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
340                 .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
341                 .setAttestationChallenge(new byte[0])
342                 .build();
343         generateKey(spec, KeyProperties.KEY_ALGORITHM_AES);
344 
345         KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
346         keyStore.load(null);
347         try {
348             assertNull(keyStore.getCertificateChain(keystoreAlias));
349         } finally {
350             keyStore.deleteEntry(keystoreAlias);
351         }
352     }
353 
testHmacAttestation()354     public void testHmacAttestation() throws Exception {
355         String keystoreAlias = "test_key";
356         KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(keystoreAlias, PURPOSE_SIGN)
357                 .build();
358 
359         generateKey(spec, KeyProperties.KEY_ALGORITHM_HMAC_SHA256);
360 
361         KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
362         keyStore.load(null);
363         try {
364             assertNull(keyStore.getCertificateChain(keystoreAlias));
365         } finally {
366             keyStore.deleteEntry(keystoreAlias);
367         }
368     }
369 
testRsaAttestations(int keySize, byte[] challenge, int purpose, String[][] paddingModes)370     private void testRsaAttestations(int keySize, byte[] challenge, int purpose,
371             String[][] paddingModes) throws Exception {
372         for (String[] paddings : paddingModes) {
373             try {
374                 testRsaAttestation(challenge, true /* includeValidityDates */, keySize, purpose,
375                         paddings);
376                 testRsaAttestation(challenge, false /* includeValidityDates */, keySize, purpose,
377                         paddings);
378             } catch (Throwable e) {
379                 throw new Exception("Failed on key size " + keySize + " challenge [" +
380                         new String(challenge) + "], purposes " +
381                         buildPurposeSet(purpose) + " and paddings " +
382                         ImmutableSet.copyOf(paddings),
383                         e);
384             }
385         }
386     }
387 
testDeviceIdAttestation()388     public void testDeviceIdAttestation() throws Exception {
389         testDeviceIdAttestationFailure(AttestationUtils.ID_TYPE_SERIAL, null);
390         testDeviceIdAttestationFailure(AttestationUtils.ID_TYPE_IMEI, "Unable to retrieve IMEI");
391         testDeviceIdAttestationFailure(AttestationUtils.ID_TYPE_MEID, "Unable to retrieve MEID");
392     }
393 
394     @SuppressWarnings("deprecation")
testRsaAttestation(byte[] challenge, boolean includeValidityDates, int keySize, int purposes, String[] paddingModes)395     private void testRsaAttestation(byte[] challenge, boolean includeValidityDates, int keySize,
396             int purposes, String[] paddingModes) throws Exception {
397         String keystoreAlias = "test_key";
398 
399         Date startTime = new Date();
400         Date originationEnd = new Date(startTime.getTime() + ORIGINATION_TIME_OFFSET);
401         Date consumptionEnd = new Date(startTime.getTime() + CONSUMPTION_TIME_OFFSET);
402         KeyGenParameterSpec.Builder builder =
403             new KeyGenParameterSpec.Builder(keystoreAlias, purposes)
404                         .setKeySize(keySize)
405                         .setDigests(DIGEST_NONE, DIGEST_SHA256, DIGEST_SHA512)
406                         .setAttestationChallenge(challenge);
407 
408         if (includeValidityDates) {
409             builder.setKeyValidityStart(startTime)
410                     .setKeyValidityForOriginationEnd(originationEnd)
411                     .setKeyValidityForConsumptionEnd(consumptionEnd);
412         }
413         if (isEncryptionPurpose(purposes)) {
414             builder.setEncryptionPaddings(paddingModes);
415             // Because we sometimes set "no padding", allow non-randomized encryption.
416             builder.setRandomizedEncryptionRequired(false);
417         }
418         if (isSignaturePurpose(purposes)) {
419             builder.setSignaturePaddings(paddingModes);
420         }
421 
422         generateKeyPair(KEY_ALGORITHM_RSA, builder.build());
423 
424         KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
425         keyStore.load(null);
426 
427         try {
428             Certificate certificates[] = keyStore.getCertificateChain(keystoreAlias);
429             verifyCertificateChain(certificates);
430 
431             X509Certificate attestationCert = (X509Certificate) certificates[0];
432             Attestation attestation = new Attestation(attestationCert);
433 
434             checkRsaKeyDetails(attestation, keySize, purposes, ImmutableSet.copyOf(paddingModes));
435             checkKeyUsage(attestationCert, purposes);
436             checkKeyIndependentAttestationInfo(challenge, purposes, startTime, includeValidityDates,
437                     attestation);
438         } finally {
439             keyStore.deleteEntry(keystoreAlias);
440         }
441     }
442 
checkKeyUsage(X509Certificate attestationCert, int purposes)443     private void checkKeyUsage(X509Certificate attestationCert, int purposes) {
444 
445         boolean[] expectedKeyUsage = new boolean[KEY_USAGE_BITSTRING_LENGTH];
446         if (isSignaturePurpose(purposes)) {
447             expectedKeyUsage[KEY_USAGE_DIGITAL_SIGNATURE_BIT_OFFSET] = true;
448         }
449         if (isEncryptionPurpose(purposes)) {
450             expectedKeyUsage[KEY_USAGE_KEY_ENCIPHERMENT_BIT_OFFSET] = true;
451             expectedKeyUsage[KEY_USAGE_DATA_ENCIPHERMENT_BIT_OFFSET] = true;
452         }
453         assertThat(attestationCert.getKeyUsage(), is(expectedKeyUsage));
454     }
455 
456     @SuppressWarnings("deprecation")
testEcAttestation(byte[] challenge, boolean includeValidityDates, String ecCurve, int keySize, int purposes)457     private void testEcAttestation(byte[] challenge, boolean includeValidityDates, String ecCurve,
458             int keySize, int purposes) throws Exception {
459         String keystoreAlias = "test_key";
460 
461         Date startTime = new Date();
462         Date originationEnd = new Date(startTime.getTime() + ORIGINATION_TIME_OFFSET);
463         Date consumptionEnd = new Date(startTime.getTime() + CONSUMPTION_TIME_OFFSET);
464         KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(keystoreAlias,
465                 purposes)
466                         .setAlgorithmParameterSpec(new ECGenParameterSpec(ecCurve))
467                         .setDigests(DIGEST_NONE, DIGEST_SHA256, DIGEST_SHA512)
468                         .setAttestationChallenge(challenge);
469 
470         if (includeValidityDates) {
471             builder.setKeyValidityStart(startTime)
472                     .setKeyValidityForOriginationEnd(originationEnd)
473                     .setKeyValidityForConsumptionEnd(consumptionEnd);
474         }
475 
476         generateKeyPair(KEY_ALGORITHM_EC, builder.build());
477 
478         KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
479         keyStore.load(null);
480 
481         try {
482             Certificate certificates[] = keyStore.getCertificateChain(keystoreAlias);
483             verifyCertificateChain(certificates);
484 
485             X509Certificate attestationCert = (X509Certificate) certificates[0];
486             Attestation attestation = new Attestation(attestationCert);
487 
488             checkEcKeyDetails(attestation, ecCurve, keySize);
489             checkKeyUsage(attestationCert, purposes);
490             checkKeyIndependentAttestationInfo(challenge, purposes, startTime, includeValidityDates,
491                     attestation);
492         } finally {
493             keyStore.deleteEntry(keystoreAlias);
494         }
495     }
496 
checkAttestationApplicationId(Attestation attestation)497     private void checkAttestationApplicationId(Attestation attestation)
498             throws NoSuchAlgorithmException, NameNotFoundException {
499         AttestationApplicationId aaid = null;
500         int kmVersion = attestation.getKeymasterVersion();
501         assertNull(attestation.getTeeEnforced().getAttestationApplicationId());
502         aaid = attestation.getSoftwareEnforced().getAttestationApplicationId();
503         if (kmVersion >= 3) {
504             // must be present and correct
505             assertNotNull(aaid);
506             assertEquals(new AttestationApplicationId(getContext()), aaid);
507         } else {
508             // may be present and
509             // must be correct if present
510             if (aaid != null) {
511                 assertEquals(new AttestationApplicationId(getContext()), aaid);
512             }
513         }
514     }
515 
checkKeyIndependentAttestationInfo(byte[] challenge, int purposes, Date startTime, boolean includesValidityDates, Attestation attestation)516     private void checkKeyIndependentAttestationInfo(byte[] challenge, int purposes, Date startTime,
517             boolean includesValidityDates, Attestation attestation)
518             throws NoSuchAlgorithmException, NameNotFoundException {
519         checkAttestationSecurityLevelDependentParams(attestation);
520         assertNotNull(attestation.getAttestationChallenge());
521         assertTrue(Arrays.equals(challenge, attestation.getAttestationChallenge()));
522         assertNotNull(attestation.getUniqueId());
523         assertEquals(0, attestation.getUniqueId().length);
524         checkPurposes(attestation, purposes);
525         checkDigests(attestation,
526                 ImmutableSet.of(KM_DIGEST_NONE, KM_DIGEST_SHA_2_256, KM_DIGEST_SHA_2_512));
527         checkValidityPeriod(attestation, startTime, includesValidityDates);
528         checkFlags(attestation);
529         checkOrigin(attestation);
530         checkAttestationApplicationId(attestation);
531     }
532 
getSystemPatchLevel()533     private int getSystemPatchLevel() {
534         Matcher matcher = OS_PATCH_LEVEL_STRING_PATTERN.matcher(Build.VERSION.SECURITY_PATCH);
535         assertTrue(matcher.matches());
536         String year_string = matcher.group(OS_PATCH_LEVEL_YEAR_GROUP_NAME);
537         String month_string = matcher.group(OS_PATCH_LEVEL_MONTH_GROUP_NAME);
538         int patch_level = Integer.parseInt(year_string) * 100 + Integer.parseInt(month_string);
539         return patch_level;
540     }
541 
getSystemOsVersion()542     private int getSystemOsVersion() {
543         return parseSystemOsVersion(Build.VERSION.RELEASE);
544     }
545 
parseSystemOsVersion(String versionString)546     private int parseSystemOsVersion(String versionString) {
547         Matcher matcher = OS_VERSION_STRING_PATTERN.matcher(versionString);
548         if (!matcher.matches()) {
549             return 0;
550         }
551 
552         int version = 0;
553         String major_string = matcher.group(OS_MAJOR_VERSION_MATCH_GROUP_NAME);
554         String minor_string = matcher.group(OS_MINOR_VERSION_MATCH_GROUP_NAME);
555         String subminor_string = matcher.group(OS_SUBMINOR_VERSION_MATCH_GROUP_NAME);
556         if (major_string != null) {
557             version += Integer.parseInt(major_string) * 10000;
558         }
559         if (minor_string != null) {
560             version += Integer.parseInt(minor_string) * 100;
561         }
562         if (subminor_string != null) {
563             version += Integer.parseInt(subminor_string);
564         }
565         return version;
566     }
567 
checkOrigin(Attestation attestation)568     private void checkOrigin(Attestation attestation) {
569         assertTrue("Origin must be defined",
570                 attestation.getSoftwareEnforced().getOrigin() != null ||
571                         attestation.getTeeEnforced().getOrigin() != null);
572         if (attestation.getKeymasterVersion() != 0) {
573             assertTrue("Origin may not be defined in both SW and TEE, except on keymaster0",
574                     attestation.getSoftwareEnforced().getOrigin() == null ||
575                             attestation.getTeeEnforced().getOrigin() == null);
576         }
577 
578         if (attestation.getKeymasterSecurityLevel() == KM_SECURITY_LEVEL_SOFTWARE) {
579             assertThat(attestation.getSoftwareEnforced().getOrigin(), is(KM_ORIGIN_GENERATED));
580         } else if (attestation.getKeymasterVersion() == 0) {
581             assertThat(attestation.getTeeEnforced().getOrigin(), is(KM_ORIGIN_UNKNOWN));
582         } else {
583             assertThat(attestation.getTeeEnforced().getOrigin(), is(KM_ORIGIN_GENERATED));
584         }
585     }
586 
checkFlags(Attestation attestation)587     private void checkFlags(Attestation attestation) {
588         assertFalse("All applications was not requested",
589                 attestation.getSoftwareEnforced().isAllApplications());
590         assertFalse("All applications was not requested",
591                 attestation.getTeeEnforced().isAllApplications());
592         assertFalse("Allow while on body was not requested",
593                 attestation.getSoftwareEnforced().isAllowWhileOnBody());
594         assertFalse("Allow while on body was not requested",
595                 attestation.getTeeEnforced().isAllowWhileOnBody());
596         assertNull("Auth binding was not requiested",
597                 attestation.getSoftwareEnforced().getUserAuthType());
598         assertNull("Auth binding was not requiested",
599                 attestation.getTeeEnforced().getUserAuthType());
600         assertTrue("noAuthRequired must be true",
601                 attestation.getSoftwareEnforced().isNoAuthRequired()
602                         || attestation.getTeeEnforced().isNoAuthRequired());
603         assertFalse("auth is either software or TEE",
604                 attestation.getSoftwareEnforced().isNoAuthRequired()
605                         && attestation.getTeeEnforced().isNoAuthRequired());
606         assertFalse("Software cannot implement rollback resistance",
607                 attestation.getSoftwareEnforced().isRollbackResistant());
608     }
609 
checkValidityPeriod(Attestation attestation, Date startTime, boolean includesValidityDates)610     private void checkValidityPeriod(Attestation attestation, Date startTime,
611             boolean includesValidityDates) {
612         AuthorizationList validityPeriodList;
613         AuthorizationList nonValidityPeriodList;
614         if (attestation.getTeeEnforced().getCreationDateTime() != null) {
615             validityPeriodList = attestation.getTeeEnforced();
616             nonValidityPeriodList = attestation.getSoftwareEnforced();
617         } else {
618             validityPeriodList = attestation.getSoftwareEnforced();
619             nonValidityPeriodList = attestation.getTeeEnforced();
620         }
621 
622         if (attestation.getKeymasterVersion() == 2) {
623             Date creationDateTime = validityPeriodList.getCreationDateTime();
624 
625             assertNotNull(creationDateTime);
626             assertNull(nonValidityPeriodList.getCreationDateTime());
627 
628             // We allow a little slop on creation times because the TEE/HAL may not be quite synced
629             // up with the system.
630             assertTrue("Test start time (" + startTime.getTime() + ") and key creation time (" +
631                     creationDateTime.getTime() + ") should be close",
632                     Math.abs(creationDateTime.getTime() - startTime.getTime()) <= 2000);
633         }
634 
635         if (includesValidityDates) {
636             Date activeDateTime = validityPeriodList.getActiveDateTime();
637             Date originationExpirationDateTime = validityPeriodList.getOriginationExpireDateTime();
638             Date usageExpirationDateTime = validityPeriodList.getUsageExpireDateTime();
639 
640             assertNotNull(activeDateTime);
641             assertNotNull(originationExpirationDateTime);
642             assertNotNull(usageExpirationDateTime);
643 
644             assertNull(nonValidityPeriodList.getActiveDateTime());
645             assertNull(nonValidityPeriodList.getOriginationExpireDateTime());
646             assertNull(nonValidityPeriodList.getUsageExpireDateTime());
647 
648             assertThat(originationExpirationDateTime.getTime(),
649                     is(startTime.getTime() + ORIGINATION_TIME_OFFSET));
650             assertThat(usageExpirationDateTime.getTime(),
651                     is(startTime.getTime() + CONSUMPTION_TIME_OFFSET));
652         }
653     }
654 
checkDigests(Attestation attestation, Set<Integer> expectedDigests)655     private void checkDigests(Attestation attestation, Set<Integer> expectedDigests) {
656         Set<Integer> softwareEnforcedDigests = attestation.getSoftwareEnforced().getDigests();
657         Set<Integer> teeEnforcedDigests = attestation.getTeeEnforced().getDigests();
658 
659         if (softwareEnforcedDigests == null) {
660             softwareEnforcedDigests = ImmutableSet.of();
661         }
662         if (teeEnforcedDigests == null) {
663             teeEnforcedDigests = ImmutableSet.of();
664         }
665 
666         Set<Integer> allDigests = ImmutableSet.<Integer> builder()
667                 .addAll(softwareEnforcedDigests)
668                 .addAll(teeEnforcedDigests)
669                 .build();
670         Set<Integer> intersection = new ArraySet<>();
671         intersection.addAll(softwareEnforcedDigests);
672         intersection.retainAll(teeEnforcedDigests);
673 
674         assertThat(allDigests, is(expectedDigests));
675         assertTrue("Digest sets must be disjoint", intersection.isEmpty());
676 
677         if (attestation.getKeymasterSecurityLevel() == KM_SECURITY_LEVEL_SOFTWARE
678                 || attestation.getKeymasterVersion() == 0) {
679             assertThat("Digests in software-enforced",
680                     softwareEnforcedDigests, is(expectedDigests));
681         } else {
682             switch (attestation.getKeymasterVersion()) {
683                 case 1:
684                     // KM1 implementations may not support SHA512 in the TEE
685                     assertTrue(softwareEnforcedDigests.contains(KM_DIGEST_SHA_2_512)
686                             || teeEnforcedDigests.contains(KM_DIGEST_SHA_2_512));
687 
688                     assertThat(teeEnforcedDigests, hasItems(KM_DIGEST_NONE, KM_DIGEST_SHA_2_256));
689                     break;
690 
691                 case 2:
692                 case 3:
693                     assertThat(teeEnforcedDigests, is(expectedDigests));
694                     break;
695 
696                 default:
697                     fail("Broken CTS test. Should be impossible to get here.");
698             }
699         }
700     }
701 
checkPurposes(Attestation attestation, int purposes)702     private Set<Integer> checkPurposes(Attestation attestation, int purposes) {
703         Set<Integer> expectedPurposes = buildPurposeSet(purposes);
704         if (attestation.getKeymasterSecurityLevel() == KM_SECURITY_LEVEL_SOFTWARE
705                 || attestation.getKeymasterVersion() == 0) {
706             assertThat("Purposes in software-enforced should match expected set",
707                     attestation.getSoftwareEnforced().getPurposes(), is(expectedPurposes));
708             assertNull("Should be no purposes in TEE-enforced",
709                     attestation.getTeeEnforced().getPurposes());
710         } else {
711             assertThat("Purposes in TEE-enforced should match expected set",
712                     attestation.getTeeEnforced().getPurposes(), is(expectedPurposes));
713             assertNull("No purposes in software-enforced",
714                     attestation.getSoftwareEnforced().getPurposes());
715         }
716         return expectedPurposes;
717     }
718 
719     @SuppressWarnings("unchecked")
checkAttestationSecurityLevelDependentParams(Attestation attestation)720     private void checkAttestationSecurityLevelDependentParams(Attestation attestation) {
721         assertThat("Attestation version must be 1 or 2", attestation.getAttestationVersion(),
722                 either(is(1)).or(is(2)));
723 
724         AuthorizationList teeEnforced = attestation.getTeeEnforced();
725         AuthorizationList softwareEnforced = attestation.getSoftwareEnforced();
726 
727         int systemOsVersion = getSystemOsVersion();
728         int systemPatchLevel = getSystemPatchLevel();
729 
730         switch (attestation.getAttestationSecurityLevel()) {
731             case KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT:
732                 assertThat("TEE attestation can only come from TEE keymaster",
733                         attestation.getKeymasterSecurityLevel(),
734                         is(KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT));
735                 assertThat(attestation.getKeymasterVersion(), either(is(2)).or(is(3)));
736 
737                 checkRootOfTrust(attestation);
738                 assertThat(teeEnforced.getOsVersion(), is(systemOsVersion));
739                 assertThat(teeEnforced.getOsPatchLevel(), is(systemPatchLevel));
740                 break;
741 
742             case KM_SECURITY_LEVEL_SOFTWARE:
743                 if (attestation
744                         .getKeymasterSecurityLevel() == KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT) {
745                     assertThat("TEE KM version must be 0 or 1 with software attestation",
746                             attestation.getKeymasterVersion(), either(is(0)).or(is(1)));
747                 } else {
748                     assertThat("Software KM is version 3", attestation.getKeymasterVersion(),
749                             is(3));
750                     assertThat(softwareEnforced.getOsVersion(), is(systemOsVersion));
751                     assertThat(softwareEnforced.getOsPatchLevel(), is(systemPatchLevel));
752                 }
753 
754                 assertNull("Software attestation cannot provide root of trust",
755                         teeEnforced.getRootOfTrust());
756 
757                 break;
758 
759             default:
760                 fail("Invalid attestation security level: "
761                         + attestation.getAttestationSecurityLevel());
762                 break;
763         }
764 
765         assertNull("Software-enforced list must not contain root of trust",
766                 softwareEnforced.getRootOfTrust());
767     }
768 
checkRootOfTrust(Attestation attestation)769     private void checkRootOfTrust(Attestation attestation) {
770         RootOfTrust rootOfTrust = attestation.getTeeEnforced().getRootOfTrust();
771         assertNotNull(rootOfTrust);
772         assertNotNull(rootOfTrust.getVerifiedBootKey());
773         assertTrue(rootOfTrust.getVerifiedBootKey().length >= 32);
774     }
775 
checkRsaKeyDetails(Attestation attestation, int keySize, int purposes, Set<String> expectedPaddingModes)776     private void checkRsaKeyDetails(Attestation attestation, int keySize, int purposes,
777             Set<String> expectedPaddingModes) throws CertificateParsingException {
778         AuthorizationList keyDetailsList;
779         AuthorizationList nonKeyDetailsList;
780         if (attestation.getKeymasterSecurityLevel() == KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT) {
781             keyDetailsList = attestation.getTeeEnforced();
782             nonKeyDetailsList = attestation.getSoftwareEnforced();
783         } else {
784             keyDetailsList = attestation.getSoftwareEnforced();
785             nonKeyDetailsList = attestation.getTeeEnforced();
786         }
787         assertEquals(keySize, keyDetailsList.getKeySize().intValue());
788         assertNull(nonKeyDetailsList.getKeySize());
789 
790         assertEquals(KM_ALGORITHM_RSA, keyDetailsList.getAlgorithm().intValue());
791         assertNull(nonKeyDetailsList.getAlgorithm());
792 
793         assertNull(keyDetailsList.getEcCurve());
794         assertNull(nonKeyDetailsList.getEcCurve());
795 
796         assertEquals(65537, keyDetailsList.getRsaPublicExponent().longValue());
797         assertNull(nonKeyDetailsList.getRsaPublicExponent());
798 
799         Set<String> paddingModes;
800         if (attestation.getKeymasterVersion() == 0) {
801             // KM0 implementations don't support padding info, so it's always in the
802             // software-enforced list.
803             paddingModes = attestation.getSoftwareEnforced().getPaddingModesAsStrings();
804             assertNull(attestation.getTeeEnforced().getPaddingModes());
805         } else {
806             paddingModes = keyDetailsList.getPaddingModesAsStrings();
807             assertNull(nonKeyDetailsList.getPaddingModes());
808         }
809 
810         // KM1 implementations may add ENCRYPTION_PADDING_NONE to the list of paddings.
811         Set<String> km1PossiblePaddingModes = expectedPaddingModes;
812         if (attestation.getKeymasterVersion() == 1 &&
813                 attestation.getKeymasterSecurityLevel() == KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT) {
814             ImmutableSet.Builder<String> builder = ImmutableSet.builder();
815             builder.addAll(expectedPaddingModes);
816             builder.add(ENCRYPTION_PADDING_NONE);
817             km1PossiblePaddingModes = builder.build();
818         }
819 
820         assertThat(paddingModes, either(is(expectedPaddingModes)).or(is(km1PossiblePaddingModes)));
821     }
822 
checkEcKeyDetails(Attestation attestation, String ecCurve, int keySize)823     private void checkEcKeyDetails(Attestation attestation, String ecCurve, int keySize) {
824         AuthorizationList keyDetailsList;
825         AuthorizationList nonKeyDetailsList;
826         if (attestation.getKeymasterSecurityLevel() == KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT) {
827             keyDetailsList = attestation.getTeeEnforced();
828             nonKeyDetailsList = attestation.getSoftwareEnforced();
829         } else {
830             keyDetailsList = attestation.getSoftwareEnforced();
831             nonKeyDetailsList = attestation.getTeeEnforced();
832         }
833         assertEquals(keySize, keyDetailsList.getKeySize().intValue());
834         assertNull(nonKeyDetailsList.getKeySize());
835         assertEquals(KM_ALGORITHM_EC, keyDetailsList.getAlgorithm().intValue());
836         assertNull(nonKeyDetailsList.getAlgorithm());
837         assertEquals(ecCurve, keyDetailsList.ecCurveAsString());
838         assertNull(nonKeyDetailsList.getEcCurve());
839         assertNull(keyDetailsList.getRsaPublicExponent());
840         assertNull(nonKeyDetailsList.getRsaPublicExponent());
841         assertNull(keyDetailsList.getPaddingModes());
842         assertNull(nonKeyDetailsList.getPaddingModes());
843     }
844 
isEncryptionPurpose(int purposes)845     private boolean isEncryptionPurpose(int purposes) {
846         return (purposes & PURPOSE_DECRYPT) != 0 || (purposes & PURPOSE_ENCRYPT) != 0;
847     }
848 
isSignaturePurpose(int purposes)849     private boolean isSignaturePurpose(int purposes) {
850         return (purposes & PURPOSE_SIGN) != 0 || (purposes & PURPOSE_VERIFY) != 0;
851     }
852 
buildPurposeSet(int purposes)853     private ImmutableSet<Integer> buildPurposeSet(int purposes) {
854         ImmutableSet.Builder<Integer> builder = ImmutableSet.builder();
855         if ((purposes & PURPOSE_SIGN) != 0)
856             builder.add(KM_PURPOSE_SIGN);
857         if ((purposes & PURPOSE_VERIFY) != 0)
858             builder.add(KM_PURPOSE_VERIFY);
859         if ((purposes & PURPOSE_ENCRYPT) != 0)
860             builder.add(KM_PURPOSE_ENCRYPT);
861         if ((purposes & PURPOSE_DECRYPT) != 0)
862             builder.add(KM_PURPOSE_DECRYPT);
863         return builder.build();
864     }
865 
generateKey(KeyGenParameterSpec spec, String algorithm)866     private void generateKey(KeyGenParameterSpec spec, String algorithm)
867             throws NoSuchAlgorithmException, NoSuchProviderException,
868             InvalidAlgorithmParameterException {
869         KeyGenerator keyGenerator = KeyGenerator.getInstance(algorithm, "AndroidKeyStore");
870         keyGenerator.init(spec);
871         keyGenerator.generateKey();
872     }
873 
generateKeyPair(String algorithm, KeyGenParameterSpec spec)874     private void generateKeyPair(String algorithm, KeyGenParameterSpec spec)
875             throws NoSuchAlgorithmException, NoSuchProviderException,
876             InvalidAlgorithmParameterException {
877         KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm,
878                 "AndroidKeyStore");
879         keyPairGenerator.initialize(spec);
880         keyPairGenerator.generateKeyPair();
881     }
882 
verifyCertificateChain(Certificate[] certChain)883     private void verifyCertificateChain(Certificate[] certChain)
884             throws GeneralSecurityException {
885         assertNotNull(certChain);
886         for (int i = 1; i < certChain.length; ++i) {
887             try {
888                 PublicKey pubKey = certChain[i].getPublicKey();
889                 certChain[i - 1].verify(pubKey);
890                 if (i == certChain.length - 1) {
891                     // Last cert should be self-signed.
892                     certChain[i].verify(pubKey);
893                 }
894 
895                 // Check that issuer in the signed cert matches subject in the signing cert.
896                 X509Certificate x509CurrCert = (X509Certificate) certChain[i];
897                 X509Certificate x509PrevCert = (X509Certificate) certChain[i - 1];
898                 X500Name signingCertSubject =
899                         new JcaX509CertificateHolder(x509CurrCert).getSubject();
900                 X500Name signedCertIssuer =
901                         new JcaX509CertificateHolder(x509PrevCert).getIssuer();
902                 // Use .toASN1Object().equals() rather than .equals() because .equals() is case
903                 // insensitive, and we want to verify an exact match.
904                 assertTrue(
905                         signedCertIssuer.toASN1Object().equals(signingCertSubject.toASN1Object()));
906 
907                 if (i == 1) {
908                     // First cert should have subject "CN=Android Keystore Key".
909                     X500Name signedCertSubject =
910                             new JcaX509CertificateHolder(x509PrevCert).getSubject();
911                     assertEquals(signedCertSubject, new X500Name("CN=Android Keystore Key"));
912                 }
913             } catch (InvalidKeyException | CertificateException | NoSuchAlgorithmException
914                     | NoSuchProviderException | SignatureException e) {
915                 throw new GeneralSecurityException("Failed to verify certificate "
916                         + certChain[i - 1] + " with public key " + certChain[i].getPublicKey(), e);
917             }
918         }
919     }
920 
testDeviceIdAttestationFailure(int idType, String acceptableDeviceIdAttestationFailureMessage)921     private void testDeviceIdAttestationFailure(int idType,
922             String acceptableDeviceIdAttestationFailureMessage) throws Exception {
923         try {
924             AttestationUtils.attestDeviceIds(getContext(), new int[] {idType}, "123".getBytes());
925             fail("Attestation should have failed.");
926         } catch (SecurityException e) {
927             // Attestation is expected to fail. If the device has the device ID type we are trying
928             // to attest, it should fail with a SecurityException as we do not hold
929             // READ_PRIVILEGED_PHONE_STATE permission.
930         } catch (DeviceIdAttestationException e) {
931             // Attestation is expected to fail. If the device does not have the device ID type we
932             // are trying to attest (e.g. no IMEI on devices without a radio), it should fail with
933             // a corresponding DeviceIdAttestationException.
934             if (acceptableDeviceIdAttestationFailureMessage == null ||
935                     !acceptableDeviceIdAttestationFailureMessage.equals(e.getMessage())) {
936                 throw e;
937             }
938         }
939     }
940 }
941