1 /* 2 * Copyright (C) 2022 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 com.android.server.security; 18 19 import static android.security.attestationverification.AttestationVerificationManager.FLAG_FAILURE_BOOT_STATE; 20 import static android.security.attestationverification.AttestationVerificationManager.FLAG_FAILURE_CERTS; 21 import static android.security.attestationverification.AttestationVerificationManager.FLAG_FAILURE_KEYSTORE_REQUIREMENTS; 22 import static android.security.attestationverification.AttestationVerificationManager.FLAG_FAILURE_LOCAL_BINDING_REQUIREMENTS; 23 import static android.security.attestationverification.AttestationVerificationManager.FLAG_FAILURE_PATCH_LEVEL_DIFF; 24 import static android.security.attestationverification.AttestationVerificationManager.FLAG_FAILURE_UNKNOWN; 25 import static android.security.attestationverification.AttestationVerificationManager.PARAM_CHALLENGE; 26 import static android.security.attestationverification.AttestationVerificationManager.PARAM_MAX_PATCH_LEVEL_DIFF_MONTHS; 27 import static android.security.attestationverification.AttestationVerificationManager.PARAM_PUBLIC_KEY; 28 import static android.security.attestationverification.AttestationVerificationManager.TYPE_CHALLENGE; 29 import static android.security.attestationverification.AttestationVerificationManager.TYPE_PUBLIC_KEY; 30 import static android.security.attestationverification.AttestationVerificationManager.localBindingTypeToString; 31 32 import static com.android.server.security.AndroidKeystoreAttestationVerificationAttributes.VerifiedBootState.VERIFIED; 33 import static com.android.server.security.AndroidKeystoreAttestationVerificationAttributes.fromCertificate; 34 35 import static java.nio.charset.StandardCharsets.UTF_8; 36 37 import android.annotation.NonNull; 38 import android.annotation.SuppressLint; 39 import android.content.Context; 40 import android.os.Build; 41 import android.os.Bundle; 42 import android.security.attestationverification.AttestationVerificationManager.LocalBindingType; 43 import android.util.IndentingPrintWriter; 44 import android.util.Slog; 45 46 import com.android.internal.R; 47 import com.android.internal.annotations.VisibleForTesting; 48 import com.android.server.security.AttestationVerificationManagerService.DumpLogger; 49 50 import java.io.ByteArrayInputStream; 51 import java.io.IOException; 52 import java.security.InvalidAlgorithmParameterException; 53 import java.security.cert.CertPath; 54 import java.security.cert.CertPathValidator; 55 import java.security.cert.CertPathValidatorException; 56 import java.security.cert.Certificate; 57 import java.security.cert.CertificateException; 58 import java.security.cert.CertificateFactory; 59 import java.security.cert.PKIXParameters; 60 import java.security.cert.TrustAnchor; 61 import java.security.cert.X509Certificate; 62 import java.time.LocalDate; 63 import java.time.ZoneId; 64 import java.time.temporal.ChronoUnit; 65 import java.util.ArrayList; 66 import java.util.Arrays; 67 import java.util.Collections; 68 import java.util.HashSet; 69 import java.util.List; 70 import java.util.Objects; 71 import java.util.Set; 72 73 /** 74 * Verifies Android key attestation according to the 75 * {@link 76 * android.security.attestationverification.AttestationVerificationManager#PROFILE_PEER_DEVICE 77 * PROFILE_PEER_DEVICE} 78 * profile. 79 * 80 * <p> 81 * The profile is satisfied by checking all the following: 82 * <ul> 83 * <li> TrustAnchor match 84 * <li> Certificate validity 85 * <li> Android OS 10 or higher 86 * <li> Hardware backed key store 87 * <li> Verified boot locked 88 * <li> Remote Patch level must be within 1 year of local patch if local patch is less than 1 year 89 * old. 90 * </ul> 91 * 92 * <p> 93 * Trust anchors are vendor-defined by populating 94 * {@link R.array#vendor_required_attestation_certificates} string array (defenined in 95 * {@code frameworks/base/core/res/res/values/vendor_required_attestation_certificates.xml}). 96 */ 97 class AttestationVerificationPeerDeviceVerifier { 98 private static final String TAG = "AVF"; 99 private static final int MAX_PATCH_AGE_MONTHS = 12; 100 101 /** 102 * Optional requirements bundle parameter key for {@code TYPE_PUBLIC_KEY} and 103 * {@code TYPE_CHALLENGE}. 104 * 105 * <p> 106 * This is NOT a part of the AVF API surface (neither public SDK nor internal to the 107 * system_server) and should really only be used by the CompanionDeviceManagerService (which 108 * duplicates the value rather than referencing it directly here). 109 */ 110 private static final String PARAM_OWNED_BY_SYSTEM = "android.key_owned_by_system"; 111 112 private static final String ANDROID_SYSTEM_PACKAGE_NAME = "AndroidSystem"; 113 private static final Set<String> ANDROID_SYSTEM_PACKAGE_NAME_SET = 114 Collections.singleton(ANDROID_SYSTEM_PACKAGE_NAME); 115 116 private final Context mContext; 117 private final Set<TrustAnchor> mTrustAnchors; 118 private final boolean mRevocationEnabled; 119 private final LocalDate mTestSystemDate; 120 private final LocalDate mTestLocalPatchDate; 121 private final CertificateFactory mCertificateFactory; 122 private final CertPathValidator mCertPathValidator; 123 private final CertificateRevocationStatusManager mCertificateRevocationStatusManager; 124 private final DumpLogger mDumpLogger; 125 AttestationVerificationPeerDeviceVerifier(@onNull Context context, @NonNull DumpLogger dumpLogger)126 AttestationVerificationPeerDeviceVerifier(@NonNull Context context, 127 @NonNull DumpLogger dumpLogger) throws Exception { 128 mContext = Objects.requireNonNull(context); 129 mDumpLogger = dumpLogger; 130 mCertificateFactory = CertificateFactory.getInstance("X.509"); 131 mCertPathValidator = CertPathValidator.getInstance("PKIX"); 132 mTrustAnchors = getTrustAnchors(); 133 mCertificateRevocationStatusManager = new CertificateRevocationStatusManager(mContext); 134 mRevocationEnabled = true; 135 mTestSystemDate = null; 136 mTestLocalPatchDate = null; 137 } 138 139 // Use ONLY for hermetic unit testing. 140 @VisibleForTesting AttestationVerificationPeerDeviceVerifier(@onNull Context context, DumpLogger dumpLogger, Set<TrustAnchor> trustAnchors, boolean revocationEnabled, LocalDate systemDate, LocalDate localPatchDate)141 AttestationVerificationPeerDeviceVerifier(@NonNull Context context, 142 DumpLogger dumpLogger, Set<TrustAnchor> trustAnchors, boolean revocationEnabled, 143 LocalDate systemDate, LocalDate localPatchDate) throws Exception { 144 mContext = Objects.requireNonNull(context); 145 mDumpLogger = dumpLogger; 146 mCertificateFactory = CertificateFactory.getInstance("X.509"); 147 mCertPathValidator = CertPathValidator.getInstance("PKIX"); 148 mTrustAnchors = trustAnchors; 149 mCertificateRevocationStatusManager = new CertificateRevocationStatusManager(mContext); 150 mRevocationEnabled = revocationEnabled; 151 mTestSystemDate = systemDate; 152 mTestLocalPatchDate = localPatchDate; 153 } 154 155 /** 156 * Verifies attestation for public key or challenge local binding. 157 * <p> 158 * The attestations must be suitable for {@link java.security.cert.CertificateFactory} 159 * The certificates in the attestation provided must be DER-encoded and may be supplied in 160 * binary or printable (Base64) encoding. If the certificate is provided in Base64 encoding, 161 * it must be bounded at the beginning by {@code -----BEGIN CERTIFICATE-----}, and must be 162 * bounded at the end by {@code -----END CERTIFICATE-----}. 163 * 164 * @param localBindingType Only {@code TYPE_PUBLIC_KEY} and {@code TYPE_CHALLENGE} supported. 165 * @param requirements Only {@code PARAM_PUBLIC_KEY} and {@code PARAM_CHALLENGE} supported. 166 * @param attestation Certificates should be DER encoded with leaf certificate appended 167 * first. 168 */ verifyAttestation( @ocalBindingType int localBindingType, @NonNull Bundle requirements, @NonNull byte[] attestation)169 int verifyAttestation( 170 @LocalBindingType int localBindingType, 171 @NonNull Bundle requirements, 172 @NonNull byte[] attestation) { 173 174 MyDumpData dumpData = new MyDumpData(); 175 176 int result = verifyAttestationInternal(localBindingType, requirements, attestation, 177 dumpData); 178 dumpData.mResult = result; 179 mDumpLogger.logAttempt(dumpData); 180 return result; 181 } 182 verifyAttestationInternal( @ocalBindingType int localBindingType, @NonNull Bundle requirements, @NonNull byte[] attestation, @NonNull MyDumpData dumpData)183 private int verifyAttestationInternal( 184 @LocalBindingType int localBindingType, 185 @NonNull Bundle requirements, 186 @NonNull byte[] attestation, 187 @NonNull MyDumpData dumpData) { 188 if (mCertificateFactory == null) { 189 Slog.e(TAG, "Unable to access CertificateFactory"); 190 return FLAG_FAILURE_CERTS; 191 } 192 dumpData.mCertificationFactoryAvailable = true; 193 194 if (mCertPathValidator == null) { 195 Slog.e(TAG, "Unable to access CertPathValidator"); 196 return FLAG_FAILURE_CERTS; 197 } 198 dumpData.mCertPathValidatorAvailable = true; 199 200 // To provide the most information in the dump logs, we track the failure state but keep 201 // verifying the rest of the attestation. For code safety, there are no transitions past 202 // here to set result = 0. 203 int result = 0; 204 205 try { 206 // 1. parse and validate the certificate chain. 207 final List<X509Certificate> certificateChain = getCertificates(attestation); 208 // (returns void, but throws CertificateException and other similar Exceptions) 209 validateCertificateChain(certificateChain); 210 dumpData.mCertChainOk = true; 211 212 final var leafCertificate = certificateChain.get(0); 213 final var attestationExtension = fromCertificate(leafCertificate); 214 215 // 2. Check if the provided local binding type is supported and if the provided 216 // requirements "match" the binding type. 217 if (!validateAttestationParameters(localBindingType, requirements)) { 218 return FLAG_FAILURE_LOCAL_BINDING_REQUIREMENTS; 219 } 220 dumpData.mAttestationParametersOk = true; 221 222 // 3. check if the attestation satisfies local binding requirements. 223 if (!checkLocalBindingRequirements( 224 leafCertificate, attestationExtension, localBindingType, requirements, 225 dumpData)) { 226 result |= FLAG_FAILURE_LOCAL_BINDING_REQUIREMENTS; 227 } 228 229 // 4. verify if the attestation satisfies the "peer device" profile. 230 result |= checkAttestationForPeerDeviceProfile(requirements, attestationExtension, 231 dumpData); 232 } catch (CertificateException | CertPathValidatorException 233 | InvalidAlgorithmParameterException | IOException e) { 234 // Catch all non-RuntimeExceptions (all of these are thrown by either getCertificates() 235 // or validateCertificateChain() or 236 // AndroidKeystoreAttestationVerificationAttributes.fromCertificate()) 237 Slog.e(TAG, "Unable to parse/validate Android Attestation certificate(s)", e); 238 result = FLAG_FAILURE_CERTS; 239 } catch (RuntimeException e) { 240 // Catch everything else (RuntimeExceptions), since we don't want to throw any 241 // exceptions out of this class/method. 242 Slog.e(TAG, "Unexpected error", e); 243 result = FLAG_FAILURE_UNKNOWN; 244 } 245 246 return result; 247 } 248 249 @NonNull getCertificates(byte[] attestation)250 private List<X509Certificate> getCertificates(byte[] attestation) 251 throws CertificateException { 252 List<X509Certificate> certificates = new ArrayList<>(); 253 ByteArrayInputStream bis = new ByteArrayInputStream(attestation); 254 while (bis.available() > 0) { 255 certificates.add((X509Certificate) mCertificateFactory.generateCertificate(bis)); 256 } 257 258 return certificates; 259 } 260 261 /** 262 * Check if the {@code localBindingType} is supported and if the {@code requirements} contains 263 * the required parameter for the given {@code @LocalBindingType}. 264 */ validateAttestationParameters( @ocalBindingType int localBindingType, @NonNull Bundle requirements)265 private boolean validateAttestationParameters( 266 @LocalBindingType int localBindingType, @NonNull Bundle requirements) { 267 if (localBindingType != TYPE_PUBLIC_KEY && localBindingType != TYPE_CHALLENGE) { 268 Slog.e(TAG, "Binding type is not supported: " + localBindingType); 269 return false; 270 } 271 272 if (requirements.size() < 1) { 273 Slog.e(TAG, "At least 1 requirement is required."); 274 return false; 275 } 276 277 if (localBindingType == TYPE_PUBLIC_KEY && !requirements.containsKey(PARAM_PUBLIC_KEY)) { 278 Slog.e(TAG, "Requirements does not contain key: " + PARAM_PUBLIC_KEY); 279 return false; 280 } 281 282 if (localBindingType == TYPE_CHALLENGE && !requirements.containsKey(PARAM_CHALLENGE)) { 283 Slog.e(TAG, "Requirements does not contain key: " + PARAM_CHALLENGE); 284 return false; 285 } 286 287 return true; 288 } 289 validateCertificateChain(List<X509Certificate> certificates)290 private void validateCertificateChain(List<X509Certificate> certificates) 291 throws CertificateException, CertPathValidatorException, 292 InvalidAlgorithmParameterException { 293 if (certificates.size() < 2) { 294 Slog.e(TAG, "Certificate chain less than 2 in size."); 295 throw new CertificateException("Certificate chain less than 2 in size."); 296 } 297 298 CertPath certificatePath = mCertificateFactory.generateCertPath(certificates); 299 PKIXParameters validationParams = new PKIXParameters(mTrustAnchors); 300 // Do not use built-in revocation status checker. 301 validationParams.setRevocationEnabled(false); 302 mCertPathValidator.validate(certificatePath, validationParams); 303 if (mRevocationEnabled) { 304 // Checks Revocation Status List based on 305 // https://developer.android.com/training/articles/security-key-attestation#certificate_status 306 // The first certificate is the leaf, which is generated at runtime with the attestation 307 // attributes such as the challenge. It is specific to this attestation instance and 308 // does not need to be checked for revocation. 309 mCertificateRevocationStatusManager.checkRevocationStatus( 310 new ArrayList<>(certificates.subList(1, certificates.size()))); 311 } 312 } 313 getTrustAnchors()314 private Set<TrustAnchor> getTrustAnchors() throws CertPathValidatorException { 315 Set<TrustAnchor> modifiableSet = new HashSet<>(); 316 try { 317 for (String certString : getTrustAnchorResources()) { 318 modifiableSet.add( 319 new TrustAnchor((X509Certificate) mCertificateFactory.generateCertificate( 320 new ByteArrayInputStream(getCertificateBytes(certString))), null)); 321 } 322 } catch (CertificateException e) { 323 e.printStackTrace(); 324 throw new CertPathValidatorException("Invalid trust anchor certificate.", e); 325 } 326 return Collections.unmodifiableSet(modifiableSet); 327 } 328 getCertificateBytes(String certString)329 private byte[] getCertificateBytes(String certString) { 330 String formattedCertString = certString.replaceAll("\\s+", "\n"); 331 formattedCertString = formattedCertString.replaceAll( 332 "-BEGIN\\nCERTIFICATE-", "-BEGIN CERTIFICATE-"); 333 formattedCertString = formattedCertString.replaceAll( 334 "-END\\nCERTIFICATE-", "-END CERTIFICATE-"); 335 return formattedCertString.getBytes(UTF_8); 336 } 337 getTrustAnchorResources()338 private String[] getTrustAnchorResources() { 339 return mContext.getResources().getStringArray( 340 R.array.vendor_required_attestation_certificates); 341 } 342 checkLocalBindingRequirements( @onNull X509Certificate leafCertificate, @NonNull AndroidKeystoreAttestationVerificationAttributes attestationAttributes, @LocalBindingType int localBindingType, @NonNull Bundle requirements, MyDumpData dumpData)343 private boolean checkLocalBindingRequirements( 344 @NonNull X509Certificate leafCertificate, 345 @NonNull AndroidKeystoreAttestationVerificationAttributes attestationAttributes, 346 @LocalBindingType int localBindingType, 347 @NonNull Bundle requirements, MyDumpData dumpData) { 348 // First: check non-optional (for the given local binding type) requirements. 349 dumpData.mBindingType = localBindingType; 350 switch (localBindingType) { 351 case TYPE_PUBLIC_KEY: 352 // Verify leaf public key matches provided public key. 353 final boolean publicKeyMatches = checkPublicKey( 354 leafCertificate, requirements.getByteArray(PARAM_PUBLIC_KEY)); 355 if (!publicKeyMatches) { 356 Slog.e(TAG, 357 "Provided public key does not match leaf certificate public key."); 358 return false; 359 } 360 break; 361 362 case TYPE_CHALLENGE: 363 // Verify challenge matches provided challenge. 364 final boolean attestationChallengeMatches = checkAttestationChallenge( 365 attestationAttributes, requirements.getByteArray(PARAM_CHALLENGE)); 366 if (!attestationChallengeMatches) { 367 Slog.e(TAG, 368 "Provided challenge does not match leaf certificate challenge."); 369 return false; 370 } 371 break; 372 373 default: 374 throw new IllegalArgumentException("Unsupported local binding type " 375 + localBindingTypeToString(localBindingType)); 376 } 377 dumpData.mBindingOk = true; 378 379 // Second: check specified optional requirements. 380 if (requirements.containsKey(PARAM_OWNED_BY_SYSTEM)) { 381 dumpData.mSystemOwnershipChecked = true; 382 if (requirements.getBoolean(PARAM_OWNED_BY_SYSTEM)) { 383 // Verify key is owned by the system. 384 final boolean ownedBySystem = checkOwnedBySystem( 385 leafCertificate, attestationAttributes); 386 if (!ownedBySystem) { 387 Slog.e(TAG, "Certificate public key is not owned by the AndroidSystem."); 388 return false; 389 } 390 dumpData.mSystemOwned = true; 391 } else { 392 throw new IllegalArgumentException("The value of the requirement key " 393 + PARAM_OWNED_BY_SYSTEM 394 + " cannot be false. You can remove the key if you don't want to verify " 395 + "it."); 396 } 397 } 398 399 return true; 400 } 401 checkAttestationForPeerDeviceProfile( @onNull Bundle requirements, @NonNull AndroidKeystoreAttestationVerificationAttributes attestationAttributes, MyDumpData dumpData)402 private int checkAttestationForPeerDeviceProfile( 403 @NonNull Bundle requirements, 404 @NonNull AndroidKeystoreAttestationVerificationAttributes attestationAttributes, 405 MyDumpData dumpData) { 406 int result = 0; 407 408 // Checks for support of Keymaster 4. 409 if (attestationAttributes.getAttestationVersion() < 3) { 410 Slog.e(TAG, "Attestation version is not at least 3 (Keymaster 4)."); 411 result |= FLAG_FAILURE_KEYSTORE_REQUIREMENTS; 412 } else { 413 dumpData.mAttestationVersionAtLeast3 = true; 414 } 415 416 // Checks for support of Keymaster 4. 417 if (attestationAttributes.getKeymasterVersion() < 4) { 418 Slog.e(TAG, "Keymaster version is not at least 4."); 419 result |= FLAG_FAILURE_KEYSTORE_REQUIREMENTS; 420 } else { 421 dumpData.mKeymasterVersionAtLeast4 = true; 422 } 423 424 // First two characters are Android OS version. 425 if (attestationAttributes.getKeyOsVersion() < 100000) { 426 Slog.e(TAG, "Android OS version is not 10+."); 427 result |= FLAG_FAILURE_KEYSTORE_REQUIREMENTS; 428 } else { 429 dumpData.mOsVersionAtLeast10 = true; 430 } 431 432 if (!attestationAttributes.isAttestationHardwareBacked()) { 433 Slog.e(TAG, "Key is not HW backed."); 434 result |= FLAG_FAILURE_KEYSTORE_REQUIREMENTS; 435 } else { 436 dumpData.mKeyHwBacked = true; 437 } 438 439 if (!attestationAttributes.isKeymasterHardwareBacked()) { 440 Slog.e(TAG, "Keymaster is not HW backed."); 441 result |= FLAG_FAILURE_KEYSTORE_REQUIREMENTS; 442 } else { 443 dumpData.mKeymasterHwBacked = true; 444 } 445 446 if (attestationAttributes.getVerifiedBootState() != VERIFIED) { 447 Slog.e(TAG, "Boot state not Verified."); 448 result |= FLAG_FAILURE_BOOT_STATE; 449 } else { 450 dumpData.mBootStateIsVerified = true; 451 } 452 453 try { 454 if (!attestationAttributes.isVerifiedBootLocked()) { 455 Slog.e(TAG, "Verified boot state is not locked."); 456 result |= FLAG_FAILURE_BOOT_STATE; 457 } else { 458 dumpData.mVerifiedBootStateLocked = true; 459 } 460 } catch (IllegalStateException e) { 461 Slog.e(TAG, "VerifiedBootLocked is not set.", e); 462 result = FLAG_FAILURE_BOOT_STATE; 463 } 464 465 int maxPatchLevelDiffMonths = requirements.getInt(PARAM_MAX_PATCH_LEVEL_DIFF_MONTHS, 466 MAX_PATCH_AGE_MONTHS); 467 468 // Patch level integer YYYYMM is expected to be within maxPatchLevelDiffMonths of today. 469 if (!isValidPatchLevel(attestationAttributes.getKeyOsPatchLevel(), 470 maxPatchLevelDiffMonths)) { 471 Slog.e(TAG, "OS patch level is not within valid range."); 472 result |= FLAG_FAILURE_PATCH_LEVEL_DIFF; 473 } else { 474 dumpData.mOsPatchLevelInRange = true; 475 } 476 477 // Patch level integer YYYYMMDD is expected to be within maxPatchLevelDiffMonths of today. 478 if (!isValidPatchLevel(attestationAttributes.getKeyBootPatchLevel(), 479 maxPatchLevelDiffMonths)) { 480 Slog.e(TAG, "Boot patch level is not within valid range."); 481 result |= FLAG_FAILURE_PATCH_LEVEL_DIFF; 482 } else { 483 dumpData.mKeyBootPatchLevelInRange = true; 484 } 485 486 if (!isValidPatchLevel(attestationAttributes.getKeyVendorPatchLevel(), 487 maxPatchLevelDiffMonths)) { 488 Slog.e(TAG, "Vendor patch level is not within valid range."); 489 result |= FLAG_FAILURE_PATCH_LEVEL_DIFF; 490 } else { 491 dumpData.mKeyVendorPatchLevelInRange = true; 492 } 493 494 if (!isValidPatchLevel(attestationAttributes.getKeyBootPatchLevel(), 495 maxPatchLevelDiffMonths)) { 496 Slog.e(TAG, "Boot patch level is not within valid range."); 497 result |= FLAG_FAILURE_PATCH_LEVEL_DIFF; 498 } else { 499 dumpData.mKeyBootPatchLevelInRange = true; 500 } 501 502 return result; 503 } 504 checkPublicKey( @onNull Certificate certificate, @NonNull byte[] expectedPublicKey)505 private boolean checkPublicKey( 506 @NonNull Certificate certificate, @NonNull byte[] expectedPublicKey) { 507 final byte[] publicKey = certificate.getPublicKey().getEncoded(); 508 return Arrays.equals(publicKey, expectedPublicKey); 509 } 510 checkAttestationChallenge( @onNull AndroidKeystoreAttestationVerificationAttributes attestationAttributes, @NonNull byte[] expectedChallenge)511 private boolean checkAttestationChallenge( 512 @NonNull AndroidKeystoreAttestationVerificationAttributes attestationAttributes, 513 @NonNull byte[] expectedChallenge) { 514 final byte[] challenge = attestationAttributes.getAttestationChallenge().toByteArray(); 515 return Arrays.equals(challenge, expectedChallenge); 516 } 517 checkOwnedBySystem(@onNull X509Certificate certificate, @NonNull AndroidKeystoreAttestationVerificationAttributes attestationAttributes)518 private boolean checkOwnedBySystem(@NonNull X509Certificate certificate, 519 @NonNull AndroidKeystoreAttestationVerificationAttributes attestationAttributes) { 520 final Set<String> ownerPackages = 521 attestationAttributes.getApplicationPackageNameVersion().keySet(); 522 if (!ANDROID_SYSTEM_PACKAGE_NAME_SET.equals(ownerPackages)) { 523 Slog.e(TAG, "Owner is not system, packages=" + ownerPackages); 524 return false; 525 } 526 527 return true; 528 } 529 530 /** 531 * Validates patchLevel passed is within range of the local device patch date if local patch is 532 * not over one year old. Since the time can be changed on device, just checking the patch date 533 * is not enough. Therefore, we also confirm the patch level for the remote and local device are 534 * similar. 535 */ isValidPatchLevel(int patchLevel, int maxPatchLevelDiffMonths)536 private boolean isValidPatchLevel(int patchLevel, int maxPatchLevelDiffMonths) { 537 LocalDate currentDate = mTestSystemDate != null 538 ? mTestSystemDate : LocalDate.now(ZoneId.systemDefault()); 539 540 // Convert local patch date to LocalDate. 541 LocalDate localPatchDate; 542 try { 543 if (mTestLocalPatchDate != null) { 544 localPatchDate = mTestLocalPatchDate; 545 } else { 546 localPatchDate = LocalDate.parse(Build.VERSION.SECURITY_PATCH); 547 } 548 } catch (Throwable t) { 549 Slog.e(TAG, "Build.VERSION.SECURITY_PATCH: " 550 + Build.VERSION.SECURITY_PATCH + " is not in format YYYY-MM-DD"); 551 return false; 552 } 553 554 // Check local patch date is not in last year of system clock. If the local patch already 555 // has a year's worth of bugs and vulnerabilities, it has no security meanings to check the 556 // remote patch level. 557 if (ChronoUnit.MONTHS.between(localPatchDate, currentDate) > MAX_PATCH_AGE_MONTHS) { 558 return true; 559 } 560 561 // Convert remote patch dates to LocalDate. 562 String remoteDeviceDateStr = String.valueOf(patchLevel); 563 if (remoteDeviceDateStr.length() != 6 && remoteDeviceDateStr.length() != 8) { 564 Slog.e(TAG, "Patch level is not in format YYYYMM or YYYYMMDD"); 565 return false; 566 } 567 568 int patchYear = Integer.parseInt(remoteDeviceDateStr.substring(0, 4)); 569 int patchMonth = Integer.parseInt(remoteDeviceDateStr.substring(4, 6)); 570 LocalDate remotePatchDate = LocalDate.of(patchYear, patchMonth, 1); 571 572 // Check patch dates are within the max patch level diff of each other 573 return Math.abs(ChronoUnit.MONTHS.between(localPatchDate, remotePatchDate)) 574 <= maxPatchLevelDiffMonths; 575 } 576 577 /* Mutable data class for tracking dump data from verifications. */ 578 private static class MyDumpData extends AttestationVerificationManagerService.DumpData { 579 580 // Top-Level Result 581 int mResult = -1; 582 583 // Configuration/Setup preconditions 584 boolean mCertificationFactoryAvailable = false; 585 boolean mCertPathValidatorAvailable = false; 586 587 // AttestationParameters (Valid Input Only) 588 boolean mAttestationParametersOk = false; 589 590 // Certificate Chain (Structure & Chaining Conditions) 591 boolean mCertChainOk = false; 592 593 // Binding 594 boolean mBindingOk = false; 595 int mBindingType = -1; 596 597 // System Ownership 598 boolean mSystemOwnershipChecked = false; 599 boolean mSystemOwned = false; 600 601 // Android Keystore attestation properties 602 boolean mOsVersionAtLeast10 = false; 603 boolean mKeyHwBacked = false; 604 boolean mAttestationVersionAtLeast3 = false; 605 boolean mKeymasterVersionAtLeast4 = false; 606 boolean mKeymasterHwBacked = false; 607 boolean mBootStateIsVerified = false; 608 boolean mVerifiedBootStateLocked = false; 609 boolean mOsPatchLevelInRange = false; 610 boolean mKeyBootPatchLevelInRange = false; 611 boolean mKeyVendorPatchLevelInRange = false; 612 613 @SuppressLint("WrongConstant") 614 @Override dumpTo(IndentingPrintWriter writer)615 public void dumpTo(IndentingPrintWriter writer) { 616 writer.println("Result: " + mResult); 617 if (!mCertificationFactoryAvailable) { 618 writer.println("Certificate Factory Unavailable"); 619 return; 620 } 621 if (!mCertPathValidatorAvailable) { 622 writer.println("Cert Path Validator Unavailable"); 623 return; 624 } 625 if (!mAttestationParametersOk) { 626 writer.println("Attestation parameters set incorrectly."); 627 return; 628 } 629 630 writer.println("Certificate Chain Valid (inc. Trust Anchor): " + booleanToOkFail( 631 mCertChainOk)); 632 if (!mCertChainOk) { 633 return; 634 } 635 636 // Binding 637 writer.println("Local Binding: " + booleanToOkFail(mBindingOk)); 638 writer.increaseIndent(); 639 writer.println("Binding Type: " + mBindingType); 640 writer.decreaseIndent(); 641 642 if (mSystemOwnershipChecked) { 643 writer.println("System Ownership: " + booleanToOkFail(mSystemOwned)); 644 } 645 646 // Keystore Attestation params 647 writer.println("KeyStore Attestation Parameters"); 648 writer.increaseIndent(); 649 writer.println("OS Version >= 10: " + booleanToOkFail(mOsVersionAtLeast10)); 650 writer.println("OS Patch Level in Range: " + booleanToOkFail(mOsPatchLevelInRange)); 651 writer.println( 652 "Attestation Version >= 3: " + booleanToOkFail(mAttestationVersionAtLeast3)); 653 writer.println("Keymaster Version >= 4: " + booleanToOkFail(mKeymasterVersionAtLeast4)); 654 writer.println("Keymaster HW-Backed: " + booleanToOkFail(mKeymasterHwBacked)); 655 writer.println("Key is HW Backed: " + booleanToOkFail(mKeyHwBacked)); 656 writer.println("Boot State is VERIFIED: " + booleanToOkFail(mBootStateIsVerified)); 657 writer.println("Verified Boot is LOCKED: " + booleanToOkFail(mVerifiedBootStateLocked)); 658 writer.println( 659 "Key Boot Level in Range: " + booleanToOkFail(mKeyBootPatchLevelInRange)); 660 writer.println("Key Vendor Patch Level in Range: " + booleanToOkFail( 661 mKeyVendorPatchLevelInRange)); 662 writer.decreaseIndent(); 663 } 664 booleanToOkFail(boolean value)665 private String booleanToOkFail(boolean value) { 666 return value ? "OK" : "FAILURE"; 667 } 668 } 669 } 670