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