1 //-----------------------------------------------------------------------------
2 // Copyright (C) 2017, 2018 Merlok
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
7 //-----------------------------------------------------------------------------
9 //-----------------------------------------------------------------------------
14 #include "proxmark3.h"
15 #include "cmdparser.h"
20 #include "test/cryptotest.h"
21 #include "cliparser/cliparser.h"
26 #define TLV_ADD(tag, value)( tlvdb_change_or_add_node(tlvRoot, tag, sizeof(value) - 1, (const unsigned char *)value) )
27 void ParamLoadDefaults(struct tlvdb
*tlvRoot
) {
28 //9F02:(Amount, authorized (Numeric)) len:6
29 TLV_ADD(0x9F02, "\x00\x00\x00\x00\x01\x00");
30 //9F1A:(Terminal Country Code) len:2
31 TLV_ADD(0x9F1A, "ru");
32 //5F2A:(Transaction Currency Code) len:2
33 // USD 840, EUR 978, RUR 810, RUB 643, RUR 810(old), UAH 980, AZN 031, n/a 999
34 TLV_ADD(0x5F2A, "\x09\x80");
35 //9A:(Transaction Date) len:3
36 TLV_ADD(0x9A, "\x00\x00\x00");
37 //9C:(Transaction Type) len:1 | 00 => Goods and service #01 => Cash
38 TLV_ADD(0x9C, "\x00");
39 // 9F37 Unpredictable Number len:4
40 TLV_ADD(0x9F37, "\x01\x02\x03\x04");
41 // 9F6A Unpredictable Number (MSD for UDOL) len:4
42 TLV_ADD(0x9F6A, "\x01\x02\x03\x04");
43 //9F66:(Terminal Transaction Qualifiers (TTQ)) len:4
44 TLV_ADD(0x9F66, "\x26\x00\x00\x00"); // qVSDC
45 //95:(Terminal Verification Results) len:5
47 TLV_ADD(0x95, "\x00\x00\x00\x00\x00");
50 void PrintChannel(EMVCommandChannel channel
) {
53 PrintAndLogEx(INFO
, "Channel: CONTACTLESS");
56 PrintAndLogEx(INFO
, "Channel: CONTACT");
61 int CmdEMVSelect(const char *cmd
) {
62 uint8_t data
[APDU_DATA_LEN
] = {0};
65 CLIParserInit("emv select",
66 "Executes select applet command",
67 "Usage:\n\temv select -s a00000000101 -> select card, select applet\n\temv select -st a00000000101 -> select card, select applet, show result in TLV\n");
71 arg_lit0("sS", "select", "activate field and select card"),
72 arg_lit0("kK", "keep", "keep field for next command"),
73 arg_lit0("aA", "apdu", "show APDU reqests and responses"),
74 arg_lit0("tT", "tlv", "TLV decode results"),
76 arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. Contactless interface set by default."),
78 arg_strx0(NULL
, NULL
, "<HEX applet AID>", NULL
),
81 CLIExecWithReturn(cmd
, argtable
, true);
83 bool activateField
= arg_get_lit(1);
84 bool leaveSignalON
= arg_get_lit(2);
85 bool APDULogging
= arg_get_lit(3);
86 bool decodeTLV
= arg_get_lit(4);
87 EMVCommandChannel channel
= ECC_CONTACTLESS
;
90 channel
= ECC_CONTACT
;
91 PrintChannel(channel
);
92 CLIGetHexWithReturn(6, data
, &datalen
);
94 CLIGetHexWithReturn(5, data
, &datalen
);
98 SetAPDULogging(APDULogging
);
101 uint8_t buf
[APDU_RESPONSE_LEN
] = {0};
104 int res
= EMVSelect(channel
, activateField
, leaveSignalON
, data
, datalen
, buf
, sizeof(buf
), &len
, &sw
, NULL
);
107 PrintAndLogEx(INFO
, "APDU response status: %04x - %s", sw
, GetAPDUCodeDescription(sw
>> 8, sw
& 0xff));
113 TLVPrintFromBuffer(buf
, len
);
118 int CmdEMVSearch(const char *cmd
) {
120 CLIParserInit("emv search",
121 "Tries to select all applets from applet list:\n",
122 "Usage:\n\temv search -s -> select card and search\n\temv search -st -> select card, search and show result in TLV\n");
126 arg_lit0("sS", "select", "activate field and select card"),
127 arg_lit0("kK", "keep", "keep field ON for next command"),
128 arg_lit0("aA", "apdu", "show APDU reqests and responses"),
129 arg_lit0("tT", "tlv", "TLV decode results of selected applets"),
130 #ifdef WITH_SMARTCARD
131 arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. Contactless interface set by default."),
135 CLIExecWithReturn(cmd
, argtable
, true);
137 bool activateField
= arg_get_lit(1);
138 bool leaveSignalON
= arg_get_lit(2);
139 bool APDULogging
= arg_get_lit(3);
140 bool decodeTLV
= arg_get_lit(4);
141 EMVCommandChannel channel
= ECC_CONTACTLESS
;
142 #ifdef WITH_SMARTCARD
144 channel
= ECC_CONTACT
;
146 PrintChannel(channel
);
149 SetAPDULogging(APDULogging
);
151 struct tlvdb
*t
= NULL
;
152 const char *al
= "Applets list";
153 t
= tlvdb_fixed(1, strlen(al
), (const unsigned char *)al
);
155 if (EMVSearch(channel
, activateField
, leaveSignalON
, decodeTLV
, t
)) {
160 PrintAndLogEx(SUCCESS
, "Search completed.");
164 TLVPrintAIDlistFromSelectTLV(t
);
172 int CmdEMVPPSE(const char *cmd
) {
174 CLIParserInit("emv pse",
175 "Executes PSE/PPSE select command. It returns list of applet on the card:\n",
176 "Usage:\n\temv pse -s1 -> select, get pse\n\temv pse -st2 -> select, get ppse, show result in TLV\n");
180 arg_lit0("sS", "select", "activate field and select card"),
181 arg_lit0("kK", "keep", "keep field ON for next command"),
182 arg_lit0("1", "pse", "pse (1PAY.SYS.DDF01) mode"),
183 arg_lit0("2", "ppse", "ppse (2PAY.SYS.DDF01) mode (default mode)"),
184 arg_lit0("aA", "apdu", "show APDU reqests and responses"),
185 arg_lit0("tT", "tlv", "TLV decode results of selected applets"),
186 #ifdef WITH_SMARTCARD
187 arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. Contactless interface set by default."),
191 CLIExecWithReturn(cmd
, argtable
, true);
193 bool activateField
= arg_get_lit(1);
194 bool leaveSignalON
= arg_get_lit(2);
200 bool APDULogging
= arg_get_lit(5);
201 bool decodeTLV
= arg_get_lit(6);
202 EMVCommandChannel channel
= ECC_CONTACTLESS
;
203 #ifdef WITH_SMARTCARD
205 channel
= ECC_CONTACT
;
207 PrintChannel(channel
);
210 SetAPDULogging(APDULogging
);
213 uint8_t buf
[APDU_RESPONSE_LEN
] = {0};
216 int res
= EMVSelectPSE(channel
, activateField
, leaveSignalON
, PSENum
, buf
, sizeof(buf
), &len
, &sw
);
219 PrintAndLogEx(INFO
, "APDU response status: %04x - %s", sw
, GetAPDUCodeDescription(sw
>> 8, sw
& 0xff));
226 TLVPrintFromBuffer(buf
, len
);
231 int CmdEMVGPO(const char *cmd
) {
232 uint8_t data
[APDU_RESPONSE_LEN
] = {0};
235 CLIParserInit("emv gpo",
236 "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.",
237 "Usage:\n\temv gpo -k -> execute GPO\n"
238 "\temv gpo -t 01020304 -> execute GPO with 4-byte PDOL data, show result in TLV\n"
239 "\temv gpo -pmt 9F 37 04 -> load params from file, make PDOL data from PDOL, execute GPO with PDOL, show result in TLV\n");
243 arg_lit0("kK", "keep", "keep field ON for next command"),
244 arg_lit0("pP", "params", "load parameters from `emv/defparams.json` file for PDOLdata making from PDOL and parameters"),
245 arg_lit0("mM", "make", "make PDOLdata from PDOL (tag 9F38) and parameters (by default uses default parameters)"),
246 arg_lit0("aA", "apdu", "show APDU reqests and responses"),
247 arg_lit0("tT", "tlv", "TLV decode results of selected applets"),
248 #ifdef WITH_SMARTCARD
249 arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. Contactless interface set by default."),
251 arg_strx0(NULL
, NULL
, "<HEX PDOLdata/PDOL>", NULL
),
254 CLIExecWithReturn(cmd
, argtable
, true);
256 bool leaveSignalON
= arg_get_lit(1);
257 bool paramsLoadFromFile
= arg_get_lit(2);
258 bool dataMakeFromPDOL
= arg_get_lit(3);
259 bool APDULogging
= arg_get_lit(4);
260 bool decodeTLV
= arg_get_lit(5);
261 EMVCommandChannel channel
= ECC_CONTACTLESS
;
262 #ifdef WITH_SMARTCARD
264 channel
= ECC_CONTACT
;
265 CLIGetHexWithReturn(7, data
, &datalen
);
267 CLIGetHexWithReturn(6, data
, &datalen
);
269 PrintChannel(channel
);
272 SetAPDULogging(APDULogging
);
275 const char *alr
= "Root terminal TLV tree";
276 struct tlvdb
*tlvRoot
= tlvdb_fixed(1, strlen(alr
), (const unsigned char *)alr
);
279 struct tlv
*pdol_data_tlv
= NULL
;
280 struct tlv data_tlv
= {
283 .value
= (uint8_t *)data
,
285 if (dataMakeFromPDOL
) {
286 ParamLoadDefaults(tlvRoot
);
288 if (paramsLoadFromFile
) {
289 PrintAndLogEx(INFO
, "Params loading from file...");
290 ParamLoadFromJson(tlvRoot
);
293 pdol_data_tlv
= dol_process((const struct tlv
*)tlvdb_external(0x9f38, datalen
, data
), tlvRoot
, 0x83);
295 PrintAndLogEx(ERR
, "Can't create PDOL TLV.");
300 if (paramsLoadFromFile
) {
301 PrintAndLogEx(WARNING
, "Don't need to load parameters. Sending plain PDOL data...");
303 pdol_data_tlv
= &data_tlv
;
306 size_t pdol_data_tlv_data_len
= 0;
307 unsigned char *pdol_data_tlv_data
= tlv_encode(pdol_data_tlv
, &pdol_data_tlv_data_len
);
308 if (!pdol_data_tlv_data
) {
309 PrintAndLogEx(ERR
, "Can't create PDOL data.");
313 PrintAndLogEx(INFO
, "PDOL data[%d]: %s", pdol_data_tlv_data_len
, sprint_hex(pdol_data_tlv_data
, pdol_data_tlv_data_len
));
316 uint8_t buf
[APDU_RESPONSE_LEN
] = {0};
319 int res
= EMVGPO(channel
, leaveSignalON
, pdol_data_tlv_data
, pdol_data_tlv_data_len
, buf
, sizeof(buf
), &len
, &sw
, tlvRoot
);
321 if (pdol_data_tlv
!= &data_tlv
)
326 PrintAndLogEx(INFO
, "APDU response status: %04x - %s", sw
, GetAPDUCodeDescription(sw
>> 8, sw
& 0xff));
332 TLVPrintFromBuffer(buf
, len
);
337 int CmdEMVReadRecord(const char *cmd
) {
338 uint8_t data
[APDU_RESPONSE_LEN
] = {0};
341 CLIParserInit("emv readrec",
342 "Executes Read Record command. It returns data in TLV format.\nNeeds a bank applet to be selected and sometimes needs GPO to be executed.",
343 "Usage:\n\temv readrec -k 0101 -> read file SFI=01, SFIrec=01\n\temv readrec -kt 0201-> read file 0201 and show result in TLV\n");
347 arg_lit0("kK", "keep", "keep field ON for next command"),
348 arg_lit0("aA", "apdu", "show APDU reqests and responses"),
349 arg_lit0("tT", "tlv", "TLV decode results of selected applets"),
350 arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. Contactless interface set by default."),
351 arg_strx1(NULL
, NULL
, "<SFI 1byte HEX><SFIrecord 1byte HEX>", NULL
),
354 CLIExecWithReturn(cmd
, argtable
, true);
356 bool leaveSignalON
= arg_get_lit(1);
357 bool APDULogging
= arg_get_lit(2);
358 bool decodeTLV
= arg_get_lit(3);
359 EMVCommandChannel channel
= ECC_CONTACTLESS
;
360 #ifdef WITH_SMARTCARD
362 channel
= ECC_CONTACT
;
363 CLIGetHexWithReturn(5, data
, &datalen
);
365 CLIGetHexWithReturn(4, data
, &datalen
);
367 PrintChannel(channel
);
371 PrintAndLogEx(ERR
, "Command needs to have 2 bytes of data");
375 SetAPDULogging(APDULogging
);
378 uint8_t buf
[APDU_RESPONSE_LEN
] = {0};
381 int res
= EMVReadRecord(channel
, leaveSignalON
, data
[0], data
[1], buf
, sizeof(buf
), &len
, &sw
, NULL
);
384 PrintAndLogEx(INFO
, "APDU response status: %04x - %s", sw
, GetAPDUCodeDescription(sw
>> 8, sw
& 0xff));
391 TLVPrintFromBuffer(buf
, len
);
396 int CmdEMVAC(const char *cmd
) {
397 uint8_t data
[APDU_RESPONSE_LEN
] = {0};
400 CLIParserInit("emv genac",
401 "Generate Application Cryptogram command. It returns data in TLV format .\nNeeds a EMV applet to be selected and GPO to be executed.",
402 "Usage:\n\temv genac -k 0102 -> generate AC with 2-byte CDOLdata and keep field ON after command\n"
403 "\temv genac -t 01020304 -> generate AC with 4-byte CDOL data, show result in TLV\n"
404 "\temv genac -Daac 01020304 -> generate AC with 4-byte CDOL data and terminal decision 'declined'\n"
405 "\temv genac -pmt 9F 37 04 -> load params from file, make CDOL data from CDOL, generate AC with CDOL, show result in TLV");
409 arg_lit0("kK", "keep", "keep field ON for next command"),
410 arg_lit0("cC", "cda", "executes CDA transaction. Needs to get SDAD in results."),
411 arg_str0("dD", "decision", "<aac|tc|arqc>", "Terminal decision. aac - declined, tc - approved, arqc - online authorisation requested"),
412 arg_lit0("pP", "params", "load parameters from `emv/defparams.json` file for CDOLdata making from CDOL and parameters"),
413 arg_lit0("mM", "make", "make CDOLdata from CDOL (tag 8C and 8D) and parameters (by default uses default parameters)"),
414 arg_lit0("aA", "apdu", "show APDU reqests and responses"),
415 arg_lit0("tT", "tlv", "TLV decode results of selected applets"),
416 #ifdef WITH_SMARTCARD
417 arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. Contactless interface set by default."),
419 arg_strx1(NULL
, NULL
, "<HEX CDOLdata/CDOL>", NULL
),
422 CLIExecWithReturn(cmd
, argtable
, false);
424 bool leaveSignalON
= arg_get_lit(1);
425 bool trTypeCDA
= arg_get_lit(2);
426 uint8_t termDecision
= 0xff;
427 if (arg_get_str_len(3)) {
428 if (!strncmp(arg_get_str(3)->sval
[0], "aac", 4))
429 termDecision
= EMVAC_AAC
;
430 if (!strncmp(arg_get_str(3)->sval
[0], "tc", 4))
431 termDecision
= EMVAC_TC
;
432 if (!strncmp(arg_get_str(3)->sval
[0], "arqc", 4))
433 termDecision
= EMVAC_ARQC
;
435 if (termDecision
== 0xff) {
436 PrintAndLog("ERROR: can't find terminal decision '%s'", arg_get_str(3)->sval
[0]);
440 termDecision
= EMVAC_TC
;
443 termDecision
= termDecision
| EMVAC_CDAREQ
;
444 bool paramsLoadFromFile
= arg_get_lit(4);
445 bool dataMakeFromCDOL
= arg_get_lit(5);
446 bool APDULogging
= arg_get_lit(6);
447 bool decodeTLV
= arg_get_lit(7);
448 EMVCommandChannel channel
= ECC_CONTACTLESS
;
449 #ifdef WITH_SMARTCARD
451 channel
= ECC_CONTACT
;
452 CLIGetHexWithReturn(9, data
, &datalen
);
454 CLIGetHexWithReturn(8, data
, &datalen
);
456 PrintChannel(channel
);
459 SetAPDULogging(APDULogging
);
462 const char *alr
= "Root terminal TLV tree";
463 struct tlvdb
*tlvRoot
= tlvdb_fixed(1, strlen(alr
), (const unsigned char *)alr
);
466 struct tlv
*cdol_data_tlv
= NULL
;
467 struct tlv data_tlv
= {
470 .value
= (uint8_t *)data
,
473 if (dataMakeFromCDOL
) {
474 ParamLoadDefaults(tlvRoot
);
476 if (paramsLoadFromFile
) {
477 PrintAndLogEx(INFO
, "Params loading from file...");
478 ParamLoadFromJson(tlvRoot
);
481 cdol_data_tlv
= dol_process((const struct tlv
*)tlvdb_external(0x8c, datalen
, data
), tlvRoot
, 0x01); // 0x01 - dummy tag
483 PrintAndLogEx(ERR
, "Can't create CDOL TLV.");
488 if (paramsLoadFromFile
) {
489 PrintAndLogEx(WARNING
, "Don't need to load parameters. Sending plain CDOL data...");
491 cdol_data_tlv
= &data_tlv
;
494 PrintAndLogEx(INFO
, "CDOL data[%d]: %s", cdol_data_tlv
->len
, sprint_hex(cdol_data_tlv
->value
, cdol_data_tlv
->len
));
497 uint8_t buf
[APDU_RESPONSE_LEN
] = {0};
500 int res
= EMVAC(channel
, leaveSignalON
, termDecision
, (uint8_t *)cdol_data_tlv
->value
, cdol_data_tlv
->len
, buf
, sizeof(buf
), &len
, &sw
, tlvRoot
);
502 if (cdol_data_tlv
!= &data_tlv
)
507 PrintAndLogEx(INFO
, "APDU response status: %04x - %s", sw
, GetAPDUCodeDescription(sw
>> 8, sw
& 0xff));
513 TLVPrintFromBuffer(buf
, len
);
518 int CmdEMVGenerateChallenge(const char *cmd
) {
520 CLIParserInit("emv challenge",
521 "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.",
522 "Usage:\n\temv challenge -> get challenge\n\temv challenge -k -> get challenge, keep fileld ON\n");
526 arg_lit0("kK", "keep", "keep field ON for next command"),
527 arg_lit0("aA", "apdu", "show APDU reqests and responses"),
528 #ifdef WITH_SMARTCARD
529 arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. Contactless interface set by default."),
533 CLIExecWithReturn(cmd
, argtable
, true);
535 bool leaveSignalON
= arg_get_lit(1);
536 bool APDULogging
= arg_get_lit(2);
537 EMVCommandChannel channel
= ECC_CONTACTLESS
;
538 #ifdef WITH_SMARTCARD
540 channel
= ECC_CONTACT
;
542 PrintChannel(channel
);
545 SetAPDULogging(APDULogging
);
548 uint8_t buf
[APDU_RESPONSE_LEN
] = {0};
551 int res
= EMVGenerateChallenge(channel
, leaveSignalON
, buf
, sizeof(buf
), &len
, &sw
, NULL
);
554 PrintAndLogEx(INFO
, "APDU response status: %04x - %s", sw
, GetAPDUCodeDescription(sw
>> 8, sw
& 0xff));
559 PrintAndLogEx(SUCCESS
, "Challenge: %s", sprint_hex(buf
, len
));
561 if (len
!= 4 && len
!= 8)
562 PrintAndLogEx(WARNING
, "Length of challenge must be 4 or 8, but it %d", len
);
567 int CmdEMVInternalAuthenticate(const char *cmd
) {
568 uint8_t data
[APDU_RESPONSE_LEN
] = {0};
571 CLIParserInit("emv intauth",
572 "Generate Internal Authenticate command. Usually needs 4-byte random number. It returns data in TLV format .\n"
573 "Needs a EMV applet to be selected and GPO to be executed.",
576 "\temv intauth -k 01020304 -> execute Internal Authenticate with 4-byte DDOLdata and keep field ON after command\n"
577 "\temv intauth -t 01020304 -> execute Internal Authenticate with 4-byte DDOL data, show result in TLV\n"
578 "\temv intauth -pmt 9F 37 04 -> load params from file, make DDOL data from DDOL, Internal Authenticate with DDOL, show result in TLV");
582 arg_lit0("kK", "keep", "keep field ON for next command"),
583 arg_lit0("pP", "params", "load parameters from `emv/defparams.json` file for DDOLdata making from DDOL and parameters"),
584 arg_lit0("mM", "make", "make DDOLdata from DDOL (tag 9F49) and parameters (by default uses default parameters)"),
585 arg_lit0("aA", "apdu", "show APDU reqests and responses"),
586 arg_lit0("tT", "tlv", "TLV decode results of selected applets"),
587 #ifdef WITH_SMARTCARD
588 arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. Contactless interface set by default."),
590 arg_strx1(NULL
, NULL
, "<HEX DDOLdata/DDOL>", NULL
),
593 CLIExecWithReturn(cmd
, argtable
, false);
595 bool leaveSignalON
= arg_get_lit(1);
596 bool paramsLoadFromFile
= arg_get_lit(2);
597 bool dataMakeFromDDOL
= arg_get_lit(3);
598 bool APDULogging
= arg_get_lit(4);
599 bool decodeTLV
= arg_get_lit(5);
600 EMVCommandChannel channel
= ECC_CONTACTLESS
;
601 #ifdef WITH_SMARTCARD
603 channel
= ECC_CONTACT
;
604 CLIGetHexWithReturn(7, data
, &datalen
);
606 CLIGetHexWithReturn(6, data
, &datalen
);
608 PrintChannel(channel
);
611 SetAPDULogging(APDULogging
);
614 const char *alr
= "Root terminal TLV tree";
615 struct tlvdb
*tlvRoot
= tlvdb_fixed(1, strlen(alr
), (const unsigned char *)alr
);
618 struct tlv
*ddol_data_tlv
= NULL
;
619 struct tlv data_tlv
= {
622 .value
= (uint8_t *)data
,
625 if (dataMakeFromDDOL
) {
626 ParamLoadDefaults(tlvRoot
);
628 if (paramsLoadFromFile
) {
629 PrintAndLogEx(INFO
, "Params loading from file...");
630 ParamLoadFromJson(tlvRoot
);
633 ddol_data_tlv
= dol_process((const struct tlv
*)tlvdb_external(0x9f49, datalen
, data
), tlvRoot
, 0x01); // 0x01 - dummy tag
635 PrintAndLogEx(ERR
, "Can't create DDOL TLV.");
640 if (paramsLoadFromFile
) {
641 PrintAndLogEx(WARNING
, "Don't need to load parameters. Sending plain DDOL data...");
643 ddol_data_tlv
= &data_tlv
;
646 PrintAndLogEx(INFO
, "DDOL data[%d]: %s", ddol_data_tlv
->len
, sprint_hex(ddol_data_tlv
->value
, ddol_data_tlv
->len
));
649 uint8_t buf
[APDU_RESPONSE_LEN
] = {0};
652 int res
= EMVInternalAuthenticate(channel
, leaveSignalON
, data
, datalen
, buf
, sizeof(buf
), &len
, &sw
, NULL
);
654 if (ddol_data_tlv
!= &data_tlv
)
659 PrintAndLogEx(INFO
, "APDU response status: %04x - %s", sw
, GetAPDUCodeDescription(sw
>> 8, sw
& 0xff));
665 TLVPrintFromBuffer(buf
, len
);
670 #define dreturn(n) {free(pdol_data_tlv);tlvdb_free(tlvSelect);tlvdb_free(tlvRoot);DropField();return n;}
672 void InitTransactionParameters(struct tlvdb
*tlvRoot
, bool paramLoadJSON
, enum TransactionType TrType
, bool GenACGPO
) {
674 ParamLoadDefaults(tlvRoot
);
677 PrintAndLog("* * Transaction parameters loading from JSON...");
678 ParamLoadFromJson(tlvRoot
);
681 //9F66:(Terminal Transaction Qualifiers (TTQ)) len:4
682 char *qVSDC
= "\x26\x00\x00\x00";
684 qVSDC
= "\x26\x80\x00\x00";
688 TLV_ADD(0x9F66, "\x86\x00\x00\x00"); // MSD
690 // not standard for contactless. just for test.
692 TLV_ADD(0x9F66, "\x46\x00\x00\x00"); // VSDC
695 TLV_ADD(0x9F66, qVSDC
); // qVSDC
698 TLV_ADD(0x9F66, qVSDC
); // qVSDC (VISA CDA not enabled)
705 void ProcessGPOResponseFormat1(struct tlvdb
*tlvRoot
, uint8_t *buf
, size_t len
, bool decodeTLV
) {
706 if (buf
[0] == 0x80) {
708 PrintAndLog("GPO response format1:");
709 TLVPrintFromBuffer(buf
, len
);
712 if (len
< 4 || (len
- 4) % 4) {
713 PrintAndLogEx(ERR
, "GPO response format1 parsing error. length=%d", len
);
716 struct tlvdb
* f1AIP
= tlvdb_fixed(0x82, 2, buf
+ 2);
717 tlvdb_add(tlvRoot
, f1AIP
);
719 PrintAndLogEx(INFO
, "\n* * Decode response format 1 (0x80) AIP and AFL:");
720 TLVPrintFromTLV(f1AIP
);
724 struct tlvdb
* f1AFL
= tlvdb_fixed(0x94, len
- 4, buf
+ 2 + 2);
725 tlvdb_add(tlvRoot
, f1AFL
);
727 TLVPrintFromTLV(f1AFL
);
731 TLVPrintFromBuffer(buf
, len
);
735 void ProcessACResponseFormat1(struct tlvdb
*tlvRoot
, uint8_t *buf
, size_t len
, bool decodeTLV
) {
736 if (buf
[0] == 0x80) {
738 PrintAndLog("GPO response format1:");
739 TLVPrintFromBuffer(buf
, len
);
742 uint8_t elmlen
= len
- 2; // wo 0x80XX
744 if (len
< 4 + 2 || (elmlen
- 2) % 4 || elmlen
!= buf
[1]) {
745 PrintAndLogEx(ERR
, "GPO response format1 parsing error. length=%d", len
);
747 struct tlvdb
*tlvElm
= NULL
;
749 PrintAndLog("\n------------ Format1 decoded ------------");
751 // CID (Cryptogram Information Data)
752 tlvdb_change_or_add_node_ex(tlvRoot
, 0x9f27, 1, &buf
[2], &tlvElm
);
754 TLVPrintFromTLV(tlvElm
);
756 // ATC (Application Transaction Counter)
757 tlvdb_change_or_add_node_ex(tlvRoot
, 0x9f36, 2, &buf
[3], &tlvElm
);
759 TLVPrintFromTLV(tlvElm
);
761 // AC (Application Cryptogram)
762 tlvdb_change_or_add_node_ex(tlvRoot
, 0x9f26, MIN(8, elmlen
- 3), &buf
[5], &tlvElm
);
764 TLVPrintFromTLV(tlvElm
);
766 // IAD (Issuer Application Data) - optional
768 tlvdb_change_or_add_node_ex(tlvRoot
, 0x9f10, elmlen
- 11, &buf
[13], &tlvElm
);
770 TLVPrintFromTLV(tlvElm
);
775 TLVPrintFromBuffer(buf
, len
);
779 int CmdEMVExec(const char *cmd
) {
780 uint8_t buf
[APDU_RESPONSE_LEN
] = {0};
783 uint8_t AID
[APDU_DATA_LEN
] = {0};
785 uint8_t ODAiList
[4096];
786 size_t ODAiListLen
= 0;
790 struct tlvdb
*tlvSelect
= NULL
;
791 struct tlvdb
*tlvRoot
= NULL
;
792 struct tlv
*pdol_data_tlv
= NULL
;
794 CLIParserInit("emv exec",
795 "Executes EMV contactless transaction",
797 "\temv exec -sat -> select card, execute MSD transaction, show APDU and TLV\n"
798 "\temv exec -satc -> select card, execute CDA transaction, show APDU and TLV\n");
802 arg_lit0("sS", "select", "activate field and select card."),
803 arg_lit0("aA", "apdu", "show APDU reqests and responses."),
804 arg_lit0("tT", "tlv", "TLV decode results."),
805 arg_lit0("jJ", "jload", "Load transaction parameters from `emv/defparams.json` file."),
806 arg_lit0("fF", "forceaid", "Force search AID. Search AID instead of execute PPSE."),
807 arg_rem("By default:", "Transaction type - MSD"),
808 arg_lit0("vV", "qvsdc", "Transaction type - qVSDC or M/Chip."),
809 arg_lit0("cC", "qvsdccda", "Transaction type - qVSDC or M/Chip plus CDA (SDAD generation)."),
810 arg_lit0("xX", "vsdc", "Transaction type - VSDC. For test only. Not a standart behavior."),
811 arg_lit0("gG", "acgpo", "VISA. generate AC from GPO."),
812 arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. Contactless interface set by default."),
815 CLIExecWithReturn(cmd
, argtable
, true);
817 bool activateField
= arg_get_lit(1);
818 bool showAPDU
= arg_get_lit(2);
819 bool decodeTLV
= arg_get_lit(3);
820 bool paramLoadJSON
= arg_get_lit(4);
821 bool forceSearch
= arg_get_lit(5);
823 enum TransactionType TrType
= TT_MSD
;
825 TrType
= TT_QVSDCMCHIP
;
831 bool GenACGPO
= arg_get_lit(10);
832 EMVCommandChannel channel
= ECC_CONTACTLESS
;
833 #ifdef WITH_SMARTCARD
835 channel
= ECC_CONTACT
;
837 PrintChannel(channel
);
838 uint8_t psenum
= (channel
== ECC_CONTACT
) ? 1 : 2;
839 char *PSE_or_PPSE
= psenum
== 1 ? "PSE" : "PPSE";
843 SetAPDULogging(showAPDU
);
845 // init applets list tree
846 const char *al
= "Applets list";
847 tlvSelect
= tlvdb_fixed(1, strlen(al
), (const unsigned char *)al
);
849 // Application Selection
850 // https://www.openscdp.org/scripts/tutorial/emv/applicationselection.html
853 PrintAndLogEx(NORMAL
, "\n* %s.", PSE_or_PPSE
);
854 SetAPDULogging(showAPDU
);
855 res
= EMVSearchPSE(channel
, activateField
, true, psenum
, decodeTLV
, tlvSelect
);
857 // check PPSE / PSE and select application id
859 TLVPrintAIDlistFromSelectTLV(tlvSelect
);
860 EMVSelectApplication(tlvSelect
, AID
, &AIDlen
);
866 PrintAndLogEx(NORMAL
, "\n* Search AID in list.");
867 SetAPDULogging(false);
868 if (EMVSearch(channel
, activateField
, true, decodeTLV
, tlvSelect
)) {
872 // check search and select application id
873 TLVPrintAIDlistFromSelectTLV(tlvSelect
);
874 EMVSelectApplication(tlvSelect
, AID
, &AIDlen
);
878 const char *alr
= "Root terminal TLV tree";
879 tlvRoot
= tlvdb_fixed(1, strlen(alr
), (const unsigned char *)alr
);
881 // check if we found EMV application on card
883 PrintAndLogEx(WARNING
, "Can't select AID. EMV AID not found");
888 PrintAndLogEx(NORMAL
, "\n* Selecting AID:%s", sprint_hex_inrow(AID
, AIDlen
));
889 SetAPDULogging(showAPDU
);
890 res
= EMVSelect(channel
, false, true, AID
, AIDlen
, buf
, sizeof(buf
), &len
, &sw
, tlvRoot
);
893 PrintAndLogEx(WARNING
, "Can't select AID (%d). Exit...", res
);
898 TLVPrintFromBuffer(buf
, len
);
899 PrintAndLogEx(INFO
, "* Selected.");
901 PrintAndLogEx(INFO
, "\n* Init transaction parameters.");
902 InitTransactionParameters(tlvRoot
, paramLoadJSON
, TrType
, GenACGPO
);
903 TLVPrintFromTLV(tlvRoot
); // TODO delete!!!
905 PrintAndLogEx(NORMAL
, "\n* Calc PDOL.");
906 pdol_data_tlv
= dol_process(tlvdb_get(tlvRoot
, 0x9f38, NULL
), tlvRoot
, 0x83);
908 PrintAndLogEx(WARNING
, "Error: can't create PDOL TLV.");
912 size_t pdol_data_tlv_data_len
;
913 unsigned char *pdol_data_tlv_data
= tlv_encode(pdol_data_tlv
, &pdol_data_tlv_data_len
);
914 if (!pdol_data_tlv_data
) {
915 PrintAndLogEx(WARNING
, "Error: can't create PDOL data.");
918 PrintAndLogEx(NORMAL
, "PDOL data[%d]: %s", pdol_data_tlv_data_len
, sprint_hex(pdol_data_tlv_data
, pdol_data_tlv_data_len
));
920 PrintAndLogEx(NORMAL
, "\n* GPO.");
921 res
= EMVGPO(channel
, true, pdol_data_tlv_data
, pdol_data_tlv_data_len
, buf
, sizeof(buf
), &len
, &sw
, tlvRoot
);
923 free(pdol_data_tlv_data
);
924 //free(pdol_data_tlv); --- free on exit.
927 PrintAndLogEx(NORMAL
, "GPO error(%d): %4x. Exit...", res
, sw
);
931 // process response template format 1 [id:80 2b AIP + x4b AFL] and format 2 [id:77 TLV]
932 ProcessGPOResponseFormat1(tlvRoot
, buf
, len
, decodeTLV
);
934 // extract PAN from track2
936 const struct tlv
*track2
= tlvdb_get(tlvRoot
, 0x57, NULL
);
937 if (!tlvdb_get(tlvRoot
, 0x5a, NULL
) && track2
&& track2
->len
>= 8) {
938 struct tlvdb
*pan
= GetPANFromTrack2(track2
);
940 tlvdb_add(tlvRoot
, pan
);
942 const struct tlv
*pantlv
= tlvdb_get(tlvRoot
, 0x5a, NULL
);
943 PrintAndLogEx(NORMAL
, "\n* * Extracted PAN from track2: %s", sprint_hex(pantlv
->value
, pantlv
->len
));
945 PrintAndLogEx(NORMAL
, "\n* * WARNING: Can't extract PAN from track2.");
950 PrintAndLogEx(NORMAL
, "\n* Read records from AFL.");
951 const struct tlv
*AFL
= tlvdb_get(tlvRoot
, 0x94, NULL
);
952 if (!AFL
|| !AFL
->len
) {
953 PrintAndLogEx(WARNING
, "AFL not found.");
956 while(AFL
&& AFL
->len
) {
958 PrintAndLogEx(WARNING
, "Error: Wrong AFL length: %d", AFL
->len
);
962 for (int i
= 0; i
< AFL
->len
/ 4; i
++) {
963 uint8_t SFI
= AFL
->value
[i
* 4 + 0] >> 3;
964 uint8_t SFIstart
= AFL
->value
[i
* 4 + 1];
965 uint8_t SFIend
= AFL
->value
[i
* 4 + 2];
966 uint8_t SFIoffline
= AFL
->value
[i
* 4 + 3];
968 PrintAndLogEx(NORMAL
, "* * SFI[%02x] start:%02x end:%02x offline count:%02x", SFI
, SFIstart
, SFIend
, SFIoffline
);
969 if (SFI
== 0 || SFI
== 31 || SFIstart
== 0 || SFIstart
> SFIend
) {
970 PrintAndLogEx(NORMAL
, "SFI ERROR! Skipped...");
974 for(int n
= SFIstart
; n
<= SFIend
; n
++) {
975 PrintAndLogEx(NORMAL
, "* * * SFI[%02x] %d", SFI
, n
);
977 res
= EMVReadRecord(channel
, true, SFI
, n
, buf
, sizeof(buf
), &len
, &sw
, tlvRoot
);
979 PrintAndLogEx(WARNING
, "Error SFI[%02x]. APDU error %4x", SFI
, sw
);
984 TLVPrintFromBuffer(buf
, len
);
985 PrintAndLogEx(NORMAL
, "");
988 // Build Input list for Offline Data Authentication
989 // EMV 4.3 book3 10.3, page 96
990 if (SFIoffline
> 0) {
992 const unsigned char *abuf
= buf
;
995 if (tlv_parse_tl(&abuf
, &elmlen
, &e
)) {
996 memcpy(&ODAiList
[ODAiListLen
], &buf
[len
- elmlen
], elmlen
);
997 ODAiListLen
+= elmlen
;
999 PrintAndLogEx(WARNING
, "Error SFI[%02x]. Creating input list for Offline Data Authentication error.", SFI
);
1002 memcpy(&ODAiList
[ODAiListLen
], buf
, len
);
1014 // copy Input list for Offline Data Authentication
1016 struct tlvdb
*oda
= tlvdb_fixed(0x21, ODAiListLen
, ODAiList
); // not a standard tag
1017 tlvdb_add(tlvRoot
, oda
);
1018 PrintAndLogEx(NORMAL
, "* Input list for Offline Data Authentication added to TLV. len=%d \n", ODAiListLen
);
1023 const struct tlv
*AIPtlv
= tlvdb_get(tlvRoot
, 0x82, NULL
);
1025 AIP
= AIPtlv
->value
[0] + AIPtlv
->value
[1] * 0x100;
1026 PrintAndLogEx(NORMAL
, "* * AIP=%04x", AIP
);
1028 PrintAndLogEx(ERR
, "Can't found AIP.");
1033 PrintAndLogEx(NORMAL
, "\n* SDA");
1039 PrintAndLogEx(NORMAL
, "\n* DDA");
1040 trDDA(channel
, decodeTLV
, tlvRoot
);
1043 // transaction check
1046 if (TrType
== TT_QVSDCMCHIP
|| TrType
== TT_CDA
){
1047 // 9F26: Application Cryptogram
1048 const struct tlv
*AC
= tlvdb_get(tlvRoot
, 0x9F26, NULL
);
1050 PrintAndLogEx(NORMAL
, "\n--> qVSDC transaction.");
1051 PrintAndLogEx(NORMAL
, "* AC path");
1053 // 9F36: Application Transaction Counter (ATC)
1054 const struct tlv
*ATC
= tlvdb_get(tlvRoot
, 0x9F36, NULL
);
1057 // 9F10: Issuer Application Data - optional
1058 const struct tlv
*IAD
= tlvdb_get(tlvRoot
, 0x9F10, NULL
);
1061 PrintAndLogEx(NORMAL
, "ATC: %s", sprint_hex(ATC
->value
, ATC
->len
));
1062 PrintAndLogEx(NORMAL
, "AC: %s", sprint_hex(AC
->value
, AC
->len
));
1064 PrintAndLogEx(NORMAL
, "IAD: %s", sprint_hex(IAD
->value
, IAD
->len
));
1066 if (IAD
->len
>= IAD
->value
[0] + 1) {
1067 PrintAndLogEx(NORMAL
, "\tKey index: 0x%02x", IAD
->value
[1]);
1068 PrintAndLogEx(NORMAL
, "\tCrypto ver: 0x%02x(%03d)", IAD
->value
[2], IAD
->value
[2]);
1069 PrintAndLogEx(NORMAL
, "\tCVR:", sprint_hex(&IAD
->value
[3], IAD
->value
[0] - 2));
1070 struct tlvdb
* cvr
= tlvdb_fixed(0x20, IAD
->value
[0] - 2, &IAD
->value
[3]);
1071 TLVPrintFromTLVLev(cvr
, 1);
1074 PrintAndLogEx(WARNING
, "IAD not found.");
1078 PrintAndLogEx(ERR
, "AC: Application Transaction Counter (ATC) not found.");
1083 // Mastercard M/CHIP
1084 if (GetCardPSVendor(AID
, AIDlen
) == CV_MASTERCARD
&& (TrType
== TT_QVSDCMCHIP
|| TrType
== TT_CDA
)){
1085 const struct tlv
*CDOL1
= tlvdb_get(tlvRoot
, 0x8c, NULL
);
1086 if (CDOL1
&& GetCardPSVendor(AID
, AIDlen
) == CV_MASTERCARD
) { // and m/chip transaction flag
1087 PrintAndLogEx(NORMAL
, "\n--> Mastercard M/Chip transaction.");
1089 PrintAndLogEx(NORMAL
, "* * Generate challenge");
1090 res
= EMVGenerateChallenge(channel
, true, buf
, sizeof(buf
), &len
, &sw
, tlvRoot
);
1092 PrintAndLogEx(WARNING
, "GetChallenge. APDU error %4x", sw
);
1096 PrintAndLogEx(WARNING
, "GetChallenge. Wrong challenge length %d", len
);
1100 // ICC Dynamic Number
1101 struct tlvdb
* ICCDynN
= tlvdb_fixed(0x9f4c, len
, buf
);
1102 tlvdb_add(tlvRoot
, ICCDynN
);
1104 PrintAndLogEx(NORMAL
, "\n* * ICC Dynamic Number:");
1105 TLVPrintFromTLV(ICCDynN
);
1108 PrintAndLogEx(NORMAL
, "* * Calc CDOL1");
1109 struct tlv
*cdol_data_tlv
= dol_process(tlvdb_get(tlvRoot
, 0x8c, NULL
), tlvRoot
, 0x01); // 0x01 - dummy tag
1110 if (!cdol_data_tlv
){
1111 PrintAndLogEx(WARNING
, "Error: can't create CDOL1 TLV.");
1114 PrintAndLogEx(NORMAL
, "CDOL1 data[%d]: %s", cdol_data_tlv
->len
, sprint_hex(cdol_data_tlv
->value
, cdol_data_tlv
->len
));
1116 PrintAndLogEx(NORMAL
, "* * AC1");
1117 // EMVAC_TC + EMVAC_CDAREQ --- to get SDAD
1118 res
= EMVAC(channel
, 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
);
1121 PrintAndLogEx(NORMAL
, "AC1 error(%d): %4x. Exit...", res
, sw
);
1126 TLVPrintFromBuffer(buf
, len
);
1129 PrintAndLogEx(NORMAL
, "\n* CDA:");
1130 struct tlvdb
*ac_tlv
= tlvdb_parse_multi(buf
, len
);
1131 res
= trCDA(tlvRoot
, ac_tlv
, pdol_data_tlv
, cdol_data_tlv
);
1133 PrintAndLogEx(NORMAL
, "CDA error (%d)", res
);
1136 free(cdol_data_tlv
);
1138 PrintAndLogEx(NORMAL
, "\n* M/Chip transaction result:");
1139 // 9F27: Cryptogram Information Data (CID)
1140 const struct tlv
*CID
= tlvdb_get(tlvRoot
, 0x9F27, NULL
);
1142 emv_tag_dump(CID
, stdout
, 0);
1143 PrintAndLogEx(NORMAL
, "------------------------------");
1145 switch(CID
->value
[0] & EMVAC_AC_MASK
){
1147 PrintAndLogEx(NORMAL
, "Transaction DECLINED.");
1150 PrintAndLogEx(NORMAL
, "Transaction approved OFFLINE.");
1153 PrintAndLogEx(NORMAL
, "Transaction approved ONLINE.");
1156 PrintAndLogEx(WARNING
, "Error: CID transaction code error %2x", CID
->value
[0] & EMVAC_AC_MASK
);
1160 PrintAndLogEx(WARNING
, "Wrong CID length %d", CID
->len
);
1163 PrintAndLogEx(WARNING
, "CID(9F27) not found.");
1170 if (AIP
& 0x8000 && TrType
== TT_MSD
) {
1171 PrintAndLogEx(NORMAL
, "\n--> MSD transaction.");
1173 PrintAndLogEx(NORMAL
, "* MSD dCVV path. Check dCVV");
1175 const struct tlv
*track2
= tlvdb_get(tlvRoot
, 0x57, NULL
);
1177 PrintAndLogEx(NORMAL
, "Track2: %s", sprint_hex(track2
->value
, track2
->len
));
1179 struct tlvdb
*dCVV
= GetdCVVRawFromTrack2(track2
);
1180 PrintAndLogEx(NORMAL
, "dCVV raw data:");
1181 TLVPrintFromTLV(dCVV
);
1183 if (GetCardPSVendor(AID
, AIDlen
) == CV_MASTERCARD
) {
1184 PrintAndLogEx(NORMAL
, "\n* Mastercard calculate UDOL");
1187 const struct tlv
*UDOL
= tlvdb_get(tlvRoot
, 0x9F69, NULL
);
1188 // UDOL(9F69) default: 9F6A (Unpredictable number) 4 bytes
1189 const struct tlv defUDOL
= {
1192 .value
= (uint8_t *)"\x9f\x6a\x04",
1195 PrintAndLogEx(NORMAL
, "Use default UDOL.");
1197 struct tlv
*udol_data_tlv
= dol_process(UDOL
? UDOL
: &defUDOL
, tlvRoot
, 0x01); // 0x01 - dummy tag
1198 if (!udol_data_tlv
){
1199 PrintAndLogEx(WARNING
, "can't create UDOL TLV.");
1203 PrintAndLogEx(NORMAL
, "UDOL data[%d]: %s", udol_data_tlv
->len
, sprint_hex(udol_data_tlv
->value
, udol_data_tlv
->len
));
1205 PrintAndLogEx(NORMAL
, "\n* Mastercard compute cryptographic checksum(UDOL)");
1207 res
= MSCComputeCryptoChecksum(channel
, true, (uint8_t *)udol_data_tlv
->value
, udol_data_tlv
->len
, buf
, sizeof(buf
), &len
, &sw
, tlvRoot
);
1209 PrintAndLogEx(WARNING
, "Compute Crypto Checksum. APDU error %4x", sw
);
1210 free(udol_data_tlv
);
1214 // Mastercard compute cryptographic checksum result
1215 TLVPrintFromBuffer(buf
, len
);
1216 PrintAndLogEx(NORMAL
, "");
1218 free(udol_data_tlv
);
1222 PrintAndLogEx(WARNING
, "MSD: Track2 data not found.");
1227 if (GetCardPSVendor(AID
, AIDlen
) == CV_VISA
&& (TrType
== TT_VSDC
|| TrType
== TT_CDA
)){
1228 PrintAndLogEx(NORMAL
, "\n--> VSDC transaction.");
1230 PrintAndLogEx(NORMAL
, "* * Calc CDOL1");
1231 struct tlv
*cdol_data_tlv
= dol_process(tlvdb_get(tlvRoot
, 0x8c, NULL
), tlvRoot
, 0x01); // 0x01 - dummy tag
1232 if (!cdol_data_tlv
) {
1233 PrintAndLogEx(WARNING
, "Error: can't create CDOL1 TLV.");
1237 PrintAndLogEx(NORMAL
, "CDOL1 data[%d]: %s", cdol_data_tlv
->len
, sprint_hex(cdol_data_tlv
->value
, cdol_data_tlv
->len
));
1239 PrintAndLogEx(NORMAL
, "* * AC1");
1240 // EMVAC_TC + EMVAC_CDAREQ --- to get SDAD
1241 res
= EMVAC(channel
, 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
);
1244 PrintAndLogEx(NORMAL
, "AC1 error(%d): %4x. Exit...", res
, sw
);
1248 // process Format1 (0x80) and print Format2 (0x77)
1249 ProcessACResponseFormat1(tlvRoot
, buf
, len
, decodeTLV
);
1251 PrintAndLogEx(NORMAL
, "\n* * Processing online request\n");
1253 // authorization response code from acquirer
1254 const char HostResponse
[] = "00"; //0 x3030
1255 PrintAndLogEx(NORMAL
, "* * Host Response: `%s`", HostResponse
);
1256 tlvdb_change_or_add_node(tlvRoot
, 0x8a, sizeof(HostResponse
) - 1, (const unsigned char *)HostResponse
);
1260 if (channel
== ECC_CONTACTLESS
) {
1265 free(pdol_data_tlv
);
1266 tlvdb_free(tlvSelect
);
1267 tlvdb_free(tlvRoot
);
1269 PrintAndLogEx(NORMAL
, "\n* Transaction completed.");
1273 int CmdEMVScan(const char *cmd
) {
1274 uint8_t AID
[APDU_DATA_LEN
] = {0};
1276 uint8_t buf
[APDU_RESPONSE_LEN
] = {0};
1283 CLIParserInit("emv scan",
1284 "Scan EMV card and save it contents to a file.",
1285 "It executes EMV contactless transaction and saves result to a file which can be used for emulation\n"
1286 "Usage:\n\temv scan -at -> scan MSD transaction mode and show APDU and TLV\n"
1287 "\temv scan -c -> scan CDA transaction mode\n");
1289 void* argtable
[] = {
1291 arg_lit0("aA", "apdu", "show APDU reqests and responses."),
1292 arg_lit0("tT", "tlv", "TLV decode results."),
1293 arg_lit0("eE", "extract", "Extract TLV elements and fill Application Data"),
1294 arg_lit0("jJ", "jload", "Load transaction parameters from `emv/defparams.json` file."),
1295 arg_rem("By default:", "Transaction type - MSD"),
1296 arg_lit0("vV", "qvsdc", "Transaction type - qVSDC or M/Chip."),
1297 arg_lit0("cC", "qvsdccda", "Transaction type - qVSDC or M/Chip plus CDA (SDAD generation)."),
1298 arg_lit0("xX", "vsdc", "Transaction type - VSDC. For test only. Not a standart behavior."),
1299 arg_lit0("gG", "acgpo", "VISA. generate AC from GPO."),
1300 arg_lit0("mM", "merge", "Merge output file with card's data. (warning: the file may be corrupted!)"),
1301 #ifdef WITH_SMARTCARD
1302 arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. Contactless interface set by default."),
1304 arg_str1(NULL
, NULL
, "output.json", "JSON output file name"),
1307 CLIExecWithReturn(cmd
, argtable
, true);
1309 bool showAPDU
= arg_get_lit(1);
1310 bool decodeTLV
= arg_get_lit(2);
1311 bool extractTLVElements
= arg_get_lit(3);
1312 bool paramLoadJSON
= arg_get_lit(4);
1314 enum TransactionType TrType
= TT_MSD
;
1316 TrType
= TT_QVSDCMCHIP
;
1322 bool GenACGPO
= arg_get_lit(9);
1323 bool MergeJSON
= arg_get_lit(10);
1324 EMVCommandChannel channel
= ECC_CONTACTLESS
;
1325 uint8_t relfname
[250] = {0};
1326 char *crelfname
= (char *)relfname
;
1327 int relfnamelen
= 0;
1328 #ifdef WITH_SMARTCARD
1329 if (arg_get_lit(11)) {
1330 channel
= ECC_CONTACT
;
1332 CLIGetStrWithReturn(12, relfname
, &relfnamelen
);
1334 CLIGetStrWithReturn(11, relfname
, &relfnamelen
);
1336 PrintChannel(channel
);
1337 uint8_t psenum
= (channel
== ECC_CONTACT
) ? 1 : 2;
1340 SetAPDULogging(showAPDU
);
1343 if (channel
== ECC_CONTACT
) {
1344 PrintAndLogEx(ERR
, "Do not use contact interface. Exit.");
1348 // current path + file name
1349 if (!strstr(crelfname
, ".json"))
1350 strcat(crelfname
, ".json");
1351 char fname
[strlen(get_my_executable_directory()) + strlen(crelfname
) + 1];
1352 strcpy(fname
, get_my_executable_directory());
1353 strcat(fname
, crelfname
);
1356 root
= json_load_file(fname
, 0, &error
);
1358 PrintAndLogEx(ERR
, "json error on line %d: %s", error
.line
, error
.text
);
1362 if (!json_is_object(root
)) {
1363 PrintAndLogEx(ERR
, "Invalid json format. root must be an object.");
1367 root
= json_object();
1370 // drop field at start
1371 if (channel
== ECC_CONTACTLESS
) {
1376 PrintAndLogEx(NORMAL
, "--> GET UID, ATS.");
1378 iso14a_card_select_t card
;
1379 if (Hf14443_4aGetCardData(&card
)) {
1383 JsonSaveStr(root
, "$.File.Created", "proxmark3 `emv scan`");
1385 JsonSaveStr(root
, "$.Card.Communication", "iso14443-4a");
1386 JsonSaveBufAsHex(root
, "$.Card.UID", (uint8_t *)&card
.uid
, card
.uidlen
);
1387 JsonSaveHex(root
, "$.Card.ATQA", card
.atqa
[0] + (card
.atqa
[1] << 2), 2);
1388 JsonSaveHex(root
, "$.Card.SAK", card
.sak
, 0);
1389 JsonSaveBufAsHex(root
, "$.Card.ATS", (uint8_t *)card
.ats
, card
.ats_len
);
1391 // init applets list tree
1392 const char *al
= "Applets list";
1393 struct tlvdb
*tlvSelect
= tlvdb_fixed(1, strlen(al
), (const unsigned char *)al
);
1396 PrintAndLogEx(NORMAL
, "--> PPSE.");
1397 res
= EMVSelectPSE(channel
, true, true, 2, buf
, sizeof(buf
), &len
, &sw
);
1399 if (!res
&& sw
== 0x9000){
1401 TLVPrintFromBuffer(buf
, len
);
1403 JsonSaveBufAsHex(root
, "$.PPSE.AID", (uint8_t *)"2PAY.SYS.DDF01", 14);
1405 struct tlvdb
*fci
= tlvdb_parse_multi(buf
, len
);
1406 if (extractTLVElements
)
1407 JsonSaveTLVTree(root
, root
, "$.PPSE.FCITemplate", fci
);
1409 JsonSaveTLVTreeElm(root
, "$.PPSE.FCITemplate", fci
, true, true, false);
1410 JsonSaveTLVValue(root
, "$.Application.KernelID", tlvdb_find_full(fci
, 0x9f2a));
1414 res
= EMVSearchPSE(channel
, false, true, psenum
, decodeTLV
, tlvSelect
);
1416 // check PPSE and select application id
1418 TLVPrintAIDlistFromSelectTLV(tlvSelect
);
1420 // EMV SEARCH with AID list
1421 SetAPDULogging(false);
1422 PrintAndLogEx(NORMAL
, "--> AID search.");
1423 if (EMVSearch(channel
, false, true, decodeTLV
, tlvSelect
)) {
1424 PrintAndLogEx(ERR
, "Can't found any of EMV AID. Exit...");
1425 tlvdb_free(tlvSelect
);
1430 // check search and select application id
1431 TLVPrintAIDlistFromSelectTLV(tlvSelect
);
1434 // EMV SELECT application
1435 SetAPDULogging(showAPDU
);
1436 EMVSelectApplication(tlvSelect
, AID
, &AIDlen
);
1438 tlvdb_free(tlvSelect
);
1441 PrintAndLogEx(INFO
, "Can't select AID. EMV AID not found. Exit...");
1442 if (channel
== ECC_CONTACTLESS
) {
1448 JsonSaveBufAsHex(root
, "$.Application.AID", AID
, AIDlen
);
1451 const char *alr
= "Root terminal TLV tree";
1452 struct tlvdb
*tlvRoot
= tlvdb_fixed(1, strlen(alr
), (const unsigned char *)alr
);
1454 // EMV SELECT applet
1456 PrintAndLogEx(NORMAL
, "\n-->Selecting AID:%s.", sprint_hex_inrow(AID
, AIDlen
));
1457 SetAPDULogging(showAPDU
);
1458 res
= EMVSelect(channel
, false, true, AID
, AIDlen
, buf
, sizeof(buf
), &len
, &sw
, tlvRoot
);
1461 PrintAndLogEx(ERR
, "Can't select AID (%d). Exit...", res
);
1462 tlvdb_free(tlvRoot
);
1468 TLVPrintFromBuffer(buf
, len
);
1471 if (tlvdb_get(tlvRoot
, 0x9f38, NULL
)) {
1472 JsonSaveStr(root
, "$.Application.Mode", TransactionTypeStr
[TrType
]);
1475 struct tlvdb
*fci
= tlvdb_parse_multi(buf
, len
);
1476 if (extractTLVElements
)
1477 JsonSaveTLVTree(root
, root
, "$.Application.FCITemplate", fci
);
1479 JsonSaveTLVTreeElm(root
, "$.Application.FCITemplate", fci
, true, true, false);
1482 // create transaction parameters
1483 PrintAndLogEx(NORMAL
, "-->Init transaction parameters.");
1484 InitTransactionParameters(tlvRoot
, paramLoadJSON
, TrType
, GenACGPO
);
1486 PrintAndLogEx(NORMAL
, "-->Calc PDOL.");
1487 struct tlv
*pdol_data_tlv
= dol_process(tlvdb_get(tlvRoot
, 0x9f38, NULL
), tlvRoot
, 0x83);
1488 if (!pdol_data_tlv
){
1489 PrintAndLogEx(ERR
, "Can't create PDOL TLV.");
1490 tlvdb_free(tlvRoot
);
1491 if (channel
== ECC_CONTACTLESS
) {
1497 size_t pdol_data_tlv_data_len
;
1498 unsigned char *pdol_data_tlv_data
= tlv_encode(pdol_data_tlv
, &pdol_data_tlv_data_len
);
1499 if (!pdol_data_tlv_data
) {
1500 PrintAndLogEx(ERR
, "Can't create PDOL data.");
1501 tlvdb_free(tlvRoot
);
1505 PrintAndLogEx(INFO
, "PDOL data[%d]: %s", pdol_data_tlv_data_len
, sprint_hex(pdol_data_tlv_data
, pdol_data_tlv_data_len
));
1507 PrintAndLogEx(INFO
, "-->GPO.");
1508 res
= EMVGPO(channel
, true, pdol_data_tlv_data
, pdol_data_tlv_data_len
, buf
, sizeof(buf
), &len
, &sw
, tlvRoot
);
1510 free(pdol_data_tlv_data
);
1511 free(pdol_data_tlv
);
1514 PrintAndLogEx(ERR
, "GPO error(%d): %4x. Exit...", res
, sw
);
1515 tlvdb_free(tlvRoot
);
1516 if (channel
== ECC_CONTACTLESS
) {
1521 ProcessGPOResponseFormat1(tlvRoot
, buf
, len
, decodeTLV
);
1523 struct tlvdb
*gpofci
= tlvdb_parse_multi(buf
, len
);
1524 if (extractTLVElements
)
1525 JsonSaveTLVTree(root
, root
, "$.Application.GPO", gpofci
);
1527 JsonSaveTLVTreeElm(root
, "$.Application.GPO", gpofci
, true, true, false);
1529 JsonSaveTLVValue(root
, "$.ApplicationData.AIP", tlvdb_find_full(gpofci
, 0x82));
1530 JsonSaveTLVValue(root
, "$.ApplicationData.AFL", tlvdb_find_full(gpofci
, 0x94));
1534 PrintAndLogEx(INFO
, "-->Read records from AFL.");
1535 const struct tlv
*AFL
= tlvdb_get(tlvRoot
, 0x94, NULL
);
1537 while(AFL
&& AFL
->len
) {
1539 PrintAndLogEx(ERR
, "Wrong AFL length: %d", AFL
->len
);
1543 json_t
*sfijson
= json_path_get(root
, "$.Application.Records");
1545 json_t
*app
= json_path_get(root
, "$.Application");
1546 json_object_set_new(app
, "Records", json_array());
1548 sfijson
= json_path_get(root
, "$.Application.Records");
1550 if (!json_is_array(sfijson
)) {
1551 PrintAndLogEx(ERR
, "Internal logic error. `$.Application.Records` is not an array.");
1554 for (int i
= 0; i
< AFL
->len
/ 4; i
++) {
1555 uint8_t SFI
= AFL
->value
[i
* 4 + 0] >> 3;
1556 uint8_t SFIstart
= AFL
->value
[i
* 4 + 1];
1557 uint8_t SFIend
= AFL
->value
[i
* 4 + 2];
1558 uint8_t SFIoffline
= AFL
->value
[i
* 4 + 3];
1560 PrintAndLogEx(INFO
, "--->SFI[%02x] start:%02x end:%02x offline:%02x", SFI
, SFIstart
, SFIend
, SFIoffline
);
1561 if (SFI
== 0 || SFI
== 31 || SFIstart
== 0 || SFIstart
> SFIend
) {
1562 PrintAndLogEx(ERR
, "SFI ERROR! Skipped...");
1566 for(int n
= SFIstart
; n
<= SFIend
; n
++) {
1567 PrintAndLogEx(INFO
, "---->SFI[%02x] %d", SFI
, n
);
1569 res
= EMVReadRecord(channel
, true, SFI
, n
, buf
, sizeof(buf
), &len
, &sw
, tlvRoot
);
1571 PrintAndLogEx(ERR
, "SFI[%02x]. APDU error %4x", SFI
, sw
);
1576 TLVPrintFromBuffer(buf
, len
);
1577 PrintAndLogEx(NORMAL
, "");
1580 json_t
*jsonelm
= json_object();
1581 json_array_append_new(sfijson
, jsonelm
);
1583 JsonSaveHex(jsonelm
, "SFI", SFI
, 1);
1584 JsonSaveHex(jsonelm
, "RecordNum", n
, 1);
1585 JsonSaveHex(jsonelm
, "Offline", SFIoffline
, 1);
1587 struct tlvdb
*rsfi
= tlvdb_parse_multi(buf
, len
);
1588 if (extractTLVElements
)
1589 JsonSaveTLVTree(root
, jsonelm
, "$.Data", rsfi
);
1591 JsonSaveTLVTreeElm(jsonelm
, "$.Data", rsfi
, true, true, false);
1599 // getting certificates
1600 if (tlvdb_get(tlvRoot
, 0x90, NULL
)) {
1601 PrintAndLogEx(INFO
, "-->Recovering certificates.");
1602 PKISetStrictExecution(false);
1603 RecoveryCertificates(tlvRoot
, root
);
1604 PKISetStrictExecution(true);
1608 tlvdb_free(tlvRoot
);
1610 if (channel
== ECC_CONTACTLESS
) {
1614 res
= json_dump_file(root
, fname
, JSON_INDENT(2));
1616 PrintAndLogEx(ERR
, "Can't save the file: %s", fname
);
1619 PrintAndLogEx(SUCCESS
, "File `%s` saved.", fname
);
1627 int CmdEMVTest(const char *cmd
) {
1628 return ExecuteCryptoTests(true);
1631 int CmdEMVRoca(const char *cmd
) {
1632 uint8_t AID
[APDU_DATA_LEN
] = {0};
1634 uint8_t buf
[APDU_RESPONSE_LEN
] = {0};
1639 CLIParserInit("emv roca",
1640 "Tries to extract public keys and run the ROCA test against them.\n",
1642 "\temv roca -w -> select --CONTACT-- card and run test\n"
1643 "\temv roca -> select --CONTACTLESS-- card and run test\n"
1646 void* argtable
[] = {
1648 arg_lit0("tT", "selftest", "self test"),
1649 arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. Contactless interface set by default."),
1652 CLIExecWithReturn(cmd
, argtable
, true);
1654 EMVCommandChannel channel
= ECC_CONTACTLESS
;
1656 return roca_self_test();
1657 #ifdef WITH_SMARTCARD
1659 channel
= ECC_CONTACT
;
1661 PrintChannel(channel
);
1664 uint8_t psenum
= (channel
== ECC_CONTACT
) ? 1 : 2;
1665 char *PSE_or_PPSE
= psenum
== 1 ? "PSE" : "PPSE";
1667 SetAPDULogging(false);
1669 // init applets list tree
1670 const char *al
= "Applets list";
1671 struct tlvdb
*tlvSelect
= tlvdb_fixed(1, strlen(al
), (const unsigned char *)al
);
1674 PrintAndLogEx(NORMAL
, "--> %s.", PSE_or_PPSE
);
1675 res
= EMVSearchPSE(channel
, true, true, psenum
, false, tlvSelect
);
1677 // check PSE/PPSE and select application id
1679 TLVPrintAIDlistFromSelectTLV(tlvSelect
);
1681 // EMV SEARCH with AID list
1682 PrintAndLogEx(NORMAL
, "--> AID search.");
1683 if (EMVSearch(channel
, false, true, false, tlvSelect
)) {
1684 PrintAndLogEx(ERR
, "Couldn't find any known EMV AID. Exit...");
1685 tlvdb_free(tlvSelect
);
1690 // check search and select application id
1691 TLVPrintAIDlistFromSelectTLV(tlvSelect
);
1694 // EMV SELECT application
1695 SetAPDULogging(true);
1696 EMVSelectApplication(tlvSelect
, AID
, &AIDlen
);
1698 tlvdb_free(tlvSelect
);
1701 PrintAndLogEx(INFO
, "Can't select AID. EMV AID not found. Exit...");
1702 if (channel
== ECC_CONTACTLESS
) {
1709 const char *alr
= "Root terminal TLV tree";
1710 struct tlvdb
*tlvRoot
= tlvdb_fixed(1, strlen(alr
), (const unsigned char *)alr
);
1712 // EMV SELECT applet
1713 PrintAndLogEx(NORMAL
, "\n-->Selecting AID:%s.", sprint_hex_inrow(AID
, AIDlen
));
1714 res
= EMVSelect(channel
, false, true, AID
, AIDlen
, buf
, sizeof(buf
), &len
, &sw
, tlvRoot
);
1717 PrintAndLogEx(ERR
, "Can't select AID (%d). Exit...", res
);
1718 tlvdb_free(tlvRoot
);
1719 if (channel
== ECC_CONTACTLESS
) {
1725 PrintAndLog("\n* Init transaction parameters.");
1726 InitTransactionParameters(tlvRoot
, true, TT_QVSDCMCHIP
, false);
1728 PrintAndLogEx(NORMAL
, "-->Calc PDOL.");
1729 struct tlv
*pdol_data_tlv
= dol_process(tlvdb_get(tlvRoot
, 0x9f38, NULL
), tlvRoot
, 0x83);
1730 if (!pdol_data_tlv
){
1731 PrintAndLogEx(ERR
, "Can't create PDOL TLV.");
1732 tlvdb_free(tlvRoot
);
1733 if (channel
== ECC_CONTACTLESS
) {
1739 size_t pdol_data_tlv_data_len
;
1740 unsigned char *pdol_data_tlv_data
= tlv_encode(pdol_data_tlv
, &pdol_data_tlv_data_len
);
1741 if (!pdol_data_tlv_data
) {
1742 PrintAndLogEx(ERR
, "Can't create PDOL data.");
1743 tlvdb_free(tlvRoot
);
1747 PrintAndLogEx(INFO
, "PDOL data[%d]: %s", pdol_data_tlv_data_len
, sprint_hex(pdol_data_tlv_data
, pdol_data_tlv_data_len
));
1749 PrintAndLogEx(INFO
, "-->GPO.");
1750 res
= EMVGPO(channel
, true, pdol_data_tlv_data
, pdol_data_tlv_data_len
, buf
, sizeof(buf
), &len
, &sw
, tlvRoot
);
1752 free(pdol_data_tlv_data
);
1753 free(pdol_data_tlv
);
1756 PrintAndLogEx(ERR
, "GPO error(%d): %4x. Exit...", res
, sw
);
1757 tlvdb_free(tlvRoot
);
1758 if (channel
== ECC_CONTACTLESS
) {
1763 ProcessGPOResponseFormat1(tlvRoot
, buf
, len
, false);
1765 PrintAndLogEx(INFO
, "-->Read records from AFL.");
1766 const struct tlv
*AFL
= tlvdb_get(tlvRoot
, 0x94, NULL
);
1768 while(AFL
&& AFL
->len
) {
1770 PrintAndLogEx(ERR
, "Wrong AFL length: %d", AFL
->len
);
1774 for (int i
= 0; i
< AFL
->len
/ 4; i
++) {
1775 uint8_t SFI
= AFL
->value
[i
* 4 + 0] >> 3;
1776 uint8_t SFIstart
= AFL
->value
[i
* 4 + 1];
1777 uint8_t SFIend
= AFL
->value
[i
* 4 + 2];
1778 uint8_t SFIoffline
= AFL
->value
[i
* 4 + 3];
1780 PrintAndLogEx(INFO
, "--->SFI[%02x] start:%02x end:%02x offline:%02x", SFI
, SFIstart
, SFIend
, SFIoffline
);
1781 if (SFI
== 0 || SFI
== 31 || SFIstart
== 0 || SFIstart
> SFIend
) {
1782 PrintAndLogEx(ERR
, "SFI ERROR! Skipped...");
1786 for(int n
= SFIstart
; n
<= SFIend
; n
++) {
1787 PrintAndLogEx(INFO
, "---->SFI[%02x] %d", SFI
, n
);
1789 res
= EMVReadRecord(channel
, true, SFI
, n
, buf
, sizeof(buf
), &len
, &sw
, tlvRoot
);
1791 PrintAndLogEx(ERR
, "SFI[%02x]. APDU error %4x", SFI
, sw
);
1800 // getting certificates
1801 if (tlvdb_get(tlvRoot
, 0x90, NULL
)) {
1802 PrintAndLogEx(INFO
, "-->Recovering certificates.");
1803 PKISetStrictExecution(false);
1805 struct emv_pk
*pk
= get_ca_pk(tlvRoot
);
1807 PrintAndLogEx(ERR
, "CA Public Key not found. Exit.");
1811 struct emv_pk
*issuer_pk
= emv_pki_recover_issuer_cert(pk
, tlvRoot
);
1814 PrintAndLogEx(WARNING
, "WARNING: Issuer certificate not found. Exit.");
1819 memcpy(RID
, sprint_hex(issuer_pk
->rid
, 5), 14);
1820 PrintAndLogEx(SUCCESS
, "Issuer PK recovered. RID %s IDX %02hhx CSN %s",
1823 sprint_hex(issuer_pk
->serial
, 3)
1827 struct emv_pk
*icc_pk
= emv_pki_recover_icc_cert(issuer_pk
, tlvRoot
, NULL
);
1830 emv_pk_free(issuer_pk
);
1831 PrintAndLogEx(WARNING
, "WARNING: ICC certificate not found. Exit.");
1835 memcpy(RID
, sprint_hex(icc_pk
->rid
, 5), 14);
1836 PrintAndLogEx(SUCCESS
, "ICC PK recovered. RID %s IDX %02hhx CSN %s\n",
1839 sprint_hex(icc_pk
->serial
, 3)
1842 PrintAndLogEx(INFO
, "ICC pk modulus: %s", sprint_hex_inrow(icc_pk
->modulus
, icc_pk
->mlen
));
1844 // icc_pk->exp, icc_pk->elen
1845 // icc_pk->modulus, icc_pk->mlen
1846 if (icc_pk
->elen
> 0 && icc_pk
->mlen
> 0) {
1847 if (emv_rocacheck(icc_pk
->modulus
, icc_pk
->mlen
, true)) {
1848 PrintAndLogEx(INFO
, "ICC pk is a subject to ROCA vulnerability, insecure..");
1850 PrintAndLogEx(INFO
, "ICC pk is OK(");
1854 PKISetStrictExecution(true);
1860 tlvdb_free(tlvRoot
);
1862 if (channel
== ECC_CONTACTLESS
) {
1869 int CmdHelp(const char *Cmd
);
1871 static command_t CommandTable
[] = {
1872 {"help", CmdHelp
, 1, "This help"},
1873 {"exec", CmdEMVExec
, 1, "Executes EMV contactless transaction."},
1874 {"pse", CmdEMVPPSE
, 1, "Execute PPSE. It selects 2PAY.SYS.DDF01 or 1PAY.SYS.DDF01 directory."},
1875 {"search", CmdEMVSearch
, 1, "Try to select all applets from applets list and print installed applets."},
1876 {"select", CmdEMVSelect
, 1, "Select applet."},
1877 {"gpo", CmdEMVGPO
, 1, "Execute GetProcessingOptions."},
1878 {"readrec", CmdEMVReadRecord
, 1, "Read files from card."},
1879 {"genac", CmdEMVAC
, 1, "Generate ApplicationCryptogram."},
1880 {"challenge", CmdEMVGenerateChallenge
, 1, "Generate challenge."},
1881 {"intauth", CmdEMVInternalAuthenticate
, 1, "Internal authentication."},
1882 {"scan", CmdEMVScan
, 1, "Scan EMV card and save it contents to json file for emulator."},
1883 {"test", CmdEMVTest
, 1, "Crypto logic test."},
1884 {"roca", CmdEMVRoca
, 1, "Extract public keys and run ROCA test"},
1885 {NULL
, NULL
, 0, NULL
}
1888 int CmdEMV(const char *Cmd
) {
1889 CmdsParse(CommandTable
, Cmd
);
1893 int CmdHelp(const char *Cmd
) {
1894 CmdsHelp(CommandTable
);