1 /*
2  * Copyright 2018 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.annotation.Nullable;
20 import android.util.ArrayMap;
21 
22 import com.android.internal.annotations.VisibleForTesting;
23 import com.android.telephony.Rlog;
24 
25 import java.io.FileDescriptor;
26 import java.io.PrintWriter;
27 import java.util.ArrayList;
28 import java.util.List;
29 import java.util.Objects;
30 
31 /**
32  * This class parses an Answer To Reset (ATR) message.
33  * The ATR message structure is defined in standard ISO/IEC 7816-3. The eUICC related ATR message
34  * is defined in standard ETSI TS 102 221 V14.0.0.
35  */
36 public class AnswerToReset {
37     private static final String TAG = "AnswerToReset";
38     private static final boolean VDBG = false; // STOPSHIP if true
39     private static final int TAG_CARD_CAPABILITIES = 0x07;
40     private static final int EXTENDED_APDU_INDEX = 2;
41     private static final int B8_MASK = 0x80;
42     private static final int B7_MASK = 0x40;
43     private static final int B2_MASK = 0x02;
44     private static final int B1_MASK = 0x01;
45 
46     public static final byte DIRECT_CONVENTION = (byte) 0x3B;
47     public static final byte INVERSE_CONVENTION = (byte) 0x3F;
48     public static final int INTERFACE_BYTES_MASK = 0xF0;
49     public static final int T_MASK = 0x0F;
50     public static final int T_VALUE_FOR_GLOBAL_INTERFACE = 15;
51     public static final int TA_MASK = 0x10;
52     public static final int TB_MASK = 0x20;
53     public static final int TC_MASK = 0x40;
54     public static final int TD_MASK = 0x80;
55 
56     private boolean mIsDirectConvention;
57     private boolean mOnlyTEqualsZero = true;
58     private boolean mIsEuiccSupported;
59     private boolean mIsMultipleEnabledProfilesSupported = false;
60     private byte mFormatByte;
61     private ArrayList<InterfaceByte> mInterfaceBytes = new ArrayList<>();
62     private HistoricalBytes mHistoricalBytes;
63     private Byte mCheckByte;
64 
65     /** Class for the historical bytes. */
66     public static class HistoricalBytes {
67         private static final int TAG_MASK = 0xF0;
68         private static final int LENGTH_MASK = 0x0F;
69 
70         private final byte[] mRawData;
71         private final ArrayMap<Integer, byte[]> mNodes;
72         private final byte mCategory;
73 
74         /** Get the category of the historical bytes. */
getCategory()75         public byte getCategory() {
76             return mCategory;
77         }
78 
79         /** Get the raw data of historical bytes. */
getRawData()80         public byte[] getRawData() {
81             return mRawData;
82         }
83 
84         /** Get the value of the tag in historical bytes. */
85         @Nullable
getValue(int tag)86         public byte[] getValue(int tag) {
87             return mNodes.get(tag);
88         }
89 
90         @Nullable
parseHistoricalBytes( byte[] originalData, int startIndex, int length)91         private static HistoricalBytes parseHistoricalBytes(
92                 byte[] originalData, int startIndex, int length) {
93             if (length <= 0 || startIndex + length > originalData.length) {
94                 return null;
95             }
96             ArrayMap<Integer, byte[]> nodes = new ArrayMap<>();
97 
98             // Start parsing from second byte since the first one is category.
99             int index = startIndex + 1;
100             while (index < startIndex + length && index > 0) {
101                 index = parseLtvNode(index, nodes, originalData, startIndex + length - 1);
102             }
103             if (index < 0) {
104                 return null;
105             }
106             byte[] rawData = new byte[length];
107             System.arraycopy(originalData, startIndex, rawData, 0, length);
108             return new HistoricalBytes(rawData, nodes, rawData[0]);
109         }
110 
HistoricalBytes(byte[] rawData, ArrayMap<Integer, byte[]> nodes, byte category)111         private HistoricalBytes(byte[] rawData, ArrayMap<Integer, byte[]> nodes, byte category) {
112             mRawData = rawData;
113             mNodes = nodes;
114             mCategory = category;
115         }
116 
parseLtvNode( int index, ArrayMap<Integer, byte[]> nodes, byte[] data, int lastByteIndex)117         private static int parseLtvNode(
118                 int index, ArrayMap<Integer, byte[]> nodes, byte[] data, int lastByteIndex) {
119             if (index > lastByteIndex) {
120                 return -1;
121             }
122             int tag = (data[index] & TAG_MASK) >> 4;
123             int length = data[index++] & LENGTH_MASK;
124             if (index + length > lastByteIndex + 1 || length == 0) {
125                 return -1;
126             }
127             byte[] value = new byte[length];
128             System.arraycopy(data, index, value, 0, length);
129             nodes.put(tag, value);
130             return index + length;
131         }
132     }
133 
134 
135     /**
136      * Returns an AnswerToReset by parsing the input atr string, return null if the parsing fails.
137      */
parseAtr(String atr)138     public static AnswerToReset parseAtr(String atr) {
139         AnswerToReset answerToReset = new AnswerToReset();
140         if (answerToReset.parseAtrString(atr)) {
141             return answerToReset;
142         }
143         return null;
144     }
145 
AnswerToReset()146     private AnswerToReset() {}
147 
byteToStringHex(Byte b)148     private static String byteToStringHex(Byte b) {
149         return b == null ? null : IccUtils.byteToHex(b);
150     }
151 
checkEuiccSupportedCapabilities()152     private void checkEuiccSupportedCapabilities() {
153         // eUICC is supported only if the b8 and b2 of the first tB after T=15 are set to 1.
154         // MEP is supported only if the b8 and b1 of the first tB after T=15 are set to 1.
155         for (int i = 0; i < mInterfaceBytes.size() - 1; i++) {
156             if (mInterfaceBytes.get(i).getTD() != null
157                     && (mInterfaceBytes.get(i).getTD() & T_MASK) == T_VALUE_FOR_GLOBAL_INTERFACE
158                     && mInterfaceBytes.get(i + 1).getTB() != null) {
159                 if ((mInterfaceBytes.get(i + 1).getTB() & B8_MASK) != 0
160                         && (mInterfaceBytes.get(i + 1).getTB() & B2_MASK) != 0) {
161                     mIsEuiccSupported = true;
162                 }
163                 if ((mInterfaceBytes.get(i + 1).getTB() & B8_MASK) != 0
164                         && (mInterfaceBytes.get(i + 1).getTB() & B1_MASK) != 0) {
165                     mIsMultipleEnabledProfilesSupported = true;
166                 }
167                 return;
168             }
169         }
170     }
171 
parseConventionByte(byte[] atrBytes, int index)172     private int parseConventionByte(byte[] atrBytes, int index) {
173         if (index >= atrBytes.length) {
174             loge("Failed to read the convention byte.");
175             return -1;
176         }
177         byte value = atrBytes[index];
178         if (value == DIRECT_CONVENTION) {
179             mIsDirectConvention = true;
180         } else if (value == INVERSE_CONVENTION) {
181             mIsDirectConvention = false;
182         } else {
183             loge("Unrecognized convention byte " + IccUtils.byteToHex(value));
184             return -1;
185         }
186         return index + 1;
187     }
188 
parseFormatByte(byte[] atrBytes, int index)189     private int parseFormatByte(byte[] atrBytes, int index) {
190         if (index >= atrBytes.length) {
191             loge("Failed to read the format byte.");
192             return -1;
193         }
194         mFormatByte = atrBytes[index];
195         if (VDBG) log("mHistoricalBytesLength: " + (mFormatByte & T_MASK));
196         return index + 1;
197     }
198 
parseInterfaceBytes(byte[] atrBytes, int index)199     private int parseInterfaceBytes(byte[] atrBytes, int index) {
200         // The first lastTD is actually not any TD but instead the format byte.
201         byte lastTD = mFormatByte;
202         while (true) {
203             if (VDBG) log("lastTD: " + IccUtils.byteToHex(lastTD));
204             // Parse the interface bytes.
205             if ((lastTD & INTERFACE_BYTES_MASK) == 0) {
206                 break;
207             }
208 
209             InterfaceByte interfaceByte = new InterfaceByte();
210             if (VDBG) log("lastTD & TA_MASK: " + IccUtils.byteToHex((byte) (lastTD & TA_MASK)));
211             if ((lastTD & TA_MASK) != 0) {
212                 if (index >= atrBytes.length) {
213                     loge("Failed to read the byte for TA.");
214                     return -1;
215                 }
216                 interfaceByte.setTA(atrBytes[index]);
217                 index++;
218             }
219             if (VDBG) log("lastTD & TB_MASK: " + IccUtils.byteToHex((byte) (lastTD & TB_MASK)));
220             if ((lastTD & TB_MASK) != 0) {
221                 if (index >= atrBytes.length) {
222                     loge("Failed to read the byte for TB.");
223                     return -1;
224                 }
225                 interfaceByte.setTB(atrBytes[index]);
226                 index++;
227             }
228             if (VDBG) log("lastTD & TC_MASK: " + IccUtils.byteToHex((byte) (lastTD & TC_MASK)));
229             if ((lastTD & TC_MASK) != 0) {
230                 if (index >= atrBytes.length) {
231                     loge("Failed to read the byte for TC.");
232                     return -1;
233                 }
234                 interfaceByte.setTC(atrBytes[index]);
235                 index++;
236             }
237             if (VDBG) log("lastTD & TD_MASK: " + IccUtils.byteToHex((byte) (lastTD & TD_MASK)));
238             if ((lastTD & TD_MASK) != 0) {
239                 if (index >= atrBytes.length) {
240                     loge("Failed to read the byte for TD.");
241                     return -1;
242                 }
243                 interfaceByte.setTD(atrBytes[index]);
244                 index++;
245             }
246             mInterfaceBytes.add(interfaceByte);
247             Byte newTD = interfaceByte.getTD();
248             if (VDBG) log("index=" + index + ", " + toString());
249             if (newTD == null) {
250                 break;
251             }
252             lastTD = newTD;
253             // Parse the T values from all the TD, here we only check whether T is equal to any
254             // other values other than 0, since the check byte can be absent only when T is
255             // equal to 0.
256             if ((lastTD & T_MASK) != 0) {
257                 mOnlyTEqualsZero = false;
258             }
259         }
260         return index;
261     }
262 
parseHistoricalBytes(byte[] atrBytes, int index)263     private int parseHistoricalBytes(byte[] atrBytes, int index) {
264         int length = mFormatByte & T_MASK;
265         if (length + index > atrBytes.length) {
266             loge("Failed to read the historical bytes.");
267             return -1;
268         }
269         if (length > 0) {
270             mHistoricalBytes = HistoricalBytes.parseHistoricalBytes(atrBytes, index, length);
271         }
272         return index + length;
273     }
274 
parseCheckBytes(byte[] atrBytes, int index)275     private int parseCheckBytes(byte[] atrBytes, int index) {
276         if (index < atrBytes.length) {
277             mCheckByte = atrBytes[index];
278             index++;
279         } else {
280             if (!mOnlyTEqualsZero) {
281                 loge("Check byte must be present because T equals to values other than 0.");
282                 return -1;
283             } else {
284                 log("Check byte can be absent because T=0.");
285             }
286         }
287         return index;
288     }
289 
parseAtrString(String atr)290     private boolean parseAtrString(String atr) {
291         if (atr == null) {
292             loge("The input ATR string can not be null");
293             return false;
294         }
295 
296         if (atr.length() % 2 != 0) {
297             loge("The length of input ATR string " + atr.length() + " is not even.");
298             return false;
299         }
300 
301         if (atr.length() < 4) {
302             loge("Valid ATR string must at least contains TS and T0.");
303             return false;
304         }
305 
306         byte[] atrBytes = IccUtils.hexStringToBytes(atr);
307         if (atrBytes == null) {
308             return false;
309         }
310 
311         int index = parseConventionByte(atrBytes, 0);
312         if (index == -1) {
313             return false;
314         }
315 
316         index = parseFormatByte(atrBytes, index);
317         if (index == -1) {
318             return false;
319         }
320 
321         index = parseInterfaceBytes(atrBytes, index);
322         if (index == -1) {
323             return false;
324         }
325 
326         index = parseHistoricalBytes(atrBytes, index);
327         if (index == -1) {
328             return false;
329         }
330 
331         index = parseCheckBytes(atrBytes, index);
332         if (index == -1) {
333             return false;
334         }
335 
336         if (index != atrBytes.length) {
337             loge("Unexpected bytes after the check byte.");
338             return false;
339         }
340         log("Successfully parsed the ATR string " + atr + " into " + toString());
341         checkEuiccSupportedCapabilities();
342         return true;
343     }
344 
345     /**
346      * This class holds the interface bytes.
347      */
348     public static class InterfaceByte {
349         private Byte mTA;
350         private Byte mTB;
351         private Byte mTC;
352         private Byte mTD;
353 
354         @Nullable
getTA()355         public Byte getTA() {
356             return mTA;
357         }
358 
359         @Nullable
getTB()360         public Byte getTB() {
361             return mTB;
362         }
363 
364         @Nullable
getTC()365         public Byte getTC() {
366             return mTC;
367         }
368 
369         @Nullable
getTD()370         public Byte getTD() {
371             return mTD;
372         }
373 
setTA(Byte tA)374         public void setTA(Byte tA) {
375             mTA = tA;
376         }
377 
setTB(Byte tB)378         public void setTB(Byte tB) {
379             mTB = tB;
380         }
381 
setTC(Byte tC)382         public void setTC(Byte tC) {
383             mTC = tC;
384         }
385 
setTD(Byte tD)386         public void setTD(Byte tD) {
387             mTD = tD;
388         }
389 
InterfaceByte()390         private InterfaceByte() {
391             mTA = null;
392             mTB = null;
393             mTC = null;
394             mTD = null;
395         }
396 
397         @VisibleForTesting
InterfaceByte(Byte tA, Byte tB, Byte tC, Byte tD)398         public InterfaceByte(Byte tA, Byte tB, Byte tC, Byte tD) {
399             this.mTA = tA;
400             this.mTB = tB;
401             this.mTC = tC;
402             this.mTD = tD;
403         }
404 
405         @Override
equals(Object o)406         public boolean equals(Object o) {
407             if (this == o) {
408                 return true;
409             }
410             if (o == null || getClass() != o.getClass()) {
411                 return false;
412             }
413             InterfaceByte ib = (InterfaceByte) o;
414             return (Objects.equals(mTA, ib.getTA())
415                     && Objects.equals(mTB, ib.getTB())
416                     && Objects.equals(mTC, ib.getTC())
417                     && Objects.equals(mTD, ib.getTD()));
418         }
419 
420         @Override
hashCode()421         public int hashCode() {
422             return Objects.hash(mTA, mTB, mTC, mTD);
423         }
424 
425         @Override
toString()426         public String toString() {
427             StringBuffer sb = new StringBuffer();
428             sb.append("{");
429             sb.append("TA=").append(byteToStringHex(mTA)).append(",");
430             sb.append("TB=").append(byteToStringHex(mTB)).append(",");
431             sb.append("TC=").append(byteToStringHex(mTC)).append(",");
432             sb.append("TD=").append(byteToStringHex(mTD));
433             sb.append("}");
434             return sb.toString();
435         }
436     };
437 
log(String msg)438     private static void log(String msg) {
439         Rlog.d(TAG, msg);
440     }
441 
loge(String msg)442     private static void loge(String msg) {
443         Rlog.e(TAG, msg);
444     }
445 
getConventionByte()446     public byte getConventionByte() {
447         return mIsDirectConvention ? DIRECT_CONVENTION : INVERSE_CONVENTION;
448     }
449 
getFormatByte()450     public byte getFormatByte() {
451         return mFormatByte;
452     }
453 
getInterfaceBytes()454     public List<InterfaceByte> getInterfaceBytes() {
455         return mInterfaceBytes;
456     }
457 
458     @Nullable
getHistoricalBytes()459     public HistoricalBytes getHistoricalBytes() {
460         return mHistoricalBytes;
461     }
462 
463     @Nullable
getCheckByte()464     public Byte getCheckByte() {
465         return mCheckByte;
466     }
467 
isEuiccSupported()468     public boolean isEuiccSupported() {
469         return mIsEuiccSupported;
470     }
471 
472     /** Return whether the extended LC & LE is supported. */
isExtendedApduSupported()473     public boolean isExtendedApduSupported() {
474         if (mHistoricalBytes == null) {
475             return false;
476         }
477         byte[] cardCapabilities = mHistoricalBytes.getValue(TAG_CARD_CAPABILITIES);
478         if (cardCapabilities == null || cardCapabilities.length < 3) {
479             return false;
480         }
481         if (mIsDirectConvention) {
482             return (cardCapabilities[EXTENDED_APDU_INDEX] & B7_MASK) > 0;
483         } else {
484             return (cardCapabilities[EXTENDED_APDU_INDEX] & B2_MASK) > 0;
485         }
486     }
487 
isMultipleEnabledProfilesSupported()488     public boolean isMultipleEnabledProfilesSupported() {
489         return mIsMultipleEnabledProfilesSupported;
490     }
491 
492     @Override
toString()493     public String toString() {
494         StringBuffer sb = new StringBuffer();
495 
496         sb.append("AnswerToReset:{");
497         sb.append("mConventionByte=")
498                 .append(IccUtils.byteToHex(getConventionByte())).append(",");
499         sb.append("mFormatByte=").append(byteToStringHex(mFormatByte)).append(",");
500         sb.append("mInterfaceBytes={");
501         for (InterfaceByte ib : mInterfaceBytes) {
502             sb.append(ib.toString());
503         }
504         sb.append("},");
505         sb.append("mHistoricalBytes={");
506         if (mHistoricalBytes != null) {
507             for (byte b : mHistoricalBytes.getRawData()) {
508                 sb.append(IccUtils.byteToHex(b)).append(",");
509             }
510         }
511         sb.append("},");
512         sb.append("mCheckByte=").append(byteToStringHex(mCheckByte));
513         sb.append("}");
514         return sb.toString();
515     }
516 
517     /**
518      * Dump
519      */
dump(FileDescriptor fd, PrintWriter pw, String[] args)520     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
521         pw.println("AnswerToReset:");
522         pw.println(toString());
523         pw.flush();
524     }
525 }
526