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