93635f858abe0c2759972db1cc7f88c3642c7eb1
[proxmark3-svn] / client / emv / cmdemv.c
1 //-----------------------------------------------------------------------------
2 // Copyright (C) 2017, 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 commands
9 //-----------------------------------------------------------------------------
10
11 #include <ctype.h>
12 #include "cmdemv.h"
13 #include "test/cryptotest.h"
14 #include "cliparser/cliparser.h"
15 #include <jansson.h>
16
17 #define TLV_ADD(tag, value)( tlvdb_change_or_add_node(tlvRoot, tag, sizeof(value) - 1, (const unsigned char *)value) )
18 void ParamLoadDefaults(struct tlvdb *tlvRoot) {
19 //9F02:(Amount, authorized (Numeric)) len:6
20 TLV_ADD(0x9F02, "\x00\x00\x00\x00\x01\x00");
21 //9F1A:(Terminal Country Code) len:2
22 TLV_ADD(0x9F1A, "ru");
23 //5F2A:(Transaction Currency Code) len:2
24 // USD 840, EUR 978, RUR 810, RUB 643, RUR 810(old), UAH 980, AZN 031, n/a 999
25 TLV_ADD(0x5F2A, "\x09\x80");
26 //9A:(Transaction Date) len:3
27 TLV_ADD(0x9A, "\x00\x00\x00");
28 //9C:(Transaction Type) len:1 | 00 => Goods and service #01 => Cash
29 TLV_ADD(0x9C, "\x00");
30 // 9F37 Unpredictable Number len:4
31 TLV_ADD(0x9F37, "\x01\x02\x03\x04");
32 // 9F6A Unpredictable Number (MSD for UDOL) len:4
33 TLV_ADD(0x9F6A, "\x01\x02\x03\x04");
34 //9F66:(Terminal Transaction Qualifiers (TTQ)) len:4
35 TLV_ADD(0x9F66, "\x26\x00\x00\x00"); // qVSDC
36 }
37
38 int CmdHFEMVSelect(const char *cmd) {
39 uint8_t data[APDU_AID_LEN] = {0};
40 int datalen = 0;
41
42 CLIParserInit("hf emv select",
43 "Executes select applet command",
44 "Usage:\n\thf emv select -s a00000000101 -> select card, select applet\n\thf emv select -st a00000000101 -> select card, select applet, show result in TLV\n");
45
46 void* argtable[] = {
47 arg_param_begin,
48 arg_lit0("sS", "select", "activate field and select card"),
49 arg_lit0("kK", "keep", "keep field for next command"),
50 arg_lit0("aA", "apdu", "show APDU reqests and responses"),
51 arg_lit0("tT", "tlv", "TLV decode results"),
52 arg_str0(NULL, NULL, "<HEX applet AID>", NULL),
53 arg_param_end
54 };
55 CLIExecWithReturn(cmd, argtable, true);
56
57 bool activateField = arg_get_lit(1);
58 bool leaveSignalON = arg_get_lit(2);
59 bool APDULogging = arg_get_lit(3);
60 bool decodeTLV = arg_get_lit(4);
61 CLIGetStrWithReturn(5, data, &datalen);
62 CLIParserFree();
63
64 SetAPDULogging(APDULogging);
65
66 // exec
67 uint8_t buf[APDU_RES_LEN] = {0};
68 size_t len = 0;
69 uint16_t sw = 0;
70 int res = EMVSelect(activateField, leaveSignalON, data, datalen, buf, sizeof(buf), &len, &sw, NULL);
71
72 if (sw)
73 PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
74
75 if (res)
76 return res;
77
78 if (decodeTLV)
79 TLVPrintFromBuffer(buf, len);
80
81 return 0;
82 }
83
84 int CmdHFEMVSearch(const char *cmd) {
85
86 CLIParserInit("hf emv search",
87 "Tries to select all applets from applet list:\n",
88 "Usage:\n\thf emv search -s -> select card and search\n\thf emv search -st -> select card, search and show result in TLV\n");
89
90 void* argtable[] = {
91 arg_param_begin,
92 arg_lit0("sS", "select", "activate field and select card"),
93 arg_lit0("kK", "keep", "keep field ON for next command"),
94 arg_lit0("aA", "apdu", "show APDU reqests and responses"),
95 arg_lit0("tT", "tlv", "TLV decode results of selected applets"),
96 arg_param_end
97 };
98 CLIExecWithReturn(cmd, argtable, true);
99
100 bool activateField = arg_get_lit(1);
101 bool leaveSignalON = arg_get_lit(2);
102 bool APDULogging = arg_get_lit(3);
103 bool decodeTLV = arg_get_lit(4);
104 CLIParserFree();
105
106 SetAPDULogging(APDULogging);
107
108 struct tlvdb *t = NULL;
109 const char *al = "Applets list";
110 t = tlvdb_fixed(1, strlen(al), (const unsigned char *)al);
111
112 if (EMVSearch(activateField, leaveSignalON, decodeTLV, t)) {
113 tlvdb_free(t);
114 return 2;
115 }
116
117 PrintAndLog("Search completed.");
118
119 // print list here
120 if (!decodeTLV) {
121 TLVPrintAIDlistFromSelectTLV(t);
122 }
123
124 tlvdb_free(t);
125
126 return 0;
127 }
128
129 int CmdHFEMVPPSE(const char *cmd) {
130
131 CLIParserInit("hf emv pse",
132 "Executes PSE/PPSE select command. It returns list of applet on the card:\n",
133 "Usage:\n\thf emv pse -s1 -> select, get pse\n\thf emv pse -st2 -> select, get ppse, show result in TLV\n");
134
135 void* argtable[] = {
136 arg_param_begin,
137 arg_lit0("sS", "select", "activate field and select card"),
138 arg_lit0("kK", "keep", "keep field ON for next command"),
139 arg_lit0("1", "pse", "pse (1PAY.SYS.DDF01) mode"),
140 arg_lit0("2", "ppse", "ppse (2PAY.SYS.DDF01) mode (default mode)"),
141 arg_lit0("aA", "apdu", "show APDU reqests and responses"),
142 arg_lit0("tT", "tlv", "TLV decode results of selected applets"),
143 arg_param_end
144 };
145 CLIExecWithReturn(cmd, argtable, true);
146
147 bool activateField = arg_get_lit(1);
148 bool leaveSignalON = arg_get_lit(2);
149 uint8_t PSENum = 2;
150 if (arg_get_lit(3))
151 PSENum = 1;
152 if (arg_get_lit(4))
153 PSENum = 2;
154 bool APDULogging = arg_get_lit(5);
155 bool decodeTLV = arg_get_lit(6);
156 CLIParserFree();
157
158 SetAPDULogging(APDULogging);
159
160 // exec
161 uint8_t buf[APDU_RES_LEN] = {0};
162 size_t len = 0;
163 uint16_t sw = 0;
164 int res = EMVSelectPSE(activateField, leaveSignalON, PSENum, buf, sizeof(buf), &len, &sw);
165
166 if (sw)
167 PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
168
169 if (res)
170 return res;
171
172
173 if (decodeTLV)
174 TLVPrintFromBuffer(buf, len);
175
176 return 0;
177 }
178
179 int CmdHFEMVGPO(const char *cmd) {
180 uint8_t data[APDU_RES_LEN] = {0};
181 int datalen = 0;
182
183 CLIParserInit("hf emv gpo",
184 "Executes Get Processing Options command. It returns data in TLV format (0x77 - format2) or plain format (0x80 - format1).\nNeeds a EMV applet to be selected.",
185 "Usage:\n\thf emv gpo -k -> execute GPO\n"
186 "\thf emv gpo -st 01020304 -> execute GPO with 4-byte PDOL data, show result in TLV\n");
187 // here need to add load params from file and gen pdol
188
189 void* argtable[] = {
190 arg_param_begin,
191 arg_lit0("kK", "keep", "keep field ON for next command"),
192 arg_lit0("pP", "params", "load parameters for PDOL making from `emv/defparams.json` file (by default uses default parameters) (NOT WORK!!!)"),
193 arg_lit0("mM", "make", "make PDOLdata from PDOL (tag 9F38) and parameters (NOT WORK!!!)"),
194 arg_lit0("aA", "apdu", "show APDU reqests and responses"),
195 arg_lit0("tT", "tlv", "TLV decode results of selected applets"),
196 arg_str0(NULL, NULL, "<HEX PDOLdata/PDOL>", NULL),
197 arg_param_end
198 };
199 CLIExecWithReturn(cmd, argtable, true);
200
201 bool leaveSignalON = arg_get_lit(1);
202 bool paramsLoadFromFile = arg_get_lit(2);
203 bool dataMakeFromPDOL = arg_get_lit(3);
204 bool APDULogging = arg_get_lit(4);
205 bool decodeTLV = arg_get_lit(5);
206 CLIGetStrWithReturn(6, data, &datalen);
207 CLIParserFree();
208
209 SetAPDULogging(APDULogging);
210
211 // Init TLV tree
212 const char *alr = "Root terminal TLV tree";
213 struct tlvdb *tlvRoot = tlvdb_fixed(1, strlen(alr), (const unsigned char *)alr);
214
215 // calc PDOL
216 struct tlv *pdol_data_tlv = NULL;
217 struct tlv data_tlv = {
218 .tag = 0x01,
219 .len = datalen,
220 .value = (uint8_t *)data,
221 };
222 if (dataMakeFromPDOL) {
223 // TODO
224 PrintAndLog("Make PDOL data not implemented!");
225
226 ParamLoadDefaults(tlvRoot);
227
228 if (paramsLoadFromFile) {
229 };
230 /* pdol_data_tlv = dol_process(tlvdb_get(tlvRoot, 0x9f38, NULL), tlvRoot, 0x83);
231 if (!pdol_data_tlv){
232 PrintAndLog("ERROR: can't create PDOL TLV.");
233 tlvdb_free(tlvRoot);
234 return 4;
235 }*/
236 return 0;
237 } else {
238 pdol_data_tlv = &data_tlv;
239 }
240
241 size_t pdol_data_tlv_data_len = 0;
242 unsigned char *pdol_data_tlv_data = tlv_encode(pdol_data_tlv, &pdol_data_tlv_data_len);
243 if (!pdol_data_tlv_data) {
244 PrintAndLog("ERROR: can't create PDOL data.");
245 tlvdb_free(tlvRoot);
246 return 4;
247 }
248 PrintAndLog("PDOL data[%d]: %s", pdol_data_tlv_data_len, sprint_hex(pdol_data_tlv_data, pdol_data_tlv_data_len));
249
250 // exec
251 uint8_t buf[APDU_RES_LEN] = {0};
252 size_t len = 0;
253 uint16_t sw = 0;
254 int res = EMVGPO(leaveSignalON, pdol_data_tlv_data, pdol_data_tlv_data_len, buf, sizeof(buf), &len, &sw, tlvRoot);
255
256 free(pdol_data_tlv_data);
257 tlvdb_free(tlvRoot);
258
259 if (sw)
260 PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
261
262 if (res)
263 return res;
264
265 if (decodeTLV)
266 TLVPrintFromBuffer(buf, len);
267
268 return 0;
269 }
270
271 int CmdHFEMVReadRecord(const char *cmd) {
272 uint8_t data[APDU_RES_LEN] = {0};
273 int datalen = 0;
274
275 CLIParserInit("hf emv readrec",
276 "Executes Read Record command. It returns data in TLV format.\nNeeds a bank applet to be selected and sometimes needs GPO to be executed.",
277 "Usage:\n\thf emv readrec -k 0101 -> read file SFI=01, SFIrec=01\n\thf emv readrec -kt 0201-> read file 0201 and show result in TLV\n");
278
279 void* argtable[] = {
280 arg_param_begin,
281 arg_lit0("kK", "keep", "keep field ON for next command"),
282 arg_lit0("aA", "apdu", "show APDU reqests and responses"),
283 arg_lit0("tT", "tlv", "TLV decode results of selected applets"),
284 arg_str1(NULL, NULL, "<SFI 1byte HEX><SFIrec 1byte HEX>", NULL),
285 arg_param_end
286 };
287 CLIExecWithReturn(cmd, argtable, true);
288
289 bool leaveSignalON = arg_get_lit(1);
290 bool APDULogging = arg_get_lit(2);
291 bool decodeTLV = arg_get_lit(3);
292 CLIGetStrWithReturn(4, data, &datalen);
293 CLIParserFree();
294
295 if (datalen != 2) {
296 PrintAndLog("ERROR: Command needs to have 2 bytes of data");
297 return 1;
298 }
299
300 SetAPDULogging(APDULogging);
301
302 // exec
303 uint8_t buf[APDU_RES_LEN] = {0};
304 size_t len = 0;
305 uint16_t sw = 0;
306 int res = EMVReadRecord(leaveSignalON, data[0], data[1], buf, sizeof(buf), &len, &sw, NULL);
307
308 if (sw)
309 PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
310
311 if (res)
312 return res;
313
314
315 if (decodeTLV)
316 TLVPrintFromBuffer(buf, len);
317
318 return 0;
319 }
320
321 int CmdHFEMVAC(const char *cmd) {
322 uint8_t data[APDU_RES_LEN] = {0};
323 int datalen = 0;
324
325 CLIParserInit("hf emv genac",
326 "Generate Application Cryptogram command. It returns data in TLV format .\nNeeds a EMV applet to be selected and GPO to be executed.",
327 "Usage:\n\thf emv genac -k 0102 -> generate AC with 2-byte CDOLdata and keep field ON after command\n"
328 "\thf emv genac -t 01020304 -> generate AC with 4-byte CDOL data, show result in TLV\n"
329 "\thf emv genac -Daac 01020304 -> generate AC with 4-byte CDOL data and terminal decision 'declined'\n");
330
331 void* argtable[] = {
332 arg_param_begin,
333 arg_lit0("kK", "keep", "keep field ON for next command"),
334 arg_lit0("cC", "cda", "executes CDA transaction. Needs to get SDAD in results."),
335 arg_str0("dD", "decision", "<aac|tc|arqc>", "Terminal decision. aac - declined, tc - approved, arqc - online authorisation requested"),
336 arg_lit0("aA", "apdu", "show APDU reqests and responses"),
337 arg_lit0("tT", "tlv", "TLV decode results of selected applets"),
338 arg_str1(NULL, NULL, "<HEX CDOLdata>", NULL),
339 arg_param_end
340 };
341 CLIExecWithReturn(cmd, argtable, false);
342
343 bool leaveSignalON = arg_get_lit(1);
344 bool trTypeCDA = arg_get_lit(2);
345 uint8_t termDecision = 0xff;
346 if (arg_get_str_len(3)) {
347 if (!strncmp(arg_get_str(3)->sval[0], "aac", 4))
348 termDecision = EMVAC_AAC;
349 if (!strncmp(arg_get_str(3)->sval[0], "tc", 4))
350 termDecision = EMVAC_TC;
351 if (!strncmp(arg_get_str(3)->sval[0], "arqc", 4))
352 termDecision = EMVAC_ARQC;
353
354 if (termDecision == 0xff) {
355 PrintAndLog("ERROR: can't find terminal decision '%s'", arg_get_str(3)->sval[0]);
356 return 1;
357 }
358 } else {
359 termDecision = EMVAC_TC;
360 }
361 if (trTypeCDA)
362 termDecision = termDecision | EMVAC_CDAREQ;
363 bool APDULogging = arg_get_lit(4);
364 bool decodeTLV = arg_get_lit(5);
365 CLIGetStrWithReturn(6, data, &datalen);
366 CLIParserFree();
367
368 SetAPDULogging(APDULogging);
369
370 // Init TLV tree
371 const char *alr = "Root terminal TLV tree";
372 struct tlvdb *tlvRoot = tlvdb_fixed(1, strlen(alr), (const unsigned char *)alr);
373
374 // calc CDOL
375 struct tlv *cdol_data_tlv = NULL;
376 // struct tlv *cdol_data_tlv = dol_process(tlvdb_get(tlvRoot, 0x8c, NULL), tlvRoot, 0x01); // 0x01 - dummy tag
377 struct tlv data_tlv = {
378 .tag = 0x01,
379 .len = datalen,
380 .value = (uint8_t *)data,
381 };
382 cdol_data_tlv = &data_tlv;
383 PrintAndLog("CDOL data[%d]: %s", cdol_data_tlv->len, sprint_hex(cdol_data_tlv->value, cdol_data_tlv->len));
384
385 // exec
386 uint8_t buf[APDU_RES_LEN] = {0};
387 size_t len = 0;
388 uint16_t sw = 0;
389 int res = EMVAC(leaveSignalON, termDecision, (uint8_t *)cdol_data_tlv->value, cdol_data_tlv->len, buf, sizeof(buf), &len, &sw, tlvRoot);
390
391 // free(cdol_data_tlv);
392 tlvdb_free(tlvRoot);
393
394 if (sw)
395 PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
396
397 if (res)
398 return res;
399
400 if (decodeTLV)
401 TLVPrintFromBuffer(buf, len);
402
403 return 0;
404 }
405
406 int CmdHFEMVGenerateChallenge(const char *cmd) {
407
408 CLIParserInit("hf emv challenge",
409 "Executes Generate Challenge command. It returns 4 or 8-byte random number from card.\nNeeds a EMV applet to be selected and GPO to be executed.",
410 "Usage:\n\thf emv challenge -> get challenge\n\thf emv challenge -k -> get challenge, keep fileld ON\n");
411
412 void* argtable[] = {
413 arg_param_begin,
414 arg_lit0("kK", "keep", "keep field ON for next command"),
415 arg_lit0("aA", "apdu", "show APDU reqests and responses"),
416 arg_param_end
417 };
418 CLIExecWithReturn(cmd, argtable, true);
419
420 bool leaveSignalON = arg_get_lit(1);
421 bool APDULogging = arg_get_lit(2);
422 CLIParserFree();
423
424 SetAPDULogging(APDULogging);
425
426 // exec
427 uint8_t buf[APDU_RES_LEN] = {0};
428 size_t len = 0;
429 uint16_t sw = 0;
430 int res = EMVGenerateChallenge(leaveSignalON, buf, sizeof(buf), &len, &sw, NULL);
431
432 if (sw)
433 PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
434
435 if (res)
436 return res;
437
438 PrintAndLog("Challenge: %s", sprint_hex(buf, len));
439
440 if (len != 4 && len != 8)
441 PrintAndLog("WARNING: length of challenge must be 4 or 8, but it %d", len);
442
443 return 0;
444 }
445
446 int CmdHFEMVInternalAuthenticate(const char *cmd) {
447 uint8_t data[APDU_RES_LEN] = {0};
448 int datalen = 0;
449
450 CLIParserInit("hf emv intauth",
451 "Generate Internal Authenticate command. Usually needs 4-byte random number. It returns data in TLV format .\nNeeds a EMV applet to be selected and GPO to be executed.",
452 "Usage:\n\thf emv intauth -k 01020304 -> execute Internal Authenticate with 4-byte DDOLdata and keep field ON after command\n"
453 "\thf emv intauth -t 01020304 -> execute Internal Authenticate with 4-byte DDOL data, show result in TLV\n");
454
455 void* argtable[] = {
456 arg_param_begin,
457 arg_lit0("kK", "keep", "keep field ON for next command"),
458 arg_lit0("aA", "apdu", "show APDU reqests and responses"),
459 arg_lit0("tT", "tlv", "TLV decode results of selected applets"),
460 arg_str1(NULL, NULL, "<HEX DDOLdata>", NULL),
461 arg_param_end
462 };
463 CLIExecWithReturn(cmd, argtable, false);
464
465 bool leaveSignalON = arg_get_lit(1);
466 bool APDULogging = arg_get_lit(2);
467 bool decodeTLV = arg_get_lit(3);
468 CLIGetStrWithReturn(4, data, &datalen);
469 CLIParserFree();
470
471 SetAPDULogging(APDULogging);
472
473 // DDOL
474 PrintAndLog("DDOL data[%d]: %s", datalen, sprint_hex(data, datalen));
475
476 // exec
477 uint8_t buf[APDU_RES_LEN] = {0};
478 size_t len = 0;
479 uint16_t sw = 0;
480 int res = EMVInternalAuthenticate(leaveSignalON, data, datalen, buf, sizeof(buf), &len, &sw, NULL);
481
482 if (sw)
483 PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
484
485 if (res)
486 return res;
487
488 if (decodeTLV)
489 TLVPrintFromBuffer(buf, len);
490
491 return 0;
492 }
493
494 #define dreturn(n) {free(pdol_data_tlv);tlvdb_free(tlvSelect);tlvdb_free(tlvRoot);DropField();return n;}
495
496 bool HexToBuffer(const char *errormsg, const char *hexvalue, uint8_t * buffer, size_t maxbufferlen, size_t *bufferlen) {
497 int buflen = 0;
498
499 switch(param_gethex_to_eol(hexvalue, 0, buffer, maxbufferlen, &buflen)) {
500 case 1:
501 PrintAndLog("%s Invalid HEX value.", errormsg);
502 return false;
503 case 2:
504 PrintAndLog("%s Hex value too large.", errormsg);
505 return false;
506 case 3:
507 PrintAndLog("%s Hex value must have even number of digits.", errormsg);
508 return false;
509 }
510
511 if (buflen > maxbufferlen) {
512 PrintAndLog("%s HEX length (%d) more than %d", errormsg, *bufferlen, maxbufferlen);
513 return false;
514 }
515
516 *bufferlen = buflen;
517
518 return true;
519 }
520
521 bool ParamLoadFromJson(struct tlvdb *tlv) {
522 json_t *root;
523 json_error_t error;
524
525 if (!tlv) {
526 PrintAndLog("ERROR load params: tlv tree is NULL.");
527 return false;
528 }
529
530 // current path + file name
531 const char *relfname = "emv/defparams.json";
532 char fname[strlen(get_my_executable_directory()) + strlen(relfname) + 1];
533 strcpy(fname, get_my_executable_directory());
534 strcat(fname, relfname);
535
536 root = json_load_file(fname, 0, &error);
537 if (!root) {
538 PrintAndLog("Load params: json error on line %d: %s", error.line, error.text);
539 return false;
540 }
541
542 if (!json_is_array(root)) {
543 PrintAndLog("Load params: Invalid json format. root must be array.");
544 return false;
545 }
546
547 PrintAndLog("Load params: json OK");
548
549 for(int i = 0; i < json_array_size(root); i++) {
550 json_t *data, *jtype, *jlength, *jvalue;
551
552 data = json_array_get(root, i);
553 if(!json_is_object(data))
554 {
555 PrintAndLog("Load params: data [%d] is not an object", i + 1);
556 json_decref(root);
557 return false;
558 }
559
560 jtype = json_object_get(data, "type");
561 if(!json_is_string(jtype))
562 {
563 PrintAndLog("Load params: data [%d] type is not a string", i + 1);
564 json_decref(root);
565 return false;
566 }
567 const char *tlvType = json_string_value(jtype);
568
569 jvalue = json_object_get(data, "value");
570 if(!json_is_string(jvalue))
571 {
572 PrintAndLog("Load params: data [%d] value is not a string", i + 1);
573 json_decref(root);
574 return false;
575 }
576 const char *tlvValue = json_string_value(jvalue);
577
578 jlength = json_object_get(data, "length");
579 if(!json_is_number(jlength))
580 {
581 PrintAndLog("Load params: data [%d] length is not a number", i + 1);
582 json_decref(root);
583 return false;
584 }
585
586 int tlvLength = json_integer_value(jlength);
587 if (tlvLength > 250) {
588 PrintAndLog("Load params: data [%d] length more than 250", i + 1);
589 json_decref(root);
590 return false;
591 }
592
593 PrintAndLog("TLV param: %s[%d]=%s", tlvType, tlvLength, tlvValue);
594 uint8_t buf[251] = {0};
595 size_t buflen = 0;
596
597 // 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...
598 if (!HexToBuffer("TLV Error type:", tlvType, buf, 2, &buflen)) {
599 json_decref(root);
600 return false;
601 }
602 tlv_tag_t tag = 0;
603 for (int i = 0; i < buflen; i++) {
604 tag = (tag << 8) + buf[i];
605 }
606
607 if (!HexToBuffer("TLV Error value:", tlvValue, buf, sizeof(buf) - 1, &buflen)) {
608 json_decref(root);
609 return false;
610 }
611
612 if (buflen != tlvLength) {
613 PrintAndLog("Load params: data [%d] length of HEX must(%d) be identical to length in TLV param(%d)", i + 1, buflen, tlvLength);
614 json_decref(root);
615 return false;
616 }
617
618 tlvdb_change_or_add_node(tlv, tag, tlvLength, (const unsigned char *)buf);
619 }
620
621 json_decref(root);
622
623 return true;
624 }
625
626 int CmdHFEMVExec(const char *cmd) {
627 uint8_t buf[APDU_RES_LEN] = {0};
628 size_t len = 0;
629 uint16_t sw = 0;
630 uint8_t AID[APDU_AID_LEN] = {0};
631 size_t AIDlen = 0;
632 uint8_t ODAiList[4096];
633 size_t ODAiListLen = 0;
634
635 int res;
636
637 struct tlvdb *tlvSelect = NULL;
638 struct tlvdb *tlvRoot = NULL;
639 struct tlv *pdol_data_tlv = NULL;
640
641 CLIParserInit("hf emv exec",
642 "Executes EMV contactless transaction",
643 "Usage:\n\thf emv exec -sat -> select card, execute MSD transaction, show APDU and TLV\n"
644 "\thf emv exec -satc -> select card, execute CDA transaction, show APDU and TLV\n");
645
646 void* argtable[] = {
647 arg_param_begin,
648 arg_lit0("sS", "select", "activate field and select card."),
649 arg_lit0("aA", "apdu", "show APDU reqests and responses."),
650 arg_lit0("tT", "tlv", "TLV decode results."),
651 arg_lit0("jJ", "jload", "Load transaction parameters from `emv/defparams.json` file."),
652 arg_lit0("fF", "forceaid", "Force search AID. Search AID instead of execute PPSE."),
653 arg_rem("By default:", "Transaction type - MSD"),
654 arg_lit0("vV", "qvsdc", "Transaction type - qVSDC or M/Chip."),
655 arg_lit0("cC", "qvsdccda", "Transaction type - qVSDC or M/Chip plus CDA (SDAD generation)."),
656 arg_lit0("xX", "vsdc", "Transaction type - VSDC. For test only. Not a standart behavior."),
657 arg_lit0("gG", "acgpo", "VISA. generate AC from GPO."),
658 arg_param_end
659 };
660 CLIExecWithReturn(cmd, argtable, true);
661
662 bool activateField = arg_get_lit(1);
663 bool showAPDU = arg_get_lit(2);
664 bool decodeTLV = arg_get_lit(3);
665 bool paramLoadJSON = arg_get_lit(4);
666 bool forceSearch = arg_get_lit(5);
667
668 enum TransactionType TrType = TT_MSD;
669 if (arg_get_lit(6))
670 TrType = TT_QVSDCMCHIP;
671 if (arg_get_lit(7))
672 TrType = TT_CDA;
673 if (arg_get_lit(8))
674 TrType = TT_VSDC;
675
676 bool GenACGPO = arg_get_lit(9);
677 CLIParserFree();
678
679 SetAPDULogging(showAPDU);
680
681 // init applets list tree
682 const char *al = "Applets list";
683 tlvSelect = tlvdb_fixed(1, strlen(al), (const unsigned char *)al);
684
685 // Application Selection
686 // https://www.openscdp.org/scripts/tutorial/emv/applicationselection.html
687 if (!forceSearch) {
688 // PPSE
689 PrintAndLog("\n* PPSE.");
690 SetAPDULogging(showAPDU);
691 res = EMVSearchPSE(activateField, true, decodeTLV, tlvSelect);
692
693 // check PPSE and select application id
694 if (!res) {
695 TLVPrintAIDlistFromSelectTLV(tlvSelect);
696 EMVSelectApplication(tlvSelect, AID, &AIDlen);
697 }
698 }
699
700 // Search
701 if (!AIDlen) {
702 PrintAndLog("\n* Search AID in list.");
703 SetAPDULogging(false);
704 if (EMVSearch(activateField, true, decodeTLV, tlvSelect)) {
705 dreturn(2);
706 }
707
708 // check search and select application id
709 TLVPrintAIDlistFromSelectTLV(tlvSelect);
710 EMVSelectApplication(tlvSelect, AID, &AIDlen);
711 }
712
713 // Init TLV tree
714 const char *alr = "Root terminal TLV tree";
715 tlvRoot = tlvdb_fixed(1, strlen(alr), (const unsigned char *)alr);
716
717 // check if we found EMV application on card
718 if (!AIDlen) {
719 PrintAndLog("Can't select AID. EMV AID not found");
720 dreturn(2);
721 }
722
723 // Select
724 PrintAndLog("\n* Selecting AID:%s", sprint_hex_inrow(AID, AIDlen));
725 SetAPDULogging(showAPDU);
726 res = EMVSelect(false, true, AID, AIDlen, buf, sizeof(buf), &len, &sw, tlvRoot);
727
728 if (res) {
729 PrintAndLog("Can't select AID (%d). Exit...", res);
730 dreturn(3);
731 }
732
733 if (decodeTLV)
734 TLVPrintFromBuffer(buf, len);
735 PrintAndLog("* Selected.");
736
737 PrintAndLog("\n* Init transaction parameters.");
738
739 ParamLoadDefaults(tlvRoot);
740
741 if (paramLoadJSON) {
742 PrintAndLog("* * Transaction parameters loading from JSON...");
743 ParamLoadFromJson(tlvRoot);
744 }
745
746 //9F66:(Terminal Transaction Qualifiers (TTQ)) len:4
747 char *qVSDC = "\x26\x00\x00\x00";
748 if (GenACGPO) {
749 qVSDC = "\x26\x80\x00\x00";
750 }
751 switch(TrType) {
752 case TT_MSD:
753 TLV_ADD(0x9F66, "\x86\x00\x00\x00"); // MSD
754 break;
755 // not standard for contactless. just for test.
756 case TT_VSDC:
757 TLV_ADD(0x9F66, "\x46\x00\x00\x00"); // VSDC
758 break;
759 case TT_QVSDCMCHIP:
760 TLV_ADD(0x9F66, qVSDC); // qVSDC
761 break;
762 case TT_CDA:
763 TLV_ADD(0x9F66, qVSDC); // qVSDC (VISA CDA not enabled)
764 break;
765 default:
766 break;
767 }
768
769 TLVPrintFromTLV(tlvRoot); // TODO delete!!!
770
771 PrintAndLog("\n* Calc PDOL.");
772 pdol_data_tlv = dol_process(tlvdb_get(tlvRoot, 0x9f38, NULL), tlvRoot, 0x83);
773 if (!pdol_data_tlv){
774 PrintAndLog("ERROR: can't create PDOL TLV.");
775 dreturn(4);
776 }
777
778 size_t pdol_data_tlv_data_len;
779 unsigned char *pdol_data_tlv_data = tlv_encode(pdol_data_tlv, &pdol_data_tlv_data_len);
780 if (!pdol_data_tlv_data) {
781 PrintAndLog("ERROR: can't create PDOL data.");
782 dreturn(4);
783 }
784 PrintAndLog("PDOL data[%d]: %s", pdol_data_tlv_data_len, sprint_hex(pdol_data_tlv_data, pdol_data_tlv_data_len));
785
786 PrintAndLog("\n* GPO.");
787 res = EMVGPO(true, pdol_data_tlv_data, pdol_data_tlv_data_len, buf, sizeof(buf), &len, &sw, tlvRoot);
788
789 free(pdol_data_tlv_data);
790 //free(pdol_data_tlv); --- free on exit.
791
792 if (res) {
793 PrintAndLog("GPO error(%d): %4x. Exit...", res, sw);
794 dreturn(5);
795 }
796
797 // process response template format 1 [id:80 2b AIP + x4b AFL] and format 2 [id:77 TLV]
798 if (buf[0] == 0x80) {
799 if (decodeTLV){
800 PrintAndLog("GPO response format1:");
801 TLVPrintFromBuffer(buf, len);
802 }
803
804 if (len < 4 || (len - 4) % 4) {
805 PrintAndLog("ERROR: GPO response format1 parsing error. length=%d", len);
806 } else {
807 // AIP
808 struct tlvdb * f1AIP = tlvdb_fixed(0x82, 2, buf + 2);
809 tlvdb_add(tlvRoot, f1AIP);
810 if (decodeTLV){
811 PrintAndLog("\n* * Decode response format 1 (0x80) AIP and AFL:");
812 TLVPrintFromTLV(f1AIP);
813 }
814
815 // AFL
816 struct tlvdb * f1AFL = tlvdb_fixed(0x94, len - 4, buf + 2 + 2);
817 tlvdb_add(tlvRoot, f1AFL);
818 if (decodeTLV)
819 TLVPrintFromTLV(f1AFL);
820 }
821 } else {
822 if (decodeTLV)
823 TLVPrintFromBuffer(buf, len);
824 }
825
826 // extract PAN from track2
827 {
828 const struct tlv *track2 = tlvdb_get(tlvRoot, 0x57, NULL);
829 if (!tlvdb_get(tlvRoot, 0x5a, NULL) && track2 && track2->len >= 8) {
830 struct tlvdb *pan = GetPANFromTrack2(track2);
831 if (pan) {
832 tlvdb_add(tlvRoot, pan);
833
834 const struct tlv *pantlv = tlvdb_get(tlvRoot, 0x5a, NULL);
835 PrintAndLog("\n* * Extracted PAN from track2: %s", sprint_hex(pantlv->value, pantlv->len));
836 } else {
837 PrintAndLog("\n* * WARNING: Can't extract PAN from track2.");
838 }
839 }
840 }
841
842 PrintAndLog("\n* Read records from AFL.");
843 const struct tlv *AFL = tlvdb_get(tlvRoot, 0x94, NULL);
844 if (!AFL || !AFL->len) {
845 PrintAndLog("WARNING: AFL not found.");
846 }
847
848 while(AFL && AFL->len) {
849 if (AFL->len % 4) {
850 PrintAndLog("ERROR: Wrong AFL length: %d", AFL->len);
851 break;
852 }
853
854 for (int i = 0; i < AFL->len / 4; i++) {
855 uint8_t SFI = AFL->value[i * 4 + 0] >> 3;
856 uint8_t SFIstart = AFL->value[i * 4 + 1];
857 uint8_t SFIend = AFL->value[i * 4 + 2];
858 uint8_t SFIoffline = AFL->value[i * 4 + 3];
859
860 PrintAndLog("* * SFI[%02x] start:%02x end:%02x offline:%02x", SFI, SFIstart, SFIend, SFIoffline);
861 if (SFI == 0 || SFI == 31 || SFIstart == 0 || SFIstart > SFIend) {
862 PrintAndLog("SFI ERROR! Skipped...");
863 continue;
864 }
865
866 for(int n = SFIstart; n <= SFIend; n++) {
867 PrintAndLog("* * * SFI[%02x] %d", SFI, n);
868
869 res = EMVReadRecord(true, SFI, n, buf, sizeof(buf), &len, &sw, tlvRoot);
870 if (res) {
871 PrintAndLog("ERROR SFI[%02x]. APDU error %4x", SFI, sw);
872 continue;
873 }
874
875 if (decodeTLV) {
876 TLVPrintFromBuffer(buf, len);
877 PrintAndLog("");
878 }
879
880 // Build Input list for Offline Data Authentication
881 // EMV 4.3 book3 10.3, page 96
882 if (SFIoffline) {
883 if (SFI < 11) {
884 const unsigned char *abuf = buf;
885 size_t elmlen = len;
886 struct tlv e;
887 if (tlv_parse_tl(&abuf, &elmlen, &e)) {
888 memcpy(&ODAiList[ODAiListLen], &buf[len - elmlen], elmlen);
889 ODAiListLen += elmlen;
890 } else {
891 PrintAndLog("ERROR SFI[%02x]. Creating input list for Offline Data Authentication error.", SFI);
892 }
893 } else {
894 memcpy(&ODAiList[ODAiListLen], buf, len);
895 ODAiListLen += len;
896 }
897 }
898 }
899 }
900
901 break;
902 }
903
904 // copy Input list for Offline Data Authentication
905 if (ODAiListLen) {
906 struct tlvdb *oda = tlvdb_fixed(0x21, ODAiListLen, ODAiList); // not a standard tag
907 tlvdb_add(tlvRoot, oda);
908 PrintAndLog("* Input list for Offline Data Authentication added to TLV. len=%d \n", ODAiListLen);
909 }
910
911 // get AIP
912 const struct tlv *AIPtlv = tlvdb_get(tlvRoot, 0x82, NULL);
913 uint16_t AIP = AIPtlv->value[0] + AIPtlv->value[1] * 0x100;
914 PrintAndLog("* * AIP=%04x", AIP);
915
916 // SDA
917 if (AIP & 0x0040) {
918 PrintAndLog("\n* SDA");
919 trSDA(tlvRoot);
920 }
921
922 // DDA
923 if (AIP & 0x0020) {
924 PrintAndLog("\n* DDA");
925 trDDA(decodeTLV, tlvRoot);
926 }
927
928 // transaction check
929
930 // qVSDC
931 if (TrType == TT_QVSDCMCHIP|| TrType == TT_CDA){
932 // 9F26: Application Cryptogram
933 const struct tlv *AC = tlvdb_get(tlvRoot, 0x9F26, NULL);
934 if (AC) {
935 PrintAndLog("\n--> qVSDC transaction.");
936 PrintAndLog("* AC path");
937
938 // 9F36: Application Transaction Counter (ATC)
939 const struct tlv *ATC = tlvdb_get(tlvRoot, 0x9F36, NULL);
940 if (ATC) {
941
942 // 9F10: Issuer Application Data - optional
943 const struct tlv *IAD = tlvdb_get(tlvRoot, 0x9F10, NULL);
944
945 // print AC data
946 PrintAndLog("ATC: %s", sprint_hex(ATC->value, ATC->len));
947 PrintAndLog("AC: %s", sprint_hex(AC->value, AC->len));
948 if (IAD){
949 PrintAndLog("IAD: %s", sprint_hex(IAD->value, IAD->len));
950
951 if (IAD->len >= IAD->value[0] + 1) {
952 PrintAndLog("\tKey index: 0x%02x", IAD->value[1]);
953 PrintAndLog("\tCrypto ver: 0x%02x(%03d)", IAD->value[2], IAD->value[2]);
954 PrintAndLog("\tCVR:", sprint_hex(&IAD->value[3], IAD->value[0] - 2));
955 struct tlvdb * cvr = tlvdb_fixed(0x20, IAD->value[0] - 2, &IAD->value[3]);
956 TLVPrintFromTLVLev(cvr, 1);
957 }
958 } else {
959 PrintAndLog("WARNING: IAD not found.");
960 }
961
962 } else {
963 PrintAndLog("ERROR AC: Application Transaction Counter (ATC) not found.");
964 }
965 }
966 }
967
968 // Mastercard M/CHIP
969 if (GetCardPSVendor(AID, AIDlen) == CV_MASTERCARD && (TrType == TT_QVSDCMCHIP || TrType == TT_CDA)){
970 const struct tlv *CDOL1 = tlvdb_get(tlvRoot, 0x8c, NULL);
971 if (CDOL1 && GetCardPSVendor(AID, AIDlen) == CV_MASTERCARD) { // and m/chip transaction flag
972 PrintAndLog("\n--> Mastercard M/Chip transaction.");
973
974 PrintAndLog("* * Generate challenge");
975 res = EMVGenerateChallenge(true, buf, sizeof(buf), &len, &sw, tlvRoot);
976 if (res) {
977 PrintAndLog("ERROR GetChallenge. APDU error %4x", sw);
978 dreturn(6);
979 }
980 if (len < 4) {
981 PrintAndLog("ERROR GetChallenge. Wrong challenge length %d", len);
982 dreturn(6);
983 }
984
985 // ICC Dynamic Number
986 struct tlvdb * ICCDynN = tlvdb_fixed(0x9f4c, len, buf);
987 tlvdb_add(tlvRoot, ICCDynN);
988 if (decodeTLV){
989 PrintAndLog("\n* * ICC Dynamic Number:");
990 TLVPrintFromTLV(ICCDynN);
991 }
992
993 PrintAndLog("* * Calc CDOL1");
994 struct tlv *cdol_data_tlv = dol_process(tlvdb_get(tlvRoot, 0x8c, NULL), tlvRoot, 0x01); // 0x01 - dummy tag
995 if (!cdol_data_tlv){
996 PrintAndLog("ERROR: can't create CDOL1 TLV.");
997 dreturn(6);
998 }
999 PrintAndLog("CDOL1 data[%d]: %s", cdol_data_tlv->len, sprint_hex(cdol_data_tlv->value, cdol_data_tlv->len));
1000
1001 PrintAndLog("* * AC1");
1002 // EMVAC_TC + EMVAC_CDAREQ --- to get SDAD
1003 res = EMVAC(true, (TrType == TT_CDA) ? EMVAC_TC + EMVAC_CDAREQ : EMVAC_TC, (uint8_t *)cdol_data_tlv->value, cdol_data_tlv->len, buf, sizeof(buf), &len, &sw, tlvRoot);
1004
1005 if (res) {
1006 PrintAndLog("AC1 error(%d): %4x. Exit...", res, sw);
1007 dreturn(7);
1008 }
1009
1010 if (decodeTLV)
1011 TLVPrintFromBuffer(buf, len);
1012
1013 // CDA
1014 PrintAndLog("\n* CDA:");
1015 struct tlvdb *ac_tlv = tlvdb_parse_multi(buf, len);
1016 res = trCDA(tlvRoot, ac_tlv, pdol_data_tlv, cdol_data_tlv);
1017 if (res) {
1018 PrintAndLog("CDA error (%d)", res);
1019 }
1020 free(ac_tlv);
1021 free(cdol_data_tlv);
1022
1023 PrintAndLog("\n* M/Chip transaction result:");
1024 // 9F27: Cryptogram Information Data (CID)
1025 const struct tlv *CID = tlvdb_get(tlvRoot, 0x9F27, NULL);
1026 if (CID) {
1027 emv_tag_dump(CID, stdout, 0);
1028 PrintAndLog("------------------------------");
1029 if (CID->len > 0) {
1030 switch(CID->value[0] & EMVAC_AC_MASK){
1031 case EMVAC_AAC:
1032 PrintAndLog("Transaction DECLINED.");
1033 break;
1034 case EMVAC_TC:
1035 PrintAndLog("Transaction approved OFFLINE.");
1036 break;
1037 case EMVAC_ARQC:
1038 PrintAndLog("Transaction approved ONLINE.");
1039 break;
1040 default:
1041 PrintAndLog("ERROR: CID transaction code error %2x", CID->value[0] & EMVAC_AC_MASK);
1042 break;
1043 }
1044 } else {
1045 PrintAndLog("ERROR: Wrong CID length %d", CID->len);
1046 }
1047 } else {
1048 PrintAndLog("ERROR: CID(9F27) not found.");
1049 }
1050
1051 }
1052 }
1053
1054 // MSD
1055 if (AIP & 0x8000 && TrType == TT_MSD) {
1056 PrintAndLog("\n--> MSD transaction.");
1057
1058 PrintAndLog("* MSD dCVV path. Check dCVV");
1059
1060 const struct tlv *track2 = tlvdb_get(tlvRoot, 0x57, NULL);
1061 if (track2) {
1062 PrintAndLog("Track2: %s", sprint_hex(track2->value, track2->len));
1063
1064 struct tlvdb *dCVV = GetdCVVRawFromTrack2(track2);
1065 PrintAndLog("dCVV raw data:");
1066 TLVPrintFromTLV(dCVV);
1067
1068 if (GetCardPSVendor(AID, AIDlen) == CV_MASTERCARD) {
1069 PrintAndLog("\n* Mastercard calculate UDOL");
1070
1071 // UDOL (9F69)
1072 const struct tlv *UDOL = tlvdb_get(tlvRoot, 0x9F69, NULL);
1073 // UDOL(9F69) default: 9F6A (Unpredictable number) 4 bytes
1074 const struct tlv defUDOL = {
1075 .tag = 0x01,
1076 .len = 3,
1077 .value = (uint8_t *)"\x9f\x6a\x04",
1078 };
1079 if (!UDOL)
1080 PrintAndLog("Use default UDOL.");
1081
1082 struct tlv *udol_data_tlv = dol_process(UDOL ? UDOL : &defUDOL, tlvRoot, 0x01); // 0x01 - dummy tag
1083 if (!udol_data_tlv){
1084 PrintAndLog("ERROR: can't create UDOL TLV.");
1085 dreturn(8);
1086 }
1087
1088 PrintAndLog("UDOL data[%d]: %s", udol_data_tlv->len, sprint_hex(udol_data_tlv->value, udol_data_tlv->len));
1089
1090 PrintAndLog("\n* Mastercard compute cryptographic checksum(UDOL)");
1091
1092 res = MSCComputeCryptoChecksum(true, (uint8_t *)udol_data_tlv->value, udol_data_tlv->len, buf, sizeof(buf), &len, &sw, tlvRoot);
1093 if (res) {
1094 PrintAndLog("ERROR Compute Crypto Checksum. APDU error %4x", sw);
1095 free(udol_data_tlv);
1096 dreturn(9);
1097 }
1098
1099 if (decodeTLV) {
1100 TLVPrintFromBuffer(buf, len);
1101 PrintAndLog("");
1102 }
1103 free(udol_data_tlv);
1104
1105 }
1106 } else {
1107 PrintAndLog("ERROR MSD: Track2 data not found.");
1108 }
1109 }
1110
1111 // DropField
1112 DropField();
1113
1114 // Destroy TLV's
1115 free(pdol_data_tlv);
1116 tlvdb_free(tlvSelect);
1117 tlvdb_free(tlvRoot);
1118
1119 PrintAndLog("\n* Transaction completed.");
1120
1121 return 0;
1122 }
1123
1124 int UsageCmdHFEMVScan(void) {
1125 PrintAndLog("HELP : Scan EMV card and save it contents to a file. \n");
1126 PrintAndLog(" It executes EMV contactless transaction and saves result to a file which can be used for emulation.\n");
1127 PrintAndLog("Usage: hf emv scan [-a][-t][-v][-c][-x][-g] <file_name>\n");
1128 PrintAndLog(" Options:");
1129 PrintAndLog(" -a : show APDU reqests and responses\n");
1130 PrintAndLog(" -t : TLV decode results\n");
1131 PrintAndLog(" -v : transaction type - qVSDC or M/Chip.\n");
1132 PrintAndLog(" -c : transaction type - qVSDC or M/Chip plus CDA (SDAD generation).\n");
1133 PrintAndLog(" -x : transaction type - VSDC. For test only. Not a standart behavior.\n");
1134 PrintAndLog(" -g : VISA. generate AC from GPO\n");
1135 PrintAndLog("By default : transaction type - MSD.\n");
1136 PrintAndLog("Samples:");
1137 PrintAndLog(" hf emv scan -a -t -> scan MSD transaction mode");
1138 PrintAndLog(" hf emv scan -a -t -c -> scan CDA transaction mode");
1139 return 0;
1140 }
1141
1142 int CmdHFEMVScan(const char *cmd) {
1143 UsageCmdHFEMVScan();
1144
1145 return 0;
1146 }
1147
1148 int CmdHFEMVTest(const char *cmd) {
1149 return ExecuteCryptoTests(true);
1150 }
1151
1152 int CmdHelp(const char *Cmd);
1153 static command_t CommandTable[] = {
1154 {"help", CmdHelp, 1, "This help"},
1155 {"exec", CmdHFEMVExec, 0, "Executes EMV contactless transaction."},
1156 {"pse", CmdHFEMVPPSE, 0, "Execute PPSE. It selects 2PAY.SYS.DDF01 or 1PAY.SYS.DDF01 directory."},
1157 {"search", CmdHFEMVSearch, 0, "Try to select all applets from applets list and print installed applets."},
1158 {"select", CmdHFEMVSelect, 0, "Select applet."},
1159 {"gpo", CmdHFEMVGPO, 0, "Execute GetProcessingOptions."},
1160 {"readrec", CmdHFEMVReadRecord, 0, "Read files from card."},
1161 {"genac", CmdHFEMVAC, 0, "Generate ApplicationCryptogram."},
1162 {"challenge", CmdHFEMVGenerateChallenge, 0, "Generate challenge."},
1163 {"intauth", CmdHFEMVInternalAuthenticate, 0, "Internal authentication."},
1164 // {"scan", CmdHFEMVScan, 0, "Scan EMV card and save it contents to json file for emulator."},
1165 {"test", CmdHFEMVTest, 0, "Crypto logic test."},
1166 {NULL, NULL, 0, NULL}
1167 };
1168
1169 int CmdHFEMV(const char *Cmd) {
1170 CmdsParse(CommandTable, Cmd);
1171 return 0;
1172 }
1173
1174 int CmdHelp(const char *Cmd) {
1175 CmdsHelp(CommandTable);
1176 return 0;
1177 }
Impressum, Datenschutz