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