X-Git-Url: http://git.zerfleddert.de/cgi-bin/gitweb.cgi/proxmark3-svn/blobdiff_plain/1511ea28a8cc647a6fc462e9c182622a43df1318..4cdd63b245e34b42df42b384009838020d8fad02:/client/emv/cmdemv.c diff --git a/client/emv/cmdemv.c b/client/emv/cmdemv.c index e7676501..832df82e 100644 --- a/client/emv/cmdemv.c +++ b/client/emv/cmdemv.c @@ -42,10 +42,24 @@ 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"); + 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", @@ -74,6 +88,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); @@ -83,7 +98,7 @@ int CmdEMVSelect(const char *cmd) { 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); @@ -128,6 +143,7 @@ int CmdEMVSearch(const char *cmd) { if (arg_get_lit(5)) channel = ECC_CONTACT; #endif + PrintChannel(channel); CLIParserFree(); SetAPDULogging(APDULogging); @@ -188,12 +204,13 @@ int CmdEMVPPSE(const char *cmd) { 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); @@ -212,7 +229,7 @@ int CmdEMVPPSE(const char *cmd) { } 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", @@ -249,6 +266,7 @@ int CmdEMVGPO(const char *cmd) { #else CLIGetHexWithReturn(6, data, &datalen); #endif + PrintChannel(channel); CLIParserFree(); SetAPDULogging(APDULogging); @@ -295,7 +313,7 @@ int CmdEMVGPO(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)); // 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); @@ -317,7 +335,7 @@ int CmdEMVGPO(const char *cmd) { } 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", @@ -346,6 +364,7 @@ int CmdEMVReadRecord(const char *cmd) { #else CLIGetHexWithReturn(4, data, &datalen); #endif + PrintChannel(channel); CLIParserFree(); if (datalen != 2) { @@ -356,7 +375,7 @@ int CmdEMVReadRecord(const char *cmd) { 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); @@ -375,7 +394,7 @@ int CmdEMVReadRecord(const char *cmd) { } 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", @@ -434,6 +453,7 @@ int CmdEMVAC(const char *cmd) { #else CLIGetHexWithReturn(8, data, &datalen); #endif + PrintChannel(channel); CLIParserFree(); SetAPDULogging(APDULogging); @@ -474,7 +494,7 @@ int CmdEMVAC(const char *cmd) { 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); @@ -519,12 +539,13 @@ int CmdEMVGenerateChallenge(const char *cmd) { 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); @@ -544,7 +565,7 @@ int CmdEMVGenerateChallenge(const char *cmd) { } 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", @@ -584,6 +605,7 @@ int CmdEMVInternalAuthenticate(const char *cmd) { #else CLIGetHexWithReturn(6, data, &datalen); #endif + PrintChannel(channel); CLIParserFree(); SetAPDULogging(APDULogging); @@ -624,7 +646,7 @@ int CmdEMVInternalAuthenticate(const char *cmd) { 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); @@ -710,11 +732,55 @@ 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_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; @@ -768,7 +834,10 @@ 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"; + CLIParserFree(); SetAPDULogging(showAPDU); @@ -780,12 +849,12 @@ int CmdEMVExec(const char *cmd) { // 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); @@ -896,7 +965,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; @@ -918,7 +987,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; @@ -933,6 +1002,8 @@ int CmdEMVExec(const char *cmd) { memcpy(&ODAiList[ODAiListLen], buf, len); ODAiListLen += len; } + + SFIoffline--; } } } @@ -1152,7 +1223,43 @@ int CmdEMVExec(const char *cmd) { } } - 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); + + } + + if (channel == ECC_CONTACTLESS) { + DropField(); + } // Destroy TLV's free(pdol_data_tlv); @@ -1164,9 +1271,9 @@ int CmdEMVExec(const char *cmd) { } 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; @@ -1226,6 +1333,7 @@ int CmdEMVScan(const char *cmd) { #else CLIGetStrWithReturn(11, relfname, &relfnamelen); #endif + PrintChannel(channel); uint8_t psenum = (channel == ECC_CONTACT) ? 1 : 2; CLIParserFree(); @@ -1260,8 +1368,10 @@ int CmdEMVScan(const char *cmd) { } // drop field at start - DropField(); - + if (channel == ECC_CONTACTLESS) { + DropField(); + } + // iso 14443 select PrintAndLogEx(NORMAL, "--> GET UID, ATS."); @@ -1329,7 +1439,9 @@ int CmdEMVScan(const char *cmd) { if (!AIDlen) { PrintAndLogEx(INFO, "Can't select AID. EMV AID not found. Exit..."); - DropField(); + if (channel == ECC_CONTACTLESS) { + DropField(); + } return 4; } @@ -1376,7 +1488,9 @@ int CmdEMVScan(const char *cmd) { if (!pdol_data_tlv){ PrintAndLogEx(ERR, "Can't create PDOL TLV."); tlvdb_free(tlvRoot); - DropField(); + if (channel == ECC_CONTACTLESS) { + DropField(); + } return 6; } @@ -1399,7 +1513,9 @@ int CmdEMVScan(const char *cmd) { if (res) { PrintAndLogEx(ERR, "GPO error(%d): %4x. Exit...", res, sw); tlvdb_free(tlvRoot); - DropField(); + if (channel == ECC_CONTACTLESS) { + DropField(); + } return 7; } ProcessGPOResponseFormat1(tlvRoot, buf, len, decodeTLV); @@ -1491,8 +1607,9 @@ int CmdEMVScan(const char *cmd) { // free tlv object tlvdb_free(tlvRoot); - // DropField - DropField(); + if (channel == ECC_CONTACTLESS) { + DropField(); + } res = json_dump_file(root, fname, JSON_INDENT(2)); if (res) { @@ -1512,9 +1629,9 @@ int CmdEMVTest(const char *cmd) { } 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; @@ -1522,10 +1639,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 }; @@ -1533,10 +1653,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); @@ -1544,18 +1670,18 @@ 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(); return 3; @@ -1566,14 +1692,16 @@ int CmdEMVRoca(const char *cmd) { } // EMV SELECT application - SetAPDULogging(false); + SetAPDULogging(true); EMVSelectApplication(tlvSelect, AID, &AIDlen); tlvdb_free(tlvSelect); if (!AIDlen) { PrintAndLogEx(INFO, "Can't select AID. EMV AID not found. Exit..."); - DropField(); + if (channel == ECC_CONTACTLESS) { + DropField(); + } return 4; } @@ -1588,7 +1716,9 @@ int CmdEMVRoca(const char *cmd) { if (res) { PrintAndLogEx(ERR, "Can't select AID (%d). Exit...", res); tlvdb_free(tlvRoot); - DropField(); + if (channel == ECC_CONTACTLESS) { + DropField(); + } return 5; } @@ -1600,7 +1730,9 @@ int CmdEMVRoca(const char *cmd) { if (!pdol_data_tlv){ PrintAndLogEx(ERR, "Can't create PDOL TLV."); tlvdb_free(tlvRoot); - DropField(); + if (channel == ECC_CONTACTLESS) { + DropField(); + } return 6; } @@ -1623,7 +1755,9 @@ int CmdEMVRoca(const char *cmd) { if (res) { PrintAndLogEx(ERR, "GPO error(%d): %4x. Exit...", res, sw); tlvdb_free(tlvRoot); - DropField(); + if (channel == ECC_CONTACTLESS) { + DropField(); + } return 7; } ProcessGPOResponseFormat1(tlvRoot, buf, len, false); @@ -1721,9 +1855,9 @@ out: // free tlv object tlvdb_free(tlvRoot); - if ( channel == ECC_CONTACTLESS) + if (channel == ECC_CONTACTLESS) { DropField(); - + } return 0; } @@ -1732,18 +1866,18 @@ int CmdHelp(const char *Cmd); 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} };