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