1 /* 2 * Copyright (C) 2017 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 android.security.keystore; 18 19 import android.Manifest; 20 import android.annotation.NonNull; 21 import android.annotation.RequiresPermission; 22 import android.annotation.SystemApi; 23 import android.annotation.TestApi; 24 import android.content.Context; 25 import android.content.res.Resources; 26 import android.os.Build; 27 import android.security.KeyStore; 28 import android.security.keymaster.KeymasterArguments; 29 import android.security.keymaster.KeymasterCertificateChain; 30 import android.security.keymaster.KeymasterDefs; 31 import android.telephony.TelephonyManager; 32 import android.text.TextUtils; 33 import android.util.ArraySet; 34 35 import java.io.ByteArrayInputStream; 36 import java.io.ByteArrayOutputStream; 37 import java.nio.charset.StandardCharsets; 38 import java.security.cert.CertificateFactory; 39 import java.security.cert.X509Certificate; 40 import java.util.Collection; 41 import java.util.Set; 42 43 /** 44 * Utilities for attesting the device's hardware identifiers. 45 * 46 * @hide 47 */ 48 @SystemApi 49 @TestApi 50 public abstract class AttestationUtils { AttestationUtils()51 private AttestationUtils() { 52 } 53 54 /** 55 * Specifies that the device should attest its serial number. For use with 56 * {@link #attestDeviceIds}. 57 * 58 * @see #attestDeviceIds 59 */ 60 public static final int ID_TYPE_SERIAL = 1; 61 62 /** 63 * Specifies that the device should attest its IMEIs. For use with {@link #attestDeviceIds}. 64 * 65 * @see #attestDeviceIds 66 */ 67 public static final int ID_TYPE_IMEI = 2; 68 69 /** 70 * Specifies that the device should attest its MEIDs. For use with {@link #attestDeviceIds}. 71 * 72 * @see #attestDeviceIds 73 */ 74 public static final int ID_TYPE_MEID = 3; 75 76 /** 77 * Creates an array of X509Certificates from the provided KeymasterCertificateChain. 78 * 79 * @hide Only called by the DevicePolicyManager. 80 */ parseCertificateChain( final KeymasterCertificateChain kmChain)81 @NonNull public static X509Certificate[] parseCertificateChain( 82 final KeymasterCertificateChain kmChain) throws 83 KeyAttestationException { 84 // Extract certificate chain. 85 final Collection<byte[]> rawChain = kmChain.getCertificates(); 86 if (rawChain.size() < 2) { 87 throw new KeyAttestationException("Attestation certificate chain contained " 88 + rawChain.size() + " entries. At least two are required."); 89 } 90 final ByteArrayOutputStream concatenatedRawChain = new ByteArrayOutputStream(); 91 try { 92 for (final byte[] cert : rawChain) { 93 concatenatedRawChain.write(cert); 94 } 95 return CertificateFactory.getInstance("X.509").generateCertificates( 96 new ByteArrayInputStream(concatenatedRawChain.toByteArray())) 97 .toArray(new X509Certificate[0]); 98 } catch (Exception e) { 99 throw new KeyAttestationException("Unable to construct certificate chain", e); 100 } 101 } 102 prepareAttestationArgumentsForDeviceId( Context context, @NonNull int[] idTypes, @NonNull byte[] attestationChallenge)103 @NonNull private static KeymasterArguments prepareAttestationArgumentsForDeviceId( 104 Context context, @NonNull int[] idTypes, @NonNull byte[] attestationChallenge) throws 105 DeviceIdAttestationException { 106 // Verify that device ID attestation types are provided. 107 if (idTypes == null) { 108 throw new NullPointerException("Missing id types"); 109 } 110 111 return prepareAttestationArguments(context, idTypes, attestationChallenge); 112 } 113 114 /** 115 * Prepares Keymaster Arguments with attestation data. 116 * @hide should only be used by KeyChain. 117 */ prepareAttestationArguments(Context context, @NonNull int[] idTypes, @NonNull byte[] attestationChallenge)118 @NonNull public static KeymasterArguments prepareAttestationArguments(Context context, 119 @NonNull int[] idTypes, @NonNull byte[] attestationChallenge) throws 120 DeviceIdAttestationException { 121 return prepareAttestationArguments(context, idTypes,attestationChallenge, Build.BRAND); 122 } 123 124 /** 125 * Prepares Keymaster Arguments with attestation data for misprovisioned Pixel 2 device. 126 * See http://go/keyAttestationFailure and http://b/69471841 for more info. 127 * @hide should only be used by KeyChain. 128 */ prepareAttestationArgumentsIfMisprovisioned( Context context, @NonNull int[] idTypes, @NonNull byte[] attestationChallenge)129 @NonNull public static KeymasterArguments prepareAttestationArgumentsIfMisprovisioned( 130 Context context, @NonNull int[] idTypes, @NonNull byte[] attestationChallenge) throws 131 DeviceIdAttestationException { 132 Resources resources = context.getResources(); 133 String misprovisionedBrand = resources.getString( 134 com.android.internal.R.string.config_misprovisionedBrandValue); 135 if (!TextUtils.isEmpty(misprovisionedBrand) && !isPotentiallyMisprovisionedDevice(context)){ 136 return null; 137 } 138 return prepareAttestationArguments( 139 context, idTypes, attestationChallenge, misprovisionedBrand); 140 } 141 isPotentiallyMisprovisionedDevice(Context context)142 @NonNull private static boolean isPotentiallyMisprovisionedDevice(Context context) { 143 Resources resources = context.getResources(); 144 String misprovisionedModel = resources.getString( 145 com.android.internal.R.string.config_misprovisionedDeviceModel); 146 return (Build.MODEL.equals(misprovisionedModel)); 147 } 148 prepareAttestationArguments(Context context, @NonNull int[] idTypes, @NonNull byte[] attestationChallenge, String brand)149 @NonNull private static KeymasterArguments prepareAttestationArguments(Context context, 150 @NonNull int[] idTypes, @NonNull byte[] attestationChallenge, String brand) throws 151 DeviceIdAttestationException { 152 // Check method arguments, retrieve requested device IDs and prepare attestation arguments. 153 if (attestationChallenge == null) { 154 throw new NullPointerException("Missing attestation challenge"); 155 } 156 final KeymasterArguments attestArgs = new KeymasterArguments(); 157 attestArgs.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_CHALLENGE, attestationChallenge); 158 // Return early if the caller did not request any device identifiers to be included in the 159 // attestation record. 160 if (idTypes == null) { 161 return attestArgs; 162 } 163 final Set<Integer> idTypesSet = new ArraySet<>(idTypes.length); 164 for (int idType : idTypes) { 165 idTypesSet.add(idType); 166 } 167 TelephonyManager telephonyService = null; 168 if (idTypesSet.contains(ID_TYPE_IMEI) || idTypesSet.contains(ID_TYPE_MEID)) { 169 telephonyService = (TelephonyManager) context.getSystemService( 170 Context.TELEPHONY_SERVICE); 171 if (telephonyService == null) { 172 throw new DeviceIdAttestationException("Unable to access telephony service"); 173 } 174 } 175 for (final Integer idType : idTypesSet) { 176 switch (idType) { 177 case ID_TYPE_SERIAL: 178 attestArgs.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_SERIAL, 179 Build.getSerial().getBytes(StandardCharsets.UTF_8)); 180 break; 181 case ID_TYPE_IMEI: { 182 final String imei = telephonyService.getImei(0); 183 if (imei == null) { 184 throw new DeviceIdAttestationException("Unable to retrieve IMEI"); 185 } 186 attestArgs.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_IMEI, 187 imei.getBytes(StandardCharsets.UTF_8)); 188 break; 189 } 190 case ID_TYPE_MEID: { 191 final String meid = telephonyService.getMeid(0); 192 if (meid == null) { 193 throw new DeviceIdAttestationException("Unable to retrieve MEID"); 194 } 195 attestArgs.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_MEID, 196 meid.getBytes(StandardCharsets.UTF_8)); 197 break; 198 } 199 default: 200 throw new IllegalArgumentException("Unknown device ID type " + idType); 201 } 202 } 203 attestArgs.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_BRAND, 204 brand.getBytes(StandardCharsets.UTF_8)); 205 attestArgs.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_DEVICE, 206 Build.DEVICE.getBytes(StandardCharsets.UTF_8)); 207 attestArgs.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_PRODUCT, 208 Build.PRODUCT.getBytes(StandardCharsets.UTF_8)); 209 attestArgs.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_MANUFACTURER, 210 Build.MANUFACTURER.getBytes(StandardCharsets.UTF_8)); 211 attestArgs.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_MODEL, 212 Build.MODEL.getBytes(StandardCharsets.UTF_8)); 213 return attestArgs; 214 } 215 216 /** 217 * Performs attestation of the device's identifiers. This method returns a certificate chain 218 * whose first element contains the requested device identifiers in an extension. The device's 219 * manufacturer, model, brand, device and product are always also included in the attestation. 220 * If the device supports attestation in secure hardware, the chain will be rooted at a 221 * trustworthy CA key. Otherwise, the chain will be rooted at an untrusted certificate. See 222 * <a href="https://developer.android.com/training/articles/security-key-attestation.html"> 223 * Key Attestation</a> for the format of the certificate extension. 224 * <p> 225 * Attestation will only be successful when all of the following are true: 226 * 1) The device has been set up to support device identifier attestation at the factory. 227 * 2) The user has not permanently disabled device identifier attestation. 228 * 3) You have permission to access the device identifiers you are requesting attestation for. 229 * <p> 230 * For privacy reasons, you cannot distinguish between (1) and (2). If attestation is 231 * unsuccessful, the device may not support it in general or the user may have permanently 232 * disabled it. 233 * 234 * @param context the context to use for retrieving device identifiers. 235 * @param idTypes the types of device identifiers to attest. 236 * @param attestationChallenge a blob to include in the certificate alongside the device 237 * identifiers. 238 * 239 * @return a certificate chain containing the requested device identifiers in the first element 240 * 241 * @exception SecurityException if you are not permitted to obtain an attestation of the 242 * device's identifiers. 243 * @exception DeviceIdAttestationException if the attestation operation fails. 244 */ 245 @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) attestDeviceIds(Context context, @NonNull int[] idTypes, @NonNull byte[] attestationChallenge)246 @NonNull public static X509Certificate[] attestDeviceIds(Context context, 247 @NonNull int[] idTypes, @NonNull byte[] attestationChallenge) throws 248 DeviceIdAttestationException { 249 final KeymasterArguments attestArgs = prepareAttestationArgumentsForDeviceId( 250 context, idTypes, attestationChallenge); 251 252 // Perform attestation. 253 final KeymasterCertificateChain outChain = new KeymasterCertificateChain(); 254 final int errorCode = KeyStore.getInstance().attestDeviceIds(attestArgs, outChain); 255 if (errorCode != KeyStore.NO_ERROR) { 256 throw new DeviceIdAttestationException("Unable to perform attestation", 257 KeyStore.getKeyStoreException(errorCode)); 258 } 259 260 try { 261 return parseCertificateChain(outChain); 262 } catch (KeyAttestationException e) { 263 throw new DeviceIdAttestationException(e.getMessage(), e); 264 } 265 } 266 267 /** 268 * Returns true if the attestation chain provided is a valid key attestation chain. 269 * @hide 270 */ isChainValid(KeymasterCertificateChain chain)271 public static boolean isChainValid(KeymasterCertificateChain chain) { 272 return chain != null && chain.getCertificates().size() >= 2; 273 } 274 } 275