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     InfoLog("trying to match card type = \"%{public}s\"", cardInfoVec_[cardIndex].name.c_str());
196     std::string checkCmdApdu = "";
197     std::string rspApdu = "";
198     for (uint8_t i = 0; i < cardInfoVec_[cardIndex].checkApdus.size(); ++i) {
199         checkCmdApdu = cardInfoVec_[cardIndex].checkApdus[i];
200         if (nciTagProxy_.expired()) {
201             WarnLog("nciTagProxy_ expired.");
202             return false;
203         }
204         nciTagProxy_.lock()->Transceive(rfDiscId, checkCmdApdu, rspApdu);
205         InfoLog("rspApdu = %{public}s", rspApdu.c_str());
206         if (!CheckApduResponse(rspApdu, cardIndex)) {
207             InfoLog("check result false");
208             return false;
209         }
210     }
211     InfoLog("check result true");
212     return true;
213 }
214 
CheckApduResponse(const std::string & response,uint8_t cardIndex)215 bool IsodepCardHandler::CheckApduResponse(const std::string &response, uint8_t cardIndex)
216 {
217     if (response.length() < APDU_RSP_OK_STR_LEN) {
218         ErrorLog("invalid response length");
219         return false;
220     }
221     if (cardInfoVec_[cardIndex].rspContain == "") {
222         return CheckApduResponse(response);
223     }
224     if (response.find(APDU_RSP_PREFIX) != std::string::npos &&
225         response.find(cardInfoVec_[cardIndex].rspContain) != std::string::npos) {
226         return true;
227     }
228     return false;
229 }
230 
CheckApduResponse(const std::string & response)231 bool IsodepCardHandler::CheckApduResponse(const std::string &response)
232 {
233     if (response.length() < APDU_RSP_OK_STR_LEN) {
234         ErrorLog("invalid response length");
235         return false;
236     }
237 
238     std::string rspStr = response.substr(response.length() - APDU_RSP_OK_STR_LEN, APDU_RSP_OK_STR_LEN);
239     if (rspStr == APDU_RSP_OK) {
240         return true;
241     }
242     return false;
243 }
244 
GetBalance(uint32_t rfDiscId,uint8_t cardIndex,int & balance)245 void IsodepCardHandler::GetBalance(uint32_t rfDiscId, uint8_t cardIndex, int &balance)
246 {
247     if (cardIndex >= cardInfoVec_.size()) {
248         ErrorLog("invalid input cardIndex[%{public}u]", cardIndex);
249         return;
250     }
251     InfoLog("start to get balance, card type = \"%{public}s\"", cardInfoVec_[cardIndex].name.c_str());
252     std::string getBalanceCmdApdu = "";
253     std::string rspApdu = "";
254     uint8_t apduNum = cardInfoVec_[cardIndex].balanceApdus.size();
255     for (uint8_t i = 0; i < apduNum; ++i) {
256         getBalanceCmdApdu = cardInfoVec_[cardIndex].balanceApdus[i];
257         if (nciTagProxy_.expired()) {
258             WarnLog("nciTagProxy_ expired.");
259             return;
260         }
261         nciTagProxy_.lock()->Transceive(rfDiscId, getBalanceCmdApdu, rspApdu);
262         InfoLog("rspApdu = %{public}s", rspApdu.c_str());
263         if (CheckApduResponse(rspApdu)) {
264             if (i != apduNum - 1) {
265                 continue;
266             }
267             std::string balanceStr = rspApdu.substr(0, APDU_RSP_BALANCE_STR_LEN);
268             DebugLog("balanceStr = %{public}s", balanceStr.c_str());
269             GetBalanceValue(balanceStr, balance);
270             return;
271         }
272     }
273     ErrorLog("fail to get balance infomation from traffic card.");
274 }
275 
GetBalanceValue(const std::string & balanceStr,int & balanceValue)276 void IsodepCardHandler::GetBalanceValue(const std::string &balanceStr, int &balanceValue)
277 {
278     if (balanceStr.length() != APDU_RSP_BALANCE_STR_LEN) {
279         ErrorLog("illegal balance string input.");
280         return;
281     }
282     std::vector<unsigned char> bytes;
283     KITS::NfcSdkCommon::HexStringToBytes(balanceStr, bytes);
284     if (bytes.size() != APDU_RSP_BALANCE_BYTES_LEN) {
285         ErrorLog("bytes size error.");
286         return;
287     }
288     balanceValue = ((bytes[BYTE_ONE] & 0xFF) << TWO_BYTES_SHIFT)
289                 + ((bytes[BYTE_TWO] & 0xFF) << ONE_BYTES_SHIFT)
290                 + (bytes[BYTE_THREE] & 0xFF); // ignore BYTE_ZERO, in case of large balance
291 }
292 
GetCardName(uint8_t cardIndex,std::string & cardName)293 void IsodepCardHandler::GetCardName(uint8_t cardIndex, std::string &cardName)
294 {
295     if (cardIndex >= cardInfoVec_.size()) {
296         ErrorLog("invalid input cardIndex[%{public}u]", cardIndex);
297         return;
298     }
299     cardName = cardInfoVec_[cardIndex].name;
300 }
301 }  // namespace TAG
302 }  // namespace NFC
303 }  // namespace OHOS
304