Home | History | Annotate | Download | only in uicc
      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 import android.telephony.Rlog;
     23 
     24 import com.android.internal.telephony.uicc.UiccCarrierPrivilegeRules.TLV;
     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         private 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
     67         public FileHandler(String pkcs15Path) {
     68             log("Creating FileHandler, pkcs15Path: " + pkcs15Path);
     69             mPkcs15Path = pkcs15Path;
     70         }
     71 
     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 
     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 
     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
    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 
    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
    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 = new ArrayList<String>();
    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 
    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
    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(ID_ACRF, obtainMessage(EVENT_LOAD_ACRF_DONE))) {
    204                       cleanUp();
    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_ACRF_DONE:
    214               if (ar.exception == null && ar.result != null) {
    215                   String idAccf = parseAcrf((String)ar.result);
    216                   if (!mFh.loadFile(idAccf, obtainMessage(EVENT_LOAD_ACCF_DONE))) {
    217                       cleanUp();
    218                   }
    219               } else {
    220                   cleanUp();
    221               }
    222               break;
    223 
    224           case EVENT_LOAD_ACCF_DONE:
    225               if (ar.exception == null && ar.result != null) {
    226                   parseAccf((String)ar.result);
    227               }
    228               // We are done here, no more file to read
    229               cleanUp();
    230               break;
    231 
    232           case EVENT_CLOSE_LOGICAL_CHANNEL_DONE:
    233               break;
    234 
    235           default:
    236               Rlog.e(LOG_TAG, "Unknown event " + msg.what);
    237         }
    238     }
    239 
    240     private void cleanUp() {
    241         log("cleanUp");
    242         if (mChannelId >= 0) {
    243             mUiccProfile.iccCloseLogicalChannel(mChannelId, obtainMessage(
    244                     EVENT_CLOSE_LOGICAL_CHANNEL_DONE));
    245             mChannelId = -1;
    246         }
    247         mLoadedCallback.sendToTarget();
    248     }
    249 
    250     // Constants defined in specs, needed for parsing
    251     private static final String CARRIER_RULE_AID = "FFFFFFFFFFFF"; // AID for carrier privilege rule
    252     private static final String ID_ACRF = "4300";
    253     private static final String TAG_ASN_SEQUENCE = "30";
    254     private static final String TAG_ASN_OCTET_STRING = "04";
    255     private static final String TAG_TARGET_AID = "A0";
    256 
    257     // parse ACRF file to get file id for ACCF file
    258     // data is hex string, return file id if parse success, null otherwise
    259     private String parseAcrf(String data) {
    260         String ret = null;
    261 
    262         String acRules = data;
    263         while (!acRules.isEmpty()) {
    264             TLV tlvRule = new TLV(TAG_ASN_SEQUENCE);
    265             try {
    266                 acRules = tlvRule.parse(acRules, false);
    267                 String ruleString = tlvRule.getValue();
    268                 if (ruleString.startsWith(TAG_TARGET_AID)) {
    269                     // rule string consists of target AID + path, example:
    270                     // [A0] 08 [04] 06 FF FF FF FF FF FF [30] 04 [04] 02 43 10
    271                     // bytes in [] are tags for the data
    272                     TLV tlvTarget = new TLV(TAG_TARGET_AID); // A0
    273                     TLV tlvAid = new TLV(TAG_ASN_OCTET_STRING); // 04
    274                     TLV tlvAsnPath = new TLV(TAG_ASN_SEQUENCE); // 30
    275                     TLV tlvPath = new TLV(TAG_ASN_OCTET_STRING);  // 04
    276 
    277                     // populate tlvTarget.value with aid data,
    278                     // ruleString has remaining data for path
    279                     ruleString = tlvTarget.parse(ruleString, false);
    280                     // parse tlvTarget.value to get actual strings for AID.
    281                     // no other tags expected so shouldConsumeAll is true.
    282                     tlvAid.parse(tlvTarget.getValue(), true);
    283 
    284                     if (CARRIER_RULE_AID.equals(tlvAid.getValue())) {
    285                         tlvAsnPath.parse(ruleString, true);
    286                         tlvPath.parse(tlvAsnPath.getValue(), true);
    287                         ret = tlvPath.getValue();
    288                     }
    289                 }
    290                 continue; // skip current rule as it doesn't have expected TAG
    291             } catch (IllegalArgumentException|IndexOutOfBoundsException ex) {
    292                 log("Error: " + ex);
    293                 break; // Bad data, ignore all remaining ACRules
    294             }
    295         }
    296         return ret;
    297     }
    298 
    299     // parse ACCF and add to mRules
    300     private void parseAccf(String data) {
    301         String acCondition = data;
    302         while (!acCondition.isEmpty()) {
    303             TLV tlvCondition = new TLV(TAG_ASN_SEQUENCE);
    304             TLV tlvCert = new TLV(TAG_ASN_OCTET_STRING);
    305             try {
    306                 acCondition = tlvCondition.parse(acCondition, false);
    307                 tlvCert.parse(tlvCondition.getValue(), true);
    308                 if (!tlvCert.getValue().isEmpty()) {
    309                     mRules.add(tlvCert.getValue());
    310                 }
    311             } catch (IllegalArgumentException|IndexOutOfBoundsException ex) {
    312                 log("Error: " + ex);
    313                 break; // Bad data, ignore all remaining acCondition data
    314             }
    315         }
    316     }
    317 
    318     public List<String> getRules() {
    319         return mRules;
    320     }
    321 
    322     private static void log(String msg) {
    323         if (DBG) Rlog.d(LOG_TAG, msg);
    324     }
    325 
    326     /**
    327      * Dumps info to Dumpsys - useful for debugging.
    328      */
    329     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
    330         if (mRules != null) {
    331             pw.println(" mRules:");
    332             for (String cert : mRules) {
    333                 pw.println("  " + cert);
    334             }
    335         }
    336     }
    337 }
    338