X-Git-Url: http://git.zerfleddert.de/cgi-bin/gitweb.cgi/proxmark3-svn/blobdiff_plain/968ad672808c4bd4ae0d413bbe877f263e3f8228..8d7d7b618777fddcde8897945f2ec42eb85095e2:/client/emv/cmdemv.c diff --git a/client/emv/cmdemv.c b/client/emv/cmdemv.c index d886834d..cb42e8f7 100644 --- a/client/emv/cmdemv.c +++ b/client/emv/cmdemv.c @@ -38,13 +38,13 @@ void ParamLoadDefaults(struct tlvdb *tlvRoot) { TLV_ADD(0x9F66, "\x26\x00\x00\x00"); // qVSDC } -int CmdHFEMVSelect(const char *cmd) { +int CmdEMVSelect(const char *cmd) { uint8_t data[APDU_AID_LEN] = {0}; int datalen = 0; - CLIParserInit("hf emv select", + CLIParserInit("emv select", "Executes select applet command", - "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"); + "Usage:\n\temv select -s a00000000101 -> select card, select applet\n\temv select -st a00000000101 -> select card, select applet, show result in TLV\n"); void* argtable[] = { arg_param_begin, @@ -52,6 +52,9 @@ int CmdHFEMVSelect(const char *cmd) { arg_lit0("kK", "keep", "keep field for next command"), arg_lit0("aA", "apdu", "show APDU reqests and responses"), arg_lit0("tT", "tlv", "TLV decode results"), +#ifdef WITH_SMARTCARD + arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. Contactless interface set by default."), +#endif arg_strx0(NULL, NULL, "", NULL), arg_param_end }; @@ -61,7 +64,14 @@ int CmdHFEMVSelect(const char *cmd) { bool leaveSignalON = arg_get_lit(2); bool APDULogging = arg_get_lit(3); bool decodeTLV = arg_get_lit(4); + EMVCommandChannel channel = ECC_CONTACTLESS; +#ifdef WITH_SMARTCARD + if (arg_get_lit(5)) + channel = ECC_CONTACT; + CLIGetHexWithReturn(6, data, &datalen); +#else CLIGetHexWithReturn(5, data, &datalen); +#endif CLIParserFree(); SetAPDULogging(APDULogging); @@ -70,10 +80,10 @@ int CmdHFEMVSelect(const char *cmd) { uint8_t buf[APDU_RES_LEN] = {0}; size_t len = 0; uint16_t sw = 0; - int res = EMVSelect(activateField, leaveSignalON, data, datalen, buf, sizeof(buf), &len, &sw, NULL); + int res = EMVSelect(channel, activateField, leaveSignalON, data, datalen, buf, sizeof(buf), &len, &sw, NULL); if (sw) - PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + PrintAndLogEx(INFO, "APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); if (res) return res; @@ -84,11 +94,11 @@ int CmdHFEMVSelect(const char *cmd) { return 0; } -int CmdHFEMVSearch(const char *cmd) { +int CmdEMVSearch(const char *cmd) { - CLIParserInit("hf emv search", + CLIParserInit("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"); + "Usage:\n\temv search -s -> select card and search\n\temv search -st -> select card, search and show result in TLV\n"); void* argtable[] = { arg_param_begin, @@ -96,6 +106,9 @@ int CmdHFEMVSearch(const char *cmd) { 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"), +#ifdef WITH_SMARTCARD + arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. Contactless interface set by default."), +#endif arg_param_end }; CLIExecWithReturn(cmd, argtable, true); @@ -104,6 +117,11 @@ int CmdHFEMVSearch(const char *cmd) { bool leaveSignalON = arg_get_lit(2); bool APDULogging = arg_get_lit(3); bool decodeTLV = arg_get_lit(4); + EMVCommandChannel channel = ECC_CONTACTLESS; +#ifdef WITH_SMARTCARD + if (arg_get_lit(5)) + channel = ECC_CONTACT; +#endif CLIParserFree(); SetAPDULogging(APDULogging); @@ -112,12 +130,12 @@ int CmdHFEMVSearch(const char *cmd) { const char *al = "Applets list"; t = tlvdb_fixed(1, strlen(al), (const unsigned char *)al); - if (EMVSearch(activateField, leaveSignalON, decodeTLV, t)) { + if (EMVSearch(channel, activateField, leaveSignalON, decodeTLV, t)) { tlvdb_free(t); return 2; } - PrintAndLog("Search completed."); + PrintAndLogEx(SUCCESS, "Search completed."); // print list here if (!decodeTLV) { @@ -129,11 +147,11 @@ int CmdHFEMVSearch(const char *cmd) { return 0; } -int CmdHFEMVPPSE(const char *cmd) { +int CmdEMVPPSE(const char *cmd) { - CLIParserInit("hf emv pse", + CLIParserInit("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"); + "Usage:\n\temv pse -s1 -> select, get pse\n\temv pse -st2 -> select, get ppse, show result in TLV\n"); void* argtable[] = { arg_param_begin, @@ -143,6 +161,9 @@ int CmdHFEMVPPSE(const char *cmd) { 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"), +#ifdef WITH_SMARTCARD + arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. Contactless interface set by default."), +#endif arg_param_end }; CLIExecWithReturn(cmd, argtable, true); @@ -156,7 +177,12 @@ int CmdHFEMVPPSE(const char *cmd) { PSENum = 2; bool APDULogging = arg_get_lit(5); bool decodeTLV = arg_get_lit(6); - CLIParserFree(); + EMVCommandChannel channel = ECC_CONTACTLESS; +#ifdef WITH_SMARTCARD + if (arg_get_lit(7)) + channel = ECC_CONTACT; +#endif + CLIParserFree(); SetAPDULogging(APDULogging); @@ -164,10 +190,10 @@ int CmdHFEMVPPSE(const char *cmd) { 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); + int res = EMVSelectPSE(channel, activateField, leaveSignalON, PSENum, buf, sizeof(buf), &len, &sw); if (sw) - PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + PrintAndLogEx(INFO, "APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); if (res) return res; @@ -179,15 +205,15 @@ int CmdHFEMVPPSE(const char *cmd) { return 0; } -int CmdHFEMVGPO(const char *cmd) { +int CmdEMVGPO(const char *cmd) { uint8_t data[APDU_RES_LEN] = {0}; int datalen = 0; - CLIParserInit("hf emv gpo", + CLIParserInit("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"); + "Usage:\n\temv gpo -k -> execute GPO\n" + "\temv gpo -t 01020304 -> execute GPO with 4-byte PDOL data, show result in TLV\n" + "\temv 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, @@ -196,6 +222,9 @@ int CmdHFEMVGPO(const char *cmd) { 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"), +#ifdef WITH_SMARTCARD + arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. Contactless interface set by default."), +#endif arg_strx0(NULL, NULL, "", NULL), arg_param_end }; @@ -206,8 +235,15 @@ int CmdHFEMVGPO(const char *cmd) { bool dataMakeFromPDOL = arg_get_lit(3); bool APDULogging = arg_get_lit(4); bool decodeTLV = arg_get_lit(5); + EMVCommandChannel channel = ECC_CONTACTLESS; +#ifdef WITH_SMARTCARD + if (arg_get_lit(6)) + channel = ECC_CONTACT; + CLIGetHexWithReturn(7, data, &datalen); +#else CLIGetHexWithReturn(6, data, &datalen); - CLIParserFree(); +#endif + CLIParserFree(); SetAPDULogging(APDULogging); @@ -226,19 +262,19 @@ int CmdHFEMVGPO(const char *cmd) { ParamLoadDefaults(tlvRoot); if (paramsLoadFromFile) { - PrintAndLog("Params loading from file..."); + PrintAndLogEx(INFO, "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."); + PrintAndLogEx(ERR, "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..."); + PrintAndLogEx(WARNING, "Don't need to load parameters. Sending plain PDOL data..."); } pdol_data_tlv = &data_tlv; } @@ -246,24 +282,24 @@ int CmdHFEMVGPO(const char *cmd) { 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."); + PrintAndLogEx(ERR, "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)); + PrintAndLogEx(INFO, "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); + int res = EMVGPO(channel, 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)); + PrintAndLogEx(INFO, "APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); if (res) return res; @@ -274,19 +310,22 @@ int CmdHFEMVGPO(const char *cmd) { return 0; } -int CmdHFEMVReadRecord(const char *cmd) { +int CmdEMVReadRecord(const char *cmd) { uint8_t data[APDU_RES_LEN] = {0}; int datalen = 0; - CLIParserInit("hf emv readrec", + CLIParserInit("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"); + "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"); 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"), +#ifdef WITH_SMARTCARD + arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. Contactless interface set by default."), +#endif arg_strx1(NULL, NULL, "", NULL), arg_param_end }; @@ -295,11 +334,18 @@ int CmdHFEMVReadRecord(const char *cmd) { bool leaveSignalON = arg_get_lit(1); bool APDULogging = arg_get_lit(2); bool decodeTLV = arg_get_lit(3); + EMVCommandChannel channel = ECC_CONTACTLESS; +#ifdef WITH_SMARTCARD + if (arg_get_lit(4)) + channel = ECC_CONTACT; + CLIGetHexWithReturn(5, data, &datalen); +#else CLIGetHexWithReturn(4, data, &datalen); +#endif CLIParserFree(); if (datalen != 2) { - PrintAndLog("ERROR: Command needs to have 2 bytes of data"); + PrintAndLogEx(ERROR, "Command needs to have 2 bytes of data"); return 1; } @@ -309,10 +355,10 @@ int CmdHFEMVReadRecord(const char *cmd) { 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); + int res = EMVReadRecord(channel, 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)); + PrintAndLogEx(INFO, "APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); if (res) return res; @@ -324,16 +370,16 @@ int CmdHFEMVReadRecord(const char *cmd) { return 0; } -int CmdHFEMVAC(const char *cmd) { +int CmdEMVAC(const char *cmd) { uint8_t data[APDU_RES_LEN] = {0}; int datalen = 0; - CLIParserInit("hf emv genac", + CLIParserInit("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"); + "Usage:\n\temv genac -k 0102 -> generate AC with 2-byte CDOLdata and keep field ON after command\n" + "\temv genac -t 01020304 -> generate AC with 4-byte CDOL data, show result in TLV\n" + "\temv genac -Daac 01020304 -> generate AC with 4-byte CDOL data and terminal decision 'declined'\n" + "\temv 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, @@ -344,6 +390,9 @@ int CmdHFEMVAC(const char *cmd) { 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"), +#ifdef WITH_SMARTCARD + arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. Contactless interface set by default."), +#endif arg_strx1(NULL, NULL, "", NULL), arg_param_end }; @@ -373,8 +422,15 @@ int CmdHFEMVAC(const char *cmd) { bool dataMakeFromCDOL = arg_get_lit(5); bool APDULogging = arg_get_lit(6); bool decodeTLV = arg_get_lit(7); + EMVCommandChannel channel = ECC_CONTACTLESS; +#ifdef WITH_SMARTCARD + if (arg_get_lit(8)) + channel = ECC_CONTACT; + CLIGetHexWithReturn(9, data, &datalen); +#else CLIGetHexWithReturn(8, data, &datalen); - CLIParserFree(); +#endif + CLIParserFree(); SetAPDULogging(APDULogging); @@ -394,37 +450,37 @@ int CmdHFEMVAC(const char *cmd) { ParamLoadDefaults(tlvRoot); if (paramsLoadFromFile) { - PrintAndLog("Params loading from file..."); + PrintAndLogEx(INFO, "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."); + PrintAndLogEx(ERR, "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..."); + PrintAndLogEx(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)); + PrintAndLogEx(INFO, "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); + int res = EMVAC(channel, 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)); + PrintAndLogEx(INFO, "APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); if (res) return res; @@ -432,26 +488,34 @@ int CmdHFEMVAC(const char *cmd) { if (decodeTLV) TLVPrintFromBuffer(buf, len); - return 0; + return 0; } -int CmdHFEMVGenerateChallenge(const char *cmd) { +int CmdEMVGenerateChallenge(const char *cmd) { - CLIParserInit("hf emv challenge", + CLIParserInit("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"); + "Usage:\n\temv challenge -> get challenge\n\temv 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"), +#ifdef WITH_SMARTCARD + arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. Contactless interface set by default."), +#endif arg_param_end }; CLIExecWithReturn(cmd, argtable, true); bool leaveSignalON = arg_get_lit(1); bool APDULogging = arg_get_lit(2); - CLIParserFree(); + EMVCommandChannel channel = ECC_CONTACTLESS; +#ifdef WITH_SMARTCARD + if (arg_get_lit(3)) + channel = ECC_CONTACT; +#endif + CLIParserFree(); SetAPDULogging(APDULogging); @@ -459,31 +523,31 @@ int CmdHFEMVGenerateChallenge(const char *cmd) { 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); + int res = EMVGenerateChallenge(channel, leaveSignalON, buf, sizeof(buf), &len, &sw, NULL); if (sw) - PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + PrintAndLogEx(INFO, "APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); if (res) return res; - PrintAndLog("Challenge: %s", sprint_hex(buf, len)); + PrintAndLogEx(SUCCESS, "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); + PrintAndLogEx(WARNING, "Length of challenge must be 4 or 8, but it %d", len); return 0; } -int CmdHFEMVInternalAuthenticate(const char *cmd) { +int CmdEMVInternalAuthenticate(const char *cmd) { uint8_t data[APDU_RES_LEN] = {0}; int datalen = 0; - CLIParserInit("hf emv intauth", + CLIParserInit("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"); + "Usage:\n\temv intauth -k 01020304 -> execute Internal Authenticate with 4-byte DDOLdata and keep field ON after command\n" + "\temv intauth -t 01020304 -> execute Internal Authenticate with 4-byte DDOL data, show result in TLV\n" + "\temv 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, @@ -492,6 +556,9 @@ int CmdHFEMVInternalAuthenticate(const char *cmd) { 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"), +#ifdef WITH_SMARTCARD + arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. Contactless interface set by default."), +#endif arg_strx1(NULL, NULL, "", NULL), arg_param_end }; @@ -502,8 +569,15 @@ int CmdHFEMVInternalAuthenticate(const char *cmd) { bool dataMakeFromDDOL = arg_get_lit(3); bool APDULogging = arg_get_lit(4); bool decodeTLV = arg_get_lit(5); + EMVCommandChannel channel = ECC_CONTACTLESS; +#ifdef WITH_SMARTCARD + if (arg_get_lit(6)) + channel = ECC_CONTACT; + CLIGetHexWithReturn(7, data, &datalen); +#else CLIGetHexWithReturn(6, data, &datalen); - CLIParserFree(); +#endif + CLIParserFree(); SetAPDULogging(APDULogging); @@ -523,37 +597,37 @@ int CmdHFEMVInternalAuthenticate(const char *cmd) { ParamLoadDefaults(tlvRoot); if (paramsLoadFromFile) { - PrintAndLog("Params loading from file..."); + PrintAndLogEx(INFO, "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."); + PrintAndLogEx(ERR, "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..."); + PrintAndLogEx(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)); + PrintAndLogEx(INFO, "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); + int res = EMVInternalAuthenticate(channel, leaveSignalON, data, datalen, buf, sizeof(buf), &len, &sw, NULL); if (ddol_data_tlv != &data_tlv) free(ddol_data_tlv); - tlvdb_free(tlvRoot); + tlvdb_free(tlvRoot); if (sw) - PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + PrintAndLogEx(INFO, "APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); if (res) return res; @@ -561,7 +635,7 @@ int CmdHFEMVInternalAuthenticate(const char *cmd) { if (decodeTLV) TLVPrintFromBuffer(buf, len); - return 0; + return 0; } #define dreturn(n) {free(pdol_data_tlv);tlvdb_free(tlvSelect);tlvdb_free(tlvRoot);DropField();return n;} @@ -607,13 +681,13 @@ void ProcessGPOResponseFormat1(struct tlvdb *tlvRoot, uint8_t *buf, size_t len, } if (len < 4 || (len - 4) % 4) { - PrintAndLog("ERROR: GPO response format1 parsing error. length=%d", len); + PrintAndLogEx(ERR, "GPO response format1 parsing error. length=%d", len); } else { // AIP struct tlvdb * f1AIP = tlvdb_fixed(0x82, 2, buf + 2); tlvdb_add(tlvRoot, f1AIP); if (decodeTLV){ - PrintAndLog("\n* * Decode response format 1 (0x80) AIP and AFL:"); + PrintAndLogEx(INFO, "\n* * Decode response format 1 (0x80) AIP and AFL:"); TLVPrintFromTLV(f1AIP); } @@ -622,14 +696,14 @@ void ProcessGPOResponseFormat1(struct tlvdb *tlvRoot, uint8_t *buf, size_t len, tlvdb_add(tlvRoot, f1AFL); if (decodeTLV) TLVPrintFromTLV(f1AFL); - } + } } else { if (decodeTLV) TLVPrintFromBuffer(buf, len); } } -int CmdHFEMVExec(const char *cmd) { +int CmdEMVExec(const char *cmd) { uint8_t buf[APDU_RES_LEN] = {0}; size_t len = 0; uint16_t sw = 0; @@ -644,10 +718,10 @@ int CmdHFEMVExec(const char *cmd) { struct tlvdb *tlvRoot = NULL; struct tlv *pdol_data_tlv = NULL; - CLIParserInit("hf emv exec", + CLIParserInit("emv exec", "Executes EMV contactless transaction", - "Usage:\n\thf emv exec -sat -> select card, execute MSD transaction, show APDU and TLV\n" - "\thf emv exec -satc -> select card, execute CDA transaction, show APDU and TLV\n"); + "Usage:\n\temv exec -sat -> select card, execute MSD transaction, show APDU and TLV\n" + "\temv exec -satc -> select card, execute CDA transaction, show APDU and TLV\n"); void* argtable[] = { arg_param_begin, @@ -661,6 +735,9 @@ int CmdHFEMVExec(const char *cmd) { arg_lit0("cC", "qvsdccda", "Transaction type - qVSDC or M/Chip plus CDA (SDAD generation)."), arg_lit0("xX", "vsdc", "Transaction type - VSDC. For test only. Not a standart behavior."), arg_lit0("gG", "acgpo", "VISA. generate AC from GPO."), +#ifdef WITH_SMARTCARD + arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. Contactless interface set by default."), +#endif arg_param_end }; CLIExecWithReturn(cmd, argtable, true); @@ -672,14 +749,19 @@ int CmdHFEMVExec(const char *cmd) { bool forceSearch = arg_get_lit(5); enum TransactionType TrType = TT_MSD; - if (arg_get_lit(6)) - TrType = TT_QVSDCMCHIP; if (arg_get_lit(7)) - TrType = TT_CDA; + TrType = TT_QVSDCMCHIP; if (arg_get_lit(8)) + TrType = TT_CDA; + if (arg_get_lit(9)) TrType = TT_VSDC; - bool GenACGPO = arg_get_lit(9); + bool GenACGPO = arg_get_lit(10); + EMVCommandChannel channel = ECC_CONTACTLESS; +#ifdef WITH_SMARTCARD + if (arg_get_lit(11)) + channel = ECC_CONTACT; +#endif CLIParserFree(); SetAPDULogging(showAPDU); @@ -692,12 +774,12 @@ int CmdHFEMVExec(const char *cmd) { // https://www.openscdp.org/scripts/tutorial/emv/applicationselection.html if (!forceSearch) { // PPSE - PrintAndLog("\n* PPSE."); + PrintAndLogEx(NORMAL, "\n* PPSE."); SetAPDULogging(showAPDU); - res = EMVSearchPSE(activateField, true, decodeTLV, tlvSelect); + res = EMVSearchPSE(channel, activateField, true, decodeTLV, tlvSelect); // check PPSE and select application id - if (!res) { + if (!res) { TLVPrintAIDlistFromSelectTLV(tlvSelect); EMVSelectApplication(tlvSelect, AID, &AIDlen); } @@ -705,9 +787,9 @@ int CmdHFEMVExec(const char *cmd) { // Search if (!AIDlen) { - PrintAndLog("\n* Search AID in list."); + PrintAndLogEx(NORMAL, "\n* Search AID in list."); SetAPDULogging(false); - if (EMVSearch(activateField, true, decodeTLV, tlvSelect)) { + if (EMVSearch(channel, activateField, true, decodeTLV, tlvSelect)) { dreturn(2); } @@ -722,51 +804,51 @@ int CmdHFEMVExec(const char *cmd) { // check if we found EMV application on card if (!AIDlen) { - PrintAndLog("Can't select AID. EMV AID not found"); + PrintAndLogEx(WARNING, "Can't select AID. EMV AID not found"); dreturn(2); } // Select - PrintAndLog("\n* Selecting AID:%s", sprint_hex_inrow(AID, AIDlen)); + PrintAndLogEx(NORMAL, "\n* Selecting AID:%s", sprint_hex_inrow(AID, AIDlen)); SetAPDULogging(showAPDU); - res = EMVSelect(false, true, AID, AIDlen, buf, sizeof(buf), &len, &sw, tlvRoot); + res = EMVSelect(channel, false, true, AID, AIDlen, buf, sizeof(buf), &len, &sw, tlvRoot); - if (res) { - PrintAndLog("Can't select AID (%d). Exit...", res); + if (res) { + PrintAndLogEx(WARNING, "Can't select AID (%d). Exit...", res); dreturn(3); } if (decodeTLV) TLVPrintFromBuffer(buf, len); - PrintAndLog("* Selected."); + PrintAndLogEx(INFO, "* Selected."); - PrintAndLog("\n* Init transaction parameters."); + PrintAndLogEx(INFO, "\n* Init transaction parameters."); InitTransactionParameters(tlvRoot, paramLoadJSON, TrType, GenACGPO); TLVPrintFromTLV(tlvRoot); // TODO delete!!! - PrintAndLog("\n* Calc PDOL."); + PrintAndLogEx(NORMAL, "\n* Calc PDOL."); pdol_data_tlv = dol_process(tlvdb_get(tlvRoot, 0x9f38, NULL), tlvRoot, 0x83); if (!pdol_data_tlv){ - PrintAndLog("ERROR: can't create PDOL TLV."); + PrintAndLogEx(WARNING, "Error: can't create PDOL TLV."); dreturn(4); } size_t pdol_data_tlv_data_len; 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."); + PrintAndLogEx(WARNING, "Error: can't create PDOL data."); dreturn(4); } - PrintAndLog("PDOL data[%d]: %s", pdol_data_tlv_data_len, sprint_hex(pdol_data_tlv_data, pdol_data_tlv_data_len)); + PrintAndLogEx(NORMAL, "PDOL data[%d]: %s", pdol_data_tlv_data_len, sprint_hex(pdol_data_tlv_data, pdol_data_tlv_data_len)); - PrintAndLog("\n* GPO."); - res = EMVGPO(true, pdol_data_tlv_data, pdol_data_tlv_data_len, buf, sizeof(buf), &len, &sw, tlvRoot); + PrintAndLogEx(NORMAL, "\n* GPO."); + res = EMVGPO(channel, true, pdol_data_tlv_data, pdol_data_tlv_data_len, buf, sizeof(buf), &len, &sw, tlvRoot); free(pdol_data_tlv_data); //free(pdol_data_tlv); --- free on exit. - if (res) { - PrintAndLog("GPO error(%d): %4x. Exit...", res, sw); + if (res) { + PrintAndLogEx(NORMAL, "GPO error(%d): %4x. Exit...", res, sw); dreturn(5); } @@ -781,23 +863,23 @@ int CmdHFEMVExec(const char *cmd) { if (pan) { tlvdb_add(tlvRoot, pan); - const struct tlv *pantlv = tlvdb_get(tlvRoot, 0x5a, NULL); - PrintAndLog("\n* * Extracted PAN from track2: %s", sprint_hex(pantlv->value, pantlv->len)); + const struct tlv *pantlv = tlvdb_get(tlvRoot, 0x5a, NULL); + PrintAndLogEx(NORMAL, "\n* * Extracted PAN from track2: %s", sprint_hex(pantlv->value, pantlv->len)); } else { - PrintAndLog("\n* * WARNING: Can't extract PAN from track2."); + PrintAndLogEx(NORMAL, "\n* * WARNING: Can't extract PAN from track2."); } } } - PrintAndLog("\n* Read records from AFL."); + PrintAndLogEx(NORMAL, "\n* Read records from AFL."); const struct tlv *AFL = tlvdb_get(tlvRoot, 0x94, NULL); if (!AFL || !AFL->len) { - PrintAndLog("WARNING: AFL not found."); + PrintAndLogEx(WARNING, "AFL not found."); } while(AFL && AFL->len) { if (AFL->len % 4) { - PrintAndLog("ERROR: Wrong AFL length: %d", AFL->len); + PrintAndLogEx(WARNING, "Error: Wrong AFL length: %d", AFL->len); break; } @@ -807,24 +889,24 @@ int CmdHFEMVExec(const char *cmd) { uint8_t SFIend = AFL->value[i * 4 + 2]; uint8_t SFIoffline = AFL->value[i * 4 + 3]; - PrintAndLog("* * SFI[%02x] start:%02x end:%02x offline:%02x", SFI, SFIstart, SFIend, SFIoffline); + PrintAndLogEx(NORMAL, "* * SFI[%02x] start:%02x end:%02x offline:%02x", SFI, SFIstart, SFIend, SFIoffline); if (SFI == 0 || SFI == 31 || SFIstart == 0 || SFIstart > SFIend) { - PrintAndLog("SFI ERROR! Skipped..."); + PrintAndLogEx(NORMAL, "SFI ERROR! Skipped..."); continue; } for(int n = SFIstart; n <= SFIend; n++) { - PrintAndLog("* * * SFI[%02x] %d", SFI, n); + PrintAndLogEx(NORMAL, "* * * SFI[%02x] %d", SFI, n); - res = EMVReadRecord(true, SFI, n, buf, sizeof(buf), &len, &sw, tlvRoot); + res = EMVReadRecord(channel, true, SFI, n, buf, sizeof(buf), &len, &sw, tlvRoot); if (res) { - PrintAndLog("ERROR SFI[%02x]. APDU error %4x", SFI, sw); + PrintAndLogEx(WARNING, "Error SFI[%02x]. APDU error %4x", SFI, sw); continue; } if (decodeTLV) { TLVPrintFromBuffer(buf, len); - PrintAndLog(""); + PrintAndLogEx(NORMAL, ""); } // Build Input list for Offline Data Authentication @@ -838,7 +920,7 @@ int CmdHFEMVExec(const char *cmd) { memcpy(&ODAiList[ODAiListLen], &buf[len - elmlen], elmlen); ODAiListLen += elmlen; } else { - PrintAndLog("ERROR SFI[%02x]. Creating input list for Offline Data Authentication error.", SFI); + PrintAndLogEx(WARNING, "Error SFI[%02x]. Creating input list for Offline Data Authentication error.", SFI); } } else { memcpy(&ODAiList[ODAiListLen], buf, len); @@ -849,31 +931,36 @@ int CmdHFEMVExec(const char *cmd) { } break; - } + } // copy Input list for Offline Data Authentication if (ODAiListLen) { struct tlvdb *oda = tlvdb_fixed(0x21, ODAiListLen, ODAiList); // not a standard tag tlvdb_add(tlvRoot, oda); - PrintAndLog("* Input list for Offline Data Authentication added to TLV. len=%d \n", ODAiListLen); + PrintAndLogEx(NORMAL, "* Input list for Offline Data Authentication added to TLV. len=%d \n", ODAiListLen); } // get AIP - const struct tlv *AIPtlv = tlvdb_get(tlvRoot, 0x82, NULL); - uint16_t AIP = AIPtlv->value[0] + AIPtlv->value[1] * 0x100; - PrintAndLog("* * AIP=%04x", AIP); + uint16_t AIP = 0; + const struct tlv *AIPtlv = tlvdb_get(tlvRoot, 0x82, NULL); + if (AIPtlv) { + AIP = AIPtlv->value[0] + AIPtlv->value[1] * 0x100; + PrintAndLogEx(NORMAL, "* * AIP=%04x", AIP); + } else { + PrintAndLogEx(ERR, "Can't found AIP."); + } // SDA if (AIP & 0x0040) { - PrintAndLog("\n* SDA"); + PrintAndLogEx(NORMAL, "\n* SDA"); trSDA(tlvRoot); } // DDA if (AIP & 0x0020) { - PrintAndLog("\n* DDA"); - trDDA(decodeTLV, tlvRoot); - } + PrintAndLogEx(NORMAL, "\n* DDA"); + trDDA(channel, decodeTLV, tlvRoot); + } // transaction check @@ -882,8 +969,8 @@ int CmdHFEMVExec(const char *cmd) { // 9F26: Application Cryptogram const struct tlv *AC = tlvdb_get(tlvRoot, 0x9F26, NULL); if (AC) { - PrintAndLog("\n--> qVSDC transaction."); - PrintAndLog("* AC path"); + PrintAndLogEx(NORMAL, "\n--> qVSDC transaction."); + PrintAndLogEx(NORMAL, "* AC path"); // 9F36: Application Transaction Counter (ATC) const struct tlv *ATC = tlvdb_get(tlvRoot, 0x9F36, NULL); @@ -893,24 +980,24 @@ int CmdHFEMVExec(const char *cmd) { const struct tlv *IAD = tlvdb_get(tlvRoot, 0x9F10, NULL); // print AC data - PrintAndLog("ATC: %s", sprint_hex(ATC->value, ATC->len)); - PrintAndLog("AC: %s", sprint_hex(AC->value, AC->len)); + PrintAndLogEx(NORMAL, "ATC: %s", sprint_hex(ATC->value, ATC->len)); + PrintAndLogEx(NORMAL, "AC: %s", sprint_hex(AC->value, AC->len)); if (IAD){ - PrintAndLog("IAD: %s", sprint_hex(IAD->value, IAD->len)); + PrintAndLogEx(NORMAL, "IAD: %s", sprint_hex(IAD->value, IAD->len)); if (IAD->len >= IAD->value[0] + 1) { - PrintAndLog("\tKey index: 0x%02x", IAD->value[1]); - PrintAndLog("\tCrypto ver: 0x%02x(%03d)", IAD->value[2], IAD->value[2]); - PrintAndLog("\tCVR:", sprint_hex(&IAD->value[3], IAD->value[0] - 2)); + PrintAndLogEx(NORMAL, "\tKey index: 0x%02x", IAD->value[1]); + PrintAndLogEx(NORMAL, "\tCrypto ver: 0x%02x(%03d)", IAD->value[2], IAD->value[2]); + PrintAndLogEx(NORMAL, "\tCVR:", sprint_hex(&IAD->value[3], IAD->value[0] - 2)); struct tlvdb * cvr = tlvdb_fixed(0x20, IAD->value[0] - 2, &IAD->value[3]); TLVPrintFromTLVLev(cvr, 1); } } else { - PrintAndLog("WARNING: IAD not found."); + PrintAndLogEx(WARNING, "IAD not found."); } } else { - PrintAndLog("ERROR AC: Application Transaction Counter (ATC) not found."); + PrintAndLogEx(ERROR, "AC: Application Transaction Counter (ATC) not found."); } } } @@ -919,16 +1006,16 @@ int CmdHFEMVExec(const char *cmd) { if (GetCardPSVendor(AID, AIDlen) == CV_MASTERCARD && (TrType == TT_QVSDCMCHIP || TrType == TT_CDA)){ const struct tlv *CDOL1 = tlvdb_get(tlvRoot, 0x8c, NULL); if (CDOL1 && GetCardPSVendor(AID, AIDlen) == CV_MASTERCARD) { // and m/chip transaction flag - PrintAndLog("\n--> Mastercard M/Chip transaction."); + PrintAndLogEx(NORMAL, "\n--> Mastercard M/Chip transaction."); - PrintAndLog("* * Generate challenge"); - res = EMVGenerateChallenge(true, buf, sizeof(buf), &len, &sw, tlvRoot); + PrintAndLogEx(NORMAL, "* * Generate challenge"); + res = EMVGenerateChallenge(channel, true, buf, sizeof(buf), &len, &sw, tlvRoot); if (res) { - PrintAndLog("ERROR GetChallenge. APDU error %4x", sw); + PrintAndLogEx(WARNING, "GetChallenge. APDU error %4x", sw); dreturn(6); } if (len < 4) { - PrintAndLog("ERROR GetChallenge. Wrong challenge length %d", len); + PrintAndLogEx(WARNING, "GetChallenge. Wrong challenge length %d", len); dreturn(6); } @@ -936,24 +1023,24 @@ int CmdHFEMVExec(const char *cmd) { struct tlvdb * ICCDynN = tlvdb_fixed(0x9f4c, len, buf); tlvdb_add(tlvRoot, ICCDynN); if (decodeTLV){ - PrintAndLog("\n* * ICC Dynamic Number:"); + PrintAndLogEx(NORMAL, "\n* * ICC Dynamic Number:"); TLVPrintFromTLV(ICCDynN); } - PrintAndLog("* * Calc CDOL1"); + PrintAndLogEx(NORMAL, "* * Calc CDOL1"); struct tlv *cdol_data_tlv = dol_process(tlvdb_get(tlvRoot, 0x8c, NULL), tlvRoot, 0x01); // 0x01 - dummy tag if (!cdol_data_tlv){ - PrintAndLog("ERROR: can't create CDOL1 TLV."); + PrintAndLogEx(WARNING, "Error: can't create CDOL1 TLV."); dreturn(6); } - PrintAndLog("CDOL1 data[%d]: %s", cdol_data_tlv->len, sprint_hex(cdol_data_tlv->value, cdol_data_tlv->len)); + PrintAndLogEx(NORMAL, "CDOL1 data[%d]: %s", cdol_data_tlv->len, sprint_hex(cdol_data_tlv->value, cdol_data_tlv->len)); - PrintAndLog("* * AC1"); + PrintAndLogEx(NORMAL, "* * AC1"); // EMVAC_TC + EMVAC_CDAREQ --- to get SDAD - 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); + 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); - if (res) { - PrintAndLog("AC1 error(%d): %4x. Exit...", res, sw); + if (res) { + PrintAndLogEx(NORMAL, "AC1 error(%d): %4x. Exit...", res, sw); dreturn(7); } @@ -961,41 +1048,41 @@ int CmdHFEMVExec(const char *cmd) { TLVPrintFromBuffer(buf, len); // CDA - PrintAndLog("\n* CDA:"); + PrintAndLogEx(NORMAL, "\n* CDA:"); struct tlvdb *ac_tlv = tlvdb_parse_multi(buf, len); res = trCDA(tlvRoot, ac_tlv, pdol_data_tlv, cdol_data_tlv); - if (res) { - PrintAndLog("CDA error (%d)", res); + if (res) { + PrintAndLogEx(NORMAL, "CDA error (%d)", res); } free(ac_tlv); free(cdol_data_tlv); - PrintAndLog("\n* M/Chip transaction result:"); + PrintAndLogEx(NORMAL, "\n* M/Chip transaction result:"); // 9F27: Cryptogram Information Data (CID) const struct tlv *CID = tlvdb_get(tlvRoot, 0x9F27, NULL); if (CID) { emv_tag_dump(CID, stdout, 0); - PrintAndLog("------------------------------"); + PrintAndLogEx(NORMAL, "------------------------------"); if (CID->len > 0) { switch(CID->value[0] & EMVAC_AC_MASK){ case EMVAC_AAC: - PrintAndLog("Transaction DECLINED."); + PrintAndLogEx(NORMAL, "Transaction DECLINED."); break; case EMVAC_TC: - PrintAndLog("Transaction approved OFFLINE."); + PrintAndLogEx(NORMAL, "Transaction approved OFFLINE."); break; case EMVAC_ARQC: - PrintAndLog("Transaction approved ONLINE."); + PrintAndLogEx(NORMAL, "Transaction approved ONLINE."); break; default: - PrintAndLog("ERROR: CID transaction code error %2x", CID->value[0] & EMVAC_AC_MASK); + PrintAndLogEx(WARNING, "Error: CID transaction code error %2x", CID->value[0] & EMVAC_AC_MASK); break; } } else { - PrintAndLog("ERROR: Wrong CID length %d", CID->len); + PrintAndLogEx(WARNING, "Wrong CID length %d", CID->len); } } else { - PrintAndLog("ERROR: CID(9F27) not found."); + PrintAndLogEx(WARNING, "CID(9F27) not found."); } } @@ -1003,20 +1090,20 @@ int CmdHFEMVExec(const char *cmd) { // MSD if (AIP & 0x8000 && TrType == TT_MSD) { - PrintAndLog("\n--> MSD transaction."); + PrintAndLogEx(NORMAL, "\n--> MSD transaction."); - PrintAndLog("* MSD dCVV path. Check dCVV"); + PrintAndLogEx(NORMAL, "* MSD dCVV path. Check dCVV"); const struct tlv *track2 = tlvdb_get(tlvRoot, 0x57, NULL); if (track2) { - PrintAndLog("Track2: %s", sprint_hex(track2->value, track2->len)); + PrintAndLogEx(NORMAL, "Track2: %s", sprint_hex(track2->value, track2->len)); struct tlvdb *dCVV = GetdCVVRawFromTrack2(track2); - PrintAndLog("dCVV raw data:"); + PrintAndLogEx(NORMAL, "dCVV raw data:"); TLVPrintFromTLV(dCVV); if (GetCardPSVendor(AID, AIDlen) == CV_MASTERCARD) { - PrintAndLog("\n* Mastercard calculate UDOL"); + PrintAndLogEx(NORMAL, "\n* Mastercard calculate UDOL"); // UDOL (9F69) const struct tlv *UDOL = tlvdb_get(tlvRoot, 0x9F69, NULL); @@ -1027,38 +1114,37 @@ int CmdHFEMVExec(const char *cmd) { .value = (uint8_t *)"\x9f\x6a\x04", }; if (!UDOL) - PrintAndLog("Use default UDOL."); + PrintAndLogEx(NORMAL, "Use default UDOL."); struct tlv *udol_data_tlv = dol_process(UDOL ? UDOL : &defUDOL, tlvRoot, 0x01); // 0x01 - dummy tag if (!udol_data_tlv){ - PrintAndLog("ERROR: can't create UDOL TLV."); + PrintAndLogEx(WARNING, "can't create UDOL TLV."); dreturn(8); } - PrintAndLog("UDOL data[%d]: %s", udol_data_tlv->len, sprint_hex(udol_data_tlv->value, udol_data_tlv->len)); + PrintAndLogEx(NORMAL, "UDOL data[%d]: %s", udol_data_tlv->len, sprint_hex(udol_data_tlv->value, udol_data_tlv->len)); - PrintAndLog("\n* Mastercard compute cryptographic checksum(UDOL)"); + PrintAndLogEx(NORMAL, "\n* Mastercard compute cryptographic checksum(UDOL)"); - res = MSCComputeCryptoChecksum(true, (uint8_t *)udol_data_tlv->value, udol_data_tlv->len, buf, sizeof(buf), &len, &sw, tlvRoot); + res = MSCComputeCryptoChecksum(channel, true, (uint8_t *)udol_data_tlv->value, udol_data_tlv->len, buf, sizeof(buf), &len, &sw, tlvRoot); if (res) { - PrintAndLog("ERROR Compute Crypto Checksum. APDU error %4x", sw); + PrintAndLogEx(WARNING, "Compute Crypto Checksum. APDU error %4x", sw); free(udol_data_tlv); dreturn(9); } - if (decodeTLV) { - TLVPrintFromBuffer(buf, len); - PrintAndLog(""); - } + // Mastercard compute cryptographic checksum result + TLVPrintFromBuffer(buf, len); + PrintAndLogEx(NORMAL, ""); + free(udol_data_tlv); } } else { - PrintAndLog("ERROR MSD: Track2 data not found."); + PrintAndLogEx(WARNING, "MSD: Track2 data not found."); } } - // DropField DropField(); // Destroy TLV's @@ -1066,12 +1152,11 @@ int CmdHFEMVExec(const char *cmd) { tlvdb_free(tlvSelect); tlvdb_free(tlvRoot); - PrintAndLog("\n* Transaction completed."); - + PrintAndLogEx(NORMAL, "\n* Transaction completed."); return 0; } -int CmdHFEMVScan(const char *cmd) { +int CmdEMVScan(const char *cmd) { uint8_t AID[APDU_AID_LEN] = {0}; size_t AIDlen = 0; uint8_t buf[APDU_RES_LEN] = {0}; @@ -1081,11 +1166,11 @@ int CmdHFEMVScan(const char *cmd) { json_t *root; json_error_t error; - CLIParserInit("hf emv scan", + CLIParserInit("emv scan", "Scan EMV card and save it contents to a file.", "It executes EMV contactless transaction and saves result to a file which can be used for emulation\n" - "Usage:\n\thf emv scan -at -> scan MSD transaction mode and show APDU and TLV\n" - "\thf emv scan -c -> scan CDA transaction mode\n"); + "Usage:\n\temv scan -at -> scan MSD transaction mode and show APDU and TLV\n" + "\temv scan -c -> scan CDA transaction mode\n"); void* argtable[] = { arg_param_begin, @@ -1099,7 +1184,10 @@ int CmdHFEMVScan(const char *cmd) { arg_lit0("xX", "vsdc", "Transaction type - VSDC. For test only. Not a standart behavior."), arg_lit0("gG", "acgpo", "VISA. generate AC from GPO."), arg_lit0("mM", "merge", "Merge output file with card's data. (warning: the file may be corrupted!)"), - arg_str1(NULL, NULL, "output.json", "JSON output file name"), +#ifdef WITH_SMARTCARD + arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. Contactless interface set by default."), +#endif + arg_str1(NULL, NULL, "output.json", "JSON output file name"), arg_param_end }; CLIExecWithReturn(cmd, argtable, true); @@ -1119,14 +1207,27 @@ int CmdHFEMVScan(const char *cmd) { bool GenACGPO = arg_get_lit(9); bool MergeJSON = arg_get_lit(10); - uint8_t relfname[250] ={0}; + EMVCommandChannel channel = ECC_CONTACTLESS; + uint8_t relfname[250] = {0}; char *crelfname = (char *)relfname; int relfnamelen = 0; +#ifdef WITH_SMARTCARD + if (arg_get_lit(11)) + channel = ECC_CONTACT; + CLIGetStrWithReturn(12, relfname, &relfnamelen); +#else CLIGetStrWithReturn(11, relfname, &relfnamelen); +#endif CLIParserFree(); SetAPDULogging(showAPDU); + // TODO + if (channel == ECC_CONTACT) { + PrintAndLogEx(ERR, "Do not use contact interface. Exit."); + return 1; + } + // current path + file name if (!strstr(crelfname, ".json")) strcat(crelfname, ".json"); @@ -1137,12 +1238,12 @@ int CmdHFEMVScan(const char *cmd) { if (MergeJSON) { root = json_load_file(fname, 0, &error); if (!root) { - PrintAndLog("ERROR: json error on line %d: %s", error.line, error.text); + PrintAndLogEx(ERROR, "json error on line %d: %s", error.line, error.text); return 1; } if (!json_is_object(root)) { - PrintAndLog("ERROR: Invalid json format. root must be an object."); + PrintAndLogEx(ERROR, "Invalid json format. root must be an object."); return 1; } } else { @@ -1153,14 +1254,14 @@ int CmdHFEMVScan(const char *cmd) { DropField(); // iso 14443 select - PrintAndLog("--> GET UID, ATS."); + PrintAndLogEx(NORMAL, "--> GET UID, ATS."); iso14a_card_select_t card; if (Hf14443_4aGetCardData(&card)) { return 2; } - JsonSaveStr(root, "$.File.Created", "proxmark3 `hf emv scan`"); + JsonSaveStr(root, "$.File.Created", "proxmark3 `emv scan`"); JsonSaveStr(root, "$.Card.Communication", "iso14443-4a"); JsonSaveBufAsHex(root, "$.Card.UID", (uint8_t *)&card.uid, card.uidlen); @@ -1173,8 +1274,8 @@ int CmdHFEMVScan(const char *cmd) { struct tlvdb *tlvSelect = tlvdb_fixed(1, strlen(al), (const unsigned char *)al); // EMV PPSE - PrintAndLog("--> PPSE."); - res = EMVSelectPSE(true, true, 2, buf, sizeof(buf), &len, &sw); + PrintAndLogEx(NORMAL, "--> PPSE."); + res = EMVSelectPSE(channel, true, true, 2, buf, sizeof(buf), &len, &sw); if (!res && sw == 0x9000){ if (decodeTLV) @@ -1191,17 +1292,17 @@ int CmdHFEMVScan(const char *cmd) { tlvdb_free(fci); } - res = EMVSearchPSE(false, true, decodeTLV, tlvSelect); + res = EMVSearchPSE(channel, false, true, decodeTLV, tlvSelect); // check PPSE and select application id - if (!res) { - TLVPrintAIDlistFromSelectTLV(tlvSelect); + if (!res) { + TLVPrintAIDlistFromSelectTLV(tlvSelect); } else { // EMV SEARCH with AID list SetAPDULogging(false); - PrintAndLog("--> AID search."); - if (EMVSearch(false, true, decodeTLV, tlvSelect)) { - PrintAndLog("E->Can't found any of EMV AID. Exit..."); + PrintAndLogEx(NORMAL, "--> AID search."); + if (EMVSearch(channel, false, true, decodeTLV, tlvSelect)) { + PrintAndLogEx(ERROR, "Can't found any of EMV AID. Exit..."); tlvdb_free(tlvSelect); DropField(); return 3; @@ -1218,7 +1319,7 @@ int CmdHFEMVScan(const char *cmd) { tlvdb_free(tlvSelect); if (!AIDlen) { - PrintAndLog("Can't select AID. EMV AID not found. Exit..."); + PrintAndLogEx(INFO, "Can't select AID. EMV AID not found. Exit..."); DropField(); return 4; } @@ -1231,12 +1332,12 @@ int CmdHFEMVScan(const char *cmd) { // EMV SELECT applet - PrintAndLog("\n-->Selecting AID:%s.", sprint_hex_inrow(AID, AIDlen)); + PrintAndLogEx(NORMAL, "\n-->Selecting AID:%s.", sprint_hex_inrow(AID, AIDlen)); SetAPDULogging(showAPDU); - res = EMVSelect(false, true, AID, AIDlen, buf, sizeof(buf), &len, &sw, tlvRoot); + res = EMVSelect(channel, false, true, AID, AIDlen, buf, sizeof(buf), &len, &sw, tlvRoot); - if (res) { - PrintAndLog("E->Can't select AID (%d). Exit...", res); + if (res) { + PrintAndLogEx(ERROR, "Can't select AID (%d). Exit...", res); tlvdb_free(tlvRoot); DropField(); return 5; @@ -1258,13 +1359,13 @@ int CmdHFEMVScan(const char *cmd) { tlvdb_free(fci); // create transaction parameters - PrintAndLog("-->Init transaction parameters."); + PrintAndLogEx(NORMAL, "-->Init transaction parameters."); InitTransactionParameters(tlvRoot, paramLoadJSON, TrType, GenACGPO); - PrintAndLog("-->Calc PDOL."); + PrintAndLogEx(NORMAL, "-->Calc PDOL."); struct tlv *pdol_data_tlv = dol_process(tlvdb_get(tlvRoot, 0x9f38, NULL), tlvRoot, 0x83); if (!pdol_data_tlv){ - PrintAndLog("E->Can't create PDOL TLV."); + PrintAndLogEx(ERROR, "Can't create PDOL TLV."); tlvdb_free(tlvRoot); DropField(); return 6; @@ -1273,21 +1374,21 @@ int CmdHFEMVScan(const char *cmd) { size_t pdol_data_tlv_data_len; unsigned char *pdol_data_tlv_data = tlv_encode(pdol_data_tlv, &pdol_data_tlv_data_len); if (!pdol_data_tlv_data) { - PrintAndLog("E->Can't create PDOL data."); + PrintAndLogEx(ERROR, "Can't create PDOL data."); tlvdb_free(tlvRoot); DropField(); return 6; } - PrintAndLog("PDOL data[%d]: %s", pdol_data_tlv_data_len, sprint_hex(pdol_data_tlv_data, pdol_data_tlv_data_len)); + PrintAndLogEx(INFO, "PDOL data[%d]: %s", pdol_data_tlv_data_len, sprint_hex(pdol_data_tlv_data, pdol_data_tlv_data_len)); - PrintAndLog("-->GPO."); - res = EMVGPO(true, pdol_data_tlv_data, pdol_data_tlv_data_len, buf, sizeof(buf), &len, &sw, tlvRoot); + PrintAndLogEx(INFO, "-->GPO."); + res = EMVGPO(channel, true, pdol_data_tlv_data, pdol_data_tlv_data_len, buf, sizeof(buf), &len, &sw, tlvRoot); free(pdol_data_tlv_data); free(pdol_data_tlv); - if (res) { - PrintAndLog("GPO error(%d): %4x. Exit...", res, sw); + if (res) { + PrintAndLogEx(ERROR, "GPO error(%d): %4x. Exit...", res, sw); tlvdb_free(tlvRoot); DropField(); return 7; @@ -1305,12 +1406,12 @@ int CmdHFEMVScan(const char *cmd) { tlvdb_free(gpofci); - PrintAndLog("-->Read records from AFL."); + PrintAndLogEx(INFO, "-->Read records from AFL."); const struct tlv *AFL = tlvdb_get(tlvRoot, 0x94, NULL); while(AFL && AFL->len) { if (AFL->len % 4) { - PrintAndLog("E->Wrong AFL length: %d", AFL->len); + PrintAndLogEx(ERROR, "Wrong AFL length: %d", AFL->len); break; } @@ -1322,7 +1423,7 @@ int CmdHFEMVScan(const char *cmd) { sfijson = json_path_get(root, "$.Application.Records"); } if (!json_is_array(sfijson)) { - PrintAndLog("E->Internal logic error. `$.Application.Records` is not an array."); + PrintAndLogEx(ERROR, "Internal logic error. `$.Application.Records` is not an array."); break; } for (int i = 0; i < AFL->len / 4; i++) { @@ -1331,24 +1432,24 @@ int CmdHFEMVScan(const char *cmd) { uint8_t SFIend = AFL->value[i * 4 + 2]; uint8_t SFIoffline = AFL->value[i * 4 + 3]; - PrintAndLog("--->SFI[%02x] start:%02x end:%02x offline:%02x", SFI, SFIstart, SFIend, SFIoffline); + PrintAndLogEx(INFO, "--->SFI[%02x] start:%02x end:%02x offline:%02x", SFI, SFIstart, SFIend, SFIoffline); if (SFI == 0 || SFI == 31 || SFIstart == 0 || SFIstart > SFIend) { - PrintAndLog("SFI ERROR! Skipped..."); + PrintAndLogEx(ERROR, "SFI ERROR! Skipped..."); continue; } for(int n = SFIstart; n <= SFIend; n++) { - PrintAndLog("---->SFI[%02x] %d", SFI, n); + PrintAndLogEx(INFO, "---->SFI[%02x] %d", SFI, n); - res = EMVReadRecord(true, SFI, n, buf, sizeof(buf), &len, &sw, tlvRoot); + res = EMVReadRecord(channel, true, SFI, n, buf, sizeof(buf), &len, &sw, tlvRoot); if (res) { - PrintAndLog("E->SFI[%02x]. APDU error %4x", SFI, sw); + PrintAndLogEx(ERROR, "SFI[%02x]. APDU error %4x", SFI, sw); continue; } if (decodeTLV) { TLVPrintFromBuffer(buf, len); - PrintAndLog(""); + PrintAndLogEx(NORMAL, ""); } json_t *jsonelm = json_object(); @@ -1372,7 +1473,7 @@ int CmdHFEMVScan(const char *cmd) { // getting certificates if (tlvdb_get(tlvRoot, 0x90, NULL)) { - PrintAndLog("-->Recovering certificates."); + PrintAndLogEx(INFO, "-->Recovering certificates."); PKISetStrictExecution(false); RecoveryCertificates(tlvRoot, root); PKISetStrictExecution(true); @@ -1386,10 +1487,10 @@ int CmdHFEMVScan(const char *cmd) { res = json_dump_file(root, fname, JSON_INDENT(2)); if (res) { - PrintAndLog("ERROR: can't save the file: %s", fname); + PrintAndLogEx(ERROR, "Can't save the file: %s", fname); return 200; } - PrintAndLog("File `%s` saved.", fname); + PrintAndLogEx(SUCCESS, "File `%s` saved.", fname); // free json object json_decref(root); @@ -1397,28 +1498,28 @@ int CmdHFEMVScan(const char *cmd) { return 0; } -int CmdHFEMVTest(const char *cmd) { +int CmdEMVTest(const char *cmd) { return ExecuteCryptoTests(true); } int CmdHelp(const char *Cmd); static command_t CommandTable[] = { - {"help", CmdHelp, 1, "This help"}, - {"exec", CmdHFEMVExec, 0, "Executes EMV contactless transaction."}, - {"pse", CmdHFEMVPPSE, 0, "Execute PPSE. It selects 2PAY.SYS.DDF01 or 1PAY.SYS.DDF01 directory."}, - {"search", CmdHFEMVSearch, 0, "Try to select all applets from applets list and print installed applets."}, - {"select", CmdHFEMVSelect, 0, "Select applet."}, - {"gpo", CmdHFEMVGPO, 0, "Execute GetProcessingOptions."}, - {"readrec", CmdHFEMVReadRecord, 0, "Read files from card."}, - {"genac", CmdHFEMVAC, 0, "Generate ApplicationCryptogram."}, - {"challenge", CmdHFEMVGenerateChallenge, 0, "Generate challenge."}, - {"intauth", CmdHFEMVInternalAuthenticate, 0, "Internal authentication."}, - {"scan", CmdHFEMVScan, 0, "Scan EMV card and save it contents to json file for emulator."}, - {"test", CmdHFEMVTest, 0, "Crypto logic test."}, - {NULL, NULL, 0, NULL} + {"help", CmdHelp, 1, "This help"}, + {"exec", CmdEMVExec, 0, "Executes EMV contactless transaction."}, + {"pse", CmdEMVPPSE, 0, "Execute PPSE. It selects 2PAY.SYS.DDF01 or 1PAY.SYS.DDF01 directory."}, + {"search", CmdEMVSearch, 0, "Try to select all applets from applets list and print installed applets."}, + {"select", CmdEMVSelect, 0, "Select applet."}, + {"gpo", CmdEMVGPO, 0, "Execute GetProcessingOptions."}, + {"readrec", CmdEMVReadRecord, 0, "Read files from card."}, + {"genac", CmdEMVAC, 0, "Generate ApplicationCryptogram."}, + {"challenge", CmdEMVGenerateChallenge, 0, "Generate challenge."}, + {"intauth", CmdEMVInternalAuthenticate, 0, "Internal authentication."}, + {"scan", CmdEMVScan, 0, "Scan EMV card and save it contents to json file for emulator."}, + {"test", CmdEMVTest, 0, "Crypto logic test."}, + {NULL, NULL, 0, NULL} }; -int CmdHFEMV(const char *Cmd) { +int CmdEMV(const char *Cmd) { CmdsParse(CommandTable, Cmd); return 0; }