1 /* 2 * Copyright (C) 2019 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 android.carrierapi.cts; 18 19 import static android.carrierapi.cts.IccUtils.hexStringToBytes; 20 21 import java.util.ArrayList; 22 import java.util.List; 23 import java.util.Objects; 24 25 import javax.annotation.Nonnull; 26 27 /** 28 * Class for representing a File Control Parameters (FCP) Template object. TS 101 220 29 * 30 * <p>A correctly formatted FCP Template will be in the format: | 1 byte: BER tag (0x62) | 1 byte: 31 * length of TLVs |...TLV objects...| 2 bytes: status | 32 */ 33 public class FcpTemplate { 34 35 // FCP Template label 36 public static final int BER_TAG_FCP_TEMPLATE = 0x62; 37 38 // FCP Template BER-TLV Tag tags. TS 101 220 Section 7.2 39 public static final int FILE_SIZE_DATA = 0x80; 40 public static final int FILE_SIZE_TOTAL = 0x81; 41 public static final int FILE_DESCRIPTOR = 0x82; 42 public static final int FILE_IDENTIFIER = 0x83; 43 public static final int DF_NAME = 0x84; 44 public static final int PROPRIETARY = 0x85; 45 public static final int SFI_SUPPORT = 0x88; 46 public static final int LIFE_CYCLE_STATUS = 0x8A; 47 public static final int SECURITY_ATTR_REFERENCE_FORMAT = 0x8B; 48 public static final int SECURITY_ATTR_COMPACT_FORMAT = 0x8C; 49 public static final int SECURITY_ATTR_TEMPLATE_EXPANDED_FORMAT = 0xAB; 50 public static final int PROPRIETARY_TEMPLATE = 0xA5; 51 public static final int PIN_STATUS_DATA_OBJECT = 0xC6; 52 53 private final List<Tlv> tlvs; 54 private final String status; 55 FcpTemplate(@onnull List<Tlv> tlvs, @Nonnull String status)56 private FcpTemplate(@Nonnull List<Tlv> tlvs, @Nonnull String status) { 57 this.tlvs = tlvs; 58 this.status = status; 59 } 60 getTlvs()61 public List<Tlv> getTlvs() { 62 return tlvs; 63 } 64 getStatus()65 public String getStatus() { 66 return status; 67 } 68 69 /** 70 * Parses and returns a FcpTemplate for the given {@code fcpResponse} 71 * 72 * @param fcpResponse The Hex String response for a given Status APDU command. Expected to be in 73 * the format: | 1 byte: BER tag | 1 byte: length of TLVs |...TLV objects...| 2 bytes: 74 * status | 75 * @return a FcpTemplate for the given hex String 76 * @throws FcpTemplateParseException for non-FCP inputs or inputs of the wrong length (encoded 77 * length does not match actual length) 78 */ parseFcpTemplate(@onnull String fcpResponse)79 public static FcpTemplate parseFcpTemplate(@Nonnull String fcpResponse) { 80 final List<Tlv> tlvObjects = new ArrayList<>(); 81 82 // Expected FcpResponse format: 83 // | 1 byte: BER tag | 1 byte: length of TLVs | ...TLV objects... | 2 bytes: status | 84 byte[] data = hexStringToBytes(fcpResponse); 85 int responseLength = data.length; 86 // don't count BER tag, length byte, or status bytes 87 int payloadLength = responseLength - 4; 88 // data[0]: Response tag 89 // data[1]: TLV data object length in bytes. Assumes that length is < 128 bytes 90 if (data[0] != BER_TAG_FCP_TEMPLATE || data[1] != payloadLength) { 91 throw new FcpTemplateParseException("Improperly formatted fcpResponse: " + fcpResponse); 92 } 93 94 int index = 2; 95 while (index < responseLength - 2) { // don't need to process the 2 byte status-word footer 96 // TLV data object format per TS 101 220: 97 // | 1 byte: tag | 1 byte: length | 'length' bytes: value | 98 int tag = data[index++] & 0xFF; 99 int length = data[index++] & 0xFF; // assumes that length is < 128 bytes. 100 String value = fcpResponse.substring(index * 2, (index + length) * 2); 101 tlvObjects.add(new Tlv(tag, length, value)); 102 index += length; 103 } 104 105 String status = fcpResponse.substring(fcpResponse.length() - 4); 106 return new FcpTemplate(tlvObjects, status); 107 } 108 109 /** Represents a Tag-Length-Value object. TS 101 220 Section 2 */ 110 public static class Tlv { 111 112 private final int tag; 113 private final int length; 114 private final String value; 115 Tlv(int tag, int length, @Nonnull String value)116 public Tlv(int tag, int length, @Nonnull String value) { 117 this.tag = tag; 118 this.length = length; 119 this.value = value; 120 } 121 getTag()122 public int getTag() { 123 return tag; 124 } 125 getLength()126 public int getLength() { 127 return length; 128 } 129 getValue()130 public String getValue() { 131 return value; 132 } 133 134 @Override equals(Object o)135 public boolean equals(Object o) { 136 if (this == o) { 137 return true; 138 } 139 if (o == null || getClass() != o.getClass()) { 140 return false; 141 } 142 Tlv tlv = (Tlv) o; 143 return tag == tlv.tag && length == tlv.length && value.equals(tlv.value); 144 } 145 146 @Override hashCode()147 public int hashCode() { 148 return Objects.hash(tag, length, value); 149 } 150 151 @Override toString()152 public String toString() { 153 return tag + "(" + length + "):" + value; 154 } 155 } 156 157 private static final class FcpTemplateParseException extends RuntimeException { FcpTemplateParseException(String message)158 public FcpTemplateParseException(String message) { 159 super(message); 160 } 161 } 162 } 163