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.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