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