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 17package com.android.internal.telephony.uicc; 18 19import android.os.AsyncResult; 20import android.os.Binder; 21import android.os.Handler; 22import android.os.Message; 23import android.telephony.Rlog; 24import android.telephony.TelephonyManager; 25 26import com.android.internal.telephony.CommandException; 27import com.android.internal.telephony.CommandsInterface; 28import com.android.internal.telephony.uicc.IccUtils; 29import com.android.internal.telephony.uicc.UiccCarrierPrivilegeRules.TLV; 30 31import java.io.ByteArrayInputStream; 32import java.io.FileDescriptor; 33import java.io.PrintWriter; 34import java.lang.IllegalArgumentException; 35import java.lang.IndexOutOfBoundsException; 36import java.util.ArrayList; 37import java.util.Arrays; 38import java.util.List; 39import 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 */ 56public 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 mUiccCard.iccOpenLogicalChannel(PKCS15_AID, 148 obtainMessage(EVENT_OPEN_LOGICAL_CHANNEL_DONE)); 149 } 150 151 @Override 152 public void handleMessage(Message msg) { 153 log("handleMessage: " + msg.what); 154 AsyncResult ar; 155 156 switch (msg.what) { 157 case EVENT_OPEN_LOGICAL_CHANNEL_DONE: 158 ar = (AsyncResult) msg.obj; 159 if (ar.exception == null && ar.result != null) { 160 mChannelId = ((int[]) ar.result)[0]; 161 log("mChannelId: " + mChannelId); 162 AsyncResult.forMessage(mCallback, null, null); 163 } else { 164 log("error: " + ar.exception); 165 AsyncResult.forMessage(mCallback, null, ar.exception); 166 // TODO: don't sendToTarget and read EF_DIR to find PKCS15 167 } 168 mCallback.sendToTarget(); 169 break; 170 171 default: 172 log("Unknown event" + msg.what); 173 } 174 } 175 } 176 177 private UiccCard mUiccCard; // Parent 178 private Message mLoadedCallback; 179 private int mChannelId = -1; // Channel Id for communicating with UICC. 180 private List<String> mRules = new ArrayList<String>(); 181 private Pkcs15Selector mPkcs15Selector; 182 private FileHandler mFh; 183 184 private static final int EVENT_SELECT_PKCS15_DONE = 1; 185 private static final int EVENT_LOAD_ODF_DONE = 2; 186 private static final int EVENT_LOAD_DODF_DONE = 3; 187 private static final int EVENT_LOAD_ACMF_DONE = 4; 188 private static final int EVENT_LOAD_ACRF_DONE = 5; 189 private static final int EVENT_LOAD_ACCF_DONE = 6; 190 private static final int EVENT_CLOSE_LOGICAL_CHANNEL_DONE = 7; 191 192 public UiccPkcs15(UiccCard uiccCard, Message loadedCallback) { 193 log("Creating UiccPkcs15"); 194 mUiccCard = uiccCard; 195 mLoadedCallback = loadedCallback; 196 mPkcs15Selector = new Pkcs15Selector(obtainMessage(EVENT_SELECT_PKCS15_DONE)); 197 } 198 199 @Override 200 public void handleMessage(Message msg) { 201 log("handleMessage: " + msg.what); 202 AsyncResult ar = (AsyncResult) msg.obj; 203 204 switch (msg.what) { 205 case EVENT_SELECT_PKCS15_DONE: 206 if (ar.exception == null) { 207 // ar.result is null if using logical channel, 208 // or string for pkcs15 path if using file access. 209 mFh = new FileHandler((String)ar.result); 210 if (!mFh.loadFile(ID_ACRF, obtainMessage(EVENT_LOAD_ACRF_DONE))) { 211 cleanUp(); 212 } 213 } else { 214 log("select pkcs15 failed: " + ar.exception); 215 // select PKCS15 failed, notify uiccCarrierPrivilegeRules 216 mLoadedCallback.sendToTarget(); 217 } 218 break; 219 220 case EVENT_LOAD_ACRF_DONE: 221 if (ar.exception == null && ar.result != null) { 222 String idAccf = parseAcrf((String)ar.result); 223 if (!mFh.loadFile(idAccf, obtainMessage(EVENT_LOAD_ACCF_DONE))) { 224 cleanUp(); 225 } 226 } else { 227 cleanUp(); 228 } 229 break; 230 231 case EVENT_LOAD_ACCF_DONE: 232 if (ar.exception == null && ar.result != null) { 233 parseAccf((String)ar.result); 234 } 235 // We are done here, no more file to read 236 cleanUp(); 237 break; 238 239 case EVENT_CLOSE_LOGICAL_CHANNEL_DONE: 240 break; 241 242 default: 243 Rlog.e(LOG_TAG, "Unknown event " + msg.what); 244 } 245 } 246 247 private void cleanUp() { 248 log("cleanUp"); 249 if (mChannelId >= 0) { 250 mUiccCard.iccCloseLogicalChannel(mChannelId, obtainMessage( 251 EVENT_CLOSE_LOGICAL_CHANNEL_DONE)); 252 mChannelId = -1; 253 } 254 mLoadedCallback.sendToTarget(); 255 } 256 257 // Constants defined in specs, needed for parsing 258 private static final String CARRIER_RULE_AID = "FFFFFFFFFFFF"; // AID for carrier privilege rule 259 private static final String ID_ACRF = "4300"; 260 private static final String TAG_ASN_SEQUENCE = "30"; 261 private static final String TAG_ASN_OCTET_STRING = "04"; 262 private static final String TAG_TARGET_AID = "A0"; 263 264 // parse ACRF file to get file id for ACCF file 265 // data is hex string, return file id if parse success, null otherwise 266 private String parseAcrf(String data) { 267 String ret = null; 268 269 String acRules = data; 270 while (!acRules.isEmpty()) { 271 TLV tlvRule = new TLV(TAG_ASN_SEQUENCE); 272 try { 273 acRules = tlvRule.parse(acRules, false); 274 String ruleString = tlvRule.getValue(); 275 if (ruleString.startsWith(TAG_TARGET_AID)) { 276 // rule string consists of target AID + path, example: 277 // [A0] 08 [04] 06 FF FF FF FF FF FF [30] 04 [04] 02 43 10 278 // bytes in [] are tags for the data 279 TLV tlvTarget = new TLV(TAG_TARGET_AID); // A0 280 TLV tlvAid = new TLV(TAG_ASN_OCTET_STRING); // 04 281 TLV tlvAsnPath = new TLV(TAG_ASN_SEQUENCE); // 30 282 TLV tlvPath = new TLV(TAG_ASN_OCTET_STRING); // 04 283 284 // populate tlvTarget.value with aid data, 285 // ruleString has remaining data for path 286 ruleString = tlvTarget.parse(ruleString, false); 287 // parse tlvTarget.value to get actual strings for AID. 288 // no other tags expected so shouldConsumeAll is true. 289 tlvAid.parse(tlvTarget.getValue(), true); 290 291 if (CARRIER_RULE_AID.equals(tlvAid.getValue())) { 292 tlvAsnPath.parse(ruleString, true); 293 tlvPath.parse(tlvAsnPath.getValue(), true); 294 ret = tlvPath.getValue(); 295 } 296 } 297 continue; // skip current rule as it doesn't have expected TAG 298 } catch (IllegalArgumentException|IndexOutOfBoundsException ex) { 299 log("Error: " + ex); 300 break; // Bad data, ignore all remaining ACRules 301 } 302 } 303 return ret; 304 } 305 306 // parse ACCF and add to mRules 307 private void parseAccf(String data) { 308 String acCondition = data; 309 while (!acCondition.isEmpty()) { 310 TLV tlvCondition = new TLV(TAG_ASN_SEQUENCE); 311 TLV tlvCert = new TLV(TAG_ASN_OCTET_STRING); 312 try { 313 acCondition = tlvCondition.parse(acCondition, false); 314 tlvCert.parse(tlvCondition.getValue(), true); 315 if (!tlvCert.getValue().isEmpty()) { 316 mRules.add(tlvCert.getValue()); 317 } 318 } catch (IllegalArgumentException|IndexOutOfBoundsException ex) { 319 log("Error: " + ex); 320 break; // Bad data, ignore all remaining acCondition data 321 } 322 } 323 } 324 325 public List<String> getRules() { 326 return mRules; 327 } 328 329 private static void log(String msg) { 330 if (DBG) Rlog.d(LOG_TAG, msg); 331 } 332 333 /** 334 * Dumps info to Dumpsys - useful for debugging. 335 */ 336 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 337 if (mRules != null) { 338 pw.println(" mRules:"); 339 for (String cert : mRules) { 340 pw.println(" " + cert); 341 } 342 } 343 } 344} 345