Merge pull request #67 from merlokk/emv_sc

Emv commands works with smartcard interface
This commit is contained in:
RFID Research Group 2018-12-07 09:30:48 +01:00 committed by GitHub
commit a86a60bd06
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 337 additions and 185 deletions

View file

@ -304,7 +304,7 @@ test_script:
#proxmark crypto tests #proxmark crypto tests
ExecTest "hf emv test" "hf emv test" {bash -lc "cd ~/client;./proxmark3 comx -c 'hf emv test'"} "Tests ?OK" ExecTest "emv test" "emv test" {bash -lc "cd ~/client;./proxmark3 comx -c 'emv test'"} "Tests ?OK"
if ($global:TestsPassed) { if ($global:TestsPassed) {

View file

@ -112,7 +112,6 @@ static command_t CommandTable[] = {
{"14b", CmdHF14B, 1, "{ ISO14443B RFIDs... }"}, {"14b", CmdHF14B, 1, "{ ISO14443B RFIDs... }"},
{"15", CmdHF15, 1, "{ ISO15693 RFIDs... }"}, {"15", CmdHF15, 1, "{ ISO15693 RFIDs... }"},
{"epa", CmdHFEPA, 1, "{ German Identification Card... }"}, {"epa", CmdHFEPA, 1, "{ German Identification Card... }"},
{"emv", CmdHFEMV, 1, "{ EMV RFIDs... }"},
{"felica", CmdHFFelica, 1, "{ ISO18092 / Felica RFIDs... }"}, {"felica", CmdHFFelica, 1, "{ ISO18092 / Felica RFIDs... }"},
{"legic", CmdHFLegic, 1, "{ LEGIC RFIDs... }"}, {"legic", CmdHFLegic, 1, "{ LEGIC RFIDs... }"},
{"iclass", CmdHFiClass, 1, "{ ICLASS RFIDs... }"}, {"iclass", CmdHFiClass, 1, "{ ICLASS RFIDs... }"},

View file

@ -30,7 +30,6 @@
#include "cmdhfmfdes.h" // DESFIRE #include "cmdhfmfdes.h" // DESFIRE
#include "cmdhftopaz.h" // TOPAZ #include "cmdhftopaz.h" // TOPAZ
#include "cmdhffelica.h" // ISO18092 / FeliCa #include "cmdhffelica.h" // ISO18092 / FeliCa
#include "emv/cmdemv.h" // EMV
#include "cmdhffido.h" // FIDO authenticators #include "cmdhffido.h" // FIDO authenticators
#include "cmdtrace.h" // trace list #include "cmdtrace.h" // trace list

View file

@ -22,6 +22,7 @@ static command_t CommandTable[] = {
{"hf", CmdHF, 1, "{ High Frequency commands... }"}, {"hf", CmdHF, 1, "{ High Frequency commands... }"},
{"hw", CmdHW, 1, "{ Hardware commands... }"}, {"hw", CmdHW, 1, "{ Hardware commands... }"},
{"lf", CmdLF, 1, "{ Low Frequency commands... }"}, {"lf", CmdLF, 1, "{ Low Frequency commands... }"},
{"emv", CmdEMV, 1, "{ EMV iso14443 and iso7816... }"},
{"rem", CmdRem, 1, "{ Add text to row in log file }"}, {"rem", CmdRem, 1, "{ Add text to row in log file }"},
{"reveng", CmdRev, 1, "{ Crc calculations from the software reveng 1.53... }"}, {"reveng", CmdRev, 1, "{ Crc calculations from the software reveng 1.53... }"},
{"script", CmdScript, 1, "{ Scripting commands }"}, {"script", CmdScript, 1, "{ Scripting commands }"},

View file

@ -31,6 +31,7 @@
#include "cmdscript.h" #include "cmdscript.h"
#include "cmdcrc.h" #include "cmdcrc.h"
#include "cmdanalyse.h" #include "cmdanalyse.h"
#include "emv/cmdemv.h" // EMV
#ifdef WITH_FLASH #ifdef WITH_FLASH
#include "cmdflashmem.h" // rdv40 flashmem commands #include "cmdflashmem.h" // rdv40 flashmem commands

View file

@ -87,6 +87,14 @@ static bool smart_select(bool silent) {
if (!silent) PrintAndLogEx(WARNING, "smart card select failed"); if (!silent) PrintAndLogEx(WARNING, "smart card select failed");
return false; return false;
} }
if (!silent) {
smart_card_atr_t card;
memcpy(&card, (smart_card_atr_t *)resp.d.asBytes, sizeof(smart_card_atr_t));
PrintAndLogEx(INFO, "ISO7816-3 ATR : %s", sprint_hex(card.atr, card.atr_len));
}
return true; return true;
} }
@ -111,29 +119,56 @@ static int smart_wait(uint8_t *data) {
return len; return len;
} }
static int smart_response(uint8_t *data) { static int smart_response(uint8_t apduINS, uint8_t *data) {
int len = -1;
int datalen = smart_wait(data); int datalen = smart_wait(data);
bool needGetData = false;
if ( data[datalen - 2] == 0x61 || data[datalen - 2] == 0x9F ) { if (datalen < 2 ) {
len = data[datalen - 1];
}
if (len == -1 ) {
goto out; goto out;
} }
PrintAndLogEx(INFO, "Requesting response"); if (datalen > 2 && data[0] != apduINS) {
uint8_t getstatus[] = {0x00, ISO7816_GETSTATUS, 0x00, 0x00, len }; PrintAndLogEx(ERR, "Card ACK error. len=0x%x data[0]=%02x", datalen, data[0]);
UsbCommand cStatus = {CMD_SMART_RAW, {SC_RAW, sizeof(getstatus), 0}}; datalen = 0;
memcpy(cStatus.d.asBytes, getstatus, sizeof(getstatus) ); goto out;
clearCommandBuffer(); }
SendCommand(&cStatus);
datalen = smart_wait(data); if ( data[datalen - 2] == 0x61 || data[datalen - 2] == 0x9F ) {
needGetData = true;
}
if (needGetData) {
int len = data[datalen - 1];
PrintAndLogEx(INFO, "Requesting response. len=0x%x", len);
uint8_t getstatus[] = {ISO7816_GETSTATUS, 0x00, 0x00, len};
UsbCommand cStatus = {CMD_SMART_RAW, {SC_RAW, sizeof(getstatus), 0}};
memcpy(cStatus.d.asBytes, getstatus, sizeof(getstatus) );
clearCommandBuffer();
SendCommand(&cStatus);
datalen = smart_wait(data);
if (datalen < 2 ) {
goto out;
}
if (datalen > 2 && data[0] != ISO7816_GETSTATUS) {
PrintAndLogEx(ERR, "GetResponse ACK error. len=0x%x data[0]=%02x", len, data[0]);
datalen = 0;
goto out;
}
if (datalen != len + 2 + 1) { // 2 - response, 1 - ACK
PrintAndLogEx(WARNING, "GetResponse wrong length. Must be: 0x%02x but: 0x%02x", len, datalen - 3);
}
}
if (datalen > 2) {
datalen--;
memmove(data, &data[1], datalen);
}
out: out:
return datalen; return datalen;
} }
@ -219,7 +254,7 @@ int CmdSmartRaw(const char *Cmd) {
if ( !buf ) if ( !buf )
return 1; return 1;
int len = smart_response(buf); int len = smart_response(data[1], buf);
if ( len < 0 ) { if ( len < 0 ) {
free(buf); free(buf);
return 2; return 2;
@ -231,7 +266,7 @@ int CmdSmartRaw(const char *Cmd) {
memcpy(c.d.asBytes, data, sizeof(data) ); memcpy(c.d.asBytes, data, sizeof(data) );
clearCommandBuffer(); clearCommandBuffer();
SendCommand(&c); SendCommand(&c);
len = smart_response(buf); len = smart_response(data[1], buf);
data[4] = 0; data[4] = 0;
} }
@ -244,6 +279,50 @@ int CmdSmartRaw(const char *Cmd) {
return 0; return 0;
} }
int ExchangeAPDUSC(uint8_t *datain, int datainlen, bool activateCard, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen) {
*dataoutlen = 0;
if (activateCard)
smart_select(false);
printf("* APDU SC\n");
UsbCommand c = {CMD_SMART_RAW, {SC_RAW | SC_CONNECT, datainlen, 0}};
if (activateCard) {
c.arg[0] |= SC_SELECT;
}
memcpy(c.d.asBytes, datain, datainlen);
clearCommandBuffer();
SendCommand(&c);
int len = smart_response(datain[1], dataout);
if ( len < 0 ) {
return 2;
}
// retry
if (len > 1 && dataout[len - 2] == 0x6c && datainlen > 4) {
UsbCommand c2 = {CMD_SMART_RAW, {SC_RAW, datainlen, 0}};
memcpy(c2.d.asBytes, datain, datainlen);
int vlen = 5 + datain[4];
if (datainlen == vlen)
datainlen++;
c2.d.asBytes[vlen] = dataout[len - 1];
clearCommandBuffer();
SendCommand(&c2);
len = smart_response(datain[1], dataout);
}
*dataoutlen = len;
return 0;
}
int CmdSmartUpgrade(const char *Cmd) { int CmdSmartUpgrade(const char *Cmd) {
PrintAndLogEx(WARNING, "WARNING - Smartcard socket firmware upgrade."); PrintAndLogEx(WARNING, "WARNING - Smartcard socket firmware upgrade.");
@ -534,7 +613,7 @@ int CmdSmartBruteforceSFI(const char *Cmd) {
clearCommandBuffer(); clearCommandBuffer();
SendCommand(&c); SendCommand(&c);
smart_response(buf); smart_response(data[1], buf);
// if 0x6C // if 0x6C
if ( buf[0] == 0x6C ) { if ( buf[0] == 0x6C ) {
@ -543,7 +622,7 @@ int CmdSmartBruteforceSFI(const char *Cmd) {
memcpy(c.d.asBytes, data, sizeof(data) ); memcpy(c.d.asBytes, data, sizeof(data) );
clearCommandBuffer(); clearCommandBuffer();
SendCommand(&c); SendCommand(&c);
uint8_t len = smart_response(buf); uint8_t len = smart_response(data[1], buf);
// TLV decoder // TLV decoder
if (len > 4) if (len > 4)

View file

@ -32,6 +32,8 @@ extern int CmdSmartUpgrade(const char* cmd);
extern int CmdSmartInfo(const char* cmd); extern int CmdSmartInfo(const char* cmd);
extern int CmdSmartReader(const char *Cmd); extern int CmdSmartReader(const char *Cmd);
extern int ExchangeAPDUSC(uint8_t *datain, int datainlen, bool activateCard, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen);
extern int usage_sm_raw(void); extern int usage_sm_raw(void);
extern int usage_sm_reader(void); extern int usage_sm_reader(void);
extern int usage_sm_info(void); extern int usage_sm_info(void);

View file

@ -40,13 +40,13 @@ void ParamLoadDefaults(struct tlvdb *tlvRoot) {
TLV_ADD(0x9F66, "\x26\x00\x00\x00"); // qVSDC 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}; uint8_t data[APDU_AID_LEN] = {0};
int datalen = 0; int datalen = 0;
CLIParserInit("hf emv select", CLIParserInit("emv select",
"Executes select applet command", "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[] = { void* argtable[] = {
arg_param_begin, arg_param_begin,
@ -54,6 +54,7 @@ int CmdHFEMVSelect(const char *cmd) {
arg_lit0("kK", "keep", "keep field for next command"), arg_lit0("kK", "keep", "keep field for next command"),
arg_lit0("aA", "apdu", "show APDU reqests and responses"), arg_lit0("aA", "apdu", "show APDU reqests and responses"),
arg_lit0("tT", "tlv", "TLV decode results"), arg_lit0("tT", "tlv", "TLV decode results"),
arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. Contactless interface set by default."),
arg_strx0(NULL, NULL, "<HEX applet AID>", NULL), arg_strx0(NULL, NULL, "<HEX applet AID>", NULL),
arg_param_end arg_param_end
}; };
@ -63,7 +64,10 @@ int CmdHFEMVSelect(const char *cmd) {
bool leaveSignalON = arg_get_lit(2); bool leaveSignalON = arg_get_lit(2);
bool APDULogging = arg_get_lit(3); bool APDULogging = arg_get_lit(3);
bool decodeTLV = arg_get_lit(4); bool decodeTLV = arg_get_lit(4);
CLIGetHexWithReturn(5, data, &datalen); EMVCommandChannel channel = ECC_CONTACTLESS;
if (arg_get_lit(5))
channel = ECC_CONTACT;
CLIGetHexWithReturn(6, data, &datalen);
CLIParserFree(); CLIParserFree();
SetAPDULogging(APDULogging); SetAPDULogging(APDULogging);
@ -72,7 +76,7 @@ int CmdHFEMVSelect(const char *cmd) {
uint8_t buf[APDU_RES_LEN] = {0}; uint8_t buf[APDU_RES_LEN] = {0};
size_t len = 0; size_t len = 0;
uint16_t sw = 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) if (sw)
PrintAndLogEx(INFO, "APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); PrintAndLogEx(INFO, "APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
@ -86,11 +90,11 @@ int CmdHFEMVSelect(const char *cmd) {
return 0; 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", "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[] = { void* argtable[] = {
arg_param_begin, arg_param_begin,
@ -98,6 +102,7 @@ int CmdHFEMVSearch(const char *cmd) {
arg_lit0("kK", "keep", "keep field ON for next command"), arg_lit0("kK", "keep", "keep field ON for next command"),
arg_lit0("aA", "apdu", "show APDU reqests and responses"), arg_lit0("aA", "apdu", "show APDU reqests and responses"),
arg_lit0("tT", "tlv", "TLV decode results of selected applets"), arg_lit0("tT", "tlv", "TLV decode results of selected applets"),
arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. Contactless interface set by default."),
arg_param_end arg_param_end
}; };
CLIExecWithReturn(cmd, argtable, true); CLIExecWithReturn(cmd, argtable, true);
@ -106,6 +111,9 @@ int CmdHFEMVSearch(const char *cmd) {
bool leaveSignalON = arg_get_lit(2); bool leaveSignalON = arg_get_lit(2);
bool APDULogging = arg_get_lit(3); bool APDULogging = arg_get_lit(3);
bool decodeTLV = arg_get_lit(4); bool decodeTLV = arg_get_lit(4);
EMVCommandChannel channel = ECC_CONTACTLESS;
if (arg_get_lit(5))
channel = ECC_CONTACT;
CLIParserFree(); CLIParserFree();
SetAPDULogging(APDULogging); SetAPDULogging(APDULogging);
@ -114,7 +122,7 @@ int CmdHFEMVSearch(const char *cmd) {
const char *al = "Applets list"; const char *al = "Applets list";
t = tlvdb_fixed(1, strlen(al), (const unsigned char *)al); 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); tlvdb_free(t);
return 2; return 2;
} }
@ -131,11 +139,11 @@ int CmdHFEMVSearch(const char *cmd) {
return 0; 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", "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[] = { void* argtable[] = {
arg_param_begin, arg_param_begin,
@ -145,6 +153,7 @@ int CmdHFEMVPPSE(const char *cmd) {
arg_lit0("2", "ppse", "ppse (2PAY.SYS.DDF01) mode (default mode)"), arg_lit0("2", "ppse", "ppse (2PAY.SYS.DDF01) mode (default mode)"),
arg_lit0("aA", "apdu", "show APDU reqests and responses"), arg_lit0("aA", "apdu", "show APDU reqests and responses"),
arg_lit0("tT", "tlv", "TLV decode results of selected applets"), arg_lit0("tT", "tlv", "TLV decode results of selected applets"),
arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. Contactless interface set by default."),
arg_param_end arg_param_end
}; };
CLIExecWithReturn(cmd, argtable, true); CLIExecWithReturn(cmd, argtable, true);
@ -158,6 +167,9 @@ int CmdHFEMVPPSE(const char *cmd) {
PSENum = 2; PSENum = 2;
bool APDULogging = arg_get_lit(5); bool APDULogging = arg_get_lit(5);
bool decodeTLV = arg_get_lit(6); bool decodeTLV = arg_get_lit(6);
EMVCommandChannel channel = ECC_CONTACTLESS;
if (arg_get_lit(7))
channel = ECC_CONTACT;
CLIParserFree(); CLIParserFree();
SetAPDULogging(APDULogging); SetAPDULogging(APDULogging);
@ -166,7 +178,7 @@ int CmdHFEMVPPSE(const char *cmd) {
uint8_t buf[APDU_RES_LEN] = {0}; uint8_t buf[APDU_RES_LEN] = {0};
size_t len = 0; size_t len = 0;
uint16_t sw = 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) if (sw)
PrintAndLogEx(INFO, "APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); PrintAndLogEx(INFO, "APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
@ -181,15 +193,15 @@ int CmdHFEMVPPSE(const char *cmd) {
return 0; return 0;
} }
int CmdHFEMVGPO(const char *cmd) { int CmdEMVGPO(const char *cmd) {
uint8_t data[APDU_RES_LEN] = {0}; uint8_t data[APDU_RES_LEN] = {0};
int datalen = 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.", "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" "Usage:\n\temv gpo -k -> execute GPO\n"
"\thf emv gpo -t 01020304 -> execute GPO with 4-byte PDOL data, show result in TLV\n" "\temv 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"); "\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[] = { void* argtable[] = {
arg_param_begin, arg_param_begin,
@ -198,6 +210,7 @@ int CmdHFEMVGPO(const char *cmd) {
arg_lit0("mM", "make", "make PDOLdata from PDOL (tag 9F38) and parameters (by default uses default parameters)"), arg_lit0("mM", "make", "make PDOLdata from PDOL (tag 9F38) and parameters (by default uses default parameters)"),
arg_lit0("aA", "apdu", "show APDU reqests and responses"), arg_lit0("aA", "apdu", "show APDU reqests and responses"),
arg_lit0("tT", "tlv", "TLV decode results of selected applets"), arg_lit0("tT", "tlv", "TLV decode results of selected applets"),
arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. Contactless interface set by default."),
arg_strx0(NULL, NULL, "<HEX PDOLdata/PDOL>", NULL), arg_strx0(NULL, NULL, "<HEX PDOLdata/PDOL>", NULL),
arg_param_end arg_param_end
}; };
@ -208,7 +221,10 @@ int CmdHFEMVGPO(const char *cmd) {
bool dataMakeFromPDOL = arg_get_lit(3); bool dataMakeFromPDOL = arg_get_lit(3);
bool APDULogging = arg_get_lit(4); bool APDULogging = arg_get_lit(4);
bool decodeTLV = arg_get_lit(5); bool decodeTLV = arg_get_lit(5);
CLIGetHexWithReturn(6, data, &datalen); EMVCommandChannel channel = ECC_CONTACTLESS;
if (arg_get_lit(6))
channel = ECC_CONTACT;
CLIGetHexWithReturn(7, data, &datalen);
CLIParserFree(); CLIParserFree();
SetAPDULogging(APDULogging); SetAPDULogging(APDULogging);
@ -258,7 +274,7 @@ int CmdHFEMVGPO(const char *cmd) {
uint8_t buf[APDU_RES_LEN] = {0}; uint8_t buf[APDU_RES_LEN] = {0};
size_t len = 0; size_t len = 0;
uint16_t sw = 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) if (pdol_data_tlv != &data_tlv)
free(pdol_data_tlv); free(pdol_data_tlv);
@ -276,19 +292,20 @@ int CmdHFEMVGPO(const char *cmd) {
return 0; return 0;
} }
int CmdHFEMVReadRecord(const char *cmd) { int CmdEMVReadRecord(const char *cmd) {
uint8_t data[APDU_RES_LEN] = {0}; uint8_t data[APDU_RES_LEN] = {0};
int datalen = 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.", "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[] = { void* argtable[] = {
arg_param_begin, arg_param_begin,
arg_lit0("kK", "keep", "keep field ON for next command"), arg_lit0("kK", "keep", "keep field ON for next command"),
arg_lit0("aA", "apdu", "show APDU reqests and responses"), arg_lit0("aA", "apdu", "show APDU reqests and responses"),
arg_lit0("tT", "tlv", "TLV decode results of selected applets"), arg_lit0("tT", "tlv", "TLV decode results of selected applets"),
arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. Contactless interface set by default."),
arg_strx1(NULL, NULL, "<SFI 1byte HEX><SFIrec 1byte HEX>", NULL), arg_strx1(NULL, NULL, "<SFI 1byte HEX><SFIrec 1byte HEX>", NULL),
arg_param_end arg_param_end
}; };
@ -297,7 +314,10 @@ int CmdHFEMVReadRecord(const char *cmd) {
bool leaveSignalON = arg_get_lit(1); bool leaveSignalON = arg_get_lit(1);
bool APDULogging = arg_get_lit(2); bool APDULogging = arg_get_lit(2);
bool decodeTLV = arg_get_lit(3); bool decodeTLV = arg_get_lit(3);
CLIGetHexWithReturn(4, data, &datalen); EMVCommandChannel channel = ECC_CONTACTLESS;
if (arg_get_lit(4))
channel = ECC_CONTACT;
CLIGetHexWithReturn(5, data, &datalen);
CLIParserFree(); CLIParserFree();
if (datalen != 2) { if (datalen != 2) {
@ -311,7 +331,7 @@ int CmdHFEMVReadRecord(const char *cmd) {
uint8_t buf[APDU_RES_LEN] = {0}; uint8_t buf[APDU_RES_LEN] = {0};
size_t len = 0; size_t len = 0;
uint16_t sw = 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) if (sw)
PrintAndLogEx(INFO, "APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); PrintAndLogEx(INFO, "APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
@ -326,16 +346,16 @@ int CmdHFEMVReadRecord(const char *cmd) {
return 0; return 0;
} }
int CmdHFEMVAC(const char *cmd) { int CmdEMVAC(const char *cmd) {
uint8_t data[APDU_RES_LEN] = {0}; uint8_t data[APDU_RES_LEN] = {0};
int datalen = 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.", "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" "Usage:\n\temv 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" "\temv 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" "\temv 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"); "\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[] = { void* argtable[] = {
arg_param_begin, arg_param_begin,
@ -346,6 +366,7 @@ 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("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("aA", "apdu", "show APDU reqests and responses"),
arg_lit0("tT", "tlv", "TLV decode results of selected applets"), arg_lit0("tT", "tlv", "TLV decode results of selected applets"),
arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. Contactless interface set by default."),
arg_strx1(NULL, NULL, "<HEX CDOLdata/CDOL>", NULL), arg_strx1(NULL, NULL, "<HEX CDOLdata/CDOL>", NULL),
arg_param_end arg_param_end
}; };
@ -375,7 +396,10 @@ int CmdHFEMVAC(const char *cmd) {
bool dataMakeFromCDOL = arg_get_lit(5); bool dataMakeFromCDOL = arg_get_lit(5);
bool APDULogging = arg_get_lit(6); bool APDULogging = arg_get_lit(6);
bool decodeTLV = arg_get_lit(7); bool decodeTLV = arg_get_lit(7);
CLIGetHexWithReturn(8, data, &datalen); EMVCommandChannel channel = ECC_CONTACTLESS;
if (arg_get_lit(8))
channel = ECC_CONTACT;
CLIGetHexWithReturn(9, data, &datalen);
CLIParserFree(); CLIParserFree();
SetAPDULogging(APDULogging); SetAPDULogging(APDULogging);
@ -419,7 +443,7 @@ int CmdHFEMVAC(const char *cmd) {
uint8_t buf[APDU_RES_LEN] = {0}; uint8_t buf[APDU_RES_LEN] = {0};
size_t len = 0; size_t len = 0;
uint16_t sw = 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) if (cdol_data_tlv != &data_tlv)
free(cdol_data_tlv); free(cdol_data_tlv);
@ -437,22 +461,26 @@ int CmdHFEMVAC(const char *cmd) {
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.", "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[] = { void* argtable[] = {
arg_param_begin, arg_param_begin,
arg_lit0("kK", "keep", "keep field ON for next command"), arg_lit0("kK", "keep", "keep field ON for next command"),
arg_lit0("aA", "apdu", "show APDU reqests and responses"), arg_lit0("aA", "apdu", "show APDU reqests and responses"),
arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. Contactless interface set by default."),
arg_param_end arg_param_end
}; };
CLIExecWithReturn(cmd, argtable, true); CLIExecWithReturn(cmd, argtable, true);
bool leaveSignalON = arg_get_lit(1); bool leaveSignalON = arg_get_lit(1);
bool APDULogging = arg_get_lit(2); bool APDULogging = arg_get_lit(2);
EMVCommandChannel channel = ECC_CONTACTLESS;
if (arg_get_lit(3))
channel = ECC_CONTACT;
CLIParserFree(); CLIParserFree();
SetAPDULogging(APDULogging); SetAPDULogging(APDULogging);
@ -461,7 +489,7 @@ int CmdHFEMVGenerateChallenge(const char *cmd) {
uint8_t buf[APDU_RES_LEN] = {0}; uint8_t buf[APDU_RES_LEN] = {0};
size_t len = 0; size_t len = 0;
uint16_t sw = 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) if (sw)
PrintAndLogEx(INFO, "APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); PrintAndLogEx(INFO, "APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
@ -477,15 +505,15 @@ int CmdHFEMVGenerateChallenge(const char *cmd) {
return 0; return 0;
} }
int CmdHFEMVInternalAuthenticate(const char *cmd) { int CmdEMVInternalAuthenticate(const char *cmd) {
uint8_t data[APDU_RES_LEN] = {0}; uint8_t data[APDU_RES_LEN] = {0};
int datalen = 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.", "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" "Usage:\n\temv 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" "\temv 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"); "\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[] = { void* argtable[] = {
arg_param_begin, arg_param_begin,
@ -494,6 +522,7 @@ int CmdHFEMVInternalAuthenticate(const char *cmd) {
arg_lit0("mM", "make", "make DDOLdata from DDOL (tag 9F49) and parameters (by default uses default parameters)"), arg_lit0("mM", "make", "make DDOLdata from DDOL (tag 9F49) and parameters (by default uses default parameters)"),
arg_lit0("aA", "apdu", "show APDU reqests and responses"), arg_lit0("aA", "apdu", "show APDU reqests and responses"),
arg_lit0("tT", "tlv", "TLV decode results of selected applets"), arg_lit0("tT", "tlv", "TLV decode results of selected applets"),
arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. Contactless interface set by default."),
arg_strx1(NULL, NULL, "<HEX DDOLdata/DDOL>", NULL), arg_strx1(NULL, NULL, "<HEX DDOLdata/DDOL>", NULL),
arg_param_end arg_param_end
}; };
@ -504,7 +533,10 @@ int CmdHFEMVInternalAuthenticate(const char *cmd) {
bool dataMakeFromDDOL = arg_get_lit(3); bool dataMakeFromDDOL = arg_get_lit(3);
bool APDULogging = arg_get_lit(4); bool APDULogging = arg_get_lit(4);
bool decodeTLV = arg_get_lit(5); bool decodeTLV = arg_get_lit(5);
CLIGetHexWithReturn(6, data, &datalen); EMVCommandChannel channel = ECC_CONTACTLESS;
if (arg_get_lit(6))
channel = ECC_CONTACT;
CLIGetHexWithReturn(7, data, &datalen);
CLIParserFree(); CLIParserFree();
SetAPDULogging(APDULogging); SetAPDULogging(APDULogging);
@ -548,7 +580,7 @@ int CmdHFEMVInternalAuthenticate(const char *cmd) {
uint8_t buf[APDU_RES_LEN] = {0}; uint8_t buf[APDU_RES_LEN] = {0};
size_t len = 0; size_t len = 0;
uint16_t sw = 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) if (ddol_data_tlv != &data_tlv)
free(ddol_data_tlv); free(ddol_data_tlv);
@ -631,7 +663,7 @@ void ProcessGPOResponseFormat1(struct tlvdb *tlvRoot, uint8_t *buf, size_t len,
} }
} }
int CmdHFEMVExec(const char *cmd) { int CmdEMVExec(const char *cmd) {
uint8_t buf[APDU_RES_LEN] = {0}; uint8_t buf[APDU_RES_LEN] = {0};
size_t len = 0; size_t len = 0;
uint16_t sw = 0; uint16_t sw = 0;
@ -646,10 +678,10 @@ int CmdHFEMVExec(const char *cmd) {
struct tlvdb *tlvRoot = NULL; struct tlvdb *tlvRoot = NULL;
struct tlv *pdol_data_tlv = NULL; struct tlv *pdol_data_tlv = NULL;
CLIParserInit("hf emv exec", CLIParserInit("emv exec",
"Executes EMV contactless transaction", "Executes EMV contactless transaction",
"Usage:\n\thf emv exec -sat -> select card, execute MSD transaction, show APDU and TLV\n" "Usage:\n\temv 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"); "\temv exec -satc -> select card, execute CDA transaction, show APDU and TLV\n");
void* argtable[] = { void* argtable[] = {
arg_param_begin, arg_param_begin,
@ -663,6 +695,7 @@ int CmdHFEMVExec(const char *cmd) {
arg_lit0("cC", "qvsdccda", "Transaction type - qVSDC or M/Chip plus CDA (SDAD generation)."), 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("xX", "vsdc", "Transaction type - VSDC. For test only. Not a standart behavior."),
arg_lit0("gG", "acgpo", "VISA. generate AC from GPO."), arg_lit0("gG", "acgpo", "VISA. generate AC from GPO."),
arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. Contactless interface set by default."),
arg_param_end arg_param_end
}; };
CLIExecWithReturn(cmd, argtable, true); CLIExecWithReturn(cmd, argtable, true);
@ -674,14 +707,17 @@ int CmdHFEMVExec(const char *cmd) {
bool forceSearch = arg_get_lit(5); bool forceSearch = arg_get_lit(5);
enum TransactionType TrType = TT_MSD; enum TransactionType TrType = TT_MSD;
if (arg_get_lit(6))
TrType = TT_QVSDCMCHIP;
if (arg_get_lit(7)) if (arg_get_lit(7))
TrType = TT_CDA; TrType = TT_QVSDCMCHIP;
if (arg_get_lit(8)) if (arg_get_lit(8))
TrType = TT_CDA;
if (arg_get_lit(9))
TrType = TT_VSDC; TrType = TT_VSDC;
bool GenACGPO = arg_get_lit(9); bool GenACGPO = arg_get_lit(10);
EMVCommandChannel channel = ECC_CONTACTLESS;
if (arg_get_lit(11))
channel = ECC_CONTACT;
CLIParserFree(); CLIParserFree();
SetAPDULogging(showAPDU); SetAPDULogging(showAPDU);
@ -696,7 +732,7 @@ int CmdHFEMVExec(const char *cmd) {
// PPSE // PPSE
PrintAndLogEx(NORMAL, "\n* PPSE."); PrintAndLogEx(NORMAL, "\n* PPSE.");
SetAPDULogging(showAPDU); SetAPDULogging(showAPDU);
res = EMVSearchPSE(activateField, true, decodeTLV, tlvSelect); res = EMVSearchPSE(channel, activateField, true, decodeTLV, tlvSelect);
// check PPSE and select application id // check PPSE and select application id
if (!res) { if (!res) {
@ -709,7 +745,7 @@ int CmdHFEMVExec(const char *cmd) {
if (!AIDlen) { if (!AIDlen) {
PrintAndLogEx(NORMAL, "\n* Search AID in list."); PrintAndLogEx(NORMAL, "\n* Search AID in list.");
SetAPDULogging(false); SetAPDULogging(false);
if (EMVSearch(activateField, true, decodeTLV, tlvSelect)) { if (EMVSearch(channel, activateField, true, decodeTLV, tlvSelect)) {
dreturn(2); dreturn(2);
} }
@ -731,7 +767,7 @@ int CmdHFEMVExec(const char *cmd) {
// Select // Select
PrintAndLogEx(NORMAL, "\n* Selecting AID:%s", sprint_hex_inrow(AID, AIDlen)); PrintAndLogEx(NORMAL, "\n* Selecting AID:%s", sprint_hex_inrow(AID, AIDlen));
SetAPDULogging(showAPDU); 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) { if (res) {
PrintAndLogEx(WARNING, "Can't select AID (%d). Exit...", res); PrintAndLogEx(WARNING, "Can't select AID (%d). Exit...", res);
@ -762,7 +798,7 @@ int CmdHFEMVExec(const char *cmd) {
PrintAndLogEx(NORMAL, "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));
PrintAndLogEx(NORMAL, "\n* GPO."); PrintAndLogEx(NORMAL, "\n* GPO.");
res = EMVGPO(true, pdol_data_tlv_data, pdol_data_tlv_data_len, buf, sizeof(buf), &len, &sw, tlvRoot); 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_data);
//free(pdol_data_tlv); --- free on exit. //free(pdol_data_tlv); --- free on exit.
@ -818,7 +854,7 @@ int CmdHFEMVExec(const char *cmd) {
for (int n = SFIstart; n <= SFIend; n++) { for (int n = SFIstart; n <= SFIend; n++) {
PrintAndLogEx(NORMAL, "* * * 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) { if (res) {
PrintAndLogEx(WARNING, "Error SFI[%02x]. APDU error %4x", SFI, sw); PrintAndLogEx(WARNING, "Error SFI[%02x]. APDU error %4x", SFI, sw);
continue; continue;
@ -861,9 +897,14 @@ int CmdHFEMVExec(const char *cmd) {
} }
// get AIP // get AIP
uint16_t AIP = 0;
const struct tlv *AIPtlv = tlvdb_get(tlvRoot, 0x82, NULL); const struct tlv *AIPtlv = tlvdb_get(tlvRoot, 0x82, NULL);
uint16_t AIP = AIPtlv->value[0] + AIPtlv->value[1] * 0x100; if (AIPtlv) {
PrintAndLogEx(NORMAL, "* * AIP=%04x", AIP); AIP = AIPtlv->value[0] + AIPtlv->value[1] * 0x100;
PrintAndLogEx(NORMAL, "* * AIP=%04x", AIP);
} else {
PrintAndLogEx(ERR, "Can't found AIP.");
}
// SDA // SDA
if (AIP & 0x0040) { if (AIP & 0x0040) {
@ -874,7 +915,7 @@ int CmdHFEMVExec(const char *cmd) {
// DDA // DDA
if (AIP & 0x0020) { if (AIP & 0x0020) {
PrintAndLogEx(NORMAL, "\n* DDA"); PrintAndLogEx(NORMAL, "\n* DDA");
trDDA(decodeTLV, tlvRoot); trDDA(channel, decodeTLV, tlvRoot);
} }
// transaction check // transaction check
@ -924,7 +965,7 @@ int CmdHFEMVExec(const char *cmd) {
PrintAndLogEx(NORMAL, "\n--> Mastercard M/Chip transaction."); PrintAndLogEx(NORMAL, "\n--> Mastercard M/Chip transaction.");
PrintAndLogEx(NORMAL, "* * Generate challenge"); PrintAndLogEx(NORMAL, "* * Generate challenge");
res = EMVGenerateChallenge(true, buf, sizeof(buf), &len, &sw, tlvRoot); res = EMVGenerateChallenge(channel, true, buf, sizeof(buf), &len, &sw, tlvRoot);
if (res) { if (res) {
PrintAndLogEx(WARNING, "Error GetChallenge. APDU error %4x", sw); PrintAndLogEx(WARNING, "Error GetChallenge. APDU error %4x", sw);
dreturn(6); dreturn(6);
@ -952,7 +993,7 @@ int CmdHFEMVExec(const char *cmd) {
PrintAndLogEx(NORMAL, "* * AC1"); PrintAndLogEx(NORMAL, "* * AC1");
// EMVAC_TC + EMVAC_CDAREQ --- to get SDAD // 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) { if (res) {
PrintAndLogEx(NORMAL, "AC1 error(%d): %4x. Exit...", res, sw); PrintAndLogEx(NORMAL, "AC1 error(%d): %4x. Exit...", res, sw);
@ -1041,17 +1082,17 @@ int CmdHFEMVExec(const char *cmd) {
PrintAndLogEx(NORMAL, "\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) { if (res) {
PrintAndLogEx(WARNING, "Error Compute Crypto Checksum. APDU error %4x", sw); PrintAndLogEx(WARNING, "Error Compute Crypto Checksum. APDU error %4x", sw);
free(udol_data_tlv); free(udol_data_tlv);
dreturn(9); dreturn(9);
} }
if (decodeTLV) { // Mastercard compute cryptographic checksum result
TLVPrintFromBuffer(buf, len); TLVPrintFromBuffer(buf, len);
PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "");
}
free(udol_data_tlv); free(udol_data_tlv);
} }
@ -1071,7 +1112,7 @@ int CmdHFEMVExec(const char *cmd) {
return 0; return 0;
} }
int CmdHFEMVScan(const char *cmd) { int CmdEMVScan(const char *cmd) {
uint8_t AID[APDU_AID_LEN] = {0}; uint8_t AID[APDU_AID_LEN] = {0};
size_t AIDlen = 0; size_t AIDlen = 0;
uint8_t buf[APDU_RES_LEN] = {0}; uint8_t buf[APDU_RES_LEN] = {0};
@ -1081,11 +1122,11 @@ int CmdHFEMVScan(const char *cmd) {
json_t *root; json_t *root;
json_error_t error; json_error_t error;
CLIParserInit("hf emv scan", CLIParserInit("emv scan",
"Scan EMV card and save it contents to a file.", "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" "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" "Usage:\n\temv scan -at -> scan MSD transaction mode and show APDU and TLV\n"
"\thf emv scan -c -> scan CDA transaction mode\n"); "\temv scan -c -> scan CDA transaction mode\n");
void* argtable[] = { void* argtable[] = {
arg_param_begin, arg_param_begin,
@ -1099,6 +1140,7 @@ int CmdHFEMVScan(const char *cmd) {
arg_lit0("xX", "vsdc", "Transaction type - VSDC. For test only. Not a standart behavior."), 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("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("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. Contactless interface set by default."),
arg_str1(NULL, NULL, "output.json", "JSON output file name"), arg_str1(NULL, NULL, "output.json", "JSON output file name"),
arg_param_end arg_param_end
}; };
@ -1119,14 +1161,23 @@ int CmdHFEMVScan(const char *cmd) {
bool GenACGPO = arg_get_lit(9); bool GenACGPO = arg_get_lit(9);
bool MergeJSON = arg_get_lit(10); bool MergeJSON = arg_get_lit(10);
EMVCommandChannel channel = ECC_CONTACTLESS;
if (arg_get_lit(11))
channel = ECC_CONTACT;
uint8_t relfname[250] ={0}; uint8_t relfname[250] ={0};
char *crelfname = (char *)relfname; char *crelfname = (char *)relfname;
int relfnamelen = 0; int relfnamelen = 0;
CLIGetStrWithReturn(11, relfname, &relfnamelen); CLIGetStrWithReturn(12, relfname, &relfnamelen);
CLIParserFree(); CLIParserFree();
SetAPDULogging(showAPDU); SetAPDULogging(showAPDU);
// TODO
if (channel == ECC_CONTACT) {
PrintAndLogEx(ERR, "Do not use contact interface. Exit.");
return 1;
}
// current path + file name // current path + file name
if (!strstr(crelfname, ".json")) if (!strstr(crelfname, ".json"))
strcat(crelfname, ".json"); strcat(crelfname, ".json");
@ -1160,7 +1211,7 @@ int CmdHFEMVScan(const char *cmd) {
return 2; return 2;
} }
JsonSaveStr(root, "$.File.Created", "proxmark3 `hf emv scan`"); JsonSaveStr(root, "$.File.Created", "proxmark3 `emv scan`");
JsonSaveStr(root, "$.Card.Communication", "iso14443-4a"); JsonSaveStr(root, "$.Card.Communication", "iso14443-4a");
JsonSaveBufAsHex(root, "$.Card.UID", (uint8_t *)&card.uid, card.uidlen); JsonSaveBufAsHex(root, "$.Card.UID", (uint8_t *)&card.uid, card.uidlen);
@ -1174,7 +1225,7 @@ int CmdHFEMVScan(const char *cmd) {
// EMV PPSE // EMV PPSE
PrintAndLogEx(NORMAL, "--> PPSE."); PrintAndLogEx(NORMAL, "--> PPSE.");
res = EMVSelectPSE(true, true, 2, buf, sizeof(buf), &len, &sw); res = EMVSelectPSE(channel, true, true, 2, buf, sizeof(buf), &len, &sw);
if (!res && sw == 0x9000){ if (!res && sw == 0x9000){
if (decodeTLV) if (decodeTLV)
@ -1191,7 +1242,7 @@ int CmdHFEMVScan(const char *cmd) {
tlvdb_free(fci); tlvdb_free(fci);
} }
res = EMVSearchPSE(false, true, decodeTLV, tlvSelect); res = EMVSearchPSE(channel, false, true, decodeTLV, tlvSelect);
// check PPSE and select application id // check PPSE and select application id
if (!res) { if (!res) {
@ -1200,7 +1251,7 @@ int CmdHFEMVScan(const char *cmd) {
// EMV SEARCH with AID list // EMV SEARCH with AID list
SetAPDULogging(false); SetAPDULogging(false);
PrintAndLogEx(NORMAL, "--> AID search."); PrintAndLogEx(NORMAL, "--> AID search.");
if (EMVSearch(false, true, decodeTLV, tlvSelect)) { if (EMVSearch(channel, false, true, decodeTLV, tlvSelect)) {
PrintAndLogEx(ERR, "Can't found any of EMV AID. Exit..."); PrintAndLogEx(ERR, "Can't found any of EMV AID. Exit...");
tlvdb_free(tlvSelect); tlvdb_free(tlvSelect);
DropField(); DropField();
@ -1233,7 +1284,7 @@ int CmdHFEMVScan(const char *cmd) {
PrintAndLogEx(NORMAL, "\n-->Selecting AID:%s.", sprint_hex_inrow(AID, AIDlen)); PrintAndLogEx(NORMAL, "\n-->Selecting AID:%s.", sprint_hex_inrow(AID, AIDlen));
SetAPDULogging(showAPDU); 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) { if (res) {
PrintAndLogEx(ERR, "Can't select AID (%d). Exit...", res); PrintAndLogEx(ERR, "Can't select AID (%d). Exit...", res);
@ -1281,7 +1332,7 @@ int CmdHFEMVScan(const char *cmd) {
PrintAndLogEx(INFO, "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));
PrintAndLogEx(INFO, "-->GPO."); PrintAndLogEx(INFO, "-->GPO.");
res = EMVGPO(true, pdol_data_tlv_data, pdol_data_tlv_data_len, buf, sizeof(buf), &len, &sw, tlvRoot); 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_data);
free(pdol_data_tlv); free(pdol_data_tlv);
@ -1340,7 +1391,7 @@ int CmdHFEMVScan(const char *cmd) {
for(int n = SFIstart; n <= SFIend; n++) { for(int n = SFIstart; n <= SFIend; n++) {
PrintAndLogEx(INFO, "---->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) { if (res) {
PrintAndLogEx(ERR, "SFI[%02x]. APDU error %4x", SFI, sw); PrintAndLogEx(ERR, "SFI[%02x]. APDU error %4x", SFI, sw);
continue; continue;
@ -1399,56 +1450,56 @@ int CmdHFEMVScan(const char *cmd) {
int usage_emv_getrnd(void){ int usage_emv_getrnd(void){
PrintAndLogEx(NORMAL, "retrieve the UN number from a terminal"); PrintAndLogEx(NORMAL, "retrieve the UN number from a terminal");
PrintAndLogEx(NORMAL, "Usage: hf emv getrnd [h]"); PrintAndLogEx(NORMAL, "Usage: emv getrnd [h]");
PrintAndLogEx(NORMAL, "Options:"); PrintAndLogEx(NORMAL, "Options:");
PrintAndLogEx(NORMAL, " h : this help"); PrintAndLogEx(NORMAL, " h : this help");
PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "");
PrintAndLogEx(NORMAL, "Examples:"); PrintAndLogEx(NORMAL, "Examples:");
PrintAndLogEx(NORMAL, " hf emv getrnd"); PrintAndLogEx(NORMAL, " emv getrnd");
return 0; return 0;
} }
//retrieve the UN number from a terminal //retrieve the UN number from a terminal
int CmdHfEMVGetrng(const char *Cmd) { int CmdEMVGetrng(const char *Cmd) {
char cmdp = param_getchar(Cmd, 0); char cmdp = param_getchar(Cmd, 0);
if ( cmdp == 'h' || cmdp == 'H') return usage_emv_getrnd(); if ( cmdp == 'h' || cmdp == 'H') return usage_emv_getrnd();
return 0; return 0;
} }
int CmdHfEMVList(const char *Cmd) { int CmdEMVList(const char *Cmd) {
return CmdTraceList("7816"); return CmdTraceList("7816");
} }
int CmdHFEMVTest(const char *cmd) { int CmdEMVTest(const char *cmd) {
return ExecuteCryptoTests(true); return ExecuteCryptoTests(true);
} }
static command_t CommandTable[] = { static command_t CommandTable[] = {
{"help", CmdHelp, 1, "This help"}, {"help", CmdHelp, 1, "This help"},
{"exec", CmdHFEMVExec, 0, "Executes EMV contactless transaction."}, {"exec", CmdEMVExec, 0, "Executes EMV contactless transaction."},
{"pse", CmdHFEMVPPSE, 0, "Execute PPSE. It selects 2PAY.SYS.DDF01 or 1PAY.SYS.DDF01 directory."}, {"pse", CmdEMVPPSE, 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."}, {"search", CmdEMVSearch, 0, "Try to select all applets from applets list and print installed applets."},
{"select", CmdHFEMVSelect, 0, "Select applet."}, {"select", CmdEMVSelect, 0, "Select applet."},
{"gpo", CmdHFEMVGPO, 0, "Execute GetProcessingOptions."}, {"gpo", CmdEMVGPO, 0, "Execute GetProcessingOptions."},
{"readrec", CmdHFEMVReadRecord, 0, "Read files from card."}, {"readrec", CmdEMVReadRecord, 0, "Read files from card."},
{"genac", CmdHFEMVAC, 0, "Generate ApplicationCryptogram."}, {"genac", CmdEMVAC, 0, "Generate ApplicationCryptogram."},
{"challenge", CmdHFEMVGenerateChallenge, 0, "Generate challenge."}, {"challenge", CmdEMVGenerateChallenge, 0, "Generate challenge."},
{"intauth", CmdHFEMVInternalAuthenticate, 0, "Internal authentication."}, {"intauth", CmdEMVInternalAuthenticate, 0, "Internal authentication."},
{"scan", CmdHFEMVScan, 0, "Scan EMV card and save it contents to json file for emulator."}, {"scan", CmdEMVScan, 0, "Scan EMV card and save it contents to json file for emulator."},
{"test", CmdHFEMVTest, 0, "Crypto logic test."}, {"test", CmdEMVTest, 0, "Crypto logic test."},
/* /*
{"getrng", CmdHfEMVGetrng, 0, "get random number from terminal"}, {"getrng", CmdEMVGetrng, 0, "get random number from terminal"},
{"eload", CmdHfEmvELoad, 0, "load EMV tag into device"}, {"eload", CmdEmvELoad, 0, "load EMV tag into device"},
{"dump", CmdHfEmvDump, 0, "dump EMV tag values"}, {"dump", CmdEmvDump, 0, "dump EMV tag values"},
{"sim", CmdHfEmvSim, 0, "simulate EMV tag"}, {"sim", CmdEmvSim, 0, "simulate EMV tag"},
{"clone", CmdHfEmvClone, 0, "clone an EMV tag"}, {"clone", CmdEmvClone, 0, "clone an EMV tag"},
*/ */
{"list", CmdHfEMVList, 0, "[Deprecated] List ISO7816 history"}, {"list", CmdEMVList, 0, "[Deprecated] List ISO7816 history"},
{NULL, NULL, 0, NULL} {NULL, NULL, 0, NULL}
}; };
int CmdHFEMV(const char *Cmd) { int CmdEMV(const char *Cmd) {
clearCommandBuffer(); clearCommandBuffer();
CmdsParse(CommandTable, Cmd); CmdsParse(CommandTable, Cmd);
return 0; return 0;

View file

@ -26,13 +26,13 @@
#include "emvcore.h" #include "emvcore.h"
#include "apduinfo.h" #include "apduinfo.h"
int CmdHFEMV(const char *Cmd); int CmdEMV(const char *Cmd);
extern int CmdHFEMVSelect(const char *cmd); extern int CmdEMVSelect(const char *cmd);
extern int CmdHFEMVSearch(const char *cmd); extern int CmdEMVSearch(const char *cmd);
extern int CmdHFEMVPPSE(const char *cmd); extern int CmdEMVPPSE(const char *cmd);
extern int CmdHFEMVExec(const char *cmd); extern int CmdEMVExec(const char *cmd);
extern int CmdHfEMVGetrng(const char *Cmd); extern int CmdEMVGetrng(const char *Cmd);
extern int CmdHfEMVList(const char *Cmd); extern int CmdEMVList(const char *Cmd);
#endif #endif

View file

@ -230,12 +230,13 @@ struct tlvdb *GetdCVVRawFromTrack2(const struct tlv *track2) {
return tlvdb_fixed(0x02, dCVVlen, dCVV); return tlvdb_fixed(0x02, dCVVlen, dCVV);
} }
int EMVExchangeEx(bool ActivateField, bool LeaveFieldON, sAPDU apdu, bool IncludeLe, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { int EMVExchangeEx(EMVCommandChannel channel, bool ActivateField, bool LeaveFieldON, sAPDU apdu, bool IncludeLe, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) {
uint8_t data[APDU_RES_LEN] = {0}; uint8_t data[APDU_RES_LEN] = {0};
*ResultLen = 0; *ResultLen = 0;
if (sw) *sw = 0; if (sw) *sw = 0;
uint16_t isw = 0; uint16_t isw = 0;
int res = 0;
if (ActivateField) { if (ActivateField) {
DropField(); DropField();
@ -250,16 +251,30 @@ int EMVExchangeEx(bool ActivateField, bool LeaveFieldON, sAPDU apdu, bool Includ
if (APDULogging) if (APDULogging)
PrintAndLogEx(NORMAL, ">>>> %s", sprint_hex(data, (IncludeLe?6:5) + apdu.Lc)); PrintAndLogEx(NORMAL, ">>>> %s", sprint_hex(data, (IncludeLe?6:5) + apdu.Lc));
// 6 byes + data = INS + CLA + P1 + P2 + Lc + <data = Nc> + Le(?IncludeLe) switch(channel) {
int res = ExchangeAPDU14a(data, (IncludeLe?6:5) + apdu.Lc, ActivateField, LeaveFieldON, Result, (int)MaxResultLen, (int *)ResultLen); case ECC_CONTACTLESS:
// 6 byes + data = INS + CLA + P1 + P2 + Lc + <data = Nc> + Le(?IncludeLe)
if (res) { res = ExchangeAPDU14a(data, (IncludeLe?6:5) + apdu.Lc, ActivateField, LeaveFieldON, Result, (int)MaxResultLen, (int *)ResultLen);
return res; if (res) {
return res;
}
break;
case ECC_CONTACT:
//int ExchangeAPDUSC(uint8_t *datain, int datainlen, bool activateCard, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen);
res = ExchangeAPDUSC(data, (IncludeLe?6:5) + apdu.Lc, ActivateField, LeaveFieldON, Result, (int)MaxResultLen, (int *)ResultLen);
if (res) {
return res;
}
break;
} }
if (APDULogging) if (APDULogging)
PrintAndLogEx(NORMAL, "<<<< %s", sprint_hex(Result, *ResultLen)); PrintAndLogEx(NORMAL, "<<<< %s", sprint_hex(Result, *ResultLen));
if (*ResultLen < 2) {
return 200;
}
*ResultLen -= 2; *ResultLen -= 2;
isw = Result[*ResultLen] * 0x0100 + Result[*ResultLen + 1]; isw = Result[*ResultLen] * 0x0100 + Result[*ResultLen + 1];
if (sw) if (sw)
@ -285,15 +300,15 @@ int EMVExchangeEx(bool ActivateField, bool LeaveFieldON, sAPDU apdu, bool Includ
return 0; return 0;
} }
int EMVExchange(bool LeaveFieldON, sAPDU apdu, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { int EMVExchange(EMVCommandChannel channel, bool LeaveFieldON, sAPDU apdu, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) {
return EMVExchangeEx(false, LeaveFieldON, apdu, true, Result, MaxResultLen, ResultLen, sw, tlv); return EMVExchangeEx(channel, false, LeaveFieldON, apdu, true, Result, MaxResultLen, ResultLen, sw, tlv);
} }
int EMVSelect(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) { int EMVSelect(EMVCommandChannel 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) {
return EMVExchangeEx(ActivateField, LeaveFieldON, (sAPDU){0x00, 0xa4, 0x04, 0x00, AIDLen, AID}, true, Result, MaxResultLen, ResultLen, sw, tlv); return EMVExchangeEx(channel, ActivateField, LeaveFieldON, (sAPDU){0x00, 0xa4, 0x04, 0x00, AIDLen, AID}, true, Result, MaxResultLen, ResultLen, sw, tlv);
} }
int EMVSelectPSE(bool ActivateField, bool LeaveFieldON, uint8_t PSENum, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) { int EMVSelectPSE(EMVCommandChannel channel, bool ActivateField, bool LeaveFieldON, uint8_t PSENum, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
uint8_t buf[APDU_AID_LEN] = {0}; uint8_t buf[APDU_AID_LEN] = {0};
*ResultLen = 0; *ResultLen = 0;
int len = 0; int len = 0;
@ -310,19 +325,19 @@ int EMVSelectPSE(bool ActivateField, bool LeaveFieldON, uint8_t PSENum, uint8_t
} }
// select // select
res = EMVSelect(ActivateField, LeaveFieldON, buf, len, Result, MaxResultLen, ResultLen, sw, NULL); res = EMVSelect(channel, ActivateField, LeaveFieldON, buf, len, Result, MaxResultLen, ResultLen, sw, NULL);
return res; return res;
} }
int EMVSearchPSE(bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvdb *tlv) { int EMVSearchPSE(EMVCommandChannel channel, bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvdb *tlv) {
uint8_t data[APDU_RES_LEN] = {0}; uint8_t data[APDU_RES_LEN] = {0};
size_t datalen = 0; size_t datalen = 0;
uint16_t sw = 0; uint16_t sw = 0;
int res; int res;
// select PPSE // select PPSE
res = EMVSelectPSE(ActivateField, true, 2, data, sizeof(data), &datalen, &sw); res = EMVSelectPSE(channel, ActivateField, true, 2, data, sizeof(data), &datalen, &sw);
if (!res){ if (!res){
struct tlvdb *t = NULL; struct tlvdb *t = NULL;
@ -336,7 +351,7 @@ int EMVSearchPSE(bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct t
while (ttmp) { while (ttmp) {
const struct tlv *tgAID = tlvdb_get_inchild(ttmp, 0x4f, NULL); const struct tlv *tgAID = tlvdb_get_inchild(ttmp, 0x4f, NULL);
if (tgAID) { if (tgAID) {
res = EMVSelect(false, true, (uint8_t *)tgAID->value, tgAID->len, data, sizeof(data), &datalen, &sw, tlv); res = EMVSelect(channel, false, true, (uint8_t *)tgAID->value, tgAID->len, data, sizeof(data), &datalen, &sw, tlv);
// retry if error and not returned sw error // retry if error and not returned sw error
if (res && res != 5) { if (res && res != 5) {
@ -383,7 +398,7 @@ int EMVSearchPSE(bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct t
return res; return res;
} }
int EMVSearch(bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvdb *tlv) { int EMVSearch(EMVCommandChannel channel, bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvdb *tlv) {
uint8_t aidbuf[APDU_AID_LEN] = {0}; uint8_t aidbuf[APDU_AID_LEN] = {0};
int aidlen = 0; int aidlen = 0;
uint8_t data[APDU_RES_LEN] = {0}; uint8_t data[APDU_RES_LEN] = {0};
@ -394,14 +409,14 @@ int EMVSearch(bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvd
int retrycnt = 0; int retrycnt = 0;
for(int i = 0; i < AIDlistLen; i ++) { for(int i = 0; i < AIDlistLen; i ++) {
param_gethex_to_eol(AIDlist[i].aid, 0, aidbuf, sizeof(aidbuf), &aidlen); param_gethex_to_eol(AIDlist[i].aid, 0, aidbuf, sizeof(aidbuf), &aidlen);
res = EMVSelect((i == 0) ? ActivateField : false, (i == AIDlistLen - 1) ? LeaveFieldON : true, aidbuf, aidlen, data, sizeof(data), &datalen, &sw, tlv); res = EMVSelect(channel, (i == 0) ? ActivateField : false, (i == AIDlistLen - 1) ? LeaveFieldON : true, aidbuf, aidlen, data, sizeof(data), &datalen, &sw, tlv);
// retry if error and not returned sw error // retry if error and not returned sw error
if (res && res != 5) { if (res && res != 5) {
if (++retrycnt < 3){ if (++retrycnt < 3){
i--; i--;
} else { } else {
// card select error, proxmark error // (1) - card select error, proxmark error OR (200) - result length = 0
if (res == 1) { if (res == 1 || res == 200) {
PrintAndLogEx(WARNING, "Exit..."); PrintAndLogEx(WARNING, "Exit...");
return 1; return 1;
} }
@ -464,38 +479,38 @@ int EMVSelectApplication(struct tlvdb *tlv, uint8_t *AID, size_t *AIDlen) {
return 0; return 0;
} }
int EMVGPO(bool LeaveFieldON, uint8_t *PDOL, size_t PDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { int EMVGPO(EMVCommandChannel channel, bool LeaveFieldON, uint8_t *PDOL, size_t PDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) {
return EMVExchange(LeaveFieldON, (sAPDU){0x80, 0xa8, 0x00, 0x00, PDOLLen, PDOL}, Result, MaxResultLen, ResultLen, sw, tlv); return EMVExchange(channel, LeaveFieldON, (sAPDU){0x80, 0xa8, 0x00, 0x00, PDOLLen, PDOL}, Result, MaxResultLen, ResultLen, sw, tlv);
} }
int EMVReadRecord(bool LeaveFieldON, uint8_t SFI, uint8_t SFIrec, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { int EMVReadRecord(EMVCommandChannel channel, bool LeaveFieldON, uint8_t SFI, uint8_t SFIrec, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) {
int res = EMVExchange(LeaveFieldON, (sAPDU){0x00, 0xb2, SFIrec, (SFI << 3) | 0x04, 0, NULL}, Result, MaxResultLen, ResultLen, sw, tlv); int res = EMVExchange(channel, LeaveFieldON, (sAPDU){0x00, 0xb2, SFIrec, (SFI << 3) | 0x04, 0, NULL}, Result, MaxResultLen, ResultLen, sw, tlv);
if (*sw == 0x6700) { if (*sw == 0x6700) {
PrintAndLogEx(INFO, ">>> trying to reissue command withouth Le..."); PrintAndLogEx(INFO, ">>> trying to reissue command withouth Le...");
res = EMVExchangeEx(false, LeaveFieldON, (sAPDU){0x00, 0xb2, SFIrec, (SFI << 3) | 0x04, 0, NULL}, false, Result, MaxResultLen, ResultLen, sw, tlv); res = EMVExchangeEx(channel, false, LeaveFieldON, (sAPDU){0x00, 0xb2, SFIrec, (SFI << 3) | 0x04, 0, NULL}, false, Result, MaxResultLen, ResultLen, sw, tlv);
} }
return res; return res;
} }
int EMVAC(bool LeaveFieldON, uint8_t RefControl, uint8_t *CDOL, size_t CDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { int EMVAC(EMVCommandChannel channel, bool LeaveFieldON, uint8_t RefControl, uint8_t *CDOL, size_t CDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) {
return EMVExchange(LeaveFieldON, (sAPDU){0x80, 0xae, RefControl, 0x00, CDOLLen, CDOL}, Result, MaxResultLen, ResultLen, sw, tlv); return EMVExchange(channel, LeaveFieldON, (sAPDU){0x80, 0xae, RefControl, 0x00, CDOLLen, CDOL}, Result, MaxResultLen, ResultLen, sw, tlv);
} }
int EMVGenerateChallenge(bool LeaveFieldON, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { int EMVGenerateChallenge(EMVCommandChannel channel, bool LeaveFieldON, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) {
int res = EMVExchange(LeaveFieldON, (sAPDU){0x00, 0x84, 0x00, 0x00, 0x00, NULL}, Result, MaxResultLen, ResultLen, sw, tlv); int res = EMVExchange(channel, LeaveFieldON, (sAPDU){0x00, 0x84, 0x00, 0x00, 0x00, NULL}, Result, MaxResultLen, ResultLen, sw, tlv);
if (*sw == 0x6700) { if (*sw == 0x6700) {
PrintAndLogEx(INFO, ">>> trying to reissue command withouth Le..."); PrintAndLogEx(INFO, ">>> trying to reissue command withouth Le...");
res = EMVExchangeEx(false, LeaveFieldON, (sAPDU){0x00, 0x84, 0x00, 0x00, 0x00, NULL}, false, Result, MaxResultLen, ResultLen, sw, tlv); res = EMVExchangeEx(channel, false, LeaveFieldON, (sAPDU){0x00, 0x84, 0x00, 0x00, 0x00, NULL}, false, Result, MaxResultLen, ResultLen, sw, tlv);
} }
return res; return res;
} }
int EMVInternalAuthenticate(bool LeaveFieldON, uint8_t *DDOL, size_t DDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { int EMVInternalAuthenticate(EMVCommandChannel channel, bool LeaveFieldON, uint8_t *DDOL, size_t DDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) {
return EMVExchange(LeaveFieldON, (sAPDU){0x00, 0x88, 0x00, 0x00, DDOLLen, DDOL}, Result, MaxResultLen, ResultLen, sw, tlv); return EMVExchange(channel, LeaveFieldON, (sAPDU){0x00, 0x88, 0x00, 0x00, DDOLLen, DDOL}, Result, MaxResultLen, ResultLen, sw, tlv);
} }
int MSCComputeCryptoChecksum(bool LeaveFieldON, uint8_t *UDOL, uint8_t UDOLlen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { int MSCComputeCryptoChecksum(EMVCommandChannel channel, bool LeaveFieldON, uint8_t *UDOL, uint8_t UDOLlen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) {
return EMVExchange(LeaveFieldON, (sAPDU){0x80, 0x2a, 0x8e, 0x80, UDOLlen, UDOL}, Result, MaxResultLen, ResultLen, sw, tlv); return EMVExchange(channel, LeaveFieldON, (sAPDU){0x80, 0x2a, 0x8e, 0x80, UDOLlen, UDOL}, Result, MaxResultLen, ResultLen, sw, tlv);
} }
// Authentication // Authentication
@ -565,7 +580,7 @@ int trSDA(struct tlvdb *tlv) {
static const unsigned char default_ddol_value[] = {0x9f, 0x37, 0x04}; static const unsigned char default_ddol_value[] = {0x9f, 0x37, 0x04};
static struct tlv default_ddol_tlv = {.tag = 0x9f49, .len = 3, .value = default_ddol_value }; static struct tlv default_ddol_tlv = {.tag = 0x9f49, .len = 3, .value = default_ddol_value };
int trDDA(bool decodeTLV, struct tlvdb *tlv) { int trDDA(EMVCommandChannel channel, bool decodeTLV, struct tlvdb *tlv) {
uint8_t buf[APDU_RES_LEN] = {0}; uint8_t buf[APDU_RES_LEN] = {0};
size_t len = 0; size_t len = 0;
uint16_t sw = 0; uint16_t sw = 0;
@ -705,7 +720,7 @@ int trDDA(bool decodeTLV, struct tlvdb *tlv) {
PrintAndLogEx(NORMAL, "DDOL data[%d]: %s", ddol_data_tlv->len, sprint_hex(ddol_data_tlv->value, ddol_data_tlv->len)); PrintAndLogEx(NORMAL, "DDOL data[%d]: %s", ddol_data_tlv->len, sprint_hex(ddol_data_tlv->value, ddol_data_tlv->len));
PrintAndLogEx(NORMAL, "\n* Internal Authenticate"); PrintAndLogEx(NORMAL, "\n* Internal Authenticate");
int res = EMVInternalAuthenticate(true, (uint8_t *)ddol_data_tlv->value, ddol_data_tlv->len, buf, sizeof(buf), &len, &sw, NULL); int res = EMVInternalAuthenticate(channel, true, (uint8_t *)ddol_data_tlv->value, ddol_data_tlv->len, buf, sizeof(buf), &len, &sw, NULL);
if (res) { if (res) {
PrintAndLogEx(WARNING, "Internal Authenticate error(%d): %4x. Exit...", res, sw); PrintAndLogEx(WARNING, "Internal Authenticate error(%d): %4x. Exit...", res, sw);
free(ddol_data_tlv); free(ddol_data_tlv);

View file

@ -32,6 +32,11 @@
#define APDU_RES_LEN 260 #define APDU_RES_LEN 260
#define APDU_AID_LEN 50 #define APDU_AID_LEN 50
typedef enum {
ECC_CONTACTLESS,
ECC_CONTACT
} EMVCommandChannel;
enum TransactionType { enum TransactionType {
TT_MSD, TT_MSD,
TT_VSDC, // not standart for contactless!!!! TT_VSDC, // not standart for contactless!!!!
@ -71,28 +76,28 @@ extern struct tlvdb *GetdCVVRawFromTrack2(const struct tlv *track2);
extern void SetAPDULogging(bool logging); extern void SetAPDULogging(bool logging);
// exchange // exchange
extern int EMVExchange(bool LeaveFieldON, sAPDU apdu, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv); extern int EMVExchange(EMVCommandChannel channel, bool LeaveFieldON, sAPDU apdu, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv);
// search application // search application
extern int EMVSearchPSE(bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvdb *tlv); extern int EMVSearchPSE(EMVCommandChannel channel, bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvdb *tlv);
extern int EMVSearch(bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvdb *tlv); extern int EMVSearch(EMVCommandChannel channel, bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvdb *tlv);
extern int EMVSelectPSE(bool ActivateField, bool LeaveFieldON, uint8_t PSENum, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw); extern int EMVSelectPSE(EMVCommandChannel channel, bool ActivateField, bool LeaveFieldON, uint8_t PSENum, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);
extern int EMVSelect(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); extern int EMVSelect(EMVCommandChannel 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 // select application
extern int EMVSelectApplication(struct tlvdb *tlv, uint8_t *AID, size_t *AIDlen); extern int EMVSelectApplication(struct tlvdb *tlv, uint8_t *AID, size_t *AIDlen);
// Get Processing Options // Get Processing Options
extern int EMVGPO(bool LeaveFieldON, uint8_t *PDOL, size_t PDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv); extern int EMVGPO(EMVCommandChannel channel, bool LeaveFieldON, uint8_t *PDOL, size_t PDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv);
extern int EMVReadRecord(bool LeaveFieldON, uint8_t SFI, uint8_t SFIrec, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv); extern int EMVReadRecord(EMVCommandChannel channel, bool LeaveFieldON, uint8_t SFI, uint8_t SFIrec, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv);
// AC // AC
extern int EMVGenerateChallenge(bool LeaveFieldON, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv); extern int EMVGenerateChallenge(EMVCommandChannel channel, bool LeaveFieldON, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv);
extern int EMVAC(bool LeaveFieldON, uint8_t RefControl, uint8_t *CDOL, size_t CDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv); extern int EMVAC(EMVCommandChannel channel, bool LeaveFieldON, uint8_t RefControl, uint8_t *CDOL, size_t CDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv);
// DDA // DDA
extern int EMVInternalAuthenticate(bool LeaveFieldON, uint8_t *DDOL, size_t DDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv); extern int EMVInternalAuthenticate(EMVCommandChannel channel, bool LeaveFieldON, uint8_t *DDOL, size_t DDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv);
// Mastercard // Mastercard
int MSCComputeCryptoChecksum(bool LeaveFieldON, uint8_t *UDOL, uint8_t UDOLlen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv); int MSCComputeCryptoChecksum(EMVCommandChannel channel, bool LeaveFieldON, uint8_t *UDOL, uint8_t UDOLlen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv);
// Auth // Auth
extern int trSDA(struct tlvdb *tlv); extern int trSDA(struct tlvdb *tlv);
extern int trDDA(bool decodeTLV, struct tlvdb *tlv); extern int trDDA(EMVCommandChannel channel, bool decodeTLV, struct tlvdb *tlv);
extern int trCDA(struct tlvdb *tlv, struct tlvdb *ac_tlv, struct tlv *pdol_data_tlv, struct tlv *ac_data_tlv); extern int trCDA(struct tlvdb *tlv, struct tlvdb *ac_tlv, struct tlv *pdol_data_tlv, struct tlv *ac_data_tlv);
extern int RecoveryCertificates(struct tlvdb *tlvRoot, json_t *root); extern int RecoveryCertificates(struct tlvdb *tlvRoot, json_t *root);

View file

@ -170,17 +170,17 @@ char *fido2GetCmdMemberDescription(uint8_t cmdCode, bool isResponse, int memberN
int FIDOSelect(bool ActivateField, bool LeaveFieldON, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) { int FIDOSelect(bool ActivateField, bool LeaveFieldON, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
uint8_t data[] = {0xA0, 0x00, 0x00, 0x06, 0x47, 0x2F, 0x00, 0x01}; uint8_t data[] = {0xA0, 0x00, 0x00, 0x06, 0x47, 0x2F, 0x00, 0x01};
return EMVSelect(ActivateField, LeaveFieldON, data, sizeof(data), Result, MaxResultLen, ResultLen, sw, NULL); return EMVSelect(ECC_CONTACTLESS, ActivateField, LeaveFieldON, data, sizeof(data), Result, MaxResultLen, ResultLen, sw, NULL);
} }
int FIDOExchange(sAPDU apdu, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) { int FIDOExchange(sAPDU apdu, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
int res = EMVExchange(true, apdu, Result, MaxResultLen, ResultLen, sw, NULL); int res = EMVExchange(ECC_CONTACTLESS, true, apdu, Result, MaxResultLen, ResultLen, sw, NULL);
if (res == 5) // apdu result (sw) not a 0x9000 if (res == 5) // apdu result (sw) not a 0x9000
res = 0; res = 0;
// software chaining // software chaining
while (!res && (*sw >> 8) == 0x61) { while (!res && (*sw >> 8) == 0x61) {
size_t oldlen = *ResultLen; size_t oldlen = *ResultLen;
res = EMVExchange(true, (sAPDU){0x00, 0xC0, 0x00, 0x00, 0x00, NULL}, &Result[oldlen], MaxResultLen - oldlen, ResultLen, sw, NULL); res = EMVExchange(ECC_CONTACTLESS, true, (sAPDU){0x00, 0xC0, 0x00, 0x00, 0x00, NULL}, &Result[oldlen], MaxResultLen - oldlen, ResultLen, sw, NULL);
if (res == 5) // apdu result (sw) not a 0x9000 if (res == 5) // apdu result (sw) not a 0x9000
res = 0; res = 0;