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