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.Handler; 21 import android.os.Message; 22 23 import com.android.internal.telephony.uicc.UiccCarrierPrivilegeRules.TLV; 24 import com.android.telephony.Rlog; 25 26 import java.io.FileDescriptor; 27 import java.io.PrintWriter; 28 import java.util.ArrayList; 29 import java.util.List; 30 import java.util.Locale; 31 32 /** 33 * Class that reads PKCS15-based rules for carrier privileges. 34 * 35 * The spec for the rules: 36 * GP Secure Element Access Control: 37 * https://www.globalplatform.org/specificationsdevice.asp 38 * 39 * The UiccPkcs15 class handles overall flow of finding/selecting PKCS15 applet 40 * and reading/parsing each file. Because PKCS15 can be selected in 2 different ways: 41 * via logical channel or EF_DIR, PKCS15Selector is a handler to encapsulate the flow. 42 * Similarly, FileHandler is used for selecting/reading each file, so common codes are 43 * all in same place. 44 * 45 * {@hide} 46 */ 47 public class UiccPkcs15 extends Handler { 48 private static final String LOG_TAG = "UiccPkcs15"; 49 private static final boolean DBG = true; 50 51 // File handler for PKCS15 files, select file and read binary, 52 // convert to String then send to callback message. 53 private class FileHandler extends Handler { 54 // EF path for PKCS15 root, eg. "3F007F50" 55 // null if logical channel is used for PKCS15 access. 56 final String mPkcs15Path; 57 // Message to send when file has been parsed. 58 private Message mCallback; 59 // File id to read data from, eg. "5031" 60 private String mFileId; 61 62 // async events for the sequence of select and read 63 static protected final int EVENT_SELECT_FILE_DONE = 101; 64 static protected final int EVENT_READ_BINARY_DONE = 102; 65 66 // pkcs15Path is nullable when using logical channel FileHandler(String pkcs15Path)67 public FileHandler(String pkcs15Path) { 68 log("Creating FileHandler, pkcs15Path: " + pkcs15Path); 69 mPkcs15Path = pkcs15Path; 70 } 71 loadFile(String fileId, Message callBack)72 public boolean loadFile(String fileId, Message callBack) { 73 log("loadFile: " + fileId); 74 if (fileId == null || callBack == null) return false; 75 mFileId = fileId; 76 mCallback = callBack; 77 selectFile(); 78 return true; 79 } 80 selectFile()81 private void selectFile() { 82 if (mChannelId >= 0) { 83 mUiccProfile.iccTransmitApduLogicalChannel(mChannelId, 0x00, 0xA4, 0x00, 0x04, 0x02, 84 mFileId, obtainMessage(EVENT_SELECT_FILE_DONE)); 85 } else { 86 log("EF based"); 87 } 88 } 89 readBinary()90 private void readBinary() { 91 if (mChannelId >=0 ) { 92 mUiccProfile.iccTransmitApduLogicalChannel(mChannelId, 0x00, 0xB0, 0x00, 0x00, 0x00, 93 "", obtainMessage(EVENT_READ_BINARY_DONE)); 94 } else { 95 log("EF based"); 96 } 97 } 98 99 @Override handleMessage(Message msg)100 public void handleMessage(Message msg) { 101 log("handleMessage: " + msg.what); 102 AsyncResult ar = (AsyncResult) msg.obj; 103 if (ar.exception != null || ar.result == null) { 104 log("Error: " + ar.exception); 105 AsyncResult.forMessage(mCallback, null, ar.exception); 106 mCallback.sendToTarget(); 107 return; 108 } 109 110 switch (msg.what) { 111 case EVENT_SELECT_FILE_DONE: 112 readBinary(); 113 break; 114 115 case EVENT_READ_BINARY_DONE: 116 IccIoResult response = (IccIoResult) ar.result; 117 String result = IccUtils.bytesToHexString(response.payload) 118 .toUpperCase(Locale.US); 119 log("IccIoResult: " + response + " payload: " + result); 120 AsyncResult.forMessage(mCallback, result, (result == null) ? 121 new IccException("Error: null response for " + mFileId) : null); 122 mCallback.sendToTarget(); 123 break; 124 125 default: 126 log("Unknown event" + msg.what); 127 } 128 } 129 } 130 131 private class Pkcs15Selector extends Handler { 132 private static final String PKCS15_AID = "A000000063504B43532D3135"; 133 private Message mCallback; 134 private static final int EVENT_OPEN_LOGICAL_CHANNEL_DONE = 201; 135 Pkcs15Selector(Message callBack)136 public Pkcs15Selector(Message callBack) { 137 mCallback = callBack; 138 // Specified in ISO 7816-4 clause 7.1.1 0x04 means that FCP template is requested. 139 int p2 = 0x04; 140 mUiccProfile.iccOpenLogicalChannel(PKCS15_AID, p2, /* supported P2 value */ 141 obtainMessage(EVENT_OPEN_LOGICAL_CHANNEL_DONE)); 142 } 143 144 @Override handleMessage(Message msg)145 public void handleMessage(Message msg) { 146 log("handleMessage: " + msg.what); 147 AsyncResult ar; 148 149 switch (msg.what) { 150 case EVENT_OPEN_LOGICAL_CHANNEL_DONE: 151 ar = (AsyncResult) msg.obj; 152 if (ar.exception == null && ar.result != null) { 153 mChannelId = ((int[]) ar.result)[0]; 154 log("mChannelId: " + mChannelId); 155 AsyncResult.forMessage(mCallback, null, null); 156 } else { 157 log("error: " + ar.exception); 158 AsyncResult.forMessage(mCallback, null, ar.exception); 159 // TODO: don't sendToTarget and read EF_DIR to find PKCS15 160 } 161 mCallback.sendToTarget(); 162 break; 163 164 default: 165 log("Unknown event" + msg.what); 166 } 167 } 168 } 169 170 private UiccProfile mUiccProfile; // Parent 171 private Message mLoadedCallback; 172 private int mChannelId = -1; // Channel Id for communicating with UICC. 173 private List<String> mRules = null; 174 private Pkcs15Selector mPkcs15Selector; 175 private FileHandler mFh; 176 177 private static final int EVENT_SELECT_PKCS15_DONE = 1; 178 private static final int EVENT_LOAD_ODF_DONE = 2; 179 private static final int EVENT_LOAD_DODF_DONE = 3; 180 private static final int EVENT_LOAD_ACMF_DONE = 4; 181 private static final int EVENT_LOAD_ACRF_DONE = 5; 182 private static final int EVENT_LOAD_ACCF_DONE = 6; 183 private static final int EVENT_CLOSE_LOGICAL_CHANNEL_DONE = 7; 184 UiccPkcs15(UiccProfile uiccProfile, Message loadedCallback)185 public UiccPkcs15(UiccProfile uiccProfile, Message loadedCallback) { 186 log("Creating UiccPkcs15"); 187 mUiccProfile = uiccProfile; 188 mLoadedCallback = loadedCallback; 189 mPkcs15Selector = new Pkcs15Selector(obtainMessage(EVENT_SELECT_PKCS15_DONE)); 190 } 191 192 @Override handleMessage(Message msg)193 public void handleMessage(Message msg) { 194 log("handleMessage: " + msg.what); 195 AsyncResult ar = (AsyncResult) msg.obj; 196 197 switch (msg.what) { 198 case EVENT_SELECT_PKCS15_DONE: 199 if (ar.exception == null) { 200 // ar.result is null if using logical channel, 201 // or string for pkcs15 path if using file access. 202 mFh = new FileHandler((String) ar.result); 203 if (!mFh.loadFile(EFODF_PATH, obtainMessage(EVENT_LOAD_ODF_DONE))) { 204 startFromAcrf(); 205 } 206 } else { 207 log("select pkcs15 failed: " + ar.exception); 208 // select PKCS15 failed, notify uiccCarrierPrivilegeRules 209 mLoadedCallback.sendToTarget(); 210 } 211 break; 212 213 case EVENT_LOAD_ODF_DONE: 214 if (ar.exception == null && ar.result != null) { 215 String idDodf = parseOdf((String) ar.result); 216 if (!mFh.loadFile(idDodf, obtainMessage(EVENT_LOAD_DODF_DONE))) { 217 startFromAcrf(); 218 } 219 } else { 220 startFromAcrf(); 221 } 222 break; 223 224 case EVENT_LOAD_DODF_DONE: 225 if (ar.exception == null && ar.result != null) { 226 String idAcmf = parseDodf((String) ar.result); 227 if (!mFh.loadFile(idAcmf, obtainMessage(EVENT_LOAD_ACMF_DONE))) { 228 startFromAcrf(); 229 } 230 } else { 231 startFromAcrf(); 232 } 233 break; 234 235 case EVENT_LOAD_ACMF_DONE: 236 if (ar.exception == null && ar.result != null) { 237 String idAcrf = parseAcmf((String) ar.result); 238 if (!mFh.loadFile(idAcrf, obtainMessage(EVENT_LOAD_ACRF_DONE))) { 239 startFromAcrf(); 240 } 241 } else { 242 startFromAcrf(); 243 } 244 break; 245 246 case EVENT_LOAD_ACRF_DONE: 247 if (ar.exception == null && ar.result != null) { 248 mRules = new ArrayList<String>(); 249 String idAccf = parseAcrf((String) ar.result); 250 if (!mFh.loadFile(idAccf, obtainMessage(EVENT_LOAD_ACCF_DONE))) { 251 cleanUp(); 252 } 253 } else { 254 cleanUp(); 255 } 256 break; 257 258 case EVENT_LOAD_ACCF_DONE: 259 if (ar.exception == null && ar.result != null) { 260 parseAccf((String) ar.result); 261 } 262 // We are done here, no more file to read 263 cleanUp(); 264 break; 265 266 case EVENT_CLOSE_LOGICAL_CHANNEL_DONE: 267 break; 268 269 default: 270 Rlog.e(LOG_TAG, "Unknown event " + msg.what); 271 } 272 } 273 startFromAcrf()274 private void startFromAcrf() { 275 log("Fallback to use ACRF_PATH"); 276 if (!mFh.loadFile(ACRF_PATH, obtainMessage(EVENT_LOAD_ACRF_DONE))) { 277 cleanUp(); 278 } 279 } 280 cleanUp()281 private void cleanUp() { 282 log("cleanUp"); 283 if (mChannelId >= 0) { 284 mUiccProfile.iccCloseLogicalChannel(mChannelId, obtainMessage( 285 EVENT_CLOSE_LOGICAL_CHANNEL_DONE)); 286 mChannelId = -1; 287 } 288 mLoadedCallback.sendToTarget(); 289 } 290 291 // Constants defined in specs, needed for parsing 292 private static final String CARRIER_RULE_AID = "FFFFFFFFFFFF"; // AID for carrier privilege rule 293 private static final String ACRF_PATH = "4300"; 294 private static final String EFODF_PATH = "5031"; 295 private static final String TAG_ASN_SEQUENCE = "30"; 296 private static final String TAG_ASN_OCTET_STRING = "04"; 297 private static final String TAG_ASN_OID = "06"; 298 private static final String TAG_TARGET_AID = "A0"; 299 private static final String TAG_ODF = "A7"; 300 private static final String TAG_DODF = "A1"; 301 private static final String REFRESH_TAG_LEN = "08"; 302 // OID defined by Global Platform for the "Access Control". The hexstring here can be converted 303 // to OID string value 1.2.840.114283.200.1.1 304 public static final String AC_OID = "060A2A864886FC6B81480101"; 305 306 307 // parse ODF file to get file id for DODF file 308 // data is hex string, return file id if parse success, null otherwise parseOdf(String data)309 private String parseOdf(String data) { 310 // Example: 311 // [A7] 06 [30] 04 [04] 02 52 07 312 try { 313 TLV tlvRule = new TLV(TAG_ODF); // A7 314 tlvRule.parse(data, false); 315 String ruleString = tlvRule.getValue(); 316 TLV tlvAsnPath = new TLV(TAG_ASN_SEQUENCE); // 30 317 TLV tlvPath = new TLV(TAG_ASN_OCTET_STRING); // 04 318 tlvAsnPath.parse(ruleString, true); 319 tlvPath.parse(tlvAsnPath.getValue(), true); 320 return tlvPath.getValue(); 321 } catch (IllegalArgumentException | IndexOutOfBoundsException ex) { 322 log("Error: " + ex); 323 return null; 324 } 325 } 326 327 // parse DODF file to get file id for ACMF file 328 // data is hex string, return file id if parse success, null otherwise parseDodf(String data)329 private String parseDodf(String data) { 330 // Example: 331 // [A1] 29 [30] 00 [30] 0F 0C 0D 47 50 20 53 45 20 41 63 63 20 43 74 6C [A1] 14 [30] 12 332 // [06] 0A 2A 86 48 86 FC 6B 81 48 01 01 [30] 04 04 02 42 00 333 String ret = null; 334 String acRules = data; 335 while (!acRules.isEmpty()) { 336 TLV dodfTag = new TLV(TAG_DODF); // A1 337 try { 338 acRules = dodfTag.parse(acRules, false); 339 String ruleString = dodfTag.getValue(); 340 // Skip the Common Object Attributes 341 TLV commonObjectAttributes = new TLV(TAG_ASN_SEQUENCE); // 30 342 ruleString = commonObjectAttributes.parse(ruleString, false); 343 344 // Skip the Common Data Object Attributes 345 TLV commonDataObjectAttributes = new TLV(TAG_ASN_SEQUENCE); // 30 346 ruleString = commonDataObjectAttributes.parse(ruleString, false); 347 348 if (ruleString.startsWith(TAG_TARGET_AID)) { 349 // Skip SubClassAttributes [Optional] 350 TLV subClassAttributes = new TLV(TAG_TARGET_AID); // A0 351 ruleString = subClassAttributes.parse(ruleString, false); 352 } 353 354 if (ruleString.startsWith(TAG_DODF)) { 355 TLV oidDoTag = new TLV(TAG_DODF); // A1 356 oidDoTag.parse(ruleString, true); 357 ruleString = oidDoTag.getValue(); 358 359 TLV oidDo = new TLV(TAG_ASN_SEQUENCE); // 30 360 oidDo.parse(ruleString, true); 361 ruleString = oidDo.getValue(); 362 363 TLV oidTag = new TLV(TAG_ASN_OID); // 06 364 oidTag.parse(ruleString, false); 365 // Example : [06] 0A 2A 86 48 86 FC 6B 81 48 01 01 366 String oid = oidTag.getValue(); 367 if (oid.equals(AC_OID)) { 368 // Skip OID and get the AC to the ACCM 369 ruleString = oidTag.parse(ruleString, false); 370 TLV tlvAsnPath = new TLV(TAG_ASN_SEQUENCE); // 30 371 TLV tlvPath = new TLV(TAG_ASN_OCTET_STRING); // 04 372 tlvAsnPath.parse(ruleString, true); 373 tlvPath.parse(tlvAsnPath.getValue(), true); 374 return tlvPath.getValue(); 375 } 376 } 377 continue; // skip current rule as it doesn't have expected TAG 378 } catch (IllegalArgumentException | IndexOutOfBoundsException ex) { 379 log("Error: " + ex); 380 break; // Bad data, ignore all remaining ACRules 381 } 382 } 383 return ret; 384 } 385 386 // parse ACMF file to get file id for ACRF file 387 // data is hex string, return file id if parse success, null otherwise parseAcmf(String data)388 private String parseAcmf(String data) { 389 try { 390 // [30] 10 [04] 08 01 02 03 04 05 06 07 08 [30] 04 [04] 02 43 00 391 TLV acmfTag = new TLV(TAG_ASN_SEQUENCE); // 30 392 acmfTag.parse(data, false); 393 String ruleString = acmfTag.getValue(); 394 TLV refreshTag = new TLV(TAG_ASN_OCTET_STRING); // 04 395 String refreshTagLength = refreshTag.parseLength(ruleString); 396 if (!refreshTagLength.equals(REFRESH_TAG_LEN)) { 397 log("Error: refresh tag in ACMF must be 8."); 398 return null; 399 } 400 ruleString = refreshTag.parse(ruleString, false); 401 TLV tlvAsnPath = new TLV(TAG_ASN_SEQUENCE); // 30 402 TLV tlvPath = new TLV(TAG_ASN_OCTET_STRING); // 04 403 tlvAsnPath.parse(ruleString, true); 404 tlvPath.parse(tlvAsnPath.getValue(), true); 405 return tlvPath.getValue(); 406 } catch (IllegalArgumentException | IndexOutOfBoundsException ex) { 407 log("Error: " + ex); 408 return null; 409 } 410 411 } 412 413 // parse ACRF file to get file id for ACCF file 414 // data is hex string, return file id if parse success, null otherwise parseAcrf(String data)415 private String parseAcrf(String data) { 416 String ret = null; 417 418 String acRules = data; 419 while (!acRules.isEmpty()) { 420 // Example: 421 // [30] 10 [A0] 08 04 06 FF FF FF FF FF FF [30] 04 [04] 02 43 10 422 // bytes in [] are tags for the data 423 TLV tlvRule = new TLV(TAG_ASN_SEQUENCE); 424 try { 425 acRules = tlvRule.parse(acRules, false); 426 String ruleString = tlvRule.getValue(); 427 if (ruleString.startsWith(TAG_TARGET_AID)) { 428 TLV tlvTarget = new TLV(TAG_TARGET_AID); // A0 429 TLV tlvAid = new TLV(TAG_ASN_OCTET_STRING); // 04 430 TLV tlvAsnPath = new TLV(TAG_ASN_SEQUENCE); // 30 431 TLV tlvPath = new TLV(TAG_ASN_OCTET_STRING); // 04 432 433 // populate tlvTarget.value with aid data, 434 // ruleString has remaining data for path 435 ruleString = tlvTarget.parse(ruleString, false); 436 // parse tlvTarget.value to get actual strings for AID. 437 // no other tags expected so shouldConsumeAll is true. 438 tlvAid.parse(tlvTarget.getValue(), true); 439 440 if (CARRIER_RULE_AID.equals(tlvAid.getValue())) { 441 tlvAsnPath.parse(ruleString, true); 442 tlvPath.parse(tlvAsnPath.getValue(), true); 443 ret = tlvPath.getValue(); 444 } 445 } 446 continue; // skip current rule as it doesn't have expected TAG 447 } catch (IllegalArgumentException|IndexOutOfBoundsException ex) { 448 log("Error: " + ex); 449 break; // Bad data, ignore all remaining ACRules 450 } 451 } 452 return ret; 453 } 454 455 // parse ACCF and add to mRules parseAccf(String data)456 private void parseAccf(String data) { 457 String acCondition = data; 458 while (!acCondition.isEmpty()) { 459 TLV tlvCondition = new TLV(TAG_ASN_SEQUENCE); 460 TLV tlvCert = new TLV(TAG_ASN_OCTET_STRING); 461 try { 462 acCondition = tlvCondition.parse(acCondition, false); 463 tlvCert.parse(tlvCondition.getValue(), true); 464 if (!tlvCert.getValue().isEmpty()) { 465 mRules.add(tlvCert.getValue()); 466 } 467 } catch (IllegalArgumentException|IndexOutOfBoundsException ex) { 468 log("Error: " + ex); 469 break; // Bad data, ignore all remaining acCondition data 470 } 471 } 472 } 473 getRules()474 public List<String> getRules() { 475 return mRules; 476 } 477 log(String msg)478 private static void log(String msg) { 479 if (DBG) Rlog.d(LOG_TAG, msg); 480 } 481 482 /** 483 * Dumps info to Dumpsys - useful for debugging. 484 */ dump(FileDescriptor fd, PrintWriter pw, String[] args)485 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 486 if (mRules != null) { 487 pw.println(" mRules:"); 488 for (String cert : mRules) { 489 pw.println(" " + cert); 490 } 491 } 492 } 493 } 494