#include "cmdemv.h"
#include <ctype.h>
+#include <string.h>
#include "proxmark3.h"
#include "cmdparser.h"
+#include "ui.h"
+#include "util.h"
#include "mifare.h"
#include "emvjson.h"
#include "emv_pki.h"
#include "cliparser/cliparser.h"
#include "jansson.h"
#include "emv_roca.h"
-
+#include "pcsc.h"
+#include "apduinfo.h"
+#include "dol.h"
+#include "emv_tags.h"
+#include "cmdhf14a.h"
#define TLV_ADD(tag, value)( tlvdb_change_or_add_node(tlvRoot, tag, sizeof(value) - 1, (const unsigned char *)value) )
void ParamLoadDefaults(struct tlvdb *tlvRoot) {
TLV_ADD(0x9F6A, "\x01\x02\x03\x04");
//9F66:(Terminal Transaction Qualifiers (TTQ)) len:4
TLV_ADD(0x9F66, "\x26\x00\x00\x00"); // qVSDC
+ //95:(Terminal Verification Results) len:5
+ // all OK TVR
+ TLV_ADD(0x95, "\x00\x00\x00\x00\x00");
+}
+
+void PrintChannel(EMVCommandChannel channel) {
+ switch(channel) {
+ case ECC_CONTACTLESS:
+ PrintAndLogEx(INFO, "Channel: CONTACTLESS");
+ break;
+ case ECC_CONTACT:
+ PrintAndLogEx(INFO, "Channel: CONTACT, using %s", getAlternativeSmartcardReader());
+ break;
+ }
}
int CmdEMVSelect(const char *cmd) {
- uint8_t data[APDU_AID_LEN] = {0};
+ uint8_t data[APDU_DATA_LEN] = {0};
int datalen = 0;
CLIParserInit("emv select",
#ifdef WITH_SMARTCARD
if (arg_get_lit(5))
channel = ECC_CONTACT;
+ PrintChannel(channel);
CLIGetHexWithReturn(6, data, &datalen);
#else
CLIGetHexWithReturn(5, data, &datalen);
SetAPDULogging(APDULogging);
// exec
- uint8_t buf[APDU_RES_LEN] = {0};
+ uint8_t buf[APDU_RESPONSE_LEN] = {0};
size_t len = 0;
uint16_t sw = 0;
int res = EMVSelect(channel, activateField, leaveSignalON, data, datalen, buf, sizeof(buf), &len, &sw, NULL);
if (arg_get_lit(5))
channel = ECC_CONTACT;
#endif
+ PrintChannel(channel);
CLIParserFree();
SetAPDULogging(APDULogging);
if (arg_get_lit(7))
channel = ECC_CONTACT;
#endif
+ PrintChannel(channel);
CLIParserFree();
SetAPDULogging(APDULogging);
// exec
- uint8_t buf[APDU_RES_LEN] = {0};
+ uint8_t buf[APDU_RESPONSE_LEN] = {0};
size_t len = 0;
uint16_t sw = 0;
int res = EMVSelectPSE(channel, activateField, leaveSignalON, PSENum, buf, sizeof(buf), &len, &sw);
}
int CmdEMVGPO(const char *cmd) {
- uint8_t data[APDU_RES_LEN] = {0};
+ uint8_t data[APDU_RESPONSE_LEN] = {0};
int datalen = 0;
CLIParserInit("emv gpo",
#else
CLIGetHexWithReturn(6, data, &datalen);
#endif
+ PrintChannel(channel);
CLIParserFree();
SetAPDULogging(APDULogging);
PrintAndLogEx(INFO, "PDOL data[%d]: %s", pdol_data_tlv_data_len, sprint_hex(pdol_data_tlv_data, pdol_data_tlv_data_len));
// exec
- uint8_t buf[APDU_RES_LEN] = {0};
+ uint8_t buf[APDU_RESPONSE_LEN] = {0};
size_t len = 0;
uint16_t sw = 0;
int res = EMVGPO(channel, leaveSignalON, pdol_data_tlv_data, pdol_data_tlv_data_len, buf, sizeof(buf), &len, &sw, tlvRoot);
}
int CmdEMVReadRecord(const char *cmd) {
- uint8_t data[APDU_RES_LEN] = {0};
+ uint8_t data[APDU_RESPONSE_LEN] = {0};
int datalen = 0;
CLIParserInit("emv readrec",
#else
CLIGetHexWithReturn(4, data, &datalen);
#endif
+ PrintChannel(channel);
CLIParserFree();
if (datalen != 2) {
SetAPDULogging(APDULogging);
// exec
- uint8_t buf[APDU_RES_LEN] = {0};
+ uint8_t buf[APDU_RESPONSE_LEN] = {0};
size_t len = 0;
uint16_t sw = 0;
int res = EMVReadRecord(channel, leaveSignalON, data[0], data[1], buf, sizeof(buf), &len, &sw, NULL);
}
int CmdEMVAC(const char *cmd) {
- uint8_t data[APDU_RES_LEN] = {0};
+ uint8_t data[APDU_RESPONSE_LEN] = {0};
int datalen = 0;
CLIParserInit("emv genac",
#else
CLIGetHexWithReturn(8, data, &datalen);
#endif
+ PrintChannel(channel);
CLIParserFree();
SetAPDULogging(APDULogging);
PrintAndLogEx(INFO, "CDOL data[%d]: %s", cdol_data_tlv->len, sprint_hex(cdol_data_tlv->value, cdol_data_tlv->len));
// exec
- uint8_t buf[APDU_RES_LEN] = {0};
+ uint8_t buf[APDU_RESPONSE_LEN] = {0};
size_t len = 0;
uint16_t sw = 0;
int res = EMVAC(channel, leaveSignalON, termDecision, (uint8_t *)cdol_data_tlv->value, cdol_data_tlv->len, buf, sizeof(buf), &len, &sw, tlvRoot);
if (arg_get_lit(3))
channel = ECC_CONTACT;
#endif
+ PrintChannel(channel);
CLIParserFree();
SetAPDULogging(APDULogging);
// exec
- uint8_t buf[APDU_RES_LEN] = {0};
+ uint8_t buf[APDU_RESPONSE_LEN] = {0};
size_t len = 0;
uint16_t sw = 0;
int res = EMVGenerateChallenge(channel, leaveSignalON, buf, sizeof(buf), &len, &sw, NULL);
}
int CmdEMVInternalAuthenticate(const char *cmd) {
- uint8_t data[APDU_RES_LEN] = {0};
+ uint8_t data[APDU_RESPONSE_LEN] = {0};
int datalen = 0;
CLIParserInit("emv intauth",
#else
CLIGetHexWithReturn(6, data, &datalen);
#endif
+ PrintChannel(channel);
CLIParserFree();
SetAPDULogging(APDULogging);
PrintAndLogEx(INFO, "DDOL data[%d]: %s", ddol_data_tlv->len, sprint_hex(ddol_data_tlv->value, ddol_data_tlv->len));
// exec
- uint8_t buf[APDU_RES_LEN] = {0};
+ uint8_t buf[APDU_RESPONSE_LEN] = {0};
size_t len = 0;
uint16_t sw = 0;
int res = EMVInternalAuthenticate(channel, leaveSignalON, data, datalen, buf, sizeof(buf), &len, &sw, NULL);
return 0;
}
-#define dreturn(n) {free(pdol_data_tlv);tlvdb_free(tlvSelect);tlvdb_free(tlvRoot);DropField();return n;}
+#define dreturn(n) {free(pdol_data_tlv); tlvdb_free(tlvSelect); tlvdb_free(tlvRoot); DropFieldEx( channel ); return n;}
void InitTransactionParameters(struct tlvdb *tlvRoot, bool paramLoadJSON, enum TransactionType TrType, bool GenACGPO) {
}
}
+void ProcessACResponseFormat1(struct tlvdb *tlvRoot, uint8_t *buf, size_t len, bool decodeTLV) {
+ if (buf[0] == 0x80) {
+ if (decodeTLV){
+ PrintAndLog("GPO response format1:");
+ TLVPrintFromBuffer(buf, len);
+ }
+
+ uint8_t elmlen = len - 2; // wo 0x80XX
+
+ if (len < 4 + 2 || (elmlen - 2) % 4 || elmlen != buf[1]) {
+ PrintAndLogEx(ERR, "GPO response format1 parsing error. length=%d", len);
+ } else {
+ struct tlvdb *tlvElm = NULL;
+ if (decodeTLV)
+ PrintAndLog("\n------------ Format1 decoded ------------");
+
+ // CID (Cryptogram Information Data)
+ tlvdb_change_or_add_node_ex(tlvRoot, 0x9f27, 1, &buf[2], &tlvElm);
+ if (decodeTLV)
+ TLVPrintFromTLV(tlvElm);
+
+ // ATC (Application Transaction Counter)
+ tlvdb_change_or_add_node_ex(tlvRoot, 0x9f36, 2, &buf[3], &tlvElm);
+ if (decodeTLV)
+ TLVPrintFromTLV(tlvElm);
+
+ // AC (Application Cryptogram)
+ tlvdb_change_or_add_node_ex(tlvRoot, 0x9f26, MIN(8, elmlen - 3), &buf[5], &tlvElm);
+ if (decodeTLV)
+ TLVPrintFromTLV(tlvElm);
+
+ // IAD (Issuer Application Data) - optional
+ if (len > 11 + 2) {
+ tlvdb_change_or_add_node_ex(tlvRoot, 0x9f10, elmlen - 11, &buf[13], &tlvElm);
+ if (decodeTLV)
+ TLVPrintFromTLV(tlvElm);
+ }
+ }
+ } else {
+ if (decodeTLV)
+ TLVPrintFromBuffer(buf, len);
+ }
+}
+
int CmdEMVExec(const char *cmd) {
- uint8_t buf[APDU_RES_LEN] = {0};
+ uint8_t buf[APDU_RESPONSE_LEN] = {0};
size_t len = 0;
uint16_t sw = 0;
- uint8_t AID[APDU_AID_LEN] = {0};
+ uint8_t AID[APDU_DATA_LEN] = {0};
size_t AIDlen = 0;
uint8_t ODAiList[4096];
size_t ODAiListLen = 0;
if (arg_get_lit(11))
channel = ECC_CONTACT;
#endif
+ PrintChannel(channel);
uint8_t psenum = (channel == ECC_CONTACT) ? 1 : 2;
+ char *PSE_or_PPSE = psenum == 1 ? "PSE" : "PPSE";
+
CLIParserFree();
SetAPDULogging(showAPDU);
// Application Selection
// https://www.openscdp.org/scripts/tutorial/emv/applicationselection.html
if (!forceSearch) {
- // PPSE
- PrintAndLogEx(NORMAL, "\n* PPSE.");
+ // PPSE / PSE
+ PrintAndLogEx(NORMAL, "\n* %s.", PSE_or_PPSE);
SetAPDULogging(showAPDU);
res = EMVSearchPSE(channel, activateField, true, psenum, decodeTLV, tlvSelect);
- // check PPSE and select application id
+ // check PPSE / PSE and select application id
if (!res) {
TLVPrintAIDlistFromSelectTLV(tlvSelect);
EMVSelectApplication(tlvSelect, AID, &AIDlen);
uint8_t SFIend = AFL->value[i * 4 + 2];
uint8_t SFIoffline = AFL->value[i * 4 + 3];
- PrintAndLogEx(NORMAL, "* * SFI[%02x] start:%02x end:%02x offline:%02x", SFI, SFIstart, SFIend, SFIoffline);
+ PrintAndLogEx(NORMAL, "* * SFI[%02x] start:%02x end:%02x offline count:%02x", SFI, SFIstart, SFIend, SFIoffline);
if (SFI == 0 || SFI == 31 || SFIstart == 0 || SFIstart > SFIend) {
PrintAndLogEx(NORMAL, "SFI ERROR! Skipped...");
continue;
// Build Input list for Offline Data Authentication
// EMV 4.3 book3 10.3, page 96
- if (SFIoffline) {
+ if (SFIoffline > 0) {
if (SFI < 11) {
const unsigned char *abuf = buf;
size_t elmlen = len;
memcpy(&ODAiList[ODAiListLen], buf, len);
ODAiListLen += len;
}
+
+ SFIoffline--;
}
}
}
}
}
- DropField();
+ // VSDC
+ if (GetCardPSVendor(AID, AIDlen) == CV_VISA && (TrType == TT_VSDC || TrType == TT_CDA)){
+ PrintAndLogEx(NORMAL, "\n--> VSDC transaction.");
+
+ PrintAndLogEx(NORMAL, "* * Calc CDOL1");
+ struct tlv *cdol_data_tlv = dol_process(tlvdb_get(tlvRoot, 0x8c, NULL), tlvRoot, 0x01); // 0x01 - dummy tag
+ if (!cdol_data_tlv) {
+ PrintAndLogEx(WARNING, "Error: can't create CDOL1 TLV.");
+ dreturn(6);
+ }
+
+ PrintAndLogEx(NORMAL, "CDOL1 data[%d]: %s", cdol_data_tlv->len, sprint_hex(cdol_data_tlv->value, cdol_data_tlv->len));
+
+ PrintAndLogEx(NORMAL, "* * AC1");
+ // EMVAC_TC + EMVAC_CDAREQ --- to get SDAD
+ 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) {
+ PrintAndLogEx(NORMAL, "AC1 error(%d): %4x. Exit...", res, sw);
+ dreturn(7);
+ }
+
+ // process Format1 (0x80) and print Format2 (0x77)
+ ProcessACResponseFormat1(tlvRoot, buf, len, decodeTLV);
+
+ PrintAndLogEx(NORMAL, "\n* * Processing online request\n");
+
+ // authorization response code from acquirer
+ const char HostResponse[] = "00"; //0 x3030
+ PrintAndLogEx(NORMAL, "* * Host Response: `%s`", HostResponse);
+ tlvdb_change_or_add_node(tlvRoot, 0x8a, sizeof(HostResponse) - 1, (const unsigned char *)HostResponse);
+
+
+ }
+
+ DropFieldEx( channel );
// Destroy TLV's
free(pdol_data_tlv);
}
int CmdEMVScan(const char *cmd) {
- uint8_t AID[APDU_AID_LEN] = {0};
+ uint8_t AID[APDU_DATA_LEN] = {0};
size_t AIDlen = 0;
- uint8_t buf[APDU_RES_LEN] = {0};
+ uint8_t buf[APDU_RESPONSE_LEN] = {0};
size_t len = 0;
uint16_t sw = 0;
int res;
#else
CLIGetStrWithReturn(11, relfname, &relfnamelen);
#endif
+ PrintChannel(channel);
uint8_t psenum = (channel == ECC_CONTACT) ? 1 : 2;
CLIParserFree();
}
// drop field at start
- DropField();
-
+ DropFieldEx( channel );
+
// iso 14443 select
PrintAndLogEx(NORMAL, "--> GET UID, ATS.");
if (EMVSearch(channel, false, true, decodeTLV, tlvSelect)) {
PrintAndLogEx(ERR, "Can't found any of EMV AID. Exit...");
tlvdb_free(tlvSelect);
- DropField();
+ DropFieldEx( channel );
return 3;
}
if (!AIDlen) {
PrintAndLogEx(INFO, "Can't select AID. EMV AID not found. Exit...");
- DropField();
+ DropFieldEx( channel );
return 4;
}
if (res) {
PrintAndLogEx(ERR, "Can't select AID (%d). Exit...", res);
tlvdb_free(tlvRoot);
- DropField();
+ DropFieldEx( channel );
return 5;
}
if (!pdol_data_tlv){
PrintAndLogEx(ERR, "Can't create PDOL TLV.");
tlvdb_free(tlvRoot);
- DropField();
+ DropFieldEx( channel );
return 6;
}
if (!pdol_data_tlv_data) {
PrintAndLogEx(ERR, "Can't create PDOL data.");
tlvdb_free(tlvRoot);
- DropField();
+ DropFieldEx( channel );
return 6;
}
PrintAndLogEx(INFO, "PDOL data[%d]: %s", pdol_data_tlv_data_len, sprint_hex(pdol_data_tlv_data, pdol_data_tlv_data_len));
if (res) {
PrintAndLogEx(ERR, "GPO error(%d): %4x. Exit...", res, sw);
tlvdb_free(tlvRoot);
- DropField();
+ DropFieldEx( channel );
return 7;
}
ProcessGPOResponseFormat1(tlvRoot, buf, len, decodeTLV);
// free tlv object
tlvdb_free(tlvRoot);
- // DropField
- DropField();
+ DropFieldEx( channel );
res = json_dump_file(root, fname, JSON_INDENT(2));
if (res) {
}
int CmdEMVRoca(const char *cmd) {
- uint8_t AID[APDU_AID_LEN] = {0};
+ uint8_t AID[APDU_DATA_LEN] = {0};
size_t AIDlen = 0;
- uint8_t buf[APDU_RES_LEN] = {0};
+ uint8_t buf[APDU_RESPONSE_LEN] = {0};
size_t len = 0;
uint16_t sw = 0;
int res;
CLIParserInit("emv roca",
"Tries to extract public keys and run the ROCA test against them.\n",
"Usage:\n"
- "\temv roca -w -> select CONTACT card and run test\n\temv roca -> select CONTACTLESS card and run test\n");
+ "\temv roca -w -> select --CONTACT-- card and run test\n"
+ "\temv roca -> select --CONTACTLESS-- card and run test\n"
+ );
void* argtable[] = {
arg_param_begin,
+ arg_lit0("tT", "selftest", "self test"),
arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. Contactless interface set by default."),
arg_param_end
};
EMVCommandChannel channel = ECC_CONTACTLESS;
if (arg_get_lit(1))
+ return roca_self_test();
+#ifdef WITH_SMARTCARD
+ if (arg_get_lit(2))
channel = ECC_CONTACT;
+#endif
+ PrintChannel(channel);
// select card
uint8_t psenum = (channel == ECC_CONTACT) ? 1 : 2;
+ char *PSE_or_PPSE = psenum == 1 ? "PSE" : "PPSE";
SetAPDULogging(false);
const char *al = "Applets list";
struct tlvdb *tlvSelect = tlvdb_fixed(1, strlen(al), (const unsigned char *)al);
- // EMV PPSE
- PrintAndLogEx(NORMAL, "--> PPSE.");
- res = EMVSearchPSE(channel, false, true, psenum, false, tlvSelect);
+ // EMV PSE/PPSE
+ PrintAndLogEx(NORMAL, "--> %s.", PSE_or_PPSE);
+ res = EMVSearchPSE(channel, true, true, psenum, false, tlvSelect);
- // check PPSE and select application id
+ // check PSE/PPSE and select application id
if (!res) {
TLVPrintAIDlistFromSelectTLV(tlvSelect);
} else {
// EMV SEARCH with AID list
PrintAndLogEx(NORMAL, "--> AID search.");
if (EMVSearch(channel, false, true, false, tlvSelect)) {
- PrintAndLogEx(ERR, "Can't found any of EMV AID. Exit...");
+ PrintAndLogEx(ERR, "Couldn't find any known EMV AID. Exit...");
tlvdb_free(tlvSelect);
- DropField();
+ DropFieldEx( channel );
return 3;
}
if (!AIDlen) {
PrintAndLogEx(INFO, "Can't select AID. EMV AID not found. Exit...");
- DropField();
+ DropFieldEx( channel );
return 4;
}
if (res) {
PrintAndLogEx(ERR, "Can't select AID (%d). Exit...", res);
tlvdb_free(tlvRoot);
- DropField();
+ DropFieldEx( channel );
return 5;
}
if (!pdol_data_tlv){
PrintAndLogEx(ERR, "Can't create PDOL TLV.");
tlvdb_free(tlvRoot);
- DropField();
+ DropFieldEx( channel );
return 6;
}
if (!pdol_data_tlv_data) {
PrintAndLogEx(ERR, "Can't create PDOL data.");
tlvdb_free(tlvRoot);
- DropField();
+ DropFieldEx( channel );
return 6;
}
PrintAndLogEx(INFO, "PDOL data[%d]: %s", pdol_data_tlv_data_len, sprint_hex(pdol_data_tlv_data, pdol_data_tlv_data_len));
if (res) {
PrintAndLogEx(ERR, "GPO error(%d): %4x. Exit...", res, sw);
tlvdb_free(tlvRoot);
- DropField();
+ DropFieldEx( channel );
return 7;
}
ProcessGPOResponseFormat1(tlvRoot, buf, len, false);
struct emv_pk *pk = get_ca_pk(tlvRoot);
if (!pk) {
- PrintAndLogEx(ERR, "ERROR: Key not found. Exit.");
+ PrintAndLogEx(ERR, "CA Public Key not found. Exit.");
goto out;
}
goto out;
}
+ char RID[15] = {0};
+ memcpy(RID, sprint_hex(issuer_pk->rid, 5), 14);
PrintAndLogEx(SUCCESS, "Issuer PK recovered. RID %s IDX %02hhx CSN %s",
- sprint_hex(issuer_pk->rid, 5),
+ RID,
issuer_pk->index,
sprint_hex(issuer_pk->serial, 3)
);
PrintAndLogEx(WARNING, "WARNING: ICC certificate not found. Exit.");
goto out;
}
+
+ memcpy(RID, sprint_hex(icc_pk->rid, 5), 14);
PrintAndLogEx(SUCCESS, "ICC PK recovered. RID %s IDX %02hhx CSN %s\n",
- sprint_hex(icc_pk->rid, 5),
+ RID,
icc_pk->index,
sprint_hex(icc_pk->serial, 3)
);
// free tlv object
tlvdb_free(tlvRoot);
- if ( channel == ECC_CONTACTLESS)
- DropField();
-
-
+ DropFieldEx( channel );
return 0;
}
static command_t CommandTable[] = {
{"help", CmdHelp, 1, "This help"},
- {"exec", CmdEMVExec, 0, "Executes EMV contactless transaction."},
- {"pse", CmdEMVPPSE, 0, "Execute PPSE. It selects 2PAY.SYS.DDF01 or 1PAY.SYS.DDF01 directory."},
- {"search", CmdEMVSearch, 0, "Try to select all applets from applets list and print installed applets."},
- {"select", CmdEMVSelect, 0, "Select applet."},
- {"gpo", CmdEMVGPO, 0, "Execute GetProcessingOptions."},
- {"readrec", CmdEMVReadRecord, 0, "Read files from card."},
- {"genac", CmdEMVAC, 0, "Generate ApplicationCryptogram."},
- {"challenge", CmdEMVGenerateChallenge, 0, "Generate challenge."},
- {"intauth", CmdEMVInternalAuthenticate, 0, "Internal authentication."},
- {"scan", CmdEMVScan, 0, "Scan EMV card and save it contents to json file for emulator."},
- {"test", CmdEMVTest, 0, "Crypto logic test."},
- {"roca", CmdEMVRoca, 0, "Extract public keys and run ROCA test"},
+ {"exec", CmdEMVExec, 1, "Executes EMV contactless transaction."},
+ {"pse", CmdEMVPPSE, 1, "Execute PPSE. It selects 2PAY.SYS.DDF01 or 1PAY.SYS.DDF01 directory."},
+ {"search", CmdEMVSearch, 1, "Try to select all applets from applets list and print installed applets."},
+ {"select", CmdEMVSelect, 1, "Select applet."},
+ {"gpo", CmdEMVGPO, 1, "Execute GetProcessingOptions."},
+ {"readrec", CmdEMVReadRecord, 1, "Read files from card."},
+ {"genac", CmdEMVAC, 1, "Generate ApplicationCryptogram."},
+ {"challenge", CmdEMVGenerateChallenge, 1, "Generate challenge."},
+ {"intauth", CmdEMVInternalAuthenticate, 1, "Internal authentication."},
+ {"scan", CmdEMVScan, 1, "Scan EMV card and save it contents to json file for emulator."},
+ {"test", CmdEMVTest, 1, "Crypto logic test."},
+ {"roca", CmdEMVRoca, 1, "Extract public keys and run ROCA test"},
{NULL, NULL, 0, NULL}
};