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 * Performs attestation of the device's identifiers. This method returns a certificate chain 77 * whose first element contains the requested device identifiers in an extension. The device's 78 * manufacturer, model, brand, device and product are always also included in the attestation. 79 * If the device supports attestation in secure hardware, the chain will be rooted at a 80 * trustworthy CA key. Otherwise, the chain will be rooted at an untrusted certificate. See 81 * <a href="https://developer.android.com/training/articles/security-key-attestation.html"> 82 * Key Attestation</a> for the format of the certificate extension. 83 * <p> 84 * Attestation will only be successful when all of the following are true: 85 * 1) The device has been set up to support device identifier attestation at the factory. 86 * 2) The user has not permanently disabled device identifier attestation. 87 * 3) You have permission to access the device identifiers you are requesting attestation for. 88 * <p> 89 * For privacy reasons, you cannot distinguish between (1) and (2). If attestation is 90 * unsuccessful, the device may not support it in general or the user may have permanently 91 * disabled it. 92 * 93 * @param context the context to use for retrieving device identifiers. 94 * @param idTypes the types of device identifiers to attest. 95 * @param attestationChallenge a blob to include in the certificate alongside the device 96 * identifiers. 97 * 98 * @return a certificate chain containing the requested device identifiers in the first element 99 * 100 * @exception SecurityException if you are not permitted to obtain an attestation of the 101 * device's identifiers. 102 * @exception DeviceIdAttestationException if the attestation operation fails. 103 */ 104 @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) attestDeviceIds(Context context, @NonNull int[] idTypes, @NonNull byte[] attestationChallenge)105 @NonNull public static X509Certificate[] attestDeviceIds(Context context, 106 @NonNull int[] idTypes, @NonNull byte[] attestationChallenge) throws 107 DeviceIdAttestationException { 108 // Check method arguments, retrieve requested device IDs and prepare attestation arguments. 109 if (idTypes == null) { 110 throw new NullPointerException("Missing id types"); 111 } 112 if (attestationChallenge == null) { 113 throw new NullPointerException("Missing attestation challenge"); 114 } 115 final KeymasterArguments attestArgs = new KeymasterArguments(); 116 attestArgs.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_CHALLENGE, attestationChallenge); 117 final Set<Integer> idTypesSet = new ArraySet<>(idTypes.length); 118 for (int idType : idTypes) { 119 idTypesSet.add(idType); 120 } 121 TelephonyManager telephonyService = null; 122 if (idTypesSet.contains(ID_TYPE_IMEI) || idTypesSet.contains(ID_TYPE_MEID)) { 123 telephonyService = (TelephonyManager) context.getSystemService( 124 Context.TELEPHONY_SERVICE); 125 if (telephonyService == null) { 126 throw new DeviceIdAttestationException("Unable to access telephony service"); 127 } 128 } 129 for (final Integer idType : idTypesSet) { 130 switch (idType) { 131 case ID_TYPE_SERIAL: 132 attestArgs.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_SERIAL, 133 Build.getSerial().getBytes(StandardCharsets.UTF_8)); 134 break; 135 case ID_TYPE_IMEI: { 136 final String imei = telephonyService.getImei(0); 137 if (imei == null) { 138 throw new DeviceIdAttestationException("Unable to retrieve IMEI"); 139 } 140 attestArgs.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_IMEI, 141 imei.getBytes(StandardCharsets.UTF_8)); 142 break; 143 } 144 case ID_TYPE_MEID: { 145 final String meid = telephonyService.getDeviceId(); 146 if (meid == null) { 147 throw new DeviceIdAttestationException("Unable to retrieve MEID"); 148 } 149 attestArgs.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_MEID, 150 meid.getBytes(StandardCharsets.UTF_8)); 151 break; 152 } 153 default: 154 throw new IllegalArgumentException("Unknown device ID type " + idType); 155 } 156 } 157 attestArgs.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_BRAND, 158 Build.BRAND.getBytes(StandardCharsets.UTF_8)); 159 attestArgs.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_DEVICE, 160 Build.DEVICE.getBytes(StandardCharsets.UTF_8)); 161 attestArgs.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_PRODUCT, 162 Build.PRODUCT.getBytes(StandardCharsets.UTF_8)); 163 attestArgs.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_MANUFACTURER, 164 Build.MANUFACTURER.getBytes(StandardCharsets.UTF_8)); 165 attestArgs.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_MODEL, 166 Build.MODEL.getBytes(StandardCharsets.UTF_8)); 167 168 // Perform attestation. 169 final KeymasterCertificateChain outChain = new KeymasterCertificateChain(); 170 final int errorCode = KeyStore.getInstance().attestDeviceIds(attestArgs, outChain); 171 if (errorCode != KeyStore.NO_ERROR) { 172 throw new DeviceIdAttestationException("Unable to perform attestation", 173 KeyStore.getKeyStoreException(errorCode)); 174 } 175 176 // Extract certificate chain. 177 final Collection<byte[]> rawChain = outChain.getCertificates(); 178 if (rawChain.size() < 2) { 179 throw new DeviceIdAttestationException("Attestation certificate chain contained " 180 + rawChain.size() + " entries. At least two are required."); 181 } 182 final ByteArrayOutputStream concatenatedRawChain = new ByteArrayOutputStream(); 183 try { 184 for (final byte[] cert : rawChain) { 185 concatenatedRawChain.write(cert); 186 } 187 return CertificateFactory.getInstance("X.509").generateCertificates( 188 new ByteArrayInputStream(concatenatedRawChain.toByteArray())) 189 .toArray(new X509Certificate[0]); 190 } catch (Exception e) { 191 throw new DeviceIdAttestationException("Unable to construct certificate chain", e); 192 } 193 } 194 } 195