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