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