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.PARAM_CHALLENGE;
20 import static android.security.attestationverification.AttestationVerificationManager.RESULT_FAILURE;
21 import static android.security.attestationverification.AttestationVerificationManager.RESULT_SUCCESS;
22 import static android.security.attestationverification.AttestationVerificationManager.TYPE_CHALLENGE;
23 
24 import android.annotation.NonNull;
25 import android.os.Build;
26 import android.os.Bundle;
27 import android.security.keystore.KeyGenParameterSpec;
28 import android.security.keystore.KeyProperties;
29 import android.util.Log;
30 import android.util.Slog;
31 
32 import com.android.internal.org.bouncycastle.asn1.ASN1InputStream;
33 import com.android.internal.org.bouncycastle.asn1.ASN1ObjectIdentifier;
34 import com.android.internal.org.bouncycastle.asn1.ASN1OctetString;
35 import com.android.internal.org.bouncycastle.asn1.ASN1Sequence;
36 import com.android.internal.org.bouncycastle.asn1.x509.Certificate;
37 
38 import java.io.ByteArrayInputStream;
39 import java.io.IOException;
40 import java.security.KeyPairGenerator;
41 import java.security.KeyStore;
42 import java.security.cert.CertPath;
43 import java.security.cert.CertPathValidator;
44 import java.security.cert.CertificateEncodingException;
45 import java.security.cert.CertificateException;
46 import java.security.cert.CertificateFactory;
47 import java.security.cert.PKIXParameters;
48 import java.security.cert.TrustAnchor;
49 import java.security.cert.X509Certificate;
50 import java.util.ArrayList;
51 import java.util.Arrays;
52 import java.util.Collections;
53 import java.util.List;
54 import java.util.Set;
55 
56 /**
57  * Verifies {@code PROFILE_SELF_TRUSTED} attestations.
58  *
59  * Verifies that the attesting environment can create an attestation with the same root certificate
60  * as the verifying device with a matching attestation challenge. Skips CRL revocations checking
61  * so this verifier can work in a hermetic test environment.
62  *
63  * This verifier profile is intended to be used only for testing.
64  */
65 class AttestationVerificationSelfTrustedVerifierForTesting {
66     private static final String TAG = "AVF";
67     private static final boolean DEBUG = Build.IS_DEBUGGABLE && Log.isLoggable(TAG, Log.VERBOSE);
68 
69     // The OID for the extension Android Keymint puts into device-generated certificates.
70     private static final String ANDROID_KEYMINT_KEY_DESCRIPTION_EXTENSION_OID =
71             "1.3.6.1.4.1.11129.2.1.17";
72 
73     // ASN.1 sequence index values for the Android Keymint extension.
74     private static final int ATTESTATION_CHALLENGE_INDEX = 4;
75 
76     private static final String ANDROID_KEYSTORE = "AndroidKeyStore";
77     private static final String GOLDEN_ALIAS =
78             AttestationVerificationSelfTrustedVerifierForTesting.class.getCanonicalName()
79                     + ".Golden";
80 
81     private static volatile AttestationVerificationSelfTrustedVerifierForTesting
82             sAttestationVerificationSelfTrustedVerifier = null;
83 
84     private final CertificateFactory mCertificateFactory;
85     private final CertPathValidator mCertPathValidator;
86     private final KeyStore mAndroidKeyStore;
87     private X509Certificate mGoldenRootCert;
88 
getInstance()89     static AttestationVerificationSelfTrustedVerifierForTesting getInstance()
90             throws Exception {
91         if (sAttestationVerificationSelfTrustedVerifier == null) {
92             synchronized (AttestationVerificationSelfTrustedVerifierForTesting.class) {
93                 if (sAttestationVerificationSelfTrustedVerifier == null) {
94                     sAttestationVerificationSelfTrustedVerifier =
95                             new AttestationVerificationSelfTrustedVerifierForTesting();
96                 }
97             }
98         }
99         return sAttestationVerificationSelfTrustedVerifier;
100     }
101 
debugVerboseLog(String str, Throwable t)102     private static void debugVerboseLog(String str, Throwable t) {
103         if (DEBUG) {
104             Slog.v(TAG, str, t);
105         }
106     }
107 
debugVerboseLog(String str)108     private static void debugVerboseLog(String str) {
109         if (DEBUG) {
110             Slog.v(TAG, str);
111         }
112     }
113 
AttestationVerificationSelfTrustedVerifierForTesting()114     private AttestationVerificationSelfTrustedVerifierForTesting() throws Exception {
115         mCertificateFactory = CertificateFactory.getInstance("X.509");
116         mCertPathValidator = CertPathValidator.getInstance("PKIX");
117         mAndroidKeyStore = KeyStore.getInstance(ANDROID_KEYSTORE);
118         mAndroidKeyStore.load(null);
119         if (!mAndroidKeyStore.containsAlias(GOLDEN_ALIAS)) {
120             KeyPairGenerator kpg =
121                     KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_EC, ANDROID_KEYSTORE);
122             KeyGenParameterSpec parameterSpec = new KeyGenParameterSpec.Builder(
123                     GOLDEN_ALIAS, KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
124                     .setAttestationChallenge(GOLDEN_ALIAS.getBytes())
125                     .setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512).build();
126             kpg.initialize(parameterSpec);
127             kpg.generateKeyPair();
128         }
129 
130         X509Certificate[] goldenCerts = (X509Certificate[])
131                 ((KeyStore.PrivateKeyEntry) mAndroidKeyStore.getEntry(GOLDEN_ALIAS, null))
132                         .getCertificateChain();
133         mGoldenRootCert = goldenCerts[goldenCerts.length - 1];
134     }
135 
verifyAttestation( int localBindingType, @NonNull Bundle requirements, @NonNull byte[] attestation)136     int verifyAttestation(
137             int localBindingType, @NonNull Bundle requirements,  @NonNull byte[] attestation) {
138         List<X509Certificate> certificates = new ArrayList<>();
139         ByteArrayInputStream bis = new ByteArrayInputStream(attestation);
140         try {
141             while (bis.available() > 0) {
142                 certificates.add((X509Certificate) mCertificateFactory.generateCertificate(bis));
143             }
144         } catch (CertificateException e) {
145             debugVerboseLog("Unable to parse certificates from attestation", e);
146             return RESULT_FAILURE;
147         }
148 
149         if (localBindingType == TYPE_CHALLENGE
150                 && validateRequirements(requirements)
151                 && checkLeafChallenge(requirements, certificates)
152                 && verifyCertificateChain(certificates)) {
153             return RESULT_SUCCESS;
154         }
155 
156         return RESULT_FAILURE;
157     }
158 
verifyCertificateChain(List<X509Certificate> certificates)159     private boolean verifyCertificateChain(List<X509Certificate> certificates) {
160         if (certificates.size() < 2) {
161             debugVerboseLog("Certificate chain less than 2 in size.");
162             return false;
163         }
164 
165         try {
166             CertPath certificatePath = mCertificateFactory.generateCertPath(certificates);
167             PKIXParameters validationParams = new PKIXParameters(getTrustAnchors());
168             // Skipping revocation checking because we want this to work in a hermetic test
169             // environment.
170             validationParams.setRevocationEnabled(false);
171             mCertPathValidator.validate(certificatePath, validationParams);
172         } catch (Throwable t) {
173             debugVerboseLog("Invalid certificate chain", t);
174             return false;
175         }
176 
177         return true;
178     }
179 
getTrustAnchors()180     private Set<TrustAnchor> getTrustAnchors() {
181         return Collections.singleton(new TrustAnchor(mGoldenRootCert, null));
182     }
183 
validateRequirements(Bundle requirements)184     private boolean validateRequirements(Bundle requirements) {
185         if (requirements.size() != 1) {
186             debugVerboseLog("Requirements does not contain exactly 1 key.");
187             return false;
188         }
189         if (!requirements.containsKey(PARAM_CHALLENGE)) {
190             debugVerboseLog("Requirements does not contain key: " + PARAM_CHALLENGE);
191             return false;
192         }
193         return true;
194     }
195 
checkLeafChallenge(Bundle requirements, List<X509Certificate> certificates)196     private boolean checkLeafChallenge(Bundle requirements, List<X509Certificate> certificates) {
197         // Verify challenge
198         byte[] challenge;
199         try {
200             challenge = getChallengeFromCert(certificates.get(0));
201         } catch (Throwable t) {
202             debugVerboseLog("Unable to parse challenge from certificate.", t);
203             return false;
204         }
205 
206         if (Arrays.equals(requirements.getByteArray(PARAM_CHALLENGE), challenge)) {
207             return true;
208         } else {
209             debugVerboseLog("Self-Trusted validation failed; challenge mismatch.");
210             return false;
211         }
212     }
213 
getChallengeFromCert(@onNull X509Certificate x509Certificate)214     private byte[] getChallengeFromCert(@NonNull X509Certificate x509Certificate)
215             throws CertificateEncodingException, IOException {
216         Certificate certificate = Certificate.getInstance(
217                 new ASN1InputStream(x509Certificate.getEncoded()).readObject());
218         ASN1Sequence keyAttributes = (ASN1Sequence) certificate.getTBSCertificate().getExtensions()
219                 .getExtensionParsedValue(
220                         new ASN1ObjectIdentifier(ANDROID_KEYMINT_KEY_DESCRIPTION_EXTENSION_OID));
221         return ((ASN1OctetString) keyAttributes.getObjectAt(ATTESTATION_CHALLENGE_INDEX))
222                 .getOctets();
223     }
224 }
225