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