]>
Commit | Line | Data |
---|---|---|
1 | //----------------------------------------------------------------------------- | |
2 | // Copyright (C) 2018 Merlok | |
3 | // | |
4 | // This code is licensed to you under the terms of the GNU GPL, version 2 or, | |
5 | // at your option, any later version. See the LICENSE.txt file for the text of | |
6 | // the license. | |
7 | //----------------------------------------------------------------------------- | |
8 | // EMV json logic | |
9 | //----------------------------------------------------------------------------- | |
10 | ||
11 | #include "emvjson.h" | |
12 | #include <ctype.h> | |
13 | #include <stdio.h> | |
14 | #include <stdlib.h> | |
15 | #include <inttypes.h> | |
16 | #include <string.h> | |
17 | #include "util.h" | |
18 | #include "ui.h" | |
19 | #include "proxmark3.h" | |
20 | #include "emv_tags.h" | |
21 | ||
22 | static const ApplicationDataElm ApplicationData[] = { | |
23 | {0x82, "AIP"}, | |
24 | {0x94, "AFL"}, | |
25 | ||
26 | {0x5A, "PAN"}, | |
27 | {0x5F34, "PANSeqNo"}, | |
28 | {0x5F24, "ExpirationDate"}, | |
29 | {0x5F25, "EffectiveDate"}, | |
30 | {0x5F28, "IssuerCountryCode"}, | |
31 | ||
32 | {0x50, "ApplicationLabel"}, | |
33 | {0x9F08, "VersionNumber"}, | |
34 | {0x9F42, "CurrencyCode"}, | |
35 | {0x5F2D, "LanguagePreference"}, | |
36 | {0x87, "PriorityIndicator"}, | |
37 | {0x9F36, "ATC"}, //Application Transaction Counter | |
38 | ||
39 | {0x5F20, "CardholderName"}, | |
40 | ||
41 | {0x9F38, "PDOL"}, | |
42 | {0x8C, "CDOL1"}, | |
43 | {0x8D, "CDOL2"}, | |
44 | ||
45 | {0x9F07, "AUC"}, // Application Usage Control | |
46 | {0x9F6C, "CTQ"}, | |
47 | {0x8E, "CVMList"}, | |
48 | {0x9F0D, "IACDefault"}, | |
49 | {0x9F0E, "IACDeny"}, | |
50 | {0x9F0F, "IACOnline"}, | |
51 | ||
52 | {0x8F, "CertificationAuthorityPublicKeyIndex"}, | |
53 | {0x9F32, "IssuerPublicKeyExponent"}, | |
54 | {0x92, "IssuerPublicKeyRemainder"}, | |
55 | {0x90, "IssuerPublicKeyCertificate"}, | |
56 | {0x9F47, "ICCPublicKeyExponent"}, | |
57 | {0x9F46, "ICCPublicKeyCertificate"}, | |
58 | ||
59 | {0x00, "end..."} | |
60 | }; | |
61 | int ApplicationDataLen = sizeof(ApplicationData) / sizeof(ApplicationDataElm); | |
62 | ||
63 | char* GetApplicationDataName(tlv_tag_t tag) { | |
64 | for (int i = 0; i < ApplicationDataLen; i++) | |
65 | if (ApplicationData[i].Tag == tag) | |
66 | return ApplicationData[i].Name; | |
67 | ||
68 | return NULL; | |
69 | } | |
70 | ||
71 | int JsonSaveStr(json_t *root, char *path, char *value) { | |
72 | json_error_t error; | |
73 | ||
74 | if (strlen(path) < 1) | |
75 | return 1; | |
76 | ||
77 | if (path[0] == '$') { | |
78 | if (json_path_set(root, path, json_string(value), 0, &error)) { | |
79 | PrintAndLog("ERROR: can't set json path: ", error.text); | |
80 | return 2; | |
81 | } else { | |
82 | return 0; | |
83 | } | |
84 | } else { | |
85 | return json_object_set_new(root, path, json_string(value)); | |
86 | } | |
87 | }; | |
88 | ||
89 | int JsonSaveBufAsHex(json_t *elm, char *path, uint8_t *data, size_t datalen) { | |
90 | char * msg = sprint_hex(data, datalen); | |
91 | if (msg && strlen(msg) && msg[strlen(msg) - 1] == ' ') | |
92 | msg[strlen(msg) - 1] = '\0'; | |
93 | ||
94 | return JsonSaveStr(elm, path, msg); | |
95 | } | |
96 | ||
97 | int JsonSaveHex(json_t *elm, char *path, uint64_t data, int datalen) { | |
98 | uint8_t bdata[8] = {0}; | |
99 | int len = 0; | |
100 | if (!datalen) { | |
101 | for (uint64_t u = 0xffffffffffffffff; u; u = u << 8) { | |
102 | if (!(data & u)) { | |
103 | break; | |
104 | } | |
105 | len++; | |
106 | } | |
107 | if (!len) | |
108 | len = 1; | |
109 | } else { | |
110 | len = datalen; | |
111 | } | |
112 | num_to_bytes(data, len, bdata); | |
113 | ||
114 | return JsonSaveBufAsHex(elm, path, bdata, len); | |
115 | } | |
116 | ||
117 | int JsonSaveTLVValue(json_t *root, char *path, struct tlvdb *tlvdbelm) { | |
118 | const struct tlv *tlvelm = tlvdb_get_tlv(tlvdbelm); | |
119 | if (tlvelm) | |
120 | return JsonSaveBufAsHex(root, path, (uint8_t *)tlvelm->value, tlvelm->len); | |
121 | else | |
122 | return 1; | |
123 | } | |
124 | ||
125 | int JsonSaveTLVElm(json_t *elm, char *path, struct tlv *tlvelm, bool saveName, bool saveValue, bool saveAppDataLink) { | |
126 | json_error_t error; | |
127 | ||
128 | if (strlen(path) < 1 || !tlvelm) | |
129 | return 1; | |
130 | ||
131 | if (path[0] == '$') { | |
132 | ||
133 | json_t *obj = json_path_get(elm, path); | |
134 | if (!obj) { | |
135 | obj = json_object(); | |
136 | ||
137 | if (json_is_array(elm)) { | |
138 | if (json_array_append_new(elm, obj)) { | |
139 | PrintAndLog("ERROR: can't append array: %s", path); | |
140 | return 2; | |
141 | } | |
142 | } else { | |
143 | if (json_path_set(elm, path, obj, 0, &error)) { | |
144 | PrintAndLog("ERROR: can't set json path: ", error.text); | |
145 | return 2; | |
146 | } | |
147 | } | |
148 | } | |
149 | ||
150 | if (saveAppDataLink) { | |
151 | char * AppDataName = GetApplicationDataName(tlvelm->tag); | |
152 | if (AppDataName) | |
153 | JsonSaveStr(obj, "appdata", AppDataName); | |
154 | } else { | |
155 | char * name = emv_get_tag_name(tlvelm); | |
156 | if (saveName && name && strlen(name) > 0 && strncmp(name, "Unknown", 7)) | |
157 | JsonSaveStr(obj, "name", emv_get_tag_name(tlvelm)); | |
158 | JsonSaveHex(obj, "tag", tlvelm->tag, 0); | |
159 | if (saveValue) { | |
160 | JsonSaveHex(obj, "length", tlvelm->len, 0); | |
161 | JsonSaveBufAsHex(obj, "value", (uint8_t *)tlvelm->value, tlvelm->len); | |
162 | }; | |
163 | } | |
164 | } | |
165 | ||
166 | return 0; | |
167 | } | |
168 | ||
169 | int JsonSaveTLVTreeElm(json_t *elm, char *path, struct tlvdb *tlvdbelm, bool saveName, bool saveValue, bool saveAppDataLink) { | |
170 | return JsonSaveTLVElm(elm, path, (struct tlv *)tlvdb_get_tlv(tlvdbelm), saveName, saveValue, saveAppDataLink); | |
171 | } | |
172 | ||
173 | int JsonSaveTLVTree(json_t *root, json_t *elm, char *path, struct tlvdb *tlvdbelm) { | |
174 | struct tlvdb *tlvp = tlvdbelm; | |
175 | while (tlvp) { | |
176 | const struct tlv * tlvpelm = tlvdb_get_tlv(tlvp); | |
177 | char * AppDataName = NULL; | |
178 | if (tlvpelm) | |
179 | AppDataName = GetApplicationDataName(tlvpelm->tag); | |
180 | ||
181 | if (AppDataName) { | |
182 | char appdatalink[200] = {0}; | |
183 | sprintf(appdatalink, "$.ApplicationData.%s", AppDataName); | |
184 | JsonSaveBufAsHex(root, appdatalink, (uint8_t *)tlvpelm->value, tlvpelm->len); | |
185 | } | |
186 | ||
187 | json_t *pelm = json_path_get(elm, path); | |
188 | if (pelm && json_is_array(pelm)) { | |
189 | json_t *appendelm = json_object(); | |
190 | json_array_append_new(pelm, appendelm); | |
191 | JsonSaveTLVTreeElm(appendelm, "$", tlvp, !AppDataName, !tlvdb_elm_get_children(tlvp), AppDataName); | |
192 | pelm = appendelm; | |
193 | } else { | |
194 | JsonSaveTLVTreeElm(elm, path, tlvp, !AppDataName, !tlvdb_elm_get_children(tlvp), AppDataName); | |
195 | pelm = json_path_get(elm, path); | |
196 | } | |
197 | ||
198 | if (tlvdb_elm_get_children(tlvp)) { | |
199 | // get path element | |
200 | if(!pelm) | |
201 | return 1; | |
202 | ||
203 | // check childs element and add it if not found | |
204 | json_t *chjson = json_path_get(pelm, "$.Childs"); | |
205 | if (!chjson) { | |
206 | json_object_set_new(pelm, "Childs", json_array()); | |
207 | ||
208 | chjson = json_path_get(pelm, "$.Childs"); | |
209 | } | |
210 | ||
211 | // check | |
212 | if (!json_is_array(chjson)) { | |
213 | PrintAndLog("E->Internal logic error. `$.Childs` is not an array."); | |
214 | break; | |
215 | } | |
216 | ||
217 | // Recursion | |
218 | JsonSaveTLVTree(root, chjson, "$", tlvdb_elm_get_children(tlvp)); | |
219 | } | |
220 | ||
221 | tlvp = tlvdb_elm_get_next(tlvp); | |
222 | } | |
223 | return 0; | |
224 | } | |
225 | ||
226 | bool HexToBuffer(const char *errormsg, const char *hexvalue, uint8_t * buffer, size_t maxbufferlen, size_t *bufferlen) { | |
227 | int buflen = 0; | |
228 | ||
229 | switch(param_gethex_to_eol(hexvalue, 0, buffer, maxbufferlen, &buflen)) { | |
230 | case 1: | |
231 | PrintAndLog("%s Invalid HEX value.", errormsg); | |
232 | return false; | |
233 | case 2: | |
234 | PrintAndLog("%s Hex value too large.", errormsg); | |
235 | return false; | |
236 | case 3: | |
237 | PrintAndLog("%s Hex value must have even number of digits.", errormsg); | |
238 | return false; | |
239 | } | |
240 | ||
241 | if (buflen > maxbufferlen) { | |
242 | PrintAndLog("%s HEX length (%d) more than %d", errormsg, *bufferlen, maxbufferlen); | |
243 | return false; | |
244 | } | |
245 | ||
246 | *bufferlen = buflen; | |
247 | ||
248 | return true; | |
249 | } | |
250 | ||
251 | bool ParamLoadFromJson(struct tlvdb *tlv) { | |
252 | json_t *root; | |
253 | json_error_t error; | |
254 | ||
255 | if (!tlv) { | |
256 | PrintAndLog("ERROR load params: tlv tree is NULL."); | |
257 | return false; | |
258 | } | |
259 | ||
260 | // current path + file name | |
261 | const char *relfname = "emv/defparams.json"; | |
262 | char fname[strlen(get_my_executable_directory()) + strlen(relfname) + 1]; | |
263 | strcpy(fname, get_my_executable_directory()); | |
264 | strcat(fname, relfname); | |
265 | ||
266 | root = json_load_file(fname, 0, &error); | |
267 | if (!root) { | |
268 | PrintAndLog("Load params: json error on line %d: %s", error.line, error.text); | |
269 | return false; | |
270 | } | |
271 | ||
272 | if (!json_is_array(root)) { | |
273 | PrintAndLog("Load params: Invalid json format. root must be array."); | |
274 | return false; | |
275 | } | |
276 | ||
277 | PrintAndLog("Load params: json(%d) OK", json_array_size(root)); | |
278 | ||
279 | for(int i = 0; i < json_array_size(root); i++) { | |
280 | json_t *data, *jtag, *jlength, *jvalue; | |
281 | ||
282 | data = json_array_get(root, i); | |
283 | if(!json_is_object(data)) | |
284 | { | |
285 | PrintAndLog("Load params: data [%d] is not an object", i + 1); | |
286 | json_decref(root); | |
287 | return false; | |
288 | } | |
289 | ||
290 | jtag = json_object_get(data, "tag"); | |
291 | if(!json_is_string(jtag)) | |
292 | { | |
293 | PrintAndLog("Load params: data [%d] tag is not a string", i + 1); | |
294 | json_decref(root); | |
295 | return false; | |
296 | } | |
297 | const char *tlvTag = json_string_value(jtag); | |
298 | ||
299 | jvalue = json_object_get(data, "value"); | |
300 | if(!json_is_string(jvalue)) | |
301 | { | |
302 | PrintAndLog("Load params: data [%d] value is not a string", i + 1); | |
303 | json_decref(root); | |
304 | return false; | |
305 | } | |
306 | const char *tlvValue = json_string_value(jvalue); | |
307 | ||
308 | jlength = json_object_get(data, "length"); | |
309 | if(!json_is_number(jlength)) | |
310 | { | |
311 | PrintAndLog("Load params: data [%d] length is not a number", i + 1); | |
312 | json_decref(root); | |
313 | return false; | |
314 | } | |
315 | ||
316 | int tlvLength = json_integer_value(jlength); | |
317 | if (tlvLength > 250) { | |
318 | PrintAndLog("Load params: data [%d] length more than 250", i + 1); | |
319 | json_decref(root); | |
320 | return false; | |
321 | } | |
322 | ||
323 | PrintAndLog("TLV param: %s[%d]=%s", tlvTag, tlvLength, tlvValue); | |
324 | uint8_t buf[251] = {0}; | |
325 | size_t buflen = 0; | |
326 | ||
327 | // here max length must be 4, but now tlv_tag_t is 2-byte var. so let it be 2 by now... TODO: needs refactoring tlv_tag_t... | |
328 | if (!HexToBuffer("TLV Error type:", tlvTag, buf, 2, &buflen)) { | |
329 | json_decref(root); | |
330 | return false; | |
331 | } | |
332 | tlv_tag_t tag = 0; | |
333 | for (int i = 0; i < buflen; i++) { | |
334 | tag = (tag << 8) + buf[i]; | |
335 | } | |
336 | ||
337 | if (!HexToBuffer("TLV Error value:", tlvValue, buf, sizeof(buf) - 1, &buflen)) { | |
338 | json_decref(root); | |
339 | return false; | |
340 | } | |
341 | ||
342 | if (buflen != tlvLength) { | |
343 | PrintAndLog("Load params: data [%d] length of HEX must(%d) be identical to length in TLV param(%d)", i + 1, buflen, tlvLength); | |
344 | json_decref(root); | |
345 | return false; | |
346 | } | |
347 | ||
348 | tlvdb_change_or_add_node(tlv, tag, tlvLength, (const unsigned char *)buf); | |
349 | } | |
350 | ||
351 | json_decref(root); | |
352 | ||
353 | return true; | |
354 | } | |
355 |