1 /*
2 * Copyright (C) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "isodep_card_handler.h"
17
18 #include "cJSON.h"
19 #include "file_ex.h"
20 #include "loghelper.h"
21 #include "nfc_sdk_common.h"
22
23 namespace OHOS {
24 namespace NFC {
25 namespace TAG {
IsodepCardHandler(std::weak_ptr<NCI::INciTagInterface> nciTagProxy)26 IsodepCardHandler::IsodepCardHandler(std::weak_ptr<NCI::INciTagInterface> nciTagProxy)
27 : nciTagProxy_(nciTagProxy)
28 {
29 InfoLog("IsodepCardHandler constructor enter.");
30 }
31
~IsodepCardHandler()32 IsodepCardHandler::~IsodepCardHandler()
33 {
34 InfoLog("IsodepCardHandler destructor enter.");
35 }
36
InitTransportCardInfo()37 void IsodepCardHandler::InitTransportCardInfo()
38 {
39 if (isInitialized_) {
40 DebugLog("already initialized.");
41 return;
42 }
43 cardInfoVec_.clear();
44 if (DoJsonRead()) {
45 InfoLog("transport card info initialized.");
46 isInitialized_ = true;
47 }
48 }
49
GetCheckApduFromJson(cJSON * json,cJSON * cardInfoEach,TransportCardInfo * cardInfoList,int index)50 static bool GetCheckApduFromJson(cJSON *json, cJSON *cardInfoEach, TransportCardInfo *cardInfoList, int index)
51 {
52 cJSON *checkApdus = cJSON_GetObjectItemCaseSensitive(cardInfoEach, KEY_APDU_CHECK_APDUS.c_str());
53 if (checkApdus == nullptr || !cJSON_IsArray(checkApdus)) {
54 ErrorLog("json param not array, or has no field \"checkApdus\", index = %{public}d", index);
55 return false;
56 }
57 int checkApduArraySize = cJSON_GetArraySize(checkApdus);
58 if (checkApduArraySize == 0 || checkApduArraySize > MAX_APDU_ARRAY_SIZE) {
59 ErrorLog("illegal array size [%{public}d]", checkApduArraySize);
60 return false;
61 }
62 for (int i = 0; i < checkApduArraySize; ++i) {
63 cJSON *value = cJSON_GetArrayItem(checkApdus, i);
64 if (value == nullptr || !cJSON_IsString(value)) {
65 ErrorLog("json param not string");
66 return false;
67 }
68 cardInfoList[index].checkApdus.push_back(value->valuestring);
69 }
70 return true;
71 }
72
GetBalanceApduFromJson(cJSON * json,cJSON * cardInfoEach,TransportCardInfo * cardInfoList,int index)73 static bool GetBalanceApduFromJson(cJSON *json, cJSON *cardInfoEach, TransportCardInfo *cardInfoList, int index)
74 {
75 cJSON *balanceApdus = cJSON_GetObjectItemCaseSensitive(cardInfoEach, KEY_APDU_BALANCE_APDUS.c_str());
76 if (balanceApdus == nullptr || !cJSON_IsArray(balanceApdus)) {
77 WarnLog("json param not array, or has no field \"balanceApdus\", index = %{public}d", index);
78 } else {
79 int balanceApduArraySize = cJSON_GetArraySize(balanceApdus);
80 if (balanceApduArraySize == 0 || balanceApduArraySize > MAX_APDU_ARRAY_SIZE) {
81 ErrorLog("illegal array size [%{public}d]", balanceApduArraySize);
82 return false;
83 }
84 for (int i = 0; i < balanceApduArraySize; ++i) {
85 cJSON *value = cJSON_GetArrayItem(balanceApdus, i);
86 if (value == nullptr || !cJSON_IsString(value)) {
87 ErrorLog("json param not string");
88 return false;
89 }
90 cardInfoList[index].balanceApdus.push_back(value->valuestring);
91 }
92 }
93 return true;
94 }
95
GetEachCardInfoFromJson(cJSON * json,cJSON * cardInfo,TransportCardInfo * cardInfoList,size_t cardInfoListLen)96 static bool GetEachCardInfoFromJson(cJSON *json, cJSON *cardInfo,
97 TransportCardInfo *cardInfoList, size_t cardInfoListLen)
98 {
99 cJSON *cardInfoEach = nullptr;
100 int index = 0;
101 cJSON_ArrayForEach(cardInfoEach, cardInfo) {
102 if (index >= MAX_CARD_INFO_VEC_LEN) {
103 ErrorLog("index exceeds");
104 return false;
105 }
106 cJSON *name = cJSON_GetObjectItemCaseSensitive(cardInfoEach, KEY_APDU_NAME.c_str());
107 if (name == nullptr || !cJSON_IsString(name)) {
108 ErrorLog("json param not string, or has no field \"name\", index = %{public}d", index);
109 return false;
110 }
111 cardInfoList[index].name = name->valuestring;
112
113 cJSON *aid = cJSON_GetObjectItemCaseSensitive(cardInfoEach, KEY_APDU_AID.c_str());
114 if (aid == nullptr || !cJSON_IsString(aid)) {
115 WarnLog("json param not string, or has no field \"aid\", index = %{public}d", index);
116 } else {
117 cardInfoList[index].aid = aid->valuestring;
118 }
119
120 if (!GetCheckApduFromJson(json, cardInfoEach, cardInfoList, index)) {
121 ErrorLog("fail to get check apdu array from json.");
122 return false;
123 }
124
125 if (!GetBalanceApduFromJson(json, cardInfoEach, cardInfoList, index)) {
126 ErrorLog("fail to get balance apdu array from json.");
127 return false;
128 }
129
130 cJSON *rspContains = cJSON_GetObjectItemCaseSensitive(cardInfoEach, KEY_APDU_RSP_CONTAINS.c_str());
131 if (rspContains == nullptr || !cJSON_IsString(rspContains)) {
132 WarnLog("json param not string, or has no fild \"rspContain\", index = %{public}d", index);
133 } else {
134 cardInfoList[index].rspContain = rspContains->valuestring;
135 }
136
137 index++;
138 }
139 return true;
140 }
141
DoJsonRead()142 bool IsodepCardHandler::DoJsonRead()
143 {
144 InfoLog("Reading apdu from json config.");
145 TransportCardInfo cardInfoList[MAX_CARD_INFO_VEC_LEN];
146 std::string content;
147 LoadStringFromFile(NFC_CARD_APDU_JSON_FILEPATH, content);
148 cJSON *json = cJSON_Parse(content.c_str());
149 if (json == nullptr) {
150 ErrorLog("json nullptr.");
151 return false;
152 }
153
154 cJSON *cardInfo = cJSON_GetObjectItemCaseSensitive(json, KEY_CARD_INFO.c_str());
155 if (cardInfo == nullptr || cJSON_GetArraySize(cardInfo) != MAX_CARD_INFO_VEC_LEN) {
156 ErrorLog("fail to parse cardinfo");
157 cJSON_Delete(json);
158 return false;
159 }
160
161 if (!GetEachCardInfoFromJson(json, cardInfo, cardInfoList, sizeof(cardInfoList) / sizeof(cardInfoList[0]))) {
162 ErrorLog("fail to get each cardinfo from json");
163 cJSON_Delete(json);
164 return false;
165 }
166
167 for (uint8_t i = 0; i < MAX_CARD_INFO_VEC_LEN; ++i) {
168 cardInfoVec_.push_back(cardInfoList[i]);
169 }
170 cJSON_Delete(json);
171 return true;
172 }
173
IsSupportedTransportCard(uint32_t rfDiscId,uint8_t & cardIndex)174 bool IsodepCardHandler::IsSupportedTransportCard(uint32_t rfDiscId, uint8_t &cardIndex)
175 {
176 InfoLog("IsSupportedTransportCard, cardInfoVec_ size = [%{public}lu]", cardInfoVec_.size());
177 if (nciTagProxy_.expired()) {
178 WarnLog("nciTagProxy_ expired.");
179 return false;
180 }
181 nciTagProxy_.lock()->Connect(rfDiscId, static_cast<int>(KITS::TagTechnology::NFC_ISODEP_TECH));
182 for (uint8_t index = 0; index < cardInfoVec_.size(); ++index) {
183 if (MatchCity(rfDiscId, index)) {
184 InfoLog("card match \"%{public}s\"", cardInfoVec_[index].name.c_str());
185 cardIndex = index;
186 return true;
187 }
188 }
189 InfoLog("no matching city, ignore.");
190 return false;
191 }
192
MatchCity(uint32_t rfDiscId,uint8_t cardIndex)193 bool IsodepCardHandler::MatchCity(uint32_t rfDiscId, uint8_t cardIndex)
194 {
195 if (static_cast<size_t>(cardIndex) >= cardInfoVec_.size()) {
196 ErrorLog("invalid input cardIndex[%{public}u]", cardIndex);
197 return false;
198 }
199 InfoLog("trying to match card type = \"%{public}s\"", cardInfoVec_[cardIndex].name.c_str());
200 std::string checkCmdApdu = "";
201 std::string rspApdu = "";
202 for (uint8_t i = 0; i < cardInfoVec_[cardIndex].checkApdus.size(); ++i) {
203 checkCmdApdu = cardInfoVec_[cardIndex].checkApdus[i];
204 if (nciTagProxy_.expired()) {
205 WarnLog("nciTagProxy_ expired.");
206 return false;
207 }
208 nciTagProxy_.lock()->Transceive(rfDiscId, checkCmdApdu, rspApdu);
209 InfoLog("rspApdu = %{public}s", rspApdu.c_str());
210 if (!CheckApduResponse(rspApdu, cardIndex)) {
211 InfoLog("check result false");
212 return false;
213 }
214 }
215 InfoLog("check result true");
216 return true;
217 }
218
CheckApduResponse(const std::string & response,uint8_t cardIndex)219 bool IsodepCardHandler::CheckApduResponse(const std::string &response, uint8_t cardIndex)
220 {
221 if (static_cast<size_t>(cardIndex) >= cardInfoVec_.size()) {
222 ErrorLog("invalid input cardIndex[%{public}u]", cardIndex);
223 return false;
224 }
225 if (response.length() < APDU_RSP_OK_STR_LEN) {
226 ErrorLog("invalid response length");
227 return false;
228 }
229 if (cardInfoVec_[cardIndex].rspContain == "") {
230 return CheckApduResponse(response);
231 }
232 if (response.find(APDU_RSP_PREFIX) != std::string::npos &&
233 response.find(cardInfoVec_[cardIndex].rspContain) != std::string::npos) {
234 return true;
235 }
236 return false;
237 }
238
CheckApduResponse(const std::string & response)239 bool IsodepCardHandler::CheckApduResponse(const std::string &response)
240 {
241 if (response.length() < APDU_RSP_OK_STR_LEN) {
242 ErrorLog("invalid response length");
243 return false;
244 }
245
246 std::string rspStr = response.substr(response.length() - APDU_RSP_OK_STR_LEN, APDU_RSP_OK_STR_LEN);
247 if (rspStr == APDU_RSP_OK) {
248 return true;
249 }
250 return false;
251 }
252
GetBalance(uint32_t rfDiscId,uint8_t cardIndex,int & balance)253 void IsodepCardHandler::GetBalance(uint32_t rfDiscId, uint8_t cardIndex, int &balance)
254 {
255 if (static_cast<size_t>(cardIndex) >= cardInfoVec_.size()) {
256 ErrorLog("invalid input cardIndex[%{public}u]", cardIndex);
257 return;
258 }
259 InfoLog("start to get balance, card type = \"%{public}s\"", cardInfoVec_[cardIndex].name.c_str());
260 std::string getBalanceCmdApdu = "";
261 std::string rspApdu = "";
262 uint8_t apduNum = cardInfoVec_[cardIndex].balanceApdus.size();
263 for (uint8_t i = 0; i < apduNum; ++i) {
264 getBalanceCmdApdu = cardInfoVec_[cardIndex].balanceApdus[i];
265 if (nciTagProxy_.expired()) {
266 WarnLog("nciTagProxy_ expired.");
267 return;
268 }
269 nciTagProxy_.lock()->Transceive(rfDiscId, getBalanceCmdApdu, rspApdu);
270 InfoLog("rspApdu = %{public}s", rspApdu.c_str());
271 if (CheckApduResponse(rspApdu)) {
272 if (i != apduNum - 1) {
273 continue;
274 }
275 std::string balanceStr = rspApdu.substr(0, APDU_RSP_BALANCE_STR_LEN);
276 DebugLog("balanceStr = %{public}s", balanceStr.c_str());
277 GetBalanceValue(balanceStr, balance);
278 return;
279 }
280 }
281 ErrorLog("fail to get balance infomation from traffic card.");
282 }
283
GetBalanceValue(const std::string & balanceStr,int & balanceValue)284 void IsodepCardHandler::GetBalanceValue(const std::string &balanceStr, int &balanceValue)
285 {
286 if (balanceStr.length() != APDU_RSP_BALANCE_STR_LEN) {
287 ErrorLog("illegal balance string input.");
288 return;
289 }
290 std::vector<unsigned char> bytes;
291 KITS::NfcSdkCommon::HexStringToBytes(balanceStr, bytes);
292 if (bytes.size() != APDU_RSP_BALANCE_BYTES_LEN) {
293 ErrorLog("bytes size error.");
294 return;
295 }
296 balanceValue = ((bytes[BYTE_ONE] & 0xFF) << TWO_BYTES_SHIFT)
297 + ((bytes[BYTE_TWO] & 0xFF) << ONE_BYTES_SHIFT)
298 + (bytes[BYTE_THREE] & 0xFF); // ignore BYTE_ZERO, in case of large balance
299 }
300
GetCardName(uint8_t cardIndex,std::string & cardName)301 void IsodepCardHandler::GetCardName(uint8_t cardIndex, std::string &cardName)
302 {
303 if (static_cast<size_t>(cardIndex) >= cardInfoVec_.size()) {
304 ErrorLog("invalid input cardIndex[%{public}u]", cardIndex);
305 return;
306 }
307 cardName = cardInfoVec_[cardIndex].name;
308 }
309 } // namespace TAG
310 } // namespace NFC
311 } // namespace OHOS
312