Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7541d468 authored by Junda Liu's avatar Junda Liu
Browse files

Handle long UICC rules correctly.

Repeatedly read from UICC until all data has been read and parse the length field correctly.

Bug: b/17572167
Change-Id: I7c9c0d36911fef246e0d73a3bfd5eebf3bc9cf73
parent fe1472a2
Loading
Loading
Loading
Loading
+73 −12
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ import java.io.ByteArrayInputStream;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.IllegalArgumentException;
import java.lang.IndexOutOfBoundsException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.cert.Certificate;
@@ -72,6 +73,7 @@ public class UiccCarrierPrivilegeRules extends Handler {
    private static final int COMMAND = 0xCA;
    private static final int P1 = 0xFF;
    private static final int P2 = 0x40;
    private static final int P2_EXTENDED_DATA = 0x60;
    private static final int P3 = 0x00;
    private static final String DATA = "";

@@ -140,7 +142,14 @@ public class UiccCarrierPrivilegeRules extends Handler {

    // Used for parsing the data from the UICC.
    private static class TLV {
        private static final int SINGLE_BYTE_MAX_LENGTH = 0x80;
        private String tag;
        // Length encoding is in GPC_Specification_2.2.1: 11.1.5 APDU Message and Data Length.
        // Length field could be either 1 byte if length < 128, or multiple bytes with first byte
        // specifying how many bytes are used for length, followed by length bytes.
        // Bytes for the length field, in ASCII HEX string form.
        private String lengthBytes;
        // Decoded length as integer.
        private Integer length;
        private String value;

@@ -148,6 +157,22 @@ public class UiccCarrierPrivilegeRules extends Handler {
            this.tag = tag;
        }

        public String parseLength(String data) {
            int offset = tag.length();
            int firstByte = Integer.parseInt(data.substring(offset, offset + 2), 16);
            // TODO: remove second condition before launch. b/18012893
            if (firstByte < SINGLE_BYTE_MAX_LENGTH || (offset + 2 + firstByte * 2 == data.length())) {
                length = firstByte * 2;
                lengthBytes = data.substring(offset, offset + 2);
            } else {
                int numBytes = firstByte - SINGLE_BYTE_MAX_LENGTH;
                length = Integer.parseInt(data.substring(offset + 2, offset + 2 + numBytes * 2), 16) * 2;
                lengthBytes = data.substring(offset, offset + 2 + numBytes * 2);
            }
            Rlog.d(LOG_TAG, "TLV parseLength length=" + length + "lenghtBytes: " + lengthBytes);
            return lengthBytes;
        }

        public String parse(String data, boolean shouldConsumeAll) {
            Rlog.d(LOG_TAG, "Parse TLV: " + tag);
            if (!data.startsWith(tag)) {
@@ -157,10 +182,11 @@ public class UiccCarrierPrivilegeRules extends Handler {
            if (index + 2 > data.length()) {
                throw new IllegalArgumentException("No length.");
            }
            length = new Integer(2 * Integer.parseInt(
                    data.substring(index, index + 2), 16));
            index += 2;

            parseLength(data);
            index += lengthBytes.length();

            Rlog.d(LOG_TAG, "index="+index+" length="+length+"data.length="+data.length());
            int remainingLength = data.length() - (index + length);
            if (remainingLength < 0) {
                throw new IllegalArgumentException("Not enough data.");
@@ -179,8 +205,10 @@ public class UiccCarrierPrivilegeRules extends Handler {
    private UiccCard mUiccCard;  // Parent
    private AtomicInteger mState;
    private List<AccessRule> mAccessRules;
    private String mRules;
    private Message mLoadedCallback;
    private String mStatusMessage;  // Only used for debugging.
    private int mChannelId; // Channel Id for communicating with UICC.

    public UiccCarrierPrivilegeRules(UiccCard uiccCard, Message loadedCallback) {
        Rlog.d(LOG_TAG, "Creating UiccCarrierPrivilegeRules");
@@ -188,6 +216,7 @@ public class UiccCarrierPrivilegeRules extends Handler {
        mState = new AtomicInteger(STATE_LOADING);
        mStatusMessage = "Not loaded.";
        mLoadedCallback = loadedCallback;
        mRules = "";

        // Start loading the rules.
        mUiccCard.iccOpenLogicalChannel(AID,
@@ -322,9 +351,9 @@ public class UiccCarrierPrivilegeRules extends Handler {
              Rlog.d(LOG_TAG, "EVENT_OPEN_LOGICAL_CHANNEL_DONE");
              ar = (AsyncResult) msg.obj;
              if (ar.exception == null && ar.result != null) {
                  int channelId = ((int[]) ar.result)[0];
                  mUiccCard.iccTransmitApduLogicalChannel(channelId, CLA, COMMAND, P1, P2, P3, DATA,
                      obtainMessage(EVENT_TRANSMIT_LOGICAL_CHANNEL_DONE, new Integer(channelId)));
                  mChannelId = ((int[]) ar.result)[0];
                  mUiccCard.iccTransmitApduLogicalChannel(mChannelId, CLA, COMMAND, P1, P2, P3, DATA,
                      obtainMessage(EVENT_TRANSMIT_LOGICAL_CHANNEL_DONE, new Integer(mChannelId)));
              } else {
                  updateState(STATE_ERROR, "Error opening channel");
              }
@@ -335,12 +364,22 @@ public class UiccCarrierPrivilegeRules extends Handler {
              ar = (AsyncResult) msg.obj;
              if (ar.exception == null && ar.result != null) {
                  IccIoResult response = (IccIoResult) ar.result;
                  if (response.payload != null && response.sw1 == 0x90 && response.sw2 == 0x00) {
                  if (response.sw1 == 0x90 && response.sw2 == 0x00 &&
                      response.payload != null && response.payload.length > 0) {
                      try {
                          mAccessRules = parseRules(IccUtils.bytesToHexString(response.payload));
                          updateState(STATE_LOADED, "Loaded successfully");
                          mRules += IccUtils.bytesToHexString(response.payload).toUpperCase(Locale.US);
                          if (isDataComplete()) {
                              mAccessRules = parseRules(mRules);
                              updateState(STATE_LOADED, "Success!");
                          } else {
                              mUiccCard.iccTransmitApduLogicalChannel(mChannelId, CLA, COMMAND, P1, P2_EXTENDED_DATA, P3, DATA,
                                  obtainMessage(EVENT_TRANSMIT_LOGICAL_CHANNEL_DONE, new Integer(mChannelId)));
                              break;
                          }
                      } catch (IllegalArgumentException ex) {
                          updateState(STATE_ERROR, "Error parsing rules: " + ex);
                      } catch (IndexOutOfBoundsException ex) {
                          updateState(STATE_ERROR, "Error parsing rules: " + ex);
                      }
                   } else {
                      String errorMsg = "Invalid response: payload=" + response.payload +
@@ -351,9 +390,9 @@ public class UiccCarrierPrivilegeRules extends Handler {
                  updateState(STATE_ERROR, "Error reading value from SIM.");
              }

              int channelId = (Integer) ar.userObj;
              mUiccCard.iccCloseLogicalChannel(channelId, obtainMessage(
              mUiccCard.iccCloseLogicalChannel(mChannelId, obtainMessage(
                      EVENT_CLOSE_LOGICAL_CHANNEL_DONE));
              mChannelId = -1;
              break;

          case EVENT_CLOSE_LOGICAL_CHANNEL_DONE:
@@ -365,11 +404,33 @@ public class UiccCarrierPrivilegeRules extends Handler {
        }
    }

    /*
     * Check if all rule bytes have been read from UICC.
     * For long payload, we need to fetch it repeatly before start parsing it.
     */
    private boolean isDataComplete() {
        Rlog.d(LOG_TAG, "isDataComplete mRules:" + mRules);
        if (mRules.startsWith(TAG_ALL_REF_AR_DO)) {
            TLV allRules = new TLV(TAG_ALL_REF_AR_DO);
            String lengthBytes = allRules.parseLength(mRules);
            Rlog.d(LOG_TAG, "isDataComplete lengthBytes: " + lengthBytes);
            if (mRules.length() == TAG_ALL_REF_AR_DO.length() + lengthBytes.length() +
                                   allRules.length) {
                Rlog.d(LOG_TAG, "isDataComplete yes");
                return true;
            } else {
                Rlog.d(LOG_TAG, "isDataComplete no");
                return false;
            }
        } else {
            throw new IllegalArgumentException("Tags don't match.");
        }
    }

    /*
     * Parses the rules from the input string.
     */
    private static List<AccessRule> parseRules(String rules) {
        rules = rules.toUpperCase(Locale.US);
        Rlog.d(LOG_TAG, "Got rules: " + rules);

        TLV allRefArDo = new TLV(TAG_ALL_REF_AR_DO); //FF40