1 /* 2 * Copyright (C) 2016 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 com.android.internal.telephony.uicc; 18 19 import android.os.AsyncResult; 20 import android.os.Binder; 21 import android.os.Handler; 22 import android.os.Message; 23 import android.telephony.Rlog; 24 import android.telephony.TelephonyManager; 25 26 import com.android.internal.telephony.CommandException; 27 import com.android.internal.telephony.CommandsInterface; 28 import com.android.internal.telephony.uicc.IccUtils; 29 import com.android.internal.telephony.uicc.UiccCarrierPrivilegeRules.TLV; 30 31 import java.io.ByteArrayInputStream; 32 import java.io.FileDescriptor; 33 import java.io.PrintWriter; 34 import java.lang.IllegalArgumentException; 35 import java.lang.IndexOutOfBoundsException; 36 import java.util.ArrayList; 37 import java.util.Arrays; 38 import java.util.List; 39 import java.util.Locale; 40 41 /** 42 * Class that reads PKCS15-based rules for carrier privileges. 43 * 44 * The spec for the rules: 45 * GP Secure Element Access Control: 46 * https://www.globalplatform.org/specificationsdevice.asp 47 * 48 * The UiccPkcs15 class handles overall flow of finding/selecting PKCS15 applet 49 * and reading/parsing each file. Because PKCS15 can be selected in 2 different ways: 50 * via logical channel or EF_DIR, PKCS15Selector is a handler to encapsulate the flow. 51 * Similarly, FileHandler is used for selecting/reading each file, so common codes are 52 * all in same place. 53 * 54 * {@hide} 55 */ 56 public class UiccPkcs15 extends Handler { 57 private static final String LOG_TAG = "UiccPkcs15"; 58 private static final boolean DBG = true; 59 60 // File handler for PKCS15 files, select file and read binary, 61 // convert to String then send to callback message. 62 private class FileHandler extends Handler { 63 // EF path for PKCS15 root, eg. "3F007F50" 64 // null if logical channel is used for PKCS15 access. 65 private final String mPkcs15Path; 66 // Message to send when file has been parsed. 67 private Message mCallback; 68 // File id to read data from, eg. "5031" 69 private String mFileId; 70 71 // async events for the sequence of select and read 72 static protected final int EVENT_SELECT_FILE_DONE = 101; 73 static protected final int EVENT_READ_BINARY_DONE = 102; 74 75 // pkcs15Path is nullable when using logical channel 76 public FileHandler(String pkcs15Path) { 77 log("Creating FileHandler, pkcs15Path: " + pkcs15Path); 78 mPkcs15Path = pkcs15Path; 79 } 80 81 public boolean loadFile(String fileId, Message callBack) { 82 log("loadFile: " + fileId); 83 if (fileId == null || callBack == null) return false; 84 mFileId = fileId; 85 mCallback = callBack; 86 selectFile(); 87 return true; 88 } 89 90 private void selectFile() { 91 if (mChannelId >= 0) { 92 mUiccCard.iccTransmitApduLogicalChannel(mChannelId, 0x00, 0xA4, 0x00, 0x04, 0x02, 93 mFileId, obtainMessage(EVENT_SELECT_FILE_DONE)); 94 } else { 95 log("EF based"); 96 } 97 } 98 99 private void readBinary() { 100 if (mChannelId >=0 ) { 101 mUiccCard.iccTransmitApduLogicalChannel(mChannelId, 0x00, 0xB0, 0x00, 0x00, 0x00, 102 "", obtainMessage(EVENT_READ_BINARY_DONE)); 103 } else { 104 log("EF based"); 105 } 106 } 107 108 @Override 109 public void handleMessage(Message msg) { 110 log("handleMessage: " + msg.what); 111 AsyncResult ar = (AsyncResult) msg.obj; 112 if (ar.exception != null || ar.result == null) { 113 log("Error: " + ar.exception); 114 AsyncResult.forMessage(mCallback, null, ar.exception); 115 mCallback.sendToTarget(); 116 return; 117 } 118 119 switch (msg.what) { 120 case EVENT_SELECT_FILE_DONE: 121 readBinary(); 122 break; 123 124 case EVENT_READ_BINARY_DONE: 125 IccIoResult response = (IccIoResult) ar.result; 126 String result = IccUtils.bytesToHexString(response.payload) 127 .toUpperCase(Locale.US); 128 log("IccIoResult: " + response + " payload: " + result); 129 AsyncResult.forMessage(mCallback, result, (result == null) ? 130 new IccException("Error: null response for " + mFileId) : null); 131 mCallback.sendToTarget(); 132 break; 133 134 default: 135 log("Unknown event" + msg.what); 136 } 137 } 138 } 139 140 private class Pkcs15Selector extends Handler { 141 private static final String PKCS15_AID = "A000000063504B43532D3135"; 142 private Message mCallback; 143 private static final int EVENT_OPEN_LOGICAL_CHANNEL_DONE = 201; 144 145 public Pkcs15Selector(Message callBack) { 146 mCallback = callBack; 147 // Specified in ISO 7816-4 clause 7.1.1 0x04 means that FCP template is requested. 148 int p2 = 0x04; 149 mUiccCard.iccOpenLogicalChannel(PKCS15_AID, p2, /* supported P2 value */ 150 obtainMessage(EVENT_OPEN_LOGICAL_CHANNEL_DONE)); 151 } 152 153 @Override 154 public void handleMessage(Message msg) { 155 log("handleMessage: " + msg.what); 156 AsyncResult ar; 157 158 switch (msg.what) { 159 case EVENT_OPEN_LOGICAL_CHANNEL_DONE: 160 ar = (AsyncResult) msg.obj; 161 if (ar.exception == null && ar.result != null) { 162 mChannelId = ((int[]) ar.result)[0]; 163 log("mChannelId: " + mChannelId); 164 AsyncResult.forMessage(mCallback, null, null); 165 } else { 166 log("error: " + ar.exception); 167 AsyncResult.forMessage(mCallback, null, ar.exception); 168 // TODO: don't sendToTarget and read EF_DIR to find PKCS15 169 } 170 mCallback.sendToTarget(); 171 break; 172 173 default: 174 log("Unknown event" + msg.what); 175 } 176 } 177 } 178 179 private UiccCard mUiccCard; // Parent 180 private Message mLoadedCallback; 181 private int mChannelId = -1; // Channel Id for communicating with UICC. 182 private List<String> mRules = new ArrayList<String>(); 183 private Pkcs15Selector mPkcs15Selector; 184 private FileHandler mFh; 185 186 private static final int EVENT_SELECT_PKCS15_DONE = 1; 187 private static final int EVENT_LOAD_ODF_DONE = 2; 188 private static final int EVENT_LOAD_DODF_DONE = 3; 189 private static final int EVENT_LOAD_ACMF_DONE = 4; 190 private static final int EVENT_LOAD_ACRF_DONE = 5; 191 private static final int EVENT_LOAD_ACCF_DONE = 6; 192 private static final int EVENT_CLOSE_LOGICAL_CHANNEL_DONE = 7; 193 194 public UiccPkcs15(UiccCard uiccCard, Message loadedCallback) { 195 log("Creating UiccPkcs15"); 196 mUiccCard = uiccCard; 197 mLoadedCallback = loadedCallback; 198 mPkcs15Selector = new Pkcs15Selector(obtainMessage(EVENT_SELECT_PKCS15_DONE)); 199 } 200 201 @Override 202 public void handleMessage(Message msg) { 203 log("handleMessage: " + msg.what); 204 AsyncResult ar = (AsyncResult) msg.obj; 205 206 switch (msg.what) { 207 case EVENT_SELECT_PKCS15_DONE: 208 if (ar.exception == null) { 209 // ar.result is null if using logical channel, 210 // or string for pkcs15 path if using file access. 211 mFh = new FileHandler((String)ar.result); 212 if (!mFh.loadFile(ID_ACRF, obtainMessage(EVENT_LOAD_ACRF_DONE))) { 213 cleanUp(); 214 } 215 } else { 216 log("select pkcs15 failed: " + ar.exception); 217 // select PKCS15 failed, notify uiccCarrierPrivilegeRules 218 mLoadedCallback.sendToTarget(); 219 } 220 break; 221 222 case EVENT_LOAD_ACRF_DONE: 223 if (ar.exception == null && ar.result != null) { 224 String idAccf = parseAcrf((String)ar.result); 225 if (!mFh.loadFile(idAccf, obtainMessage(EVENT_LOAD_ACCF_DONE))) { 226 cleanUp(); 227 } 228 } else { 229 cleanUp(); 230 } 231 break; 232 233 case EVENT_LOAD_ACCF_DONE: 234 if (ar.exception == null && ar.result != null) { 235 parseAccf((String)ar.result); 236 } 237 // We are done here, no more file to read 238 cleanUp(); 239 break; 240 241 case EVENT_CLOSE_LOGICAL_CHANNEL_DONE: 242 break; 243 244 default: 245 Rlog.e(LOG_TAG, "Unknown event " + msg.what); 246 } 247 } 248 249 private void cleanUp() { 250 log("cleanUp"); 251 if (mChannelId >= 0) { 252 mUiccCard.iccCloseLogicalChannel(mChannelId, obtainMessage( 253 EVENT_CLOSE_LOGICAL_CHANNEL_DONE)); 254 mChannelId = -1; 255 } 256 mLoadedCallback.sendToTarget(); 257 } 258 259 // Constants defined in specs, needed for parsing 260 private static final String CARRIER_RULE_AID = "FFFFFFFFFFFF"; // AID for carrier privilege rule 261 private static final String ID_ACRF = "4300"; 262 private static final String TAG_ASN_SEQUENCE = "30"; 263 private static final String TAG_ASN_OCTET_STRING = "04"; 264 private static final String TAG_TARGET_AID = "A0"; 265 266 // parse ACRF file to get file id for ACCF file 267 // data is hex string, return file id if parse success, null otherwise 268 private String parseAcrf(String data) { 269 String ret = null; 270 271 String acRules = data; 272 while (!acRules.isEmpty()) { 273 TLV tlvRule = new TLV(TAG_ASN_SEQUENCE); 274 try { 275 acRules = tlvRule.parse(acRules, false); 276 String ruleString = tlvRule.getValue(); 277 if (ruleString.startsWith(TAG_TARGET_AID)) { 278 // rule string consists of target AID + path, example: 279 // [A0] 08 [04] 06 FF FF FF FF FF FF [30] 04 [04] 02 43 10 280 // bytes in [] are tags for the data 281 TLV tlvTarget = new TLV(TAG_TARGET_AID); // A0 282 TLV tlvAid = new TLV(TAG_ASN_OCTET_STRING); // 04 283 TLV tlvAsnPath = new TLV(TAG_ASN_SEQUENCE); // 30 284 TLV tlvPath = new TLV(TAG_ASN_OCTET_STRING); // 04 285 286 // populate tlvTarget.value with aid data, 287 // ruleString has remaining data for path 288 ruleString = tlvTarget.parse(ruleString, false); 289 // parse tlvTarget.value to get actual strings for AID. 290 // no other tags expected so shouldConsumeAll is true. 291 tlvAid.parse(tlvTarget.getValue(), true); 292 293 if (CARRIER_RULE_AID.equals(tlvAid.getValue())) { 294 tlvAsnPath.parse(ruleString, true); 295 tlvPath.parse(tlvAsnPath.getValue(), true); 296 ret = tlvPath.getValue(); 297 } 298 } 299 continue; // skip current rule as it doesn't have expected TAG 300 } catch (IllegalArgumentException|IndexOutOfBoundsException ex) { 301 log("Error: " + ex); 302 break; // Bad data, ignore all remaining ACRules 303 } 304 } 305 return ret; 306 } 307 308 // parse ACCF and add to mRules 309 private void parseAccf(String data) { 310 String acCondition = data; 311 while (!acCondition.isEmpty()) { 312 TLV tlvCondition = new TLV(TAG_ASN_SEQUENCE); 313 TLV tlvCert = new TLV(TAG_ASN_OCTET_STRING); 314 try { 315 acCondition = tlvCondition.parse(acCondition, false); 316 tlvCert.parse(tlvCondition.getValue(), true); 317 if (!tlvCert.getValue().isEmpty()) { 318 mRules.add(tlvCert.getValue()); 319 } 320 } catch (IllegalArgumentException|IndexOutOfBoundsException ex) { 321 log("Error: " + ex); 322 break; // Bad data, ignore all remaining acCondition data 323 } 324 } 325 } 326 327 public List<String> getRules() { 328 return mRules; 329 } 330 331 private static void log(String msg) { 332 if (DBG) Rlog.d(LOG_TAG, msg); 333 } 334 335 /** 336 * Dumps info to Dumpsys - useful for debugging. 337 */ 338 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 339 if (mRules != null) { 340 pw.println(" mRules:"); 341 for (String cert : mRules) { 342 pw.println(" " + cert); 343 } 344 } 345 } 346 } 347