• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
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 b8 and b2 of the first tB after T=15 are set to 1.
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() & B8_MASK) != 0
157                     && (mInterfaceBytes.get(i + 1).getTB() & B2_MASK) != 0) {
158                 mIsEuiccSupported = true;
159                 return;
160             }
161         }
162     }
163 
parseConventionByte(byte[] atrBytes, int index)164     private int parseConventionByte(byte[] atrBytes, int index) {
165         if (index >= atrBytes.length) {
166             loge("Failed to read the convention byte.");
167             return -1;
168         }
169         byte value = atrBytes[index];
170         if (value == DIRECT_CONVENTION) {
171             mIsDirectConvention = true;
172         } else if (value == INVERSE_CONVENTION) {
173             mIsDirectConvention = false;
174         } else {
175             loge("Unrecognized convention byte " + IccUtils.byteToHex(value));
176             return -1;
177         }
178         return index + 1;
179     }
180 
parseFormatByte(byte[] atrBytes, int index)181     private int parseFormatByte(byte[] atrBytes, int index) {
182         if (index >= atrBytes.length) {
183             loge("Failed to read the format byte.");
184             return -1;
185         }
186         mFormatByte = atrBytes[index];
187         if (VDBG) log("mHistoricalBytesLength: " + (mFormatByte & T_MASK));
188         return index + 1;
189     }
190 
parseInterfaceBytes(byte[] atrBytes, int index)191     private int parseInterfaceBytes(byte[] atrBytes, int index) {
192         // The first lastTD is actually not any TD but instead the format byte.
193         byte lastTD = mFormatByte;
194         while (true) {
195             if (VDBG) log("lastTD: " + IccUtils.byteToHex(lastTD));
196             // Parse the interface bytes.
197             if ((lastTD & INTERFACE_BYTES_MASK) == 0) {
198                 break;
199             }
200 
201             InterfaceByte interfaceByte = new InterfaceByte();
202             if (VDBG) log("lastTD & TA_MASK: " + IccUtils.byteToHex((byte) (lastTD & TA_MASK)));
203             if ((lastTD & TA_MASK) != 0) {
204                 if (index >= atrBytes.length) {
205                     loge("Failed to read the byte for TA.");
206                     return -1;
207                 }
208                 interfaceByte.setTA(atrBytes[index]);
209                 index++;
210             }
211             if (VDBG) log("lastTD & TB_MASK: " + IccUtils.byteToHex((byte) (lastTD & TB_MASK)));
212             if ((lastTD & TB_MASK) != 0) {
213                 if (index >= atrBytes.length) {
214                     loge("Failed to read the byte for TB.");
215                     return -1;
216                 }
217                 interfaceByte.setTB(atrBytes[index]);
218                 index++;
219             }
220             if (VDBG) log("lastTD & TC_MASK: " + IccUtils.byteToHex((byte) (lastTD & TC_MASK)));
221             if ((lastTD & TC_MASK) != 0) {
222                 if (index >= atrBytes.length) {
223                     loge("Failed to read the byte for TC.");
224                     return -1;
225                 }
226                 interfaceByte.setTC(atrBytes[index]);
227                 index++;
228             }
229             if (VDBG) log("lastTD & TD_MASK: " + IccUtils.byteToHex((byte) (lastTD & TD_MASK)));
230             if ((lastTD & TD_MASK) != 0) {
231                 if (index >= atrBytes.length) {
232                     loge("Failed to read the byte for TD.");
233                     return -1;
234                 }
235                 interfaceByte.setTD(atrBytes[index]);
236                 index++;
237             }
238             mInterfaceBytes.add(interfaceByte);
239             Byte newTD = interfaceByte.getTD();
240             if (VDBG) log("index=" + index + ", " + toString());
241             if (newTD == null) {
242                 break;
243             }
244             lastTD = newTD;
245             // Parse the T values from all the TD, here we only check whether T is equal to any
246             // other values other than 0, since the check byte can be absent only when T is
247             // equal to 0.
248             if ((lastTD & T_MASK) != 0) {
249                 mOnlyTEqualsZero = false;
250             }
251         }
252         return index;
253     }
254 
parseHistoricalBytes(byte[] atrBytes, int index)255     private int parseHistoricalBytes(byte[] atrBytes, int index) {
256         int length = mFormatByte & T_MASK;
257         if (length + index > atrBytes.length) {
258             loge("Failed to read the historical bytes.");
259             return -1;
260         }
261         if (length > 0) {
262             mHistoricalBytes = HistoricalBytes.parseHistoricalBytes(atrBytes, index, length);
263         }
264         return index + length;
265     }
266 
parseCheckBytes(byte[] atrBytes, int index)267     private int parseCheckBytes(byte[] atrBytes, int index) {
268         if (index < atrBytes.length) {
269             mCheckByte = atrBytes[index];
270             index++;
271         } else {
272             if (!mOnlyTEqualsZero) {
273                 loge("Check byte must be present because T equals to values other than 0.");
274                 return -1;
275             } else {
276                 log("Check byte can be absent because T=0.");
277             }
278         }
279         return index;
280     }
281 
parseAtrString(String atr)282     private boolean parseAtrString(String atr) {
283         if (atr == null) {
284             loge("The input ATR string can not be null");
285             return false;
286         }
287 
288         if (atr.length() % 2 != 0) {
289             loge("The length of input ATR string " + atr.length() + " is not even.");
290             return false;
291         }
292 
293         if (atr.length() < 4) {
294             loge("Valid ATR string must at least contains TS and T0.");
295             return false;
296         }
297 
298         byte[] atrBytes = IccUtils.hexStringToBytes(atr);
299         if (atrBytes == null) {
300             return false;
301         }
302 
303         int index = parseConventionByte(atrBytes, 0);
304         if (index == -1) {
305             return false;
306         }
307 
308         index = parseFormatByte(atrBytes, index);
309         if (index == -1) {
310             return false;
311         }
312 
313         index = parseInterfaceBytes(atrBytes, index);
314         if (index == -1) {
315             return false;
316         }
317 
318         index = parseHistoricalBytes(atrBytes, index);
319         if (index == -1) {
320             return false;
321         }
322 
323         index = parseCheckBytes(atrBytes, index);
324         if (index == -1) {
325             return false;
326         }
327 
328         if (index != atrBytes.length) {
329             loge("Unexpected bytes after the check byte.");
330             return false;
331         }
332         log("Successfully parsed the ATR string " + atr + " into " + toString());
333         checkIsEuiccSupported();
334         return true;
335     }
336 
337     /**
338      * This class holds the interface bytes.
339      */
340     public static class InterfaceByte {
341         private Byte mTA;
342         private Byte mTB;
343         private Byte mTC;
344         private Byte mTD;
345 
346         @Nullable
getTA()347         public Byte getTA() {
348             return mTA;
349         }
350 
351         @Nullable
getTB()352         public Byte getTB() {
353             return mTB;
354         }
355 
356         @Nullable
getTC()357         public Byte getTC() {
358             return mTC;
359         }
360 
361         @Nullable
getTD()362         public Byte getTD() {
363             return mTD;
364         }
365 
setTA(Byte tA)366         public void setTA(Byte tA) {
367             mTA = tA;
368         }
369 
setTB(Byte tB)370         public void setTB(Byte tB) {
371             mTB = tB;
372         }
373 
setTC(Byte tC)374         public void setTC(Byte tC) {
375             mTC = tC;
376         }
377 
setTD(Byte tD)378         public void setTD(Byte tD) {
379             mTD = tD;
380         }
381 
InterfaceByte()382         private InterfaceByte() {
383             mTA = null;
384             mTB = null;
385             mTC = null;
386             mTD = null;
387         }
388 
389         @VisibleForTesting
InterfaceByte(Byte tA, Byte tB, Byte tC, Byte tD)390         public InterfaceByte(Byte tA, Byte tB, Byte tC, Byte tD) {
391             this.mTA = tA;
392             this.mTB = tB;
393             this.mTC = tC;
394             this.mTD = tD;
395         }
396 
397         @Override
equals(Object o)398         public boolean equals(Object o) {
399             if (this == o) {
400                 return true;
401             }
402             if (o == null || getClass() != o.getClass()) {
403                 return false;
404             }
405             InterfaceByte ib = (InterfaceByte) o;
406             return (Objects.equals(mTA, ib.getTA())
407                     && Objects.equals(mTB, ib.getTB())
408                     && Objects.equals(mTC, ib.getTC())
409                     && Objects.equals(mTD, ib.getTD()));
410         }
411 
412         @Override
hashCode()413         public int hashCode() {
414             return Objects.hash(mTA, mTB, mTC, mTD);
415         }
416 
417         @Override
toString()418         public String toString() {
419             StringBuffer sb = new StringBuffer();
420             sb.append("{");
421             sb.append("TA=").append(byteToStringHex(mTA)).append(",");
422             sb.append("TB=").append(byteToStringHex(mTB)).append(",");
423             sb.append("TC=").append(byteToStringHex(mTC)).append(",");
424             sb.append("TD=").append(byteToStringHex(mTD));
425             sb.append("}");
426             return sb.toString();
427         }
428     };
429 
log(String msg)430     private static void log(String msg) {
431         Rlog.d(TAG, msg);
432     }
433 
loge(String msg)434     private static void loge(String msg) {
435         Rlog.e(TAG, msg);
436     }
437 
getConventionByte()438     public byte getConventionByte() {
439         return mIsDirectConvention ? DIRECT_CONVENTION : INVERSE_CONVENTION;
440     }
441 
getFormatByte()442     public byte getFormatByte() {
443         return mFormatByte;
444     }
445 
getInterfaceBytes()446     public List<InterfaceByte> getInterfaceBytes() {
447         return mInterfaceBytes;
448     }
449 
450     @Nullable
getHistoricalBytes()451     public HistoricalBytes getHistoricalBytes() {
452         return mHistoricalBytes;
453     }
454 
455     @Nullable
getCheckByte()456     public Byte getCheckByte() {
457         return mCheckByte;
458     }
459 
isEuiccSupported()460     public boolean isEuiccSupported() {
461         return mIsEuiccSupported;
462     }
463 
464     /** Return whether the extended LC & LE is supported. */
isExtendedApduSupported()465     public boolean isExtendedApduSupported() {
466         if (mHistoricalBytes == null) {
467             return false;
468         }
469         byte[] cardCapabilities = mHistoricalBytes.getValue(TAG_CARD_CAPABILITIES);
470         if (cardCapabilities == null || cardCapabilities.length < 3) {
471             return false;
472         }
473         if (mIsDirectConvention) {
474             return (cardCapabilities[EXTENDED_APDU_INDEX] & B7_MASK) > 0;
475         } else {
476             return (cardCapabilities[EXTENDED_APDU_INDEX] & B2_MASK) > 0;
477         }
478     }
479 
480     @Override
toString()481     public String toString() {
482         StringBuffer sb = new StringBuffer();
483 
484         sb.append("AnswerToReset:{");
485         sb.append("mConventionByte=")
486                 .append(IccUtils.byteToHex(getConventionByte())).append(",");
487         sb.append("mFormatByte=").append(byteToStringHex(mFormatByte)).append(",");
488         sb.append("mInterfaceBytes={");
489         for (InterfaceByte ib : mInterfaceBytes) {
490             sb.append(ib.toString());
491         }
492         sb.append("},");
493         sb.append("mHistoricalBytes={");
494         if (mHistoricalBytes != null) {
495             for (byte b : mHistoricalBytes.getRawData()) {
496                 sb.append(IccUtils.byteToHex(b)).append(",");
497             }
498         }
499         sb.append("},");
500         sb.append("mCheckByte=").append(byteToStringHex(mCheckByte));
501         sb.append("}");
502         return sb.toString();
503     }
504 
505     /**
506      * Dump
507      */
dump(FileDescriptor fd, PrintWriter pw, String[] args)508     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
509         pw.println("AnswerToReset:");
510         pw.println(toString());
511         pw.flush();
512     }
513 }
514