From 0b6efd01ec437f17bcd94475083108eacde144c7 Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko <807634+merlokk@users.noreply.github.com> Date: Thu, 21 Feb 2019 23:02:22 +0200 Subject: [PATCH] Emv scan via contact interface (#789) * share getATR from smartcard.h/c * remove duplicates in tlv.h and add get_uint_8 * check ATS/ATR length --- client/cmdsmartcard.c | 7 +- client/cmdsmartcard.h | 2 + client/emv/cmdemv.c | 163 +++++++++++++++++++++++++++++++++++------- client/emv/tlv.c | 6 ++ client/emv/tlv.h | 3 +- 5 files changed, 152 insertions(+), 29 deletions(-) diff --git a/client/cmdsmartcard.c b/client/cmdsmartcard.c index ac642fc9..b0d96f13 100644 --- a/client/cmdsmartcard.c +++ b/client/cmdsmartcard.c @@ -311,7 +311,7 @@ static int PrintATR(uint8_t *atr, size_t atrlen) { return 0; } -static bool smart_getATR(smart_card_atr_t *card) +bool smart_getATR(smart_card_atr_t *card) { if (UseAlternativeSmartcardReader) { return pcscGetATR(card); @@ -804,6 +804,11 @@ static int CmdSmartInfo(const char *Cmd){ if (!silent) PrintAndLogEx(WARNING, "smart card select failed"); return 1; } + + if (!card.atr_len) { + if (!silent) PrintAndLogEx(ERR, "can't get ATR from a smart card"); + return 1; + } // print header PrintAndLogEx(INFO, "--- Smartcard Information ---------"); diff --git a/client/cmdsmartcard.h b/client/cmdsmartcard.h index 310a417c..8925ac5a 100644 --- a/client/cmdsmartcard.h +++ b/client/cmdsmartcard.h @@ -13,8 +13,10 @@ #include #include +#include "smartcard.h" extern int CmdSmartcard(const char *Cmd); +extern bool smart_getATR(smart_card_atr_t *card); extern int ExchangeAPDUSC(uint8_t *datain, int datainlen, bool activateCard, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen); #endif diff --git a/client/emv/cmdemv.c b/client/emv/cmdemv.c index 7e29b584..0e84260a 100644 --- a/client/emv/cmdemv.c +++ b/client/emv/cmdemv.c @@ -29,6 +29,7 @@ #include "dol.h" #include "emv_tags.h" #include "cmdhf14a.h" +#include "cmdsmartcard.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) { @@ -1255,16 +1256,113 @@ int CmdEMVExec(const char *cmd) { // process Format1 (0x80) and print Format2 (0x77) ProcessACResponseFormat1(tlvRoot, buf, len, decodeTLV); - PrintAndLogEx(NORMAL, "\n* * Processing online request\n"); + uint8_t CID = 0; + tlvdb_get_uint8(tlvRoot, 0x9f27, &CID); + + // AC1 print result + PrintAndLog(""); + if ((CID & EMVAC_AC_MASK) == EMVAC_AAC) PrintAndLogEx(INFO, "AC1 result: AAC (Transaction declined)"); + if ((CID & EMVAC_AC_MASK) == EMVAC_TC) PrintAndLogEx(INFO, "AC1 result: TC (Transaction approved)"); + if ((CID & EMVAC_AC_MASK) == EMVAC_ARQC) PrintAndLogEx(INFO, "AC1 result: ARQC (Online authorisation requested)"); + if ((CID & EMVAC_AC_MASK) == EMVAC_AC_MASK) PrintAndLogEx(INFO, "AC1 result: RFU"); + + // decode Issuer Application Data (IAD) + uint8_t CryptoVersion = 0; + const struct tlv *IAD = tlvdb_get(tlvRoot, 0x9f10, NULL); + if (IAD && (IAD->len > 1)) { + PrintAndLogEx(NORMAL, "\n* * Issuer Application Data (IAD):"); + uint8_t VDDlen = IAD->value[0]; // Visa discretionary data length + uint8_t IDDlen = 0; // Issuer discretionary data length + PrintAndLogEx(NORMAL, "IAD length: %d", IAD->len); + PrintAndLogEx(NORMAL, "VDDlen: %d", VDDlen); + if (VDDlen < IAD->len - 1) + IDDlen = IAD->value[VDDlen + 1]; + PrintAndLogEx(NORMAL, "IDDlen: %d", IDDlen); + + uint8_t DerivKeyIndex = IAD->value[1]; + CryptoVersion = IAD->value[2]; + + PrintAndLogEx(NORMAL, "CryptoVersion: %d", CryptoVersion); + PrintAndLogEx(NORMAL, "DerivKeyIndex: %d", DerivKeyIndex); + + // Card Verification Results (CVR) decode + if ((VDDlen - 2) > 0) { + uint8_t CVRlen = IAD->value[3]; + if (CVRlen == (VDDlen - 2 - 1)) { + PrintAndLogEx(NORMAL, "CVR length: %d", CVRlen); + PrintAndLogEx(NORMAL, "CVR: %s", sprint_hex(&IAD->value[4], CVRlen)); + } else { + PrintAndLogEx(NORMAL, "Wrong CVR length! CVR: %s", sprint_hex(&IAD->value[3], VDDlen - 2)); + } + } + if (IDDlen) + PrintAndLogEx(NORMAL, "IDD: %s", sprint_hex(&IAD->value[VDDlen + 1], IDDlen)); + } else { + PrintAndLogEx(NORMAL, "Issuer Application Data (IAD) not found."); + } + + PrintAndLogEx(NORMAL, "\n* * Processing online request"); // 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); + const char HostResponse[] = "00"; // 0x3030 + size_t HostResponseLen = sizeof(HostResponse) - 1; + PrintAndLogEx(NORMAL, "Host Response: `%s`", HostResponse); + tlvdb_change_or_add_node(tlvRoot, 0x8a, HostResponseLen, (const unsigned char *)HostResponse); + + if (CryptoVersion == 10) { + PrintAndLogEx(NORMAL, "\n* * Generate ARPC"); + + // Application Cryptogram (AC) + const struct tlv *AC = tlvdb_get(tlvRoot, 0x9f26, NULL); + if (AC && (AC->len > 0)) { + PrintAndLogEx(NORMAL, "AC: %s", sprint_hex(AC->value, AC->len)); + size_t rawARPClen = AC->len; + uint8_t rawARPC[rawARPClen]; + memcpy(rawARPC, AC->value, AC->len); + for (int i = 0; (i < HostResponseLen) && (i < rawARPClen); i++) + rawARPC[i] ^= HostResponse[i]; + PrintAndLogEx(NORMAL, "raw ARPC: %s", sprint_hex(rawARPC, rawARPClen)); + + // here must be calculation of ARPC, but we dont know a bank keys. + PrintAndLogEx(NORMAL, "ARPC: n/a"); + + } else { + PrintAndLogEx(NORMAL, "Application Cryptogram (AC) not found."); + } + // here must be external authenticate, but we dont know ARPC + } - + + + // needs to send AC2 command (res == ARQC) + if ((CID & EMVAC_AC_MASK) == EMVAC_ARQC) { + PrintAndLogEx(NORMAL, "\n* * Calc CDOL2"); + struct tlv *cdol2_data_tlv = dol_process(tlvdb_get(tlvRoot, 0x8d, NULL), tlvRoot, 0x01); // 0x01 - dummy tag + if (!cdol2_data_tlv) { + PrintAndLogEx(WARNING, "Error: can't create CDOL2 TLV."); + dreturn(6); + } + + PrintAndLogEx(NORMAL, "CDOL2 data[%d]: %s", cdol2_data_tlv->len, sprint_hex(cdol2_data_tlv->value, cdol2_data_tlv->len)); + + //PrintAndLogEx(NORMAL, "* * AC2"); + + + // here must be AC2, but we dont make external authenticate ( + +/* // AC2 + PRINT_INDENT(level); + if ((CID & EMVAC_AC2_MASK) == EMVAC_AAC2) fprintf(f, "\tAC2: AAC (Transaction declined)\n"); + if ((CID & EMVAC_AC2_MASK) == EMVAC_TC2) fprintf(f, "\tAC2: TC (Transaction approved)\n"); + if ((CID & EMVAC_AC2_MASK) == EMVAC_ARQC2) fprintf(f, "\tAC2: not requested (ARQC)\n"); + if ((CID & EMVAC_AC2_MASK) == EMVAC_AC2_MASK) fprintf(f, "\tAC2: RFU\n"); +*/ + } + + } + DropFieldEx( channel ); // Destroy TLV's @@ -1342,15 +1440,9 @@ int CmdEMVScan(const char *cmd) { PrintChannel(channel); uint8_t psenum = (channel == ECC_CONTACT) ? 1 : 2; CLIParserFree(); - + SetAPDULogging(showAPDU); - - // TODO - if (channel == ECC_CONTACT) { - PrintAndLogEx(ERR, "Do not use contact interface. Exit."); - return 1; - } - + // current path + file name if (!strstr(crelfname, ".json")) strcat(crelfname, ".json"); @@ -1376,22 +1468,41 @@ int CmdEMVScan(const char *cmd) { // drop field at start DropFieldEx( channel ); - // iso 14443 select - PrintAndLogEx(NORMAL, "--> GET UID, ATS."); - - iso14a_card_select_t card; - if (Hf14443_4aGetCardData(&card)) { - return 2; - } - JsonSaveStr(root, "$.File.Created", "proxmark3 `emv scan`"); + + if (channel == ECC_CONTACTLESS) { + // iso 14443 select + PrintAndLogEx(NORMAL, "--> GET UID, ATS."); + + iso14a_card_select_t card; + if (Hf14443_4aGetCardData(&card)) { + return 2; + } + if (!card.uidlen) { + PrintAndLogEx(ERR, "get ATS error"); + return 2; + } - JsonSaveStr(root, "$.Card.Communication", "iso14443-4a"); - JsonSaveBufAsHex(root, "$.Card.UID", (uint8_t *)&card.uid, card.uidlen); - JsonSaveHex(root, "$.Card.ATQA", card.atqa[0] + (card.atqa[1] << 2), 2); - JsonSaveHex(root, "$.Card.SAK", card.sak, 0); - JsonSaveBufAsHex(root, "$.Card.ATS", (uint8_t *)card.ats, card.ats_len); + JsonSaveStr(root, "$.Card.Contactless.Communication", "iso14443-4a"); + JsonSaveBufAsHex(root, "$.Card.Contactless.UID", (uint8_t *)&card.uid, card.uidlen); + JsonSaveHex(root, "$.Card.Contactless.ATQA", card.atqa[0] + (card.atqa[1] << 2), 2); + JsonSaveHex(root, "$.Card.Contactless.SAK", card.sak, 0); + JsonSaveBufAsHex(root, "$.Card.Contactless.ATS", (uint8_t *)card.ats, card.ats_len); + } else { + PrintAndLogEx(NORMAL, "--> GET ATR."); + + smart_card_atr_t ccard; + smart_getATR(&ccard); + + if (!ccard.atr_len) { + PrintAndLogEx(ERR, "get ATR error"); + return 2; + } + JsonSaveStr(root, "$.Card.Contact.Communication", "iso7816"); + JsonSaveBufAsHex(root, "$.Card.Contact.ATR", (uint8_t *)ccard.atr, ccard.atr_len); + } + // init applets list tree const char *al = "Applets list"; struct tlvdb *tlvSelect = tlvdb_fixed(1, strlen(al), (const unsigned char *)al); diff --git a/client/emv/tlv.c b/client/emv/tlv.c index 05de928e..5472c47a 100644 --- a/client/emv/tlv.c +++ b/client/emv/tlv.c @@ -588,3 +588,9 @@ bool tlv_get_int(const struct tlv *etlv, int *value) } return false; } + +bool tlvdb_get_uint8(struct tlvdb *tlvRoot, tlv_tag_t tag, uint8_t *value) +{ + const struct tlv *tlvelm = tlvdb_get(tlvRoot, tag, NULL); + return tlv_get_uint8(tlvelm, value); +} diff --git a/client/emv/tlv.h b/client/emv/tlv.h index 80f6b74f..54900f41 100644 --- a/client/emv/tlv.h +++ b/client/emv/tlv.h @@ -65,7 +65,6 @@ bool tlv_equal(const struct tlv *a, const struct tlv *b); bool tlv_get_uint8(const struct tlv *etlv, uint8_t *value); bool tlv_get_int(const struct tlv *etlv, int *value); -bool tlv_get_uint8(const struct tlv *etlv, uint8_t *value); -bool tlv_get_int(const struct tlv *etlv, int *value); +bool tlvdb_get_uint8(struct tlvdb *tlvRoot, tlv_tag_t tag, uint8_t *value); #endif -- 2.39.5