From: merlokk Date: Sat, 2 Dec 2017 19:47:30 +0000 (+0200) Subject: transactions MSD and M/Chip, qVSDC works X-Git-Tag: v3.1.0~111^2~3 X-Git-Url: https://git.zerfleddert.de/cgi-bin/gitweb.cgi/proxmark3-svn/commitdiff_plain/10d4f823385cda5adfc8a3e0e07b761447aa5ec6?ds=inline transactions MSD and M/Chip, qVSDC works --- diff --git a/client/emv/cmdemv.c b/client/emv/cmdemv.c index b188555e..268d9e63 100644 --- a/client/emv/cmdemv.c +++ b/client/emv/cmdemv.c @@ -283,6 +283,10 @@ int UsageCmdHFEMVExec(void) { PrintAndLog(" -a : show APDU reqests and responses\n"); PrintAndLog(" -t : TLV decode results\n"); PrintAndLog(" -f : force search AID. Search AID instead of execute PPSE.\n"); + PrintAndLog(" -v : transaction type - qVSDC or M/Chip.\n"); + PrintAndLog(" -c : transaction type - qVSDC or M/Chip plus CDA (SDAD generation).\n"); + PrintAndLog(" -x : transaction type - VSDC. For test only. Not a standart behavior.\n"); + PrintAndLog("By default : transaction type - MSD.\n"); PrintAndLog("Samples:"); PrintAndLog(" hf emv pse -s -> select card"); PrintAndLog(" hf emv pse -s -t -a -> select card, show responses in TLV, show APDU"); @@ -296,6 +300,7 @@ int CmdHFEMVExec(const char *cmd) { bool showAPDU = false; bool decodeTLV = false; bool forceSearch = false; + enum TransactionType TrType = TT_MSD; uint8_t buf[APDU_RES_LEN] = {0}; size_t len = 0; @@ -335,6 +340,18 @@ int CmdHFEMVExec(const char *cmd) { case 'F': forceSearch = true; break; + case 'x': + case 'X': + TrType = TT_VSDC; + break; + case 'v': + case 'V': + TrType = TT_QVSDCMCHIP; + break; + case 'c': + case 'C': + TrType = TT_CDA; + break; default: PrintAndLog("Unknown parameter '%c'", param_getchar_indx(cmd, 1, cmdp)); return 1; @@ -405,9 +422,24 @@ int CmdHFEMVExec(const char *cmd) { PrintAndLog("\n* Init transaction parameters."); //9F66:(Terminal Transaction Qualifiers (TTQ)) len:4 - TLV_ADD(0x9F66, "\x86\x00\x00\x00"); // MSD -// TLV_ADD(0x9F66, "\x26\x00\x00\x00"); // qVSDC -// TLV_ADD(0x9F66, "\x8e\x00\x00\x00"); // CDA + switch(TrType) { + case TT_MSD: + TLV_ADD(0x9F66, "\x86\x00\x00\x00"); // MSD + break; + // not standart for contactless. just for test. + case TT_VSDC: + TLV_ADD(0x9F66, "\x46\x00\x00\x00"); // VSDC + break; + case TT_QVSDCMCHIP: + TLV_ADD(0x9F66, "\x26\x00\x00\x00"); // qVSDC + break; + case TT_CDA: + TLV_ADD(0x9F66, "\x86\x80\x00\x00"); // CDA + break; + default: + TLV_ADD(0x9F66, "\x26\x00\x00\x00"); // qVSDC + break; + } //9F02:(Amount, Authorised (Numeric)) len:6 TLV_ADD(0x9F02, "\x00\x00\x00\x00\x01\x00"); //9F1A:(Terminal Country Code) len:2 @@ -444,6 +476,7 @@ int CmdHFEMVExec(const char *cmd) { PrintAndLog("\n* GPO."); res = EMVGPO(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); if (res) { @@ -544,13 +577,27 @@ int CmdHFEMVExec(const char *cmd) { break; } - // transaction check + // get AIP const struct tlv *AIPtlv = tlvdb_get(tlvRoot, 0x82, NULL); uint16_t AIP = AIPtlv->value[0] + AIPtlv->value[1] * 0x100; - PrintAndLog("* * AIP=%x", AIP); + PrintAndLog("* * AIP=%04x", AIP); + // SDA + if (AIP & 0x0040) { + PrintAndLog("\n* SDA"); + trSDA(AID, AIDlen, tlvRoot); + } + + // DDA + if (AIP & 0x0020) { + PrintAndLog("\n* DDA"); + + } + + // transaction check + // qVSDC - { + if (TrType == TT_QVSDCMCHIP|| TrType == TT_CDA){ // 9F26: Application Cryptogram const struct tlv *AC = tlvdb_get(tlvRoot, 0x9F26, NULL); if (AC) { @@ -587,19 +634,88 @@ int CmdHFEMVExec(const char *cmd) { } } - // TODO: Mastercard M/CHIP - { + // Mastercard M/CHIP + if (GetCardPSVendor(AID, AIDlen) == CV_MASTERCARD && (TrType == TT_QVSDCMCHIP || TrType == TT_CDA)){ const struct tlv *CDOL1 = tlvdb_get(tlvRoot, 0x8c, NULL); if (CDOL1 && GetCardPSVendor(AID, AIDlen) == CV_MASTERCARD) { // and m/chip transaction flag + PrintAndLog("\n--> Mastercard M/Chip transaction."); + + PrintAndLog("* * Generate challenge"); + res = EMVGenerateChallenge(true, buf, sizeof(buf), &len, &sw, tlvRoot); + if (res) { + PrintAndLog("ERROR GetChallenge. APDU error %4x", sw); + return 5; + } + if (len < 4) { + PrintAndLog("ERROR GetChallenge. Wrong challenge length %d", len); + return 5; + } + + // ICC Dynamic Number + struct tlvdb * ICCDynN = tlvdb_fixed(0x9f4c, len, buf); + tlvdb_add(tlvRoot, ICCDynN); + if (decodeTLV){ + PrintAndLog("\n* * ICC Dynamic Number:"); + TLVPrintFromTLV(ICCDynN); + } + + PrintAndLog("* * Calc CDOL1"); + struct tlv *cdol_data_tlv = dol_process(tlvdb_get(tlvRoot, 0x8c, NULL), tlvRoot, 0x01); // 0x01 - dummy tag + if (!cdol_data_tlv){ + PrintAndLog("ERROR: can't create CDOL1 TLV."); + return 4; + } + PrintAndLog("CDOL1 data[%d]: %s", cdol_data_tlv->len, sprint_hex(cdol_data_tlv->value, cdol_data_tlv->len)); + + PrintAndLog("* * AC1"); + // 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); + + free(cdol_data_tlv); + + if (res) { + PrintAndLog("AC1 error(%d): %4x. Exit...", res, sw); + return 5; + } + + if (decodeTLV) + TLVPrintFromBuffer(buf, len); + + PrintAndLog("* M/Chip transaction result:"); + // 9F27: Cryptogram Information Data (CID) + const struct tlv *CID = tlvdb_get(tlvRoot, 0x9F27, NULL); + if (CID) { + emv_tag_dump(CID, stdout, 0); + PrintAndLog("------------------------------"); + if (CID->len > 0) { + switch(CID->value[0] & EMVAC_AC_MASK){ + case EMVAC_AAC: + PrintAndLog("Transaction DECLINED."); + break; + case EMVAC_TC: + PrintAndLog("Transaction approved OFFLINE."); + break; + case EMVAC_ARQC: + PrintAndLog("Transaction approved ONLINE."); + break; + default: + PrintAndLog("ERROR: CID transaction code error %2x", CID->value[0] & EMVAC_AC_MASK); + break; + } + } else { + PrintAndLog("ERROR: Wrong CID length %d", CID->len); + } + } else { + PrintAndLog("ERROR: CID(9F27) not found."); + } } } // MSD - if (AIP & 0x8000) { + if (AIP & 0x8000 && TrType == TT_MSD) { PrintAndLog("\n--> MSD transaction."); - PrintAndLog("* MSD dCVV path. Check dCVV"); const struct tlv *track2 = tlvdb_get(tlvRoot, 0x57, NULL); @@ -624,7 +740,7 @@ int CmdHFEMVExec(const char *cmd) { if (!UDOL) PrintAndLog("Use default UDOL."); - struct tlv *udol_data_tlv = dol_process(UDOL ? UDOL : &defUDOL, tlvRoot, 0x01); // 0x01 - fake tag! + struct tlv *udol_data_tlv = dol_process(UDOL ? UDOL : &defUDOL, tlvRoot, 0x01); // 0x01 - dummy tag if (!udol_data_tlv){ PrintAndLog("ERROR: can't create UDOL TLV."); return 4; @@ -651,9 +767,6 @@ int CmdHFEMVExec(const char *cmd) { } } - // additional contacless EMV commands (fDDA, external authenticate) - - // DropField DropField(); diff --git a/client/emv/emv_tags.c b/client/emv/emv_tags.c index a69d7196..1c5cbb9d 100644 --- a/client/emv/emv_tags.c +++ b/client/emv/emv_tags.c @@ -35,6 +35,7 @@ enum emv_tag_t { EMV_TAG_NUMERIC, EMV_TAG_YYMMDD, EMV_TAG_CVR, + EMV_TAG_CID, }; struct emv_tag { @@ -59,7 +60,7 @@ static const struct emv_tag_bit EMV_AIP[] = { { EMV_BIT(1, 4), "Terminal risk management is to be performed" }, { EMV_BIT(1, 3), "Issuer authentication is supported" }, { EMV_BIT(1, 2), "Reserved for use by the EMV Contactless Specifications" }, - { EMV_BIT(1, 1), "CDA supported" }, + { EMV_BIT(1, 1), "CDA supported (Combined Dynamic Data Authentication / Application Cryptogram Generation)" }, { EMV_BIT(2, 8), "MSD is supported (Magnetic Stripe Data)" }, { EMV_BIT(2, 7), "Reserved for use by the EMV Contactless Specifications" }, { EMV_BIT(2, 6), "Reserved for use by the EMV Contactless Specifications" }, @@ -233,7 +234,7 @@ static const struct emv_tag emv_tags[] = { { 0x9f1f, "Track 1 Discretionary Data", EMV_TAG_STRING }, { 0x9f21, "Transaction Time" }, { 0x9f26, "Application Cryptogram" }, - { 0x9f27, "Cryptogram Information Data" }, + { 0x9f27, "Cryptogram Information Data", EMV_TAG_CID }, { 0x9f2a, "Kernel Identifier" }, { 0x9f2d, "ICC PIN Encipherment Public Key Certificate" }, { 0x9f2e, "ICC PIN Encipherment Public Key Exponent" }, @@ -471,6 +472,50 @@ static void emv_tag_dump_cvr(const struct tlv *tlv, const struct emv_tag *tag, F return; } +// EMV Book 3 +static void emv_tag_dump_cid(const struct tlv *tlv, const struct emv_tag *tag, FILE *f, int level) { + if (!tlv || tlv->len < 1) { + PRINT_INDENT(level); + fprintf(f, "\tINVALID!\n"); + return; + } + + PRINT_INDENT(level); + if ((tlv->value[0] & 0xC0) == 0x00) fprintf(f, "\tAC1: AAC (Transaction declined)\n"); + if ((tlv->value[0] & 0xC0) == 0x40) fprintf(f, "\tAC1: TC (Transaction approved)\n"); + if ((tlv->value[0] & 0xC0) == 0x80) fprintf(f, "\tAC1: ARQC (Online authorisation requested)\n"); + if ((tlv->value[0] & 0xC0) == 0xC0) fprintf(f, "\tAC1: RFU\n"); + + if ((tlv->value[0] & 0x08) != 0x00) { + PRINT_INDENT(level); + fprintf(f, "\tAdvice required!\n"); + } + + if ((tlv->value[0] & 0x07) != 0x00) { + PRINT_INDENT(level); + fprintf(f, "\tReason/advice/referral code: "); + switch((tlv->value[0] & 0x07)) { + case 0: + fprintf(f, "No information given\n"); + break; + case 1: + fprintf(f, "Service not allowed\n"); + break; + case 2: + fprintf(f, "PIN Try Limit exceeded\n"); + break; + case 3: + fprintf(f, "Issuer authentication failed\n"); + break; + default: + fprintf(f, "\tRFU: %2x\n", (tlv->value[0] & 0x07)); + break; + } + } + + return; +} + static void emv_tag_dump_cvm_list(const struct tlv *tlv, const struct emv_tag *tag, FILE *f, int level) { uint32_t X, Y; @@ -628,6 +673,10 @@ bool emv_tag_dump(const struct tlv *tlv, FILE *f, int level) fprintf(f, "\n"); emv_tag_dump_cvr(tlv, tag, f, level); break; + case EMV_TAG_CID: + fprintf(f, "\n"); + emv_tag_dump_cid(tlv, tag, f, level); + break; }; return true; diff --git a/client/emv/emvcore.c b/client/emv/emvcore.c index 825e9bf9..36438413 100644 --- a/client/emv/emvcore.c +++ b/client/emv/emvcore.c @@ -242,13 +242,13 @@ int EMVExchangeEx(bool ActivateField, bool LeaveFieldON, sAPDU apdu, uint8_t *Re // 6 byes + data = INS + CLA + P1 + P2 + Lc + + Le int res = ExchangeAPDU14a(data, 6 + apdu.Lc, ActivateField, LeaveFieldON, Result, (int)MaxResultLen, (int *)ResultLen); - if (APDULogging) - PrintAndLog("<<<< %s", sprint_hex(Result, *ResultLen)); - if (res) { return res; } + if (APDULogging) + PrintAndLog("<<<< %s", sprint_hex(Result, *ResultLen)); + *ResultLen -= 2; isw = Result[*ResultLen] * 0x0100 + Result[*ResultLen + 1]; if (sw) @@ -467,3 +467,29 @@ int EMVGenerateChallenge(bool LeaveFieldON, uint8_t *Result, size_t MaxResultLen 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) { return EMVExchange(LeaveFieldON, (sAPDU){0x80, 0x2a, 0x8e, 0x80, UDOLlen, UDOL}, Result, MaxResultLen, ResultLen, sw, tlv); } + +// Authentication +int trSDA(uint8_t *AID, size_t AIDlen, struct tlvdb *tlv) { + if (AIDlen < 5) + return 1; + + // Get public key index (0x8F) + //int PubKeyIndx = 0; + + // Get public key from key storage + // GetPublicKey(AID(0..5), PubKeyIndx) + + // Processing of Issuer Public Key Certificate (0x90) + //Certificate = + + // check issuer public key certificate + + // Verification of Signed Static Application Data (SSAD) (0x93) + + // get 9F4A Static Data Authentication Tag List + + // set Data Auth Code (9F45) from SSAD + + return 0; +} + diff --git a/client/emv/emvcore.h b/client/emv/emvcore.h index 03d5c956..94c5d9b0 100644 --- a/client/emv/emvcore.h +++ b/client/emv/emvcore.h @@ -29,6 +29,20 @@ #define APDU_RES_LEN 260 #define APDU_AID_LEN 50 +// AC +# define EMVAC_AC_MASK 0xC0 +# define EMVAC_AAC 0x00 +# define EMVAC_TC 0x40 +# define EMVAC_ARQC 0x80 +# define EMVAC_CDAREQ 0x10 + +enum TransactionType { + TT_MSD, + TT_VSDC, // not standart for contactless!!!! + TT_QVSDCMCHIP, + TT_CDA, +}; + typedef struct { uint8_t CLA; uint8_t INS; @@ -69,8 +83,13 @@ extern int EMVSelectApplication(struct tlvdb *tlv, uint8_t *AID, size_t *AIDlen) // 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 EMVReadRecord(bool LeaveFieldON, uint8_t SFI, uint8_t SFIrec, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv); +// AC +extern int EMVGenerateChallenge(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); // 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); +// Auth +extern int trSDA(uint8_t *AID, size_t AIDlen, struct tlvdb *tlv); #endif