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