// EMV commands
//-----------------------------------------------------------------------------
+#include <ctype.h>
#include "cmdemv.h"
-
-int UsageCmdHFEMVSelect(void) {
- PrintAndLog("HELP : Executes select applet command:\n");
- PrintAndLog("Usage: hf emv select [-s][-k][-a][-t] <HEX applet AID>\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, "<HEX applet AID>", 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};
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);
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, "<HEX PDOLdata/PDOL>", NULL),
+ arg_param_end
+ };
+ CLIExecWithReturn(cmd, argtable, true);
+
+ bool leaveSignalON = arg_get_lit(1);
+ bool paramsLoadFromFile = arg_get_lit(2);
+ bool dataMakeFromPDOL = arg_get_lit(3);
+ bool APDULogging = arg_get_lit(4);
+ bool decodeTLV = arg_get_lit(5);
+ CLIGetStrWithReturn(6, data, &datalen);
+ CLIParserFree();
+
+ SetAPDULogging(APDULogging);
+
+ // Init TLV tree
+ const char *alr = "Root terminal TLV tree";
+ struct tlvdb *tlvRoot = tlvdb_fixed(1, strlen(alr), (const unsigned char *)alr);
+
+ // calc PDOL
+ struct tlv *pdol_data_tlv = NULL;
+ struct tlv data_tlv = {
+ .tag = 0x83, //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;
}
- SetAPDULogging(false);
+ 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));
- 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++;
+ // 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));
+
+ if (res)
+ return res;
+
+ if (decodeTLV)
+ TLVPrintFromBuffer(buf, len);
+
+ return 0;
+}
+
+int CmdHFEMVReadRecord(const char *cmd) {
+ uint8_t data[APDU_RES_LEN] = {0};
+ int datalen = 0;
+
+ CLIParserInit("hf 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, "<SFI 1byte HEX><SFIrec 1byte HEX>", NULL),
+ arg_param_end
+ };
+ CLIExecWithReturn(cmd, argtable, true);
+
+ bool leaveSignalON = arg_get_lit(1);
+ bool APDULogging = arg_get_lit(2);
+ bool decodeTLV = arg_get_lit(3);
+ CLIGetStrWithReturn(4, data, &datalen);
+ CLIParserFree();
+
+ if (datalen != 2) {
+ PrintAndLog("ERROR: Command needs to have 2 bytes of data");
+ return 1;
}
+ SetAPDULogging(APDULogging);
+
// exec
uint8_t buf[APDU_RES_LEN] = {0};
size_t len = 0;
uint16_t sw = 0;
- int res = 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));
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;
switch (param_getchar_indx(cmd, 1, cmdp)) {
case 'h':
case 'H':
- UsageCmdHFEMVPPSE();
+ UsageCmdHFEMVExec();
return 0;
case 's':
case 'S':
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;
// init applets list tree
- struct tlvdb *tlvSelect = NULL;
const char *al = "Applets list";
tlvSelect = tlvdb_fixed(1, strlen(al), (const unsigned char *)al);
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
}
// 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
if (res) {
PrintAndLog("Can't select AID (%d). Exit...", res);
- return 3;
+ dreturn(3);
}
if (decodeTLV)
TLVPrintFromBuffer(buf, len);
PrintAndLog("* Selected.");
-PrintAndLog("-----BREAK.");
-return 0;
PrintAndLog("\n* Init transaction parameters.");
//9F66:(Terminal Transaction Qualifiers (TTQ)) len:4
- TLV_ADD(0x9F66, "\x26\x00\x00\x00"); // E6
- //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");
TLV_ADD(0x9C, "\x00");
// 9F37 Unpredictable Number len:4
TLV_ADD(0x9F37, "\x01\x02\x03\x04");
+ // 9F6A Unpredictable Number (MSD for UDOL) len:4
+ TLV_ADD(0x9F6A, "\x01\x02\x03\x04");
- TLVPrintFromTLV(tlvRoot);
+ 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("-----BREAK.");
-//return 0;
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]
if (buf[0] == 0x80) {
-
-
-
if (decodeTLV){
PrintAndLog("GPO response format1:");
TLVPrintFromBuffer(buf, len);
}
- } else {
-
-
+ if (len < 4 || (len - 4) % 4) {
+ PrintAndLog("ERROR: 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:");
+ TLVPrintFromTLV(f1AIP);
+ }
+
+ // AFL
+ struct tlvdb * f1AFL = tlvdb_fixed(0x94, len - 4, buf + 2 + 2);
+ tlvdb_add(tlvRoot, f1AFL);
+ if (decodeTLV)
+ TLVPrintFromTLV(f1AFL);
+ }
+ } else {
if (decodeTLV)
TLVPrintFromBuffer(buf, len);
}
+ // extract PAN from track2
+ {
+ const struct tlv *track2 = tlvdb_get(tlvRoot, 0x57, NULL);
+ if (!tlvdb_get(tlvRoot, 0x5a, NULL) && track2 && track2->len >= 8) {
+ struct tlvdb *pan = GetPANFromTrack2(track2);
+ 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));
+ } else {
+ PrintAndLog("\n* * WARNING: Can't extract PAN from track2.");
+ }
+ }
+ }
+
PrintAndLog("\n* Read records from AFL.");
const struct tlv *AFL = tlvdb_get(tlvRoot, 0x94, NULL);
if (!AFL || !AFL->len) {
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;
+ }
}
}
}
break;
}
- // additional contacless EMV commands (fDDA, CDA, external authenticate)
+ // 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=%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) {
+ PrintAndLog("\n--> qVSDC transaction.");
+ PrintAndLog("* AC path");
+
+ // 9F36: Application Transaction Counter (ATC)
+ const struct tlv *ATC = tlvdb_get(tlvRoot, 0x9F36, NULL);
+ if (ATC) {
+
+ // 9F10: Issuer Application Data - optional
+ 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));
+ if (IAD){
+ PrintAndLog("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));
+ struct tlvdb * cvr = tlvdb_fixed(0x20, IAD->value[0] - 2, &IAD->value[3]);
+ TLVPrintFromTLVLev(cvr, 1);
+ }
+ } else {
+ PrintAndLog("WARNING: IAD not found.");
+ }
+
+ } else {
+ PrintAndLog("ERROR AC: Application Transaction Counter (ATC) not found.");
+ }
+ }
+ }
+ // 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 && TrType == TT_MSD) {
+ PrintAndLog("\n--> MSD transaction.");
+
+ PrintAndLog("* 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));
+
+ struct tlvdb *dCVV = GetdCVVRawFromTrack2(track2);
+ PrintAndLog("dCVV raw data:");
+ TLVPrintFromTLV(dCVV);
+
+ if (GetCardPSVendor(AID, AIDlen) == CV_MASTERCARD) {
+ PrintAndLog("\n* Mastercard calculate UDOL");
+
+ // UDOL (9F69)
+ const struct tlv *UDOL = tlvdb_get(tlvRoot, 0x9F69, NULL);
+ // UDOL(9F69) default: 9F6A (Unpredictable number) 4 bytes
+ const struct tlv defUDOL = {
+ .tag = 0x01,
+ .len = 3,
+ .value = (uint8_t *)"\x9f\x6a\x04",
+ };
+ if (!UDOL)
+ PrintAndLog("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.");
+ dreturn(8);
+ }
+
+ 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, (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);
+ free(udol_data_tlv);
+ dreturn(9);
+ }
+
+ if (decodeTLV) {
+ TLVPrintFromBuffer(buf, len);
+ PrintAndLog("");
+ }
+ free(udol_data_tlv);
+
+ }
+ } else {
+ PrintAndLog("ERROR MSD: Track2 data not found.");
+ }
+ }
+
// DropField
DropField();
// Destroy TLV's
+ free(pdol_data_tlv);
tlvdb_free(tlvSelect);
tlvdb_free(tlvRoot);
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}
};