1 /*
2  * Copyright (C) 2021 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 package com.android.server.uwb.secure.iso7816;
17 
18 import androidx.annotation.Nullable;
19 
20 import com.google.common.base.Objects;
21 import com.google.common.base.Preconditions;
22 import com.google.common.collect.ImmutableSet;
23 import com.google.common.primitives.Shorts;
24 
25 import java.util.Collections;
26 import java.util.LinkedHashMap;
27 import java.util.Map;
28 
29 /** Representation of some common ISO7816-4 and GlobalPlatform status words. */
30 public final class StatusWord {
31 
32     public static final StatusWord SW_NO_ERROR =
33             new StatusWord(0x9000, "no error");
34 
35     public static final StatusWord SW_RESPONSE_BYTES_STILL_AVAILABLE =
36             new StatusWord(0x6100, "Response bytes still available");
37 
38     public static final StatusWord SW_WARNING_STATE_UNCHANGED =
39             new StatusWord(0x6200, "Warning: State unchanged");
40 
41     public static final StatusWord SW_CARD_MANAGER_LOCKED =
42             new StatusWord(0x6283, "Warning: Card Manager is locked");
43 
44     public static final StatusWord SW_WARNING_NO_INFO_GIVEN =
45             new StatusWord(0x6300, "Warning: State changed (no information given)");
46 
47     public static final StatusWord SW_WARNING_MORE_DATA =
48             new StatusWord(0x6310, "more data");
49 
50     public static final StatusWord SW_VERIFY_FAILED =
51             new StatusWord(0x63C0, "PIN authentication failed.");
52 
53     public static final StatusWord SW_NO_SPECIFIC_DIAGNOSTIC =
54             new StatusWord(0x6400, "No specific diagnostic");
55 
56     public static final StatusWord SW_REQUESTED_ELEMENTS_NOT_AVAILABLE =
57             new StatusWord(0x6402, "Requested elements not available");
58 
59     public static final StatusWord SW_ICA_ALREADY_EXISTS =
60             new StatusWord(0x6409, "ICA Already Exists");
61 
62     public static final StatusWord SW_WRONG_LENGTH =
63             new StatusWord(0x6700, "Wrong length");
64 
65     public static final StatusWord SW_SECURITY_STATUS_NOT_SATISFIED =
66             new StatusWord(0x6982, "Security status not satisfied");
67 
68     public static final StatusWord SW_FILE_INVALID =
69             new StatusWord(0x6983, "File invalid");
70 
71     public static final StatusWord SW_REFERENCE_DATA_NOT_USABLE =
72             new StatusWord(0x6984, "Reference data not usable");
73 
74     public static final StatusWord SW_CONDITIONS_NOT_SATISFIED =
75             new StatusWord(0x6985, "Conditions of use not satisfied");
76 
77     public static final StatusWord SW_COMMAND_NOT_ALLOWED =
78             new StatusWord(0x6986, "Command not allowed");
79 
80     public static final StatusWord SW_APPLET_SELECT_FAILED =
81             new StatusWord(0x6999, "Applet selection failed");
82 
83     public static final StatusWord SW_WRONG_DATA =
84             new StatusWord(0x6A80, "Wrong data");
85 
86     public static final StatusWord SW_FUNCTION_NOT_SUPPORTED =
87             new StatusWord(0x6A81, "Function not supported");
88 
89     public static final StatusWord SW_FILE_NOT_FOUND =
90             new StatusWord(0x6A82, "File not found");
91 
92     public static final StatusWord SW_RECORD_NOT_FOUND =
93             new StatusWord(0x6A83, "Record not found");
94 
95     public static final StatusWord SW_NOT_ENOUGH_MEMORY =
96             new StatusWord(0x6A84, "Not enough memory");
97 
98     public static final StatusWord SW_NC_INCONSISTENT_WITH_TLV =
99             new StatusWord(0x6A85, "Nc inconsistent with TLV structure");
100 
101     public static final StatusWord SW_INCORRECT_P1P2 =
102             new StatusWord(0x6A86, "Incorrect P1 or P2");
103 
104     public static final StatusWord SW_DATA_NOT_FOUND =
105             new StatusWord(0x6A88, "Referenced data not found");
106 
107     public static final StatusWord SW_FILE_ALREADY_EXISTS =
108             new StatusWord(0x6A89, "File already exists");
109 
110     public static final StatusWord SW_WRONG_P1P2 =
111             new StatusWord(0x6B00, "Wrong P1 or P2");
112 
113     public static final StatusWord SW_WRONG_LE =
114             new StatusWord(0x6C00, "Wrong Le");
115 
116     public static final StatusWord SW_INS_NOT_SUPPORTED =
117             new StatusWord(0x6D00, "Instruction not supported or invalid");
118 
119     public static final StatusWord SW_CLA_NOT_SUPPORTED =
120             new StatusWord(0x6E00, "Class not supported");
121 
122     public static final StatusWord SW_UNKNOWN_ERROR =
123             new StatusWord(0x6F00, "Unknown error (no precise diagnosis)");
124 
125     private static final String UNKNOWN_STATUS_WORD_MESSAGE = "Unknown status word";
126 
127     public static final ImmutableSet<StatusWord> ALL_KNOWN_STATUS_WORDS =
128             ImmutableSet.of(
129                     SW_NO_ERROR,
130                     SW_RESPONSE_BYTES_STILL_AVAILABLE,
131                     SW_WARNING_STATE_UNCHANGED,
132                     SW_CARD_MANAGER_LOCKED,
133                     SW_WARNING_NO_INFO_GIVEN,
134                     SW_WARNING_MORE_DATA,
135                     SW_VERIFY_FAILED,
136                     SW_NO_SPECIFIC_DIAGNOSTIC,
137                     SW_REQUESTED_ELEMENTS_NOT_AVAILABLE,
138                     SW_ICA_ALREADY_EXISTS,
139                     SW_WRONG_LENGTH,
140                     SW_SECURITY_STATUS_NOT_SATISFIED,
141                     SW_FILE_INVALID,
142                     SW_REFERENCE_DATA_NOT_USABLE,
143                     SW_CONDITIONS_NOT_SATISFIED,
144                     SW_COMMAND_NOT_ALLOWED,
145                     SW_APPLET_SELECT_FAILED,
146                     SW_WRONG_DATA,
147                     SW_FUNCTION_NOT_SUPPORTED,
148                     SW_FILE_NOT_FOUND,
149                     SW_RECORD_NOT_FOUND,
150                     SW_NOT_ENOUGH_MEMORY,
151                     SW_NC_INCONSISTENT_WITH_TLV,
152                     SW_INCORRECT_P1P2,
153                     SW_DATA_NOT_FOUND,
154                     SW_FILE_ALREADY_EXISTS,
155                     SW_WRONG_P1P2,
156                     SW_WRONG_LE,
157                     SW_INS_NOT_SUPPORTED,
158                     SW_CLA_NOT_SUPPORTED,
159                     SW_UNKNOWN_ERROR);
160 
161     /** A meessage that is used to construct an exception to represent this status word. */
162     private final String mMessage;
163 
164     /** The actual status word (2 bytes). */
165     private final int mStatusWord;
166 
167     /** Map status words to values for fast lookup. */
168     private static final Map<Integer, StatusWord> STATUS_WORD_MAP;
169 
170     static {
171         // Map all the values to their code.
172         Map<Integer, StatusWord> statusWordMap = new LinkedHashMap<>(ALL_KNOWN_STATUS_WORDS.size());
173         for (StatusWord value : ALL_KNOWN_STATUS_WORDS) {
Integer.valueOf(value.mStatusWord)174             statusWordMap.put(Integer.valueOf(value.mStatusWord), value);
175         }
176         STATUS_WORD_MAP = Collections.unmodifiableMap(statusWordMap);
177     }
178 
StatusWord(int sw, String message)179     private StatusWord(int sw, String message) {
180         mStatusWord = sw;
181         this.mMessage = message;
182     }
183 
184     /** Lookup a {@link StatusWord} from the status word value. */
fromInt(int sw)185     public static StatusWord fromInt(int sw) {
186         Preconditions.checkArgument((sw >> Short.SIZE) == 0);
187         StatusWord statusWord = STATUS_WORD_MAP.get(Integer.valueOf(sw));
188         if (statusWord != null) {
189             return statusWord;
190         }
191         return new StatusWord(sw, UNKNOWN_STATUS_WORD_MESSAGE);
192     }
193 
194     /**
195      * Gets the byte array form of the status word.
196      */
toBytes()197     public byte[] toBytes() {
198         Preconditions.checkState((mStatusWord >> Short.SIZE) == 0);
199         return Shorts.toByteArray((short) mStatusWord);
200     }
201 
202     /**
203      * Gets the int value of the status word.
204      */
toInt()205     public int toInt() {
206         return mStatusWord;
207     }
208 
209     /**
210      * Gets the description message of the status word.
211      */
getMessage()212     public String getMessage() {
213         return mMessage;
214     }
215 
216     /**
217      * Checks if this status word is known.
218      */
isKnown()219     public boolean isKnown() {
220         return ALL_KNOWN_STATUS_WORDS.contains(this);
221     }
222 
223     /**
224      * Checks if the given status words are known.
225      */
areAllKnown(Iterable<StatusWord> statusWords)226     public static boolean areAllKnown(Iterable<StatusWord> statusWords) {
227         for (StatusWord word : statusWords) {
228             if (!word.isKnown()) {
229                 return false;
230             }
231         }
232         return true;
233     }
234 
235     @Override
toString()236     public String toString() {
237         return String.format("'%04X': %s", mStatusWord, mMessage);
238     }
239 
240     @Override
equals(@ullable Object obj)241     public boolean equals(@Nullable Object obj) {
242         if (obj == null || obj.getClass() != this.getClass()) {
243             return false;
244         }
245 
246         StatusWord other = (StatusWord) obj;
247         return other.mStatusWord == this.mStatusWord;
248     }
249 
250     @Override
hashCode()251     public int hashCode() {
252         return Objects.hashCode(mStatusWord);
253     }
254 }
255