1 /* 2 * Copyright (C) 2023 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 package com.android.compatibility.common.deviceinfo; 17 18 import static android.security.keystore.KeyProperties.DIGEST_SHA256; 19 import static android.security.keystore.KeyProperties.KEY_ALGORITHM_EC; 20 import static android.security.keystore.KeyProperties.PURPOSE_SIGN; 21 22 import static com.android.bedstead.nene.packages.CommonPackages.FEATURE_DEVICE_ID_ATTESTATION; 23 24 import static com.google.android.attestation.ParsedAttestationRecord.createParsedAttestationRecord; 25 26 import android.content.pm.PackageManager; 27 import android.security.keystore.KeyGenParameterSpec; 28 import android.util.Log; 29 30 import com.android.compatibility.common.util.DeviceInfoStore; 31 32 import com.google.android.attestation.AuthorizationList; 33 import com.google.android.attestation.RootOfTrust; 34 35 import java.io.IOException; 36 import java.security.KeyPair; 37 import java.security.KeyPairGenerator; 38 import java.security.KeyStore; 39 import java.security.cert.Certificate; 40 import java.security.cert.X509Certificate; 41 import java.security.spec.ECGenParameterSpec; 42 import java.util.ArrayList; 43 import java.util.List; 44 45 /** 46 * Key attestation collector. Collects a subset of the information attested by the device's 47 * trusted execution environment (TEE) and by its StrongBox chip (if it has one). 48 */ 49 public final class KeystoreAttestationDeviceInfo extends DeviceInfo { 50 // Max log tag length is 23 characters, so we can't use the full class name. 51 private static final String TAG = "AttestationDeviceInfo"; 52 private static final String TEST_ALIAS_TEE = "testTeeKeyAlias"; 53 private static final String TEST_ALIAS_STRONGBOX = "testStrongBoxKeyAlias"; 54 private static final byte[] TEST_CHALLENGE = "challenge".getBytes(); 55 56 @Override collectDeviceInfo(DeviceInfoStore store)57 protected void collectDeviceInfo(DeviceInfoStore store) throws Exception { 58 collectAttestation( 59 store, "keymint_key_attestation", TEST_ALIAS_TEE, 60 /* strongBoxBacked= */ false); 61 if (getContext() 62 .getPackageManager() 63 .hasSystemFeature(PackageManager.FEATURE_STRONGBOX_KEYSTORE)) { 64 collectAttestation( 65 store, "strong_box_key_attestation", 66 TEST_ALIAS_STRONGBOX, /* strongBoxBacked= */ true); 67 } else { 68 Log.i(TAG, "StrongBox-backed Keystore not supported"); 69 } 70 } 71 collectAttestation( DeviceInfoStore store, String resultGroupName, String keyAlias, boolean strongBoxBacked)72 private void collectAttestation( 73 DeviceInfoStore store, 74 String resultGroupName, 75 String keyAlias, 76 boolean strongBoxBacked) 77 throws Exception { 78 KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); 79 keyStore.load(null); 80 81 // If this collector ran on this device before, there might already be a key 82 // with this alias, so delete it before trying to generate a fresh key with 83 // the same alias. 84 keyStore.deleteEntry(keyAlias); 85 86 KeyGenParameterSpec spec = 87 new KeyGenParameterSpec.Builder(keyAlias, PURPOSE_SIGN) 88 .setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1")) 89 .setDigests(DIGEST_SHA256) 90 .setDevicePropertiesAttestationIncluded( 91 getContext() 92 .getPackageManager() 93 .hasSystemFeature(FEATURE_DEVICE_ID_ATTESTATION)) 94 .setAttestationChallenge(TEST_CHALLENGE) 95 .setIsStrongBoxBacked(strongBoxBacked) 96 .build(); 97 98 KeyPairGenerator keyPairGenerator = 99 KeyPairGenerator.getInstance(KEY_ALGORITHM_EC, "AndroidKeyStore"); 100 keyPairGenerator.initialize(spec); 101 102 try { 103 KeyPair unusedKeyPair = keyPairGenerator.generateKeyPair(); 104 } catch (Exception e) { 105 String keystoreType = strongBoxBacked ? "StrongBox" : "TEE"; 106 Log.w(TAG, "Key pair generation failed with " + keystoreType + "-backed Keystore", e); 107 return; 108 } 109 110 List<X509Certificate> x509Certificates = new ArrayList<>(); 111 for (Certificate certificate : keyStore.getCertificateChain(keyAlias)) { 112 if (certificate instanceof X509Certificate) { 113 x509Certificates.add((X509Certificate) certificate); 114 } 115 } 116 if (x509Certificates.isEmpty()) { 117 Log.w(TAG, "Certificate chain is empty, so no attestation could be extracted"); 118 return; 119 } 120 121 AuthorizationList authorizationList; 122 123 try { 124 authorizationList = createParsedAttestationRecord( 125 x509Certificates).teeEnforced; 126 } catch (IOException e) { 127 Log.w(TAG, "Failed to parse the attestation extension", e); 128 return; 129 } 130 131 store.startGroup(resultGroupName); 132 if (authorizationList.osVersion.isPresent()) { 133 store.addResult("os_version", authorizationList.osVersion.get()); 134 } 135 if (authorizationList.osPatchLevel.isPresent()) { 136 store.addResult("os_patch_level", authorizationList.osPatchLevel.get()); 137 } 138 if (authorizationList.attestationIdBrand.isPresent()) { 139 store.addBytesResult( 140 "attestation_id_brand", 141 authorizationList.attestationIdBrand.get()); 142 } 143 if (authorizationList.attestationIdDevice.isPresent()) { 144 store.addBytesResult( 145 "attestation_id_device", 146 authorizationList.attestationIdDevice.get()); 147 } 148 if (authorizationList.attestationIdProduct.isPresent()) { 149 store.addBytesResult( 150 "attestation_id_product", 151 authorizationList.attestationIdProduct.get()); 152 } 153 if (authorizationList.attestationIdManufacturer.isPresent()) { 154 store.addBytesResult( 155 "attestation_id_manufacturer", 156 authorizationList.attestationIdManufacturer.get()); 157 } 158 if (authorizationList.attestationIdModel.isPresent()) { 159 store.addBytesResult( 160 "attestation_id_model", 161 authorizationList.attestationIdModel.get()); 162 } 163 if (authorizationList.vendorPatchLevel.isPresent()) { 164 store.addResult("vendor_patch_level", authorizationList.vendorPatchLevel.get()); 165 } 166 if (authorizationList.bootPatchLevel.isPresent()) { 167 store.addResult("boot_patch_level", authorizationList.bootPatchLevel.get()); 168 } 169 if (authorizationList.rootOfTrust.isPresent()) { 170 RootOfTrust rootOfTrust = authorizationList.rootOfTrust.get(); 171 store.startGroup("root_of_trust"); 172 store.addBytesResult( 173 "verified_boot_key", 174 rootOfTrust.verifiedBootKey); 175 store.addResult("device_locked", rootOfTrust.deviceLocked); 176 store.addResult("verified_boot_state", rootOfTrust.verifiedBootState.name()); 177 store.addBytesResult( 178 "verified_boot_hash", 179 rootOfTrust.verifiedBootHash); 180 store.endGroup(); 181 } 182 store.endGroup(); 183 } 184 } 185