• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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