+ CLIParserInit("hf emv search",
+ "Tries to select all applets from applet list:\n",
+ "Usage:\n\thf emv search -s -> select card and search\n\thf emv search -st -> select card, search and show result in TLV\n");
+
+ void* argtable[] = {
+ arg_param_begin,
+ arg_lit0("sS", "select", "activate field and select card"),
+ arg_lit0("kK", "keep", "keep field ON for next command"),
+ arg_lit0("aA", "apdu", "show APDU reqests and responses"),
+ arg_lit0("tT", "tlv", "TLV decode results of selected applets"),
+ arg_param_end
+ };
+ CLIExecWithReturn(cmd, argtable, true);
+
+ bool activateField = arg_get_lit(1);
+ bool leaveSignalON = arg_get_lit(2);
+ bool APDULogging = arg_get_lit(3);
+ bool decodeTLV = arg_get_lit(4);
+ CLIParserFree();
+
+ SetAPDULogging(APDULogging);
+
+ struct tlvdb *t = NULL;
+ const char *al = "Applets list";
+ t = tlvdb_fixed(1, strlen(al), (const unsigned char *)al);
+
+ if (EMVSearch(activateField, leaveSignalON, decodeTLV, t)) {
+ tlvdb_free(t);
+ return 2;
+ }
+
+ PrintAndLog("Search completed.");
+
+ // print list here
+ if (!decodeTLV) {
+ TLVPrintAIDlistFromSelectTLV(t);
+ }
+
+ tlvdb_free(t);
+
+ return 0;
+}
+
+int CmdHFEMVPPSE(const char *cmd) {
+
+ CLIParserInit("hf emv pse",
+ "Executes PSE/PPSE select command. It returns list of applet on the card:\n",
+ "Usage:\n\thf emv pse -s1 -> select, get pse\n\thf emv pse -st2 -> select, get ppse, show result in TLV\n");
+
+ void* argtable[] = {
+ arg_param_begin,
+ arg_lit0("sS", "select", "activate field and select card"),
+ arg_lit0("kK", "keep", "keep field ON for next command"),
+ arg_lit0("1", "pse", "pse (1PAY.SYS.DDF01) mode"),
+ arg_lit0("2", "ppse", "ppse (2PAY.SYS.DDF01) mode (default mode)"),
+ arg_lit0("aA", "apdu", "show APDU reqests and responses"),
+ arg_lit0("tT", "tlv", "TLV decode results of selected applets"),
+ arg_param_end
+ };
+ CLIExecWithReturn(cmd, argtable, true);
+
+ bool activateField = arg_get_lit(1);
+ bool leaveSignalON = arg_get_lit(2);
+ uint8_t PSENum = 2;
+ if (arg_get_lit(3))
+ PSENum = 1;
+ if (arg_get_lit(4))
+ PSENum = 2;
+ bool APDULogging = arg_get_lit(5);
+ bool decodeTLV = arg_get_lit(6);
+ CLIParserFree();
+
+ SetAPDULogging(APDULogging);
+
+ // exec
+ uint8_t buf[APDU_RES_LEN] = {0};
+ size_t len = 0;
+ uint16_t sw = 0;
+ int res = EMVSelectPSE(activateField, leaveSignalON, PSENum, buf, sizeof(buf), &len, &sw);
+
+ if (sw)
+ PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
+
+ if (res)
+ return res;
+
+
+ if (decodeTLV)
+ TLVPrintFromBuffer(buf, len);
+
+ return 0;
+}
+
+int CmdHFEMVGPO(const char *cmd) {
+ uint8_t data[APDU_RES_LEN] = {0};
+ int datalen = 0;
+
+ CLIParserInit("hf emv gpo",
+ "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.",
+ "Usage:\n\thf emv gpo -k -> execute GPO\n"
+ "\thf emv gpo -t 01020304 -> execute GPO with 4-byte PDOL data, show result in TLV\n"
+ "\thf emv gpo -pmt 9F 37 04 -> load params from file, make PDOL data from PDOL, execute GPO with PDOL, show result in TLV\n");
+
+ void* argtable[] = {
+ arg_param_begin,
+ arg_lit0("kK", "keep", "keep field ON for next command"),
+ arg_lit0("pP", "params", "load parameters from `emv/defparams.json` file for PDOLdata making from PDOL and parameters"),
+ arg_lit0("mM", "make", "make PDOLdata from PDOL (tag 9F38) and parameters (by default uses default parameters)"),
+ arg_lit0("aA", "apdu", "show APDU reqests and responses"),
+ arg_lit0("tT", "tlv", "TLV decode results of selected applets"),
+ arg_strx0(NULL, NULL, "<HEX PDOLdata/PDOL>", NULL),
+ arg_param_end
+ };
+ CLIExecWithReturn(cmd, argtable, true);
+
+ bool leaveSignalON = arg_get_lit(1);
+ bool paramsLoadFromFile = arg_get_lit(2);
+ bool dataMakeFromPDOL = arg_get_lit(3);
+ bool APDULogging = arg_get_lit(4);
+ bool decodeTLV = arg_get_lit(5);
+ CLIGetStrWithReturn(6, data, &datalen);
+ CLIParserFree();
+
+ SetAPDULogging(APDULogging);
+
+ // Init TLV tree
+ const char *alr = "Root terminal TLV tree";
+ struct tlvdb *tlvRoot = tlvdb_fixed(1, strlen(alr), (const unsigned char *)alr);
+
+ // calc PDOL
+ struct tlv *pdol_data_tlv = NULL;
+ struct tlv data_tlv = {
+ .tag = 0x83,
+ .len = datalen,
+ .value = (uint8_t *)data,
+ };
+ if (dataMakeFromPDOL) {
+ ParamLoadDefaults(tlvRoot);
+
+ if (paramsLoadFromFile) {
+ PrintAndLog("Params loading from file...");
+ ParamLoadFromJson(tlvRoot);
+ };
+
+ pdol_data_tlv = dol_process((const struct tlv *)tlvdb_external(0x9f38, datalen, data), tlvRoot, 0x83);
+ if (!pdol_data_tlv){
+ PrintAndLog("ERROR: can't create PDOL TLV.");
+ tlvdb_free(tlvRoot);
+ return 4;
+ }
+ } else {
+ if (paramsLoadFromFile) {
+ PrintAndLog("WARNING: don't need to load parameters. Sending plain PDOL data...");
+ }
+ pdol_data_tlv = &data_tlv;
+ }
+
+ size_t pdol_data_tlv_data_len = 0;
+ unsigned char *pdol_data_tlv_data = tlv_encode(pdol_data_tlv, &pdol_data_tlv_data_len);
+ if (!pdol_data_tlv_data) {
+ PrintAndLog("ERROR: can't create PDOL data.");
+ tlvdb_free(tlvRoot);
+ return 4;
+ }
+ PrintAndLog("PDOL data[%d]: %s", pdol_data_tlv_data_len, sprint_hex(pdol_data_tlv_data, pdol_data_tlv_data_len));
+
+ // exec
+ uint8_t buf[APDU_RES_LEN] = {0};
+ size_t len = 0;
+ uint16_t sw = 0;
+ int res = EMVGPO(leaveSignalON, pdol_data_tlv_data, pdol_data_tlv_data_len, buf, sizeof(buf), &len, &sw, tlvRoot);
+
+ if (pdol_data_tlv != &data_tlv)
+ free(pdol_data_tlv);
+ tlvdb_free(tlvRoot);
+
+ if (sw)
+ PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
+
+ if (res)
+ return res;
+
+ if (decodeTLV)
+ TLVPrintFromBuffer(buf, len);
+
+ return 0;
+}
+
+int CmdHFEMVReadRecord(const char *cmd) {
+ uint8_t data[APDU_RES_LEN] = {0};
+ int datalen = 0;
+
+ CLIParserInit("hf emv readrec",
+ "Executes Read Record command. It returns data in TLV format.\nNeeds a bank applet to be selected and sometimes needs GPO to be executed.",
+ "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");
+
+ void* argtable[] = {
+ arg_param_begin,
+ arg_lit0("kK", "keep", "keep field ON for next command"),
+ arg_lit0("aA", "apdu", "show APDU reqests and responses"),
+ arg_lit0("tT", "tlv", "TLV decode results of selected applets"),
+ arg_strx1(NULL, NULL, "<SFI 1byte HEX><SFIrec 1byte HEX>", NULL),
+ arg_param_end
+ };
+ CLIExecWithReturn(cmd, argtable, true);
+
+ bool leaveSignalON = arg_get_lit(1);
+ bool APDULogging = arg_get_lit(2);
+ bool decodeTLV = arg_get_lit(3);
+ CLIGetStrWithReturn(4, data, &datalen);
+ CLIParserFree();
+
+ if (datalen != 2) {
+ PrintAndLog("ERROR: Command needs to have 2 bytes of data");
+ return 1;
+ }
+
+ SetAPDULogging(APDULogging);
+
+ // exec
+ uint8_t buf[APDU_RES_LEN] = {0};
+ size_t len = 0;
+ uint16_t sw = 0;
+ int res = EMVReadRecord(leaveSignalON, data[0], data[1], buf, sizeof(buf), &len, &sw, NULL);
+
+ if (sw)
+ PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
+
+ if (res)
+ return res;
+
+
+ if (decodeTLV)
+ TLVPrintFromBuffer(buf, len);
+
+ return 0;
+}
+
+int CmdHFEMVAC(const char *cmd) {
+ uint8_t data[APDU_RES_LEN] = {0};
+ int datalen = 0;
+
+ CLIParserInit("hf emv genac",
+ "Generate Application Cryptogram command. It returns data in TLV format .\nNeeds a EMV applet to be selected and GPO to be executed.",
+ "Usage:\n\thf emv genac -k 0102 -> generate AC with 2-byte CDOLdata and keep field ON after command\n"
+ "\thf emv genac -t 01020304 -> generate AC with 4-byte CDOL data, show result in TLV\n"
+ "\thf emv genac -Daac 01020304 -> generate AC with 4-byte CDOL data and terminal decision 'declined'\n"
+ "\thf emv genac -pmt 9F 37 04 -> load params from file, make CDOL data from CDOL, generate AC with CDOL, show result in TLV");
+
+ void* argtable[] = {
+ arg_param_begin,
+ arg_lit0("kK", "keep", "keep field ON for next command"),
+ arg_lit0("cC", "cda", "executes CDA transaction. Needs to get SDAD in results."),
+ arg_str0("dD", "decision", "<aac|tc|arqc>", "Terminal decision. aac - declined, tc - approved, arqc - online authorisation requested"),
+ arg_lit0("pP", "params", "load parameters from `emv/defparams.json` file for CDOLdata making from CDOL and parameters"),
+ arg_lit0("mM", "make", "make CDOLdata from CDOL (tag 8C and 8D) and parameters (by default uses default parameters)"),
+ arg_lit0("aA", "apdu", "show APDU reqests and responses"),
+ arg_lit0("tT", "tlv", "TLV decode results of selected applets"),
+ arg_strx1(NULL, NULL, "<HEX CDOLdata/CDOL>", NULL),
+ arg_param_end
+ };
+ CLIExecWithReturn(cmd, argtable, false);
+
+ bool leaveSignalON = arg_get_lit(1);
+ bool trTypeCDA = arg_get_lit(2);
+ uint8_t termDecision = 0xff;
+ if (arg_get_str_len(3)) {
+ if (!strncmp(arg_get_str(3)->sval[0], "aac", 4))
+ termDecision = EMVAC_AAC;
+ if (!strncmp(arg_get_str(3)->sval[0], "tc", 4))
+ termDecision = EMVAC_TC;
+ if (!strncmp(arg_get_str(3)->sval[0], "arqc", 4))
+ termDecision = EMVAC_ARQC;
+
+ if (termDecision == 0xff) {
+ PrintAndLog("ERROR: can't find terminal decision '%s'", arg_get_str(3)->sval[0]);
+ return 1;
+ }
+ } else {
+ termDecision = EMVAC_TC;
+ }
+ if (trTypeCDA)
+ termDecision = termDecision | EMVAC_CDAREQ;
+ bool paramsLoadFromFile = arg_get_lit(4);
+ bool dataMakeFromCDOL = arg_get_lit(5);
+ bool APDULogging = arg_get_lit(6);
+ bool decodeTLV = arg_get_lit(7);
+ CLIGetStrWithReturn(8, data, &datalen);
+ CLIParserFree();
+
+ SetAPDULogging(APDULogging);
+
+ // Init TLV tree
+ const char *alr = "Root terminal TLV tree";
+ struct tlvdb *tlvRoot = tlvdb_fixed(1, strlen(alr), (const unsigned char *)alr);
+
+ // calc CDOL
+ struct tlv *cdol_data_tlv = NULL;
+ struct tlv data_tlv = {
+ .tag = 0x01,
+ .len = datalen,
+ .value = (uint8_t *)data,
+ };
+
+ if (dataMakeFromCDOL) {
+ ParamLoadDefaults(tlvRoot);
+
+ if (paramsLoadFromFile) {
+ PrintAndLog("Params loading from file...");
+ ParamLoadFromJson(tlvRoot);
+ };
+
+ cdol_data_tlv = dol_process((const struct tlv *)tlvdb_external(0x8c, datalen, data), tlvRoot, 0x01); // 0x01 - dummy tag
+ if (!cdol_data_tlv){
+ PrintAndLog("ERROR: can't create CDOL TLV.");
+ tlvdb_free(tlvRoot);
+ return 4;
+ }
+ } else {
+ if (paramsLoadFromFile) {
+ PrintAndLog("WARNING: don't need to load parameters. Sending plain CDOL data...");
+ }
+ cdol_data_tlv = &data_tlv;
+ }
+
+ PrintAndLog("CDOL data[%d]: %s", cdol_data_tlv->len, sprint_hex(cdol_data_tlv->value, cdol_data_tlv->len));
+
+ // exec
+ uint8_t buf[APDU_RES_LEN] = {0};
+ size_t len = 0;
+ uint16_t sw = 0;
+ int res = EMVAC(leaveSignalON, termDecision, (uint8_t *)cdol_data_tlv->value, cdol_data_tlv->len, buf, sizeof(buf), &len, &sw, tlvRoot);
+
+ if (cdol_data_tlv != &data_tlv)
+ free(cdol_data_tlv);
+ tlvdb_free(tlvRoot);
+
+ if (sw)
+ PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
+
+ if (res)
+ return res;
+
+ if (decodeTLV)
+ TLVPrintFromBuffer(buf, len);
+
+ return 0;
+}
+
+int CmdHFEMVGenerateChallenge(const char *cmd) {
+
+ CLIParserInit("hf emv challenge",
+ "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.",
+ "Usage:\n\thf emv challenge -> get challenge\n\thf emv challenge -k -> get challenge, keep fileld ON\n");
+
+ void* argtable[] = {
+ arg_param_begin,
+ arg_lit0("kK", "keep", "keep field ON for next command"),
+ arg_lit0("aA", "apdu", "show APDU reqests and responses"),
+ arg_param_end
+ };
+ CLIExecWithReturn(cmd, argtable, true);
+
+ bool leaveSignalON = arg_get_lit(1);
+ bool APDULogging = arg_get_lit(2);
+ CLIParserFree();
+
+ SetAPDULogging(APDULogging);
+
+ // exec
+ uint8_t buf[APDU_RES_LEN] = {0};
+ size_t len = 0;
+ uint16_t sw = 0;
+ int res = EMVGenerateChallenge(leaveSignalON, buf, sizeof(buf), &len, &sw, NULL);
+
+ if (sw)
+ PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
+
+ if (res)
+ return res;
+
+ PrintAndLog("Challenge: %s", sprint_hex(buf, len));
+
+ if (len != 4 && len != 8)
+ PrintAndLog("WARNING: length of challenge must be 4 or 8, but it %d", len);
+
+ return 0;
+}
+
+int CmdHFEMVInternalAuthenticate(const char *cmd) {
+ uint8_t data[APDU_RES_LEN] = {0};
+ int datalen = 0;
+
+ CLIParserInit("hf emv intauth",
+ "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.",
+ "Usage:\n\thf emv intauth -k 01020304 -> execute Internal Authenticate with 4-byte DDOLdata and keep field ON after command\n"
+ "\thf emv intauth -t 01020304 -> execute Internal Authenticate with 4-byte DDOL data, show result in TLV\n"
+ "\thf emv intauth -pmt 9F 37 04 -> load params from file, make DDOL data from DDOL, Internal Authenticate with DDOL, show result in TLV");
+
+ void* argtable[] = {
+ arg_param_begin,
+ arg_lit0("kK", "keep", "keep field ON for next command"),
+ arg_lit0("pP", "params", "load parameters from `emv/defparams.json` file for DDOLdata making from DDOL and parameters"),
+ arg_lit0("mM", "make", "make DDOLdata from DDOL (tag 9F49) and parameters (by default uses default parameters)"),
+ arg_lit0("aA", "apdu", "show APDU reqests and responses"),
+ arg_lit0("tT", "tlv", "TLV decode results of selected applets"),
+ arg_strx1(NULL, NULL, "<HEX DDOLdata/DDOL>", NULL),
+ arg_param_end
+ };
+ CLIExecWithReturn(cmd, argtable, false);
+
+ bool leaveSignalON = arg_get_lit(1);
+ bool paramsLoadFromFile = arg_get_lit(2);
+ bool dataMakeFromDDOL = arg_get_lit(3);
+ bool APDULogging = arg_get_lit(4);
+ bool decodeTLV = arg_get_lit(5);
+ CLIGetStrWithReturn(6, data, &datalen);
+ CLIParserFree();
+
+ SetAPDULogging(APDULogging);
+
+ // Init TLV tree
+ const char *alr = "Root terminal TLV tree";
+ struct tlvdb *tlvRoot = tlvdb_fixed(1, strlen(alr), (const unsigned char *)alr);
+
+ // calc DDOL
+ struct tlv *ddol_data_tlv = NULL;
+ struct tlv data_tlv = {
+ .tag = 0x01,
+ .len = datalen,
+ .value = (uint8_t *)data,
+ };
+
+ if (dataMakeFromDDOL) {
+ ParamLoadDefaults(tlvRoot);
+
+ if (paramsLoadFromFile) {
+ PrintAndLog("Params loading from file...");
+ ParamLoadFromJson(tlvRoot);
+ };
+
+ ddol_data_tlv = dol_process((const struct tlv *)tlvdb_external(0x9f49, datalen, data), tlvRoot, 0x01); // 0x01 - dummy tag
+ if (!ddol_data_tlv){
+ PrintAndLog("ERROR: can't create DDOL TLV.");
+ tlvdb_free(tlvRoot);
+ return 4;
+ }
+ } else {
+ if (paramsLoadFromFile) {
+ PrintAndLog("WARNING: don't need to load parameters. Sending plain DDOL data...");
+ }
+ ddol_data_tlv = &data_tlv;
+ }
+
+ PrintAndLog("DDOL data[%d]: %s", ddol_data_tlv->len, sprint_hex(ddol_data_tlv->value, ddol_data_tlv->len));
+
+ // exec
+ uint8_t buf[APDU_RES_LEN] = {0};
+ size_t len = 0;
+ uint16_t sw = 0;
+ int res = EMVInternalAuthenticate(leaveSignalON, data, datalen, buf, sizeof(buf), &len, &sw, NULL);
+
+ if (ddol_data_tlv != &data_tlv)
+ free(ddol_data_tlv);
+ tlvdb_free(tlvRoot);
+
+ if (sw)
+ PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
+
+ if (res)
+ return res;
+
+ if (decodeTLV)
+ TLVPrintFromBuffer(buf, len);
+
+ return 0;
+}
+
+#define dreturn(n) {free(pdol_data_tlv);tlvdb_free(tlvSelect);tlvdb_free(tlvRoot);DropField();return n;}
+
+int CmdHFEMVExec(const char *cmd) {