X-Git-Url: http://git.zerfleddert.de/cgi-bin/gitweb.cgi/proxmark3-svn/blobdiff_plain/6b5105bea972d055bb2069bf8ca2c6d105b2ee8f..41bdfce385e2c614a55c3e13734c307c03b6ffed:/client/emv/cmdemv.c diff --git a/client/emv/cmdemv.c b/client/emv/cmdemv.c index 16835ba8..abb84d43 100644 --- a/client/emv/cmdemv.c +++ b/client/emv/cmdemv.c @@ -11,8 +11,11 @@ #include "cmdemv.h" #include +#include #include "proxmark3.h" #include "cmdparser.h" +#include "ui.h" +#include "util.h" #include "mifare.h" #include "emvjson.h" #include "emv_pki.h" @@ -21,7 +24,11 @@ #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) { @@ -42,6 +49,20 @@ 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) { @@ -74,6 +95,7 @@ int CmdEMVSelect(const char *cmd) { #ifdef WITH_SMARTCARD if (arg_get_lit(5)) channel = ECC_CONTACT; + PrintChannel(channel); CLIGetHexWithReturn(6, data, &datalen); #else CLIGetHexWithReturn(5, data, &datalen); @@ -128,6 +150,7 @@ int CmdEMVSearch(const char *cmd) { if (arg_get_lit(5)) channel = ECC_CONTACT; #endif + PrintChannel(channel); CLIParserFree(); SetAPDULogging(APDULogging); @@ -188,6 +211,7 @@ int CmdEMVPPSE(const char *cmd) { if (arg_get_lit(7)) channel = ECC_CONTACT; #endif + PrintChannel(channel); CLIParserFree(); SetAPDULogging(APDULogging); @@ -249,6 +273,7 @@ int CmdEMVGPO(const char *cmd) { #else CLIGetHexWithReturn(6, data, &datalen); #endif + PrintChannel(channel); CLIParserFree(); SetAPDULogging(APDULogging); @@ -346,6 +371,7 @@ int CmdEMVReadRecord(const char *cmd) { #else CLIGetHexWithReturn(4, data, &datalen); #endif + PrintChannel(channel); CLIParserFree(); if (datalen != 2) { @@ -434,6 +460,7 @@ int CmdEMVAC(const char *cmd) { #else CLIGetHexWithReturn(8, data, &datalen); #endif + PrintChannel(channel); CLIParserFree(); SetAPDULogging(APDULogging); @@ -519,6 +546,7 @@ int CmdEMVGenerateChallenge(const char *cmd) { if (arg_get_lit(3)) channel = ECC_CONTACT; #endif + PrintChannel(channel); CLIParserFree(); SetAPDULogging(APDULogging); @@ -584,6 +612,7 @@ int CmdEMVInternalAuthenticate(const char *cmd) { #else CLIGetHexWithReturn(6, data, &datalen); #endif + PrintChannel(channel); CLIParserFree(); SetAPDULogging(APDULogging); @@ -645,7 +674,7 @@ int CmdEMVInternalAuthenticate(const char *cmd) { 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) { @@ -710,6 +739,50 @@ void ProcessGPOResponseFormat1(struct tlvdb *tlvRoot, uint8_t *buf, size_t len, } } +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_RESPONSE_LEN] = {0}; size_t len = 0; @@ -768,6 +841,7 @@ int CmdEMVExec(const char *cmd) { 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"; @@ -898,7 +972,7 @@ int CmdEMVExec(const char *cmd) { 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; @@ -920,7 +994,7 @@ int CmdEMVExec(const char *cmd) { // 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; @@ -935,6 +1009,8 @@ int CmdEMVExec(const char *cmd) { memcpy(&ODAiList[ODAiListLen], buf, len); ODAiListLen += len; } + + SFIoffline--; } } } @@ -1154,10 +1230,43 @@ int CmdEMVExec(const char *cmd) { } } - if (channel == ECC_CONTACTLESS) { - 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); tlvdb_free(tlvSelect); @@ -1230,6 +1339,7 @@ int CmdEMVScan(const char *cmd) { #else CLIGetStrWithReturn(11, relfname, &relfnamelen); #endif + PrintChannel(channel); uint8_t psenum = (channel == ECC_CONTACT) ? 1 : 2; CLIParserFree(); @@ -1264,9 +1374,7 @@ int CmdEMVScan(const char *cmd) { } // drop field at start - if (channel == ECC_CONTACTLESS) { - DropField(); - } + DropFieldEx( channel ); // iso 14443 select PrintAndLogEx(NORMAL, "--> GET UID, ATS."); @@ -1319,7 +1427,7 @@ int CmdEMVScan(const char *cmd) { 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; } @@ -1335,9 +1443,7 @@ int CmdEMVScan(const char *cmd) { if (!AIDlen) { PrintAndLogEx(INFO, "Can't select AID. EMV AID not found. Exit..."); - if (channel == ECC_CONTACTLESS) { - DropField(); - } + DropFieldEx( channel ); return 4; } @@ -1356,7 +1462,7 @@ int CmdEMVScan(const char *cmd) { if (res) { PrintAndLogEx(ERR, "Can't select AID (%d). Exit...", res); tlvdb_free(tlvRoot); - DropField(); + DropFieldEx( channel ); return 5; } @@ -1384,9 +1490,7 @@ int CmdEMVScan(const char *cmd) { if (!pdol_data_tlv){ PrintAndLogEx(ERR, "Can't create PDOL TLV."); tlvdb_free(tlvRoot); - if (channel == ECC_CONTACTLESS) { - DropField(); - } + DropFieldEx( channel ); return 6; } @@ -1395,7 +1499,7 @@ int CmdEMVScan(const char *cmd) { 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)); @@ -1409,9 +1513,7 @@ int CmdEMVScan(const char *cmd) { if (res) { PrintAndLogEx(ERR, "GPO error(%d): %4x. Exit...", res, sw); tlvdb_free(tlvRoot); - if (channel == ECC_CONTACTLESS) { - DropField(); - } + DropFieldEx( channel ); return 7; } ProcessGPOResponseFormat1(tlvRoot, buf, len, decodeTLV); @@ -1503,9 +1605,7 @@ int CmdEMVScan(const char *cmd) { // free tlv object tlvdb_free(tlvRoot); - if (channel == ECC_CONTACTLESS) { - DropField(); - } + DropFieldEx( channel ); res = json_dump_file(root, fname, JSON_INDENT(2)); if (res) { @@ -1535,10 +1635,13 @@ int CmdEMVRoca(const char *cmd) { 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 }; @@ -1546,10 +1649,16 @@ int CmdEMVRoca(const char *cmd) { 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); @@ -1557,20 +1666,20 @@ int CmdEMVRoca(const char *cmd) { 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; } @@ -1586,9 +1695,7 @@ int CmdEMVRoca(const char *cmd) { if (!AIDlen) { PrintAndLogEx(INFO, "Can't select AID. EMV AID not found. Exit..."); - if (channel == ECC_CONTACTLESS) { - DropField(); - } + DropFieldEx( channel ); return 4; } @@ -1603,9 +1710,7 @@ int CmdEMVRoca(const char *cmd) { if (res) { PrintAndLogEx(ERR, "Can't select AID (%d). Exit...", res); tlvdb_free(tlvRoot); - if (channel == ECC_CONTACTLESS) { - DropField(); - } + DropFieldEx( channel ); return 5; } @@ -1617,9 +1722,7 @@ int CmdEMVRoca(const char *cmd) { if (!pdol_data_tlv){ PrintAndLogEx(ERR, "Can't create PDOL TLV."); tlvdb_free(tlvRoot); - if (channel == ECC_CONTACTLESS) { - DropField(); - } + DropFieldEx( channel ); return 6; } @@ -1628,7 +1731,7 @@ int CmdEMVRoca(const char *cmd) { 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)); @@ -1642,9 +1745,7 @@ int CmdEMVRoca(const char *cmd) { if (res) { PrintAndLogEx(ERR, "GPO error(%d): %4x. Exit...", res, sw); tlvdb_free(tlvRoot); - if (channel == ECC_CONTACTLESS) { - DropField(); - } + DropFieldEx( channel ); return 7; } ProcessGPOResponseFormat1(tlvRoot, buf, len, false); @@ -1691,7 +1792,7 @@ int CmdEMVRoca(const char *cmd) { 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; } @@ -1702,8 +1803,10 @@ int CmdEMVRoca(const char *cmd) { 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) ); @@ -1716,8 +1819,10 @@ int CmdEMVRoca(const char *cmd) { 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) ); @@ -1742,10 +1847,7 @@ out: // free tlv object tlvdb_free(tlvRoot); - if (channel == ECC_CONTACTLESS) { - DropField(); - } - + DropFieldEx( channel ); return 0; }