diff --git a/CHANGELOG.md b/CHANGELOG.md index 21b463b28..ed09b06ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file. This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log... ## [unreleased][unreleased] + - Added `emv reader` - act as a EMV reader (@iceman1001) - Added support for Apple Wallet NFC Passes with the Value Added Services protocol implementation (@gm3197) - Fix compiling liblua on iOS (@The-SamminAter) - Changed `hf_mf_luxeo_dump.lua` - now have list of keys to iterate (@iceman1001) diff --git a/client/src/cmdhf14a.c b/client/src/cmdhf14a.c index 7bdc04c06..a541baf2f 100644 --- a/client/src/cmdhf14a.c +++ b/client/src/cmdhf14a.c @@ -2363,7 +2363,7 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf st info`")); if (isEMV) - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`emv search -s`")); + PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`emv reader`")); if (isFUDAN) { PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf fudan dump`")); diff --git a/client/src/emv/cmdemv.c b/client/src/emv/cmdemv.c index 71277d87b..720401bdb 100644 --- a/client/src/emv/cmdemv.c +++ b/client/src/emv/cmdemv.c @@ -34,6 +34,7 @@ #include "emv_tags.h" #include "fileutils.h" #include "protocols.h" // ISO7816 APDU return codes +#include "commonutil.h" // MemBeToUint2byte static int CmdHelp(const char *Cmd); @@ -74,6 +75,155 @@ static void PrintChannel(Iso7816CommandChannel channel) { } } +static int emv_parse_card_details(uint8_t *response, size_t reslen) { + + struct tlvdb *root = tlvdb_parse_multi(response, reslen); + if (root == NULL) { + return PM3_EINVARG; + } + + // extract application preferred name + struct tlvdb *prefname_full = tlvdb_find_full(root, 0x9F12); + if (prefname_full != NULL) { + const struct tlv *prefname_tlv = tlvdb_get_tlv(prefname_full); + if (prefname_tlv->len) { + char *name[64] = {0}; + size_t n = MIN(sizeof(name), prefname_tlv->len); + memcpy(name, prefname_tlv->value, n); + PrintAndLogEx(INFO, "Application.......... " _YELLOW_("%s"), name); + } + } + + // extract application label + struct tlvdb *alabel = tlvdb_find_full(root, 0x50); + if (alabel != NULL) { + const struct tlv *alabel_tlv = tlvdb_get_tlv(alabel); + if (alabel_tlv->len) { + char *name[64] = {0}; + size_t n = MIN(sizeof(name), alabel_tlv->len); + memcpy(name, alabel_tlv->value, n); + PrintAndLogEx(INFO, "Label................ " _YELLOW_("%s"), name); + } + } + + // extract language preference + struct tlvdb *lang_full = tlvdb_find_full(root, 0x5F2D); + if (lang_full != NULL) { + const struct tlv *lang_tlv = tlvdb_get_tlv(lang_full); + if (lang_tlv->len) { + char *lang[16] = {0}; + size_t n = MIN(sizeof(lang), lang_tlv->len); + memcpy(lang, lang_tlv->value, n); + PrintAndLogEx(INFO, "Language............. " _YELLOW_("%s"), lang); + } + } + + // Application Currency Code + struct tlvdb *acc_full = tlvdb_find_full(root, 0x9F42); + if (acc_full != NULL) { + const struct tlv *acc_tlv = tlvdb_get_tlv(acc_full); + if (acc_tlv->len == 2) { + uint16_t acc = MemBeToUint2byte((const uint8_t*)acc_tlv->value); + PrintAndLogEx(INFO, "Currency Code........ " _YELLOW_("%02X"), acc); + } + } + + // Application Effective Date + struct tlvdb *aeffect_full = tlvdb_find_full(root, 0x5F25); + if (aeffect_full != NULL) { + const struct tlv *aaeffect_tlv = tlvdb_get_tlv(aeffect_full); + if (aaeffect_tlv->len == 3) { + PrintAndLogEx(INFO, "Effective date....... " _YELLOW_("20%02x-%02x-%02x"), + aaeffect_tlv->value[0], + aaeffect_tlv->value[1], + aaeffect_tlv->value[2] + ); + } + } + + // Application Expiration Date + struct tlvdb *aexd_full = tlvdb_find_full(root, 0x5F24); + if (aexd_full != NULL) { + const struct tlv *aexd_tlv = tlvdb_get_tlv(aexd_full); + if (aexd_tlv->len == 3) { + PrintAndLogEx(INFO, "Expiration date...... " _YELLOW_("20%02x-%02x-%02x"), + aexd_tlv->value[0], + aexd_tlv->value[1], + aexd_tlv->value[2] + ); + } + } + + // Application Primary Account Number (PAN) + struct tlvdb *apan_full = tlvdb_find_full(root, 0x5A); + if (apan_full != NULL) { + const struct tlv *apan_tlv = tlvdb_get_tlv(apan_full); + if (apan_tlv->len == 8) { + PrintAndLogEx(INFO, "PAN.................. " _YELLOW_("%02x%02x %02x%02x %02x%02x %02x%02x"), + apan_tlv->value[0], + apan_tlv->value[1], + apan_tlv->value[2], + apan_tlv->value[3], + apan_tlv->value[4], + apan_tlv->value[5], + apan_tlv->value[6], + apan_tlv->value[7] + ); + } + } + + // Application Primary Account Number (PAN) sequence number + struct tlvdb *apansq_full = tlvdb_find_full(root, 0x5F34); + if (apansq_full != NULL) { + const struct tlv *apansq_tlv = tlvdb_get_tlv(apansq_full); + if (apansq_tlv->len == 1) { + PrintAndLogEx(INFO, "PAN Sequence......... " _YELLOW_("%u"), apansq_tlv->value[0]); + } + } + + // Cardholder Name + struct tlvdb *chm_full = tlvdb_find_full(root, 0x5F20); + if (chm_full != NULL) { + const struct tlv *chm_tlv = tlvdb_get_tlv(chm_full); + if (chm_tlv->len) { + PrintAndLogEx(INFO, "Cardhold Name........ " _YELLOW_("%s"), sprint_ascii(chm_tlv->value, chm_tlv->len)); + } + } + + // Track 1 Data + struct tlvdb *track1_full = tlvdb_find_full(root, 0x56); + if (track1_full != NULL) { + const struct tlv *track1_tlv = tlvdb_get_tlv(track1_full); + if (track1_tlv->len) { + PrintAndLogEx(INFO, "Track 1.............. " _YELLOW_("%s"), sprint_ascii(track1_tlv->value, track1_tlv->len)); + } + } + + // Track 2 Data + struct tlvdb *track2_full = tlvdb_find_full(root, 0x9F6B); + if (track2_full != NULL) { + const struct tlv *track2_tlv = tlvdb_get_tlv(track2_full); + if (track2_tlv->len) { + PrintAndLogEx(INFO, "Track 2.............. " _YELLOW_("%s"), sprint_hex_inrow(track2_tlv->value, track2_tlv->len)); + } + } + + // Track 2 Equivalent Data + struct tlvdb *track2_eq_full = tlvdb_find_full(root, 0x57); + if (track2_eq_full != NULL) { + const struct tlv *track2_eq_tlv = tlvdb_get_tlv(track2_eq_full); + if (track2_eq_tlv->len) { + PrintAndLogEx(INFO, "Track 2 equivalent... " _YELLOW_("%s"), sprint_hex_inrow(track2_eq_tlv->value, track2_eq_tlv->len)); + } + } + + // Track 3 Data + // to be impl. + + tlvdb_free(root); + return PM3_SUCCESS; +} + static int CmdEMVSelect(const char *Cmd) { uint8_t data[APDU_AID_LEN] = {0}; int datalen = 0; @@ -81,16 +231,16 @@ static int CmdEMVSelect(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "emv select", "Executes select applet command", - "emv select -s a00000000101 -> select card, select applet\n" + "emv select -s a00000000101 -> select card, select applet\n" "emv select -st a00000000101 -> select card, select applet, 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 for next command"), - arg_lit0("aA", "apdu", "Show APDU requests and responses"), - arg_lit0("tT", "tlv", "TLV decode results"), - arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. (def: Contactless interface)"), + arg_lit0("s", "select", "Activate field and select card"), + arg_lit0("k", "keep", "Keep field for next command"), + arg_lit0("a", "apdu", "Show APDU requests and responses"), + arg_lit0("t", "tlv", "TLV decode results"), + arg_lit0("w", "wired", "Send data via contact (iso7816) interface. (def: Contactless interface)"), arg_str1(NULL, NULL, "", "Applet AID"), arg_param_end }; @@ -137,11 +287,11 @@ static int CmdEMVSearch(const char *Cmd) { 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 requests and responses"), - arg_lit0("tT", "tlv", "TLV decode results of selected applets"), - arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. (def: Contactless interface)"), + arg_lit0("s", "select", "Activate field and select card"), + arg_lit0("k", "keep", "Keep field ON for next command"), + arg_lit0("a", "apdu", "Show APDU requests and responses"), + arg_lit0("t", "tlv", "TLV decode results of selected applets"), + arg_lit0("w", "wired", "Send data via contact (iso7816) interface. (def: Contactless interface)"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -164,7 +314,7 @@ static int CmdEMVSearch(const char *Cmd) { const char *al = "Applets list"; struct tlvdb *t = tlvdb_fixed(1, strlen(al), (const unsigned char *)al); - if (EMVSearch(channel, activateField, leaveSignalON, decodeTLV, t)) { + if (EMVSearch(channel, activateField, leaveSignalON, decodeTLV, t, false)) { tlvdb_free(t); return PM3_ERFTRANS; } @@ -191,13 +341,13 @@ static int CmdEMVPPSE(const char *Cmd) { 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 (def)"), - arg_lit0("aA", "apdu", "Show APDU requests and responses"), - arg_lit0("tT", "tlv", "TLV decode results of selected applets"), - arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. (def: Contactless interface)"), + arg_lit0("s", "select", "Activate field and select card"), + arg_lit0("k", "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 (def)"), + arg_lit0("a", "apdu", "Show APDU requests and responses"), + arg_lit0("t", "tlv", "TLV decode results of selected applets"), + arg_lit0("w", "wired", "Send data via contact (iso7816) interface. (def: Contactless interface)"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -251,13 +401,13 @@ static int CmdEMVGPO(const char *Cmd) { 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 (def: uses default parameters)"), - arg_lit0("aA", "apdu", "Show APDU requests and responses"), - arg_lit0("tT", "tlv", "TLV decode results of selected applets"), - arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. (def: Contactless interface)"), - arg_strx0(NULL, NULL, "", "PDOLdata/PDOL"), + arg_lit0("k", "keep", "Keep field ON for next command"), + arg_lit0("p", "params", "Load parameters from `emv_defparams.json` file for PDOLdata making from PDOL and parameters"), + arg_lit0("m", "make", "Make PDOLdata from PDOL (tag 9F38) and parameters (def: uses default parameters)"), + arg_lit0("a", "apdu", "Show APDU requests and responses"), + arg_lit0("t", "tlv", "TLV decode results of selected applets"), + arg_lit0("w", "wired", "Send data via contact (iso7816) interface. (def: Contactless interface)"), + arg_strx0(NULL, NULL, "", "PDOLdata/PDOL"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -360,11 +510,11 @@ static int CmdEMVReadRecord(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_lit0("kK", "keep", "Keep field ON for next command"), - arg_lit0("aA", "apdu", "Show APDU requests and responses"), - arg_lit0("tT", "tlv", "TLV decode results of selected applets"), - arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. (def: Contactless interface)"), - arg_strx1(NULL, NULL, "", "", "", "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 (def: use default parameters)"), - arg_lit0("aA", "apdu", "Show APDU requests and responses"), - arg_lit0("tT", "tlv", "TLV decode results of selected applets"), - arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. (def: Contactless interface)"), - arg_strx1(NULL, NULL, "", "CDOLdata/CDOL"), + arg_lit0("k", "keep", "Keep field ON for next command"), + arg_lit0("c", "cda", "Executes CDA transaction. Needs to get SDAD in results."), + arg_str0("d", "decision", "", "Terminal decision. aac - declined, tc - approved, arqc - online authorisation requested"), + arg_lit0("p", "params", "Load parameters from `emv_defparams.json` file for CDOLdata making from CDOL and parameters"), + arg_lit0("m", "make", "Make CDOLdata from CDOL (tag 8C and 8D) and parameters (def: use default parameters)"), + arg_lit0("a", "apdu", "Show APDU requests and responses"), + arg_lit0("t", "tlv", "TLV decode results of selected applets"), + arg_lit0("w", "wired", "Send data via contact (iso7816) interface. (def: Contactless interface)"), + arg_strx1(NULL, NULL, "", "CDOLdata/CDOL"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -542,9 +692,9 @@ static int CmdEMVGenerateChallenge(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_lit0("kK", "keep", "Keep field ON for next command"), - arg_lit0("aA", "apdu", "Show APDU requests and responses"), - arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. (def: Contactless interface)"), + arg_lit0("k", "keep", "Keep field ON for next command"), + arg_lit0("a", "apdu", "Show APDU requests and responses"), + arg_lit0("w", "wired", "Send data via contact (iso7816) interface. (def: Contactless interface)"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -594,13 +744,13 @@ static int CmdEMVInternalAuthenticate(const char *Cmd) { 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 (def: use default parameters)"), - arg_lit0("aA", "apdu", "Show APDU requests and responses"), - arg_lit0("tT", "tlv", "TLV decode results of selected applets"), - arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. (def: Contactless interface)"), - arg_strx1(NULL, NULL, "", "DDOLdata/DDOL"), + arg_lit0("k", "keep", "Keep field ON for next command"), + arg_lit0("p", "params", "Load parameters from `emv_defparams.json` file for DDOLdata making from DDOL and parameters"), + arg_lit0("m", "make", "Make DDOLdata from DDOL (tag 9F49) and parameters (def: use default parameters)"), + arg_lit0("a", "apdu", "Show APDU requests and responses"), + arg_lit0("t", "tlv", "TLV decode results of selected applets"), + arg_lit0("w", "wired", "Send data via contact (iso7816) interface. (def: Contactless interface)"), + arg_strx1(NULL, NULL, "", "DDOLdata/DDOL"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -718,6 +868,7 @@ static void InitTransactionParameters(struct tlvdb *tlvRoot, bool paramLoadJSON, TLV_ADD(0x9F66, "\x26\x00\x00\x00"); } break; + case TT_END: default: break; } @@ -799,20 +950,6 @@ static void ProcessACResponseFormat1(struct tlvdb *tlvRoot, uint8_t *buf, size_t } static int CmdEMVExec(const char *Cmd) { - uint8_t buf[APDU_RES_LEN] = {0}; - size_t len = 0; - uint16_t sw = 0; - uint8_t AID[APDU_AID_LEN] = {0}; - size_t AIDlen = 0; - uint8_t ODAiList[4096]; - size_t ODAiListLen = 0; - - int res; - - struct tlvdb *tlvSelect = NULL; - struct tlvdb *tlvRoot = NULL; - struct tlv *pdol_data_tlv = NULL; - CLIParserContext *ctx; CLIParserInit(&ctx, "emv exec", "Executes EMV contactless transaction", @@ -821,17 +958,17 @@ static int CmdEMVExec(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_lit0("sS", "select", "Activate field and select card"), - arg_lit0("aA", "apdu", "Show APDU requests and responses"), - arg_lit0("tT", "tlv", "TLV decode results"), - arg_lit0("jJ", "jload", "Load transaction parameters from `emv_defparams.json` file"), - arg_lit0("fF", "forceaid", "Force search AID. Search AID instead of execute PPSE"), - arg_rem("By default:", "Transaction type - MSD"), - arg_lit0("vV", "qvsdc", "Transaction type - qVSDC or M/Chip"), - 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 standard behavior"), - arg_lit0("gG", "acgpo", "VISA. generate AC from GPO"), - arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. (def: Contactless interface)"), + arg_lit0("s", "select", "Activate field and select card"), + arg_lit0("a", "apdu", "Show APDU requests and responses"), + arg_lit0("t", "tlv", "TLV decode results"), + arg_lit0("j", "jload", "Load transaction parameters from `emv_defparams.json` file"), + arg_lit0(NULL, "force", "Force search AID. Search AID instead of execute PPSE"), + arg_rem("By default:", "Transaction type - MSD"), + arg_lit0("v", "qvsdc", "Transaction type - qVSDC or M/Chip"), + arg_lit0("c", "qvsdccda", "Transaction type - qVSDC or M/Chip plus CDA (SDAD generation)"), + arg_lit0("x", "vsdc", "Transaction type - VSDC. For test only. Not a standard behavior"), + arg_lit0("g", "acgpo", "VISA. generate AC from GPO"), + arg_lit0("w", "wired", "Send data via contact (iso7816) interface. (def: Contactless interface)"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -873,6 +1010,20 @@ static int CmdEMVExec(const char *Cmd) { SetAPDULogging(showAPDU); + uint8_t buf[APDU_RES_LEN] = {0}; + size_t len = 0; + uint16_t sw = 0; + uint8_t AID[APDU_AID_LEN] = {0}; + size_t AIDlen = 0; + uint8_t ODAiList[4096]; + size_t ODAiListLen = 0; + + int res; + + struct tlvdb *tlvSelect = NULL; + struct tlvdb *tlvRoot = NULL; + struct tlv *pdol_data_tlv = NULL; + // init applets list tree const char *al = "Applets list"; tlvSelect = tlvdb_fixed(1, strlen(al), (const unsigned char *)al); @@ -902,7 +1053,7 @@ static int CmdEMVExec(const char *Cmd) { if (!AIDlen) { PrintAndLogEx(NORMAL, "\n* Search AID in list."); SetAPDULogging(false); - if (EMVSearch(channel, activateField, true, decodeTLV, tlvSelect)) { + if (EMVSearch(channel, activateField, true, decodeTLV, tlvSelect, false)) { dreturn(PM3_ERFTRANS); } @@ -1428,15 +1579,6 @@ static int CmdEMVExec(const char *Cmd) { } static int CmdEMVScan(const char *Cmd) { - uint8_t AID[APDU_AID_LEN] = {0}; - size_t AIDlen = 0; - uint8_t buf[APDU_RES_LEN] = {0}; - size_t len = 0; - uint8_t ODAI_list[4096]; - size_t ODAI_listlen = 0; - uint16_t sw = 0; - int res; - CLIParserContext *ctx; CLIParserInit(&ctx, "emv scan", "Scan EMV card and save it contents to a file.\n" @@ -1447,18 +1589,18 @@ static int CmdEMVScan(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_lit0("aA", "apdu", "Show APDU requests and responses"), - arg_lit0("tT", "tlv", "TLV decode results"), - arg_lit0("eE", "extract", "Extract TLV elements and fill Application Data"), - arg_lit0("jJ", "jload", "Load transaction parameters from `emv_defparams.json` file"), - arg_rem("By default:", "Transaction type - MSD"), - arg_lit0("vV", "qvsdc", "Transaction type - qVSDC or M/Chip"), - 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 standard 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_lit0("wW", "wired", "Send data via contact (iso7816) interface. (def: Contactless interface)"), - arg_str1(NULL, NULL, "", "JSON output filename"), + arg_lit0("a", "apdu", "Show APDU requests and responses"), + arg_lit0("t", "tlv", "TLV decode results"), + arg_lit0("e", "extract", "Extract TLV elements and fill Application Data"), + arg_lit0("j", "jload", "Load transaction parameters from `emv_defparams.json` file"), + arg_rem("By default:", "Transaction type - MSD"), + arg_lit0("v", "qvsdc", "Transaction type - qVSDC or M/Chip"), + arg_lit0("c", "qvsdccda", "Transaction type - qVSDC or M/Chip plus CDA (SDAD generation)"), + arg_lit0("x", "vsdc", "Transaction type - VSDC. For test only. Not a standard behavior"), + arg_lit0("g", "acgpo", "VISA. generate AC from GPO"), + arg_lit0("m", "merge", "Merge output file with card's data. (warning: the file may be corrupted!)"), + arg_lit0("w", "wired", "Send data via contact (iso7816) interface. (def: Contactless interface)"), + arg_str1(NULL, NULL, "", "JSON output file name"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -1502,6 +1644,15 @@ static int CmdEMVScan(const char *Cmd) { SetAPDULogging(showAPDU); + uint8_t AID[APDU_AID_LEN] = {0}; + size_t AIDlen = 0; + uint8_t buf[APDU_RES_LEN] = {0}; + size_t len = 0; + uint8_t ODAI_list[4096]; + size_t ODAI_listlen = 0; + uint16_t sw = 0; + int res; + json_t *root; json_error_t error; @@ -1587,7 +1738,7 @@ static int CmdEMVScan(const char *Cmd) { // EMV SEARCH with AID list SetAPDULogging(false); PrintAndLogEx(INFO, "AID search."); - if (EMVSearch(channel, false, true, decodeTLV, tlvSelect)) { + if (EMVSearch(channel, false, true, decodeTLV, tlvSelect, false)) { PrintAndLogEx(ERR, "Can't found any of EMV AID, exiting..."); tlvdb_free(tlvSelect); DropFieldEx(channel); @@ -1852,15 +2003,6 @@ static int CmdEMVTest(const char *Cmd) { } static int CmdEMVRoca(const char *Cmd) { - uint8_t AID[APDU_AID_LEN] = {0}; - size_t AIDlen = 0; - uint8_t buf[APDU_RES_LEN] = {0}; - size_t len = 0; - uint16_t sw = 0; - uint8_t ODAI_list[4096]; - size_t ODAI_listlen = 0; - int res; - CLIParserContext *ctx; CLIParserInit(&ctx, "emv roca", "Tries to extract public keys and run the ROCA test against them.\n", @@ -1870,9 +2012,9 @@ static int CmdEMVRoca(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_lit0("tT", "selftest", "Self test"), - arg_lit0("aA", "apdu", "Show APDU requests and responses"), - arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. (def: Contactless interface)"), + arg_lit0("t", "selftest", "Self test"), + arg_lit0("a", "apdu", "Show APDU requests and responses"), + arg_lit0("w", "wired", "Send data via contact (iso7816) interface. (def: Contactless interface)"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -1903,6 +2045,15 @@ static int CmdEMVRoca(const char *Cmd) { SetAPDULogging(show_apdu); + uint8_t AID[APDU_AID_LEN] = {0}; + size_t AIDlen = 0; + uint8_t buf[APDU_RES_LEN] = {0}; + size_t len = 0; + uint16_t sw = 0; + uint8_t ODAI_list[4096]; + size_t ODAI_listlen = 0; + int res; + // init applets list tree const char *al = "Applets list"; struct tlvdb *tlvSelect = tlvdb_fixed(1, strlen(al), (const unsigned char *)al); @@ -1917,7 +2068,7 @@ static int CmdEMVRoca(const char *Cmd) { } else { // EMV SEARCH with AID list PrintAndLogEx(INFO, "starting AID search"); - if (EMVSearch(channel, false, true, false, tlvSelect)) { + if (EMVSearch(channel, false, true, false, tlvSelect, false)) { PrintAndLogEx(ERR, "Can't found any of EMV AID, exiting"); tlvdb_free(tlvSelect); DropFieldEx(channel); @@ -2121,28 +2272,182 @@ out: return ret; } +static int CmdEMVReader(const char *Cmd){ + CLIParserContext *ctx; + CLIParserInit(&ctx, "emv reader", + "Act as a EMV reader to identify tag. Look for EMV tags until Enter or the pm3 button is pressed", + "emv reader\n" + "emv reader -@ -> Continuous mode\n" + ); + void *argtable[] = { + arg_param_begin, + arg_lit0("w", "wired", "Send data via contact (iso7816) interface. (def: Contactless interface)"), + arg_lit0("@", NULL, "continuous reader mode"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + Iso7816CommandChannel channel = CC_CONTACTLESS; + if (arg_get_lit(ctx, 1)) { + channel = CC_CONTACT; + } + + uint8_t psenum = (channel == CC_CONTACT) ? 1 : 2; + + bool continuous = arg_get_lit(ctx, 2); + CLIParserFree(ctx); + + if (continuous) { + PrintAndLogEx(INFO, "Press " _GREEN_("Enter") " to exit"); + } + + + uint8_t AID[APDU_AID_LEN] = {0}; + size_t AIDlen = 0; + uint8_t buf[APDU_RES_LEN] = {0}; + size_t len = 0; + int res = 0; + uint16_t sw = 0; + + do { + if (continuous && kbd_enter_pressed()) { + break; + } + + // init applets list tree + const char *al = "Applets"; + struct tlvdb *tlvSelect = tlvdb_fixed(1, strlen(al), (const unsigned char *)al); + + res = EMVSelectPSE(channel, true, true, 2, buf, sizeof(buf), &len, &sw); + + // search PSE / PPSE + res = EMVSearchPSE(channel, false, true, psenum, false, tlvSelect); + if (res) { + // EMV SEARCH with AID list + DropFieldEx(channel); + if (EMVSearch(channel, true, true, false, tlvSelect, false)) { + tlvdb_free(tlvSelect); + DropFieldEx(channel); + continue; + } + } + + // select application + EMVSelectApplication(tlvSelect, AID, &AIDlen); + tlvdb_free(tlvSelect); + + if (AIDlen == 0) { + DropFieldEx(channel); + continue; + } + + // Init TLV tree + const char *alr = "Root"; + struct tlvdb *tlvRoot = tlvdb_fixed(1, strlen(alr), (const unsigned char *)alr); + + // EMV SELECT applet + res = EMVSelect(channel, false, true, AID, AIDlen, buf, sizeof(buf), &len, &sw, tlvRoot); + if (res) { + DropFieldEx(channel); + continue; + } + + // decode application parts + emv_parse_card_details(buf, len); + + for (TransactionType_t tt = TT_MSD; tt < TT_END; tt++) { + + // create transaction parameters + bool gen_acgpo = false; + InitTransactionParameters(tlvRoot, false, tt, gen_acgpo); + + struct tlv *pdol_tlv = dol_process(tlvdb_get(tlvRoot, 0x9F38, NULL), tlvRoot, 0x83); + if (!pdol_tlv) { + continue; + } + + size_t pdtd_len = 0; + unsigned char *pdol_data_tlv = tlv_encode(pdol_tlv, &pdtd_len); + if (!pdol_data_tlv) { + free(pdol_tlv); + continue; + } + + res = EMVGPO(channel, true, pdol_data_tlv, pdtd_len, buf, sizeof(buf), &len, &sw, tlvRoot); + free(pdol_data_tlv); + free(pdol_tlv); + + if (res) { + continue; + } + + ProcessGPOResponseFormat1(tlvRoot, buf, len, false); + + emv_parse_card_details(buf, len); + + if (tlvdb_get(tlvRoot, 0x77, NULL)) { + break; + } + } + + const struct tlv *AFL = tlvdb_get(tlvRoot, 0x94, NULL); + + if (AFL && AFL->len) { + + if (AFL->len % 4) { + continue; + } + + for (int i = 0; i < AFL->len / 4; i++) { + uint8_t SFI = AFL->value[i * 4 + 0] >> 3; + uint8_t SFIstart = AFL->value[i * 4 + 1]; + uint8_t SFIend = AFL->value[i * 4 + 2]; + + if (SFI == 0 || SFI == 31 || SFIstart == 0 || SFIstart > SFIend) { + continue; + } + + for (int n = SFIstart; n <= SFIend; n++) { + res = EMVReadRecord(channel, true, SFI, n, buf, sizeof(buf), &len, &sw, tlvRoot); + if (res) { + continue; + } + emv_parse_card_details(buf, len); + } + } + } + + } while (continuous); + + return PM3_SUCCESS; +} + static command_t CommandTable[] = { + {"-----------", CmdHelp, AlwaysAvailable, "----------------------- " _CYAN_("general") " -----------------------"}, {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"list", CmdEMVList, AlwaysAvailable, "List ISO7816 history"}, + {"test", CmdEMVTest, AlwaysAvailable, "Crypto logic test"}, + {"-----------", CmdHelp, IfPm3Iso14443a, "---------------------- " _CYAN_("operations") " ---------------------"}, + {"challenge", CmdEMVGenerateChallenge, IfPm3Iso14443, "Generate challenge"}, {"exec", CmdEMVExec, IfPm3Iso14443, "Executes EMV contactless transaction"}, + {"genac", CmdEMVAC, IfPm3Iso14443, "Generate ApplicationCryptogram"}, + {"gpo", CmdEMVGPO, IfPm3Iso14443, "Execute GetProcessingOptions"}, + {"intauth", CmdEMVInternalAuthenticate, IfPm3Iso14443, "Internal authentication"}, {"pse", CmdEMVPPSE, IfPm3Iso14443, "Execute PPSE. It selects 2PAY.SYS.DDF01 or 1PAY.SYS.DDF01 directory"}, + {"reader", CmdEMVReader, IfPm3Iso14443a, "Act like an EMV reader"}, + {"readrec", CmdEMVReadRecord, IfPm3Iso14443, "Read files from card"}, + {"roca", CmdEMVRoca, IfPm3Iso14443, "Extract public keys and run ROCA test"}, + {"scan", CmdEMVScan, IfPm3Iso14443, "Scan EMV card and save it contents to json file for emulator"}, {"search", CmdEMVSearch, IfPm3Iso14443, "Try to select all applets from applets list and print installed applets"}, {"select", CmdEMVSelect, IfPm3Iso14443, "Select applet"}, - {"gpo", CmdEMVGPO, IfPm3Iso14443, "Execute GetProcessingOptions"}, - {"readrec", CmdEMVReadRecord, IfPm3Iso14443, "Read files from card"}, - {"genac", CmdEMVAC, IfPm3Iso14443, "Generate ApplicationCryptogram"}, - {"challenge", CmdEMVGenerateChallenge, IfPm3Iso14443, "Generate challenge"}, - {"intauth", CmdEMVInternalAuthenticate, IfPm3Iso14443, "Internal authentication"}, - {"scan", CmdEMVScan, IfPm3Iso14443, "Scan EMV card and save it contents to json file for emulator"}, - {"test", CmdEMVTest, AlwaysAvailable, "Crypto logic test"}, /* + {"-----------", CmdHelp, IfPm3Iso14443a, "---------------------- " _CYAN_("simulation") " ---------------------"}, {"getrng", CmdEMVGetrng, IfPm3Iso14443, "get random number from terminal"}, {"eload", CmdEmvELoad, IfPm3Iso14443, "load EMV tag into device"}, {"dump", CmdEmvDump, IfPm3Iso14443, "dump EMV tag values"}, {"sim", CmdEmvSim, IfPm3Iso14443, "simulate EMV tag"}, {"clone", CmdEmvClone, IfPm3Iso14443, "clone an EMV tag"}, */ - {"list", CmdEMVList, AlwaysAvailable, "List ISO7816 history"}, - {"roca", CmdEMVRoca, IfPm3Iso14443, "Extract public keys and run ROCA test"}, {NULL, NULL, NULL, NULL} }; diff --git a/client/src/emv/emvcore.c b/client/src/emv/emvcore.c index 0628fcf76..45a415f41 100644 --- a/client/src/emv/emvcore.c +++ b/client/src/emv/emvcore.c @@ -204,7 +204,7 @@ void TLVPrintAIDlistFromSelectTLV(struct tlvdb *tlv) { break; } PrintAndLogEx(INFO, "| %s| %s | %s|", - sprint_hex_inrow_ex(tgAID->value, tgAID->len, 17), + sprint_hex_inrow_ex(tgAID->value, tgAID->len, 16), (tgPrio) ? sprint_hex(tgPrio->value, 1) : " ", (tgName) ? sprint_ascii_ex(tgName->value, tgName->len, 24) : " "); @@ -473,7 +473,7 @@ int EMVSearchPSE(Iso7816CommandChannel channel, bool ActivateField, bool LeaveFi return res; } -int EMVSearch(Iso7816CommandChannel channel, bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvdb *tlv) { +int EMVSearch(Iso7816CommandChannel channel, bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvdb *tlv, bool verbose) { uint8_t aidbuf[APDU_AID_LEN] = {0}; int aidlen = 0; uint8_t data[APDU_RES_LEN] = {0}; @@ -500,12 +500,16 @@ int EMVSearch(Iso7816CommandChannel channel, bool ActivateField, bool LeaveField if (LeaveFieldON == false) DropFieldEx(channel); - PrintAndLogEx(WARNING, "exiting..."); + if (verbose) { + PrintAndLogEx(WARNING, "exiting..."); + } return 1; } retrycnt = 0; - PrintAndLogEx(FAILED, "Retry failed [%s]. Skipped...", AIDlist[i].aid); + if (verbose) { + PrintAndLogEx(FAILED, "Retry failed [%s]. Skipped...", AIDlist[i].aid); + } } continue; } diff --git a/client/src/emv/emvcore.h b/client/src/emv/emvcore.h index bb76aeb2f..6cf646c60 100644 --- a/client/src/emv/emvcore.h +++ b/client/src/emv/emvcore.h @@ -28,12 +28,13 @@ #include "iso7816/iso7816core.h" #include "emv_pki.h" -enum TransactionType { +typedef enum TransactionType { TT_MSD, TT_VSDC, // contact only. not standard for contactless TT_QVSDCMCHIP, TT_CDA, -}; + TT_END, +} TransactionType_t; extern const char *TransactionTypeStr[]; enum CardPSVendor { @@ -62,7 +63,7 @@ int EMVExchange(Iso7816CommandChannel channel, bool LeaveFieldON, sAPDU_t apdu, // search application int EMVSearchPSE(Iso7816CommandChannel channel, bool ActivateField, bool LeaveFieldON, uint8_t PSENum, bool decodeTLV, struct tlvdb *tlv); -int EMVSearch(Iso7816CommandChannel channel, bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvdb *tlv); +int EMVSearch(Iso7816CommandChannel channel, bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvdb *tlv, bool verbose); int EMVSelectPSE(Iso7816CommandChannel channel, bool ActivateField, bool LeaveFieldON, uint8_t PSENum, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw); int EMVSelect(Iso7816CommandChannel channel, bool ActivateField, bool LeaveFieldON, uint8_t *AID, size_t AIDLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv); // select application