X-Git-Url: http://git.zerfleddert.de/cgi-bin/gitweb.cgi/proxmark3-svn/blobdiff_plain/66efdc1f646eb4e8e0c0f50003cf9d469f4e54c1..cd2f1acd1b4e1458422462f9584a06900f8f3719:/client/emv/cmdemv.c diff --git a/client/emv/cmdemv.c b/client/emv/cmdemv.c index 679c26ed..496b3cb8 100644 --- a/client/emv/cmdemv.c +++ b/client/emv/cmdemv.c @@ -8,86 +8,39 @@ // EMV commands //----------------------------------------------------------------------------- +#include #include "cmdemv.h" - -int UsageCmdHFEMVSelect(void) { - PrintAndLog("HELP : Executes select applet command:\n"); - PrintAndLog("Usage: hf emv select [-s][-k][-a][-t] \n"); - PrintAndLog(" Options:"); - PrintAndLog(" -s : select card"); - PrintAndLog(" -k : keep field for next command"); - PrintAndLog(" -a : show APDU reqests and responses\n"); - PrintAndLog(" -t : TLV decode results\n"); - PrintAndLog("Samples:"); - PrintAndLog(" hf emv select -s a00000000101 -> select card, select applet"); - PrintAndLog(" hf emv select -s -t a00000000101 -> select card, select applet, show result in TLV"); - return 0; -} +#include "test/cryptotest.h" +#include "cliparser/cliparser.h" int CmdHFEMVSelect(const char *cmd) { uint8_t data[APDU_AID_LEN] = {0}; int datalen = 0; - bool activateField = false; - bool leaveSignalON = false; - bool decodeTLV = false; - if (strlen(cmd) < 1) { - UsageCmdHFEMVSelect(); - return 0; - } - - SetAPDULogging(false); - - int cmdp = 0; - while(param_getchar(cmd, cmdp) != 0x00) { - char c = param_getchar(cmd, cmdp); - if ((c == '-') && (param_getlength(cmd, cmdp) == 2)) - switch (param_getchar_indx(cmd, 1, cmdp)) { - case 'h': - case 'H': - UsageCmdHFEMVSelect(); - return 0; - case 's': - case 'S': - activateField = true; - break; - case 'k': - case 'K': - leaveSignalON = true; - break; - case 'a': - case 'A': - SetAPDULogging(true); - break; - case 't': - case 'T': - decodeTLV = true; - break; - default: - PrintAndLog("Unknown parameter '%c'", param_getchar_indx(cmd, 1, cmdp)); - return 1; - } - - if (isxdigit(c)) { - switch(param_gethex_to_eol(cmd, cmdp, data, sizeof(data), &datalen)) { - case 1: - PrintAndLog("Invalid HEX value."); - return 1; - case 2: - PrintAndLog("AID too large."); - return 1; - case 3: - PrintAndLog("Hex must have even number of digits."); - return 1; - } - - // we get all the hex to end of line with spaces - break; - } + CLIParserInit("hf 14a 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"); - cmdp++; - } + 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 reqests and responses"), + arg_lit0("tT", "tlv", "TLV decode results"), + arg_str0(NULL, NULL, "", NULL), + 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); + CLIGetStrWithReturn(5, data, &datalen); + CLIParserFree(); + + SetAPDULogging(APDULogging); // exec uint8_t buf[APDU_RES_LEN] = {0}; @@ -107,65 +60,30 @@ int CmdHFEMVSelect(const char *cmd) { return 0; } -int UsageCmdHFEMVSearch(void) { - PrintAndLog("HELP : Tries to select all applets from applet list:\n"); - PrintAndLog("Usage: hf emv search [-s][-k][-a][-t]\n"); - PrintAndLog(" Options:"); - PrintAndLog(" -s : select card"); - PrintAndLog(" -k : keep field for next command"); - PrintAndLog(" -a : show APDU reqests and responses\n"); - PrintAndLog(" -t : TLV decode results of selected applets\n"); - PrintAndLog("Samples:"); - PrintAndLog(" hf emv search -s -> select card and search"); - PrintAndLog(" hf emv search -s -t -> select card, search and show result in TLV"); - return 0; -} - int CmdHFEMVSearch(const char *cmd) { - bool activateField = false; - bool leaveSignalON = false; - bool decodeTLV = false; + CLIParserInit("hf 14a select", + "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"); - if (strlen(cmd) < 1) { - UsageCmdHFEMVSearch(); - return 0; - } + 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); - SetAPDULogging(false); + 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); - int cmdp = 0; - while(param_getchar(cmd, cmdp) != 0x00) { - char c = param_getchar(cmd, cmdp); - if ((c == '-') && (param_getlength(cmd, cmdp) == 2)) - switch (param_getchar_indx(cmd, 1, cmdp)) { - case 'h': - case 'H': - UsageCmdHFEMVSearch(); - return 0; - case 's': - case 'S': - activateField = true; - break; - case 'k': - case 'K': - leaveSignalON = true; - break; - case 'a': - case 'A': - SetAPDULogging(true); - break; - case 't': - case 'T': - decodeTLV = true; - break; - default: - PrintAndLog("Unknown parameter '%c'", param_getchar_indx(cmd, 1, cmdp)); - return 1; - } - cmdp++; - } - struct tlvdb *t = NULL; const char *al = "Applets list"; t = tlvdb_fixed(1, strlen(al), (const unsigned char *)al); @@ -187,80 +105,179 @@ int CmdHFEMVSearch(const char *cmd) { return 0; } -int UsageCmdHFEMVPPSE(void) { - PrintAndLog("HELP : Executes PSE/PPSE select command. It returns list of applet on the card:\n"); - PrintAndLog("Usage: hf emv pse [-s][-k][-1][-2][-a][-t]\n"); - PrintAndLog(" Options:"); - PrintAndLog(" -s : select card"); - PrintAndLog(" -k : keep field for next command"); - PrintAndLog(" -1 : ppse (1PAY.SYS.DDF01)"); - PrintAndLog(" -2 : pse (2PAY.SYS.DDF01)"); - PrintAndLog(" -a : show APDU reqests and responses\n"); - PrintAndLog(" -t : TLV decode results\n"); - PrintAndLog("Samples:"); - PrintAndLog(" hf emv pse -s -1 -> select, get pse"); - PrintAndLog(" hf emv pse -s -k -2 -> select, get ppse, keep field"); - PrintAndLog(" hf emv pse -s -t -2 -> select, get ppse, show result in TLV"); - return 0; -} - int CmdHFEMVPPSE(const char *cmd) { + CLIParserInit("hf 14a 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; - bool activateField = false; - bool leaveSignalON = false; - bool decodeTLV = false; + 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 (strlen(cmd) < 1) { - UsageCmdHFEMVPPSE(); + 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 14a gpo", + "Executes Get Processing Options command. It returns data in TLV format (0x77 - format2) or plain (0x80 - format1) formats.\nNeeds a bank applet to be selected.\n", + "Usage:\n\thf emv gpo -k -> select, execute GPO\n\thf emv gpo -st 01020304 -> select, execute GPO with 4-byte PDOL data, 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 for PDOL making from `emv/defparams.json` file (by default uses default parameters) (NOT WORK!!!)"), + arg_lit0("mM", "make", "make PDOLdata from PDOL (tag 9F38) and parameters (NOT WORK!!!)"), + arg_lit0("aA", "apdu", "show APDU reqests and responses"), + arg_lit0("tT", "tlv", "TLV decode results of selected applets"), + arg_str0(NULL, NULL, "", 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, //was 01 + .len = datalen, + .value = (uint8_t *)data, + }; + if (dataMakeFromPDOL) { + // TODO + PrintAndLog("Make PDOL data not implemented!"); + if (paramsLoadFromFile) { + }; +/* pdol_data_tlv = dol_process(tlvdb_get(tlvRoot, 0x9f38, NULL), tlvRoot, 0x83); + if (!pdol_data_tlv){ + PrintAndLog("ERROR: can't create PDOL TLV."); + tlvdb_free(tlvRoot); + return 4; + }*/ return 0; + } else { + 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); + + free(pdol_data_tlv_data); + tlvdb_free(tlvRoot); + + if (sw) + PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); - SetAPDULogging(false); + if (res) + return res; - int cmdp = 0; - while(param_getchar(cmd, cmdp) != 0x00) { - char c = param_getchar(cmd, cmdp); - if ((c == '-') && (param_getlength(cmd, cmdp) == 2)) - switch (param_getchar_indx(cmd, 1, cmdp)) { - case 'h': - case 'H': - UsageCmdHFEMVPPSE(); - return 0; - case 's': - case 'S': - activateField = true; - break; - case 'k': - case 'K': - leaveSignalON = true; - break; - case 'a': - case 'A': - SetAPDULogging(true); - break; - case 't': - case 'T': - decodeTLV = true; - break; - case '1': - PSENum = 1; - break; - case '2': - PSENum = 2; - break; - default: - PrintAndLog("Unknown parameter '%c'", param_getchar_indx(cmd, 1, cmdp)); - return 1; - } - cmdp++; + if (decodeTLV) + TLVPrintFromBuffer(buf, len); + + return 0; +} + +int CmdHFEMVReadRecord(const char *cmd) { + uint8_t data[APDU_RES_LEN] = {0}; + int datalen = 0; + + CLIParserInit("hf 14a 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.\n", + "Usage:\n\thf emv readrec -k -> select, get pse\n\thf emv readrec -st2 -> select, get ppse, 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_str0(NULL, NULL, "", 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 = EMVSelectPSE(activateField, leaveSignalON, PSENum, buf, sizeof(buf), &len, &sw); + 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)); @@ -275,36 +292,65 @@ int CmdHFEMVPPSE(const char *cmd) { return 0; } +int CmdHFEMVAC(const char *cmd) { + + return 0; +} + +int CmdHFEMVGenerateChallenge(const char *cmd) { + + return 0; +} + +int CmdHFEMVInternalAuthenticate(const char *cmd) { + + return 0; +} + int UsageCmdHFEMVExec(void) { PrintAndLog("HELP : Executes EMV contactless transaction:\n"); - PrintAndLog("Usage: hf emv exec [-s][-a][-t]\n"); + PrintAndLog("Usage: hf emv exec [-s][-a][-t][-f][-v][-c][-x][-g]\n"); PrintAndLog(" Options:"); PrintAndLog(" -s : select card"); PrintAndLog(" -a : show APDU reqests and responses\n"); PrintAndLog(" -t : TLV decode results\n"); PrintAndLog(" -f : force search AID. Search AID instead of execute PPSE.\n"); + PrintAndLog(" -v : transaction type - qVSDC or M/Chip.\n"); + PrintAndLog(" -c : transaction type - qVSDC or M/Chip plus CDA (SDAD generation).\n"); + PrintAndLog(" -x : transaction type - VSDC. For test only. Not a standart behavior.\n"); + PrintAndLog(" -g : VISA. generate AC from GPO\n"); + PrintAndLog("By default : transaction type - MSD.\n"); PrintAndLog("Samples:"); - PrintAndLog(" hf emv pse -s -> select card"); - PrintAndLog(" hf emv pse -s -t -a -> select card, show responses in TLV, show APDU"); + PrintAndLog(" hf emv exec -s -a -t -> execute MSD transaction"); + PrintAndLog(" hf emv exec -s -a -t -c -> execute CDA transaction"); return 0; } #define TLV_ADD(tag, value)( tlvdb_add(tlvRoot, tlvdb_fixed(tag, sizeof(value) - 1, (const unsigned char *)value)) ) +#define dreturn(n) {free(pdol_data_tlv);tlvdb_free(tlvSelect);tlvdb_free(tlvRoot);DropField();return n;} int CmdHFEMVExec(const char *cmd) { bool activateField = false; bool showAPDU = false; bool decodeTLV = false; bool forceSearch = false; + enum TransactionType TrType = TT_MSD; + bool GenACGPO = false; 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; + if (strlen(cmd) < 1) { UsageCmdHFEMVExec(); return 0; @@ -317,7 +363,7 @@ int CmdHFEMVExec(const char *cmd) { switch (param_getchar_indx(cmd, 1, cmdp)) { case 'h': case 'H': - UsageCmdHFEMVPPSE(); + UsageCmdHFEMVExec(); return 0; case 's': case 'S': @@ -335,6 +381,22 @@ int CmdHFEMVExec(const char *cmd) { case 'F': forceSearch = true; break; + case 'x': + case 'X': + TrType = TT_VSDC; + break; + case 'v': + case 'V': + TrType = TT_QVSDCMCHIP; + break; + case 'c': + case 'C': + TrType = TT_CDA; + break; + case 'g': + case 'G': + GenACGPO = true; + break; default: PrintAndLog("Unknown parameter '%c'", param_getchar_indx(cmd, 1, cmdp)); return 1; @@ -344,7 +406,6 @@ int CmdHFEMVExec(const char *cmd) { // init applets list tree - struct tlvdb *tlvSelect = NULL; const char *al = "Applets list"; tlvSelect = tlvdb_fixed(1, strlen(al), (const unsigned char *)al); @@ -368,8 +429,7 @@ int CmdHFEMVExec(const char *cmd) { PrintAndLog("\n* Search AID in list."); SetAPDULogging(false); if (EMVSearch(activateField, true, decodeTLV, tlvSelect)) { - tlvdb_free(tlvSelect); - return 2; + dreturn(2); } // check search and select application id @@ -378,14 +438,13 @@ int CmdHFEMVExec(const char *cmd) { } // Init TLV tree - struct tlvdb *tlvRoot = NULL; const char *alr = "Root terminal TLV tree"; tlvRoot = tlvdb_fixed(1, strlen(alr), (const unsigned char *)alr); // check if we found EMV application on card if (!AIDlen) { PrintAndLog("Can't select AID. EMV AID not found"); - return 2; + dreturn(2); } // Select @@ -395,7 +454,7 @@ int CmdHFEMVExec(const char *cmd) { if (res) { PrintAndLog("Can't select AID (%d). Exit...", res); - return 3; + dreturn(3); } if (decodeTLV) @@ -405,10 +464,30 @@ int CmdHFEMVExec(const char *cmd) { PrintAndLog("\n* Init transaction parameters."); //9F66:(Terminal Transaction Qualifiers (TTQ)) len:4 -// TLV_ADD(0x9F66, "\x86\x00\x00\x00"); // MSD - TLV_ADD(0x9F66, "\x26\x00\x00\x00"); // qVSDC -// TLV_ADD(0x9F66, "\x8e\x00\x00\x00"); // CDA - //9F02:(Amount, Authorised (Numeric)) len:6 + char *qVSDC = "\x26\x00\x00\x00"; + if (GenACGPO) { + qVSDC = "\x26\x80\x00\x00"; + } + switch(TrType) { + case TT_MSD: + TLV_ADD(0x9F66, "\x86\x00\x00\x00"); // MSD + break; + // not standard for contactless. just for test. + case TT_VSDC: + TLV_ADD(0x9F66, "\x46\x00\x00\x00"); // VSDC + break; + case TT_QVSDCMCHIP: + TLV_ADD(0x9F66, qVSDC); // qVSDC + break; + case TT_CDA: + TLV_ADD(0x9F66, qVSDC); // qVSDC (VISA CDA not enabled) + break; + default: + TLV_ADD(0x9F66, "\x26\x00\x00\x00"); // qVSDC + break; + } + + //9F02:(Amount, authorized (Numeric)) len:6 TLV_ADD(0x9F02, "\x00\x00\x00\x00\x01\x00"); //9F1A:(Terminal Country Code) len:2 TLV_ADD(0x9F1A, "ru"); @@ -427,28 +506,29 @@ int CmdHFEMVExec(const char *cmd) { TLVPrintFromTLV(tlvRoot); // TODO delete!!! PrintAndLog("\n* Calc PDOL."); - struct tlv *pdol_data_tlv = dol_process(tlvdb_get(tlvRoot, 0x9f38, NULL), tlvRoot, 0x83); + pdol_data_tlv = dol_process(tlvdb_get(tlvRoot, 0x9f38, NULL), tlvRoot, 0x83); if (!pdol_data_tlv){ PrintAndLog("ERROR: can't create PDOL TLV."); - return 4; + 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."); - return 4; + dreturn(4); } PrintAndLog("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); - free(pdol_data_tlv); + free(pdol_data_tlv_data); + //free(pdol_data_tlv); --- free on exit. if (res) { PrintAndLog("GPO error(%d): %4x. Exit...", res, sw); - return 5; + dreturn(5); } // process response template format 1 [id:80 2b AIP + x4b AFL] and format 2 [id:77 TLV] @@ -534,9 +614,23 @@ int CmdHFEMVExec(const char *cmd) { PrintAndLog(""); } + // Build Input list for Offline Data Authentication + // EMV 4.3 book3 10.3, page 96 if (SFIoffline) { - // here will be offline records storing... - // dont foget: if (sfi < 11) + if (SFI < 11) { + const unsigned char *abuf = buf; + size_t elmlen = len; + struct tlv e; + if (tlv_parse_tl(&abuf, &elmlen, &e)) { + memcpy(&ODAiList[ODAiListLen], &buf[len - elmlen], elmlen); + ODAiListLen += elmlen; + } else { + PrintAndLog("ERROR SFI[%02x]. Creating input list for Offline Data Authentication error.", SFI); + } + } else { + memcpy(&ODAiList[ODAiListLen], buf, len); + ODAiListLen += len; + } } } } @@ -544,13 +638,34 @@ int CmdHFEMVExec(const char *cmd) { break; } - // transaction check + // 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); + } + + // get AIP const struct tlv *AIPtlv = tlvdb_get(tlvRoot, 0x82, NULL); uint16_t AIP = AIPtlv->value[0] + AIPtlv->value[1] * 0x100; - PrintAndLog("* * AIP=%x", AIP); + PrintAndLog("* * AIP=%04x", AIP); + // SDA + if (AIP & 0x0040) { + PrintAndLog("\n* SDA"); + trSDA(tlvRoot); + } + + // DDA + if (AIP & 0x0020) { + PrintAndLog("\n* DDA"); + trDDA(decodeTLV, tlvRoot); + } + + // transaction check + // qVSDC - { + if (TrType == TT_QVSDCMCHIP|| TrType == TT_CDA){ // 9F26: Application Cryptogram const struct tlv *AC = tlvdb_get(tlvRoot, 0x9F26, NULL); if (AC) { @@ -587,19 +702,96 @@ int CmdHFEMVExec(const char *cmd) { } } - // TODO: Mastercard M/CHIP - { + // Mastercard M/CHIP + 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."); + + PrintAndLog("* * Generate challenge"); + res = EMVGenerateChallenge(true, buf, sizeof(buf), &len, &sw, tlvRoot); + if (res) { + PrintAndLog("ERROR GetChallenge. APDU error %4x", sw); + dreturn(6); + } + if (len < 4) { + PrintAndLog("ERROR GetChallenge. Wrong challenge length %d", len); + dreturn(6); + } + + // ICC Dynamic Number + struct tlvdb * ICCDynN = tlvdb_fixed(0x9f4c, len, buf); + tlvdb_add(tlvRoot, ICCDynN); + if (decodeTLV){ + PrintAndLog("\n* * ICC Dynamic Number:"); + TLVPrintFromTLV(ICCDynN); + } + + PrintAndLog("* * 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."); + dreturn(6); + } + PrintAndLog("CDOL1 data[%d]: %s", cdol_data_tlv->len, sprint_hex(cdol_data_tlv->value, cdol_data_tlv->len)); + + PrintAndLog("* * 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); + + if (res) { + PrintAndLog("AC1 error(%d): %4x. Exit...", res, sw); + dreturn(7); + } + + if (decodeTLV) + TLVPrintFromBuffer(buf, len); + + // CDA + PrintAndLog("\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); + } + free(ac_tlv); + free(cdol_data_tlv); + + PrintAndLog("\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("------------------------------"); + if (CID->len > 0) { + switch(CID->value[0] & EMVAC_AC_MASK){ + case EMVAC_AAC: + PrintAndLog("Transaction DECLINED."); + break; + case EMVAC_TC: + PrintAndLog("Transaction approved OFFLINE."); + break; + case EMVAC_ARQC: + PrintAndLog("Transaction approved ONLINE."); + break; + default: + PrintAndLog("ERROR: CID transaction code error %2x", CID->value[0] & EMVAC_AC_MASK); + break; + } + } else { + PrintAndLog("ERROR: Wrong CID length %d", CID->len); + } + } else { + PrintAndLog("ERROR: CID(9F27) not found."); + } } } // MSD - if (AIP & 0x8000) { + if (AIP & 0x8000 && TrType == TT_MSD) { PrintAndLog("\n--> MSD transaction."); - PrintAndLog("* MSD dCVV path. Check dCVV"); const struct tlv *track2 = tlvdb_get(tlvRoot, 0x57, NULL); @@ -624,51 +816,40 @@ int CmdHFEMVExec(const char *cmd) { if (!UDOL) PrintAndLog("Use default UDOL."); - struct tlv *udol_data_tlv = dol_process(UDOL ? UDOL : &defUDOL, tlvRoot, 0x01); // 0x01 - fake tag! + 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."); - return 4; - } - - size_t udol_data_tlv_data_len; - unsigned char *udol_data_tlv_data = tlv_encode(udol_data_tlv, &udol_data_tlv_data_len); - if (!udol_data_tlv_data) { - PrintAndLog("ERROR: can't create UDOL data."); - return 4; + dreturn(8); } - // eliminate fake tag - udol_data_tlv_data_len -= 2; - udol_data_tlv_data += 2; - - PrintAndLog("UDOL data[%d]: %s", udol_data_tlv_data_len, sprint_hex(udol_data_tlv_data, udol_data_tlv_data_len)); + PrintAndLog("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)"); - res = MSCComputeCryptoChecksum(true, udol_data_tlv_data, udol_data_tlv_data_len, buf, sizeof(buf), &len, &sw, tlvRoot); + res = MSCComputeCryptoChecksum(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); - return 5; + free(udol_data_tlv); + dreturn(9); } if (decodeTLV) { TLVPrintFromBuffer(buf, len); PrintAndLog(""); } + free(udol_data_tlv); } } else { PrintAndLog("ERROR MSD: Track2 data not found."); } } - - // additional contacless EMV commands (fDDA, external authenticate) - - + // DropField DropField(); // Destroy TLV's + free(pdol_data_tlv); tlvdb_free(tlvSelect); tlvdb_free(tlvRoot); @@ -677,13 +858,23 @@ int CmdHFEMVExec(const char *cmd) { return 0; } +int CmdHFEMVTest(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."}, + {"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."}, + {"test", CmdHFEMVTest, 0, "Crypto logic test."}, {NULL, NULL, 0, NULL} };