X-Git-Url: http://git.zerfleddert.de/cgi-bin/gitweb.cgi/proxmark3-svn/blobdiff_plain/3c5fce2ba7d49f3ebea05eed187a9b5ee8189803..7a7afeba5cbbb518216d0d1d5bb5228a41a4db17:/client/emv/cmdemv.c diff --git a/client/emv/cmdemv.c b/client/emv/cmdemv.c index 3c3c1f11..92ae01f9 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; @@ -317,7 +322,7 @@ int CmdHFEMVExec(const char *cmd) { switch (param_getchar_indx(cmd, 1, cmdp)) { case 'h': case 'H': - UsageCmdHFEMVPPSE(); + UsageCmdHFEMVExec(); return 0; case 's': case 'S': @@ -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; @@ -402,12 +419,27 @@ int CmdHFEMVExec(const char *cmd) { TLVPrintFromBuffer(buf, len); PrintAndLog("* Selected."); -PrintAndLog("-----BREAK."); -return 0; PrintAndLog("\n* Init transaction parameters."); //9F66:(Terminal Transaction Qualifiers (TTQ)) len:4 - TLV_ADD(0x9F66, "\x26\x00\x00\x00"); // E6 + 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 @@ -421,8 +453,10 @@ return 0; TLV_ADD(0x9C, "\x00"); // 9F37 Unpredictable Number len:4 TLV_ADD(0x9F37, "\x01\x02\x03\x04"); + // 9F6A Unpredictable Number (MSD for UDOL) len:4 + TLV_ADD(0x9F6A, "\x01\x02\x03\x04"); - TLVPrintFromTLV(tlvRoot); + TLVPrintFromTLV(tlvRoot); // TODO delete!!! PrintAndLog("\n* Calc PDOL."); struct tlv *pdol_data_tlv = dol_process(tlvdb_get(tlvRoot, 0x9f38, NULL), tlvRoot, 0x83); @@ -439,11 +473,10 @@ return 0; } PrintAndLog("PDOL data[%d]: %s", pdol_data_tlv_data_len, sprint_hex(pdol_data_tlv_data, pdol_data_tlv_data_len)); -//PrintAndLog("-----BREAK."); -//return 0; 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) { @@ -453,21 +486,49 @@ return 0; // process response template format 1 [id:80 2b AIP + x4b AFL] and format 2 [id:77 TLV] if (buf[0] == 0x80) { - - - if (decodeTLV){ PrintAndLog("GPO response format1:"); TLVPrintFromBuffer(buf, len); } - } else { - - + if (len < 4 || (len - 4) % 4) { + PrintAndLog("ERROR: GPO response format1 parsing error. length=%d", len); + } else { + // AIP + struct tlvdb * f1AIP = tlvdb_fixed(0x82, 2, buf + 2); + tlvdb_add(tlvRoot, f1AIP); + if (decodeTLV){ + PrintAndLog("\n* * Decode response format 1 (0x80) AIP and AFL:"); + TLVPrintFromTLV(f1AIP); + } + + // AFL + struct tlvdb * f1AFL = tlvdb_fixed(0x94, len - 4, buf + 2 + 2); + tlvdb_add(tlvRoot, f1AFL); + if (decodeTLV) + TLVPrintFromTLV(f1AFL); + } + } else { if (decodeTLV) TLVPrintFromBuffer(buf, len); } + // extract PAN from track2 + { + const struct tlv *track2 = tlvdb_get(tlvRoot, 0x57, NULL); + if (!tlvdb_get(tlvRoot, 0x5a, NULL) && track2 && track2->len >= 8) { + struct tlvdb *pan = GetPANFromTrack2(track2); + if (pan) { + tlvdb_add(tlvRoot, pan); + + const struct tlv *pantlv = tlvdb_get(tlvRoot, 0x5a, NULL); + PrintAndLog("\n* * Extracted PAN from track2: %s", sprint_hex(pantlv->value, pantlv->len)); + } else { + PrintAndLog("\n* * WARNING: Can't extract PAN from track2."); + } + } + } + PrintAndLog("\n* Read records from AFL."); const struct tlv *AFL = tlvdb_get(tlvRoot, 0x94, NULL); if (!AFL || !AFL->len) { @@ -516,8 +577,195 @@ return 0; break; } - // additional contacless EMV commands (fDDA, CDA, external authenticate) + // get AIP + const struct tlv *AIPtlv = tlvdb_get(tlvRoot, 0x82, NULL); + uint16_t AIP = AIPtlv->value[0] + AIPtlv->value[1] * 0x100; + 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) { + PrintAndLog("\n--> qVSDC transaction."); + PrintAndLog("* AC path"); + + // 9F36: Application Transaction Counter (ATC) + const struct tlv *ATC = tlvdb_get(tlvRoot, 0x9F36, NULL); + if (ATC) { + + // 9F10: Issuer Application Data - optional + const struct tlv *IAD = tlvdb_get(tlvRoot, 0x9F10, NULL); + + // print AC data + PrintAndLog("ATC: %s", sprint_hex(ATC->value, ATC->len)); + PrintAndLog("AC: %s", sprint_hex(AC->value, AC->len)); + if (IAD){ + PrintAndLog("IAD: %s", sprint_hex(IAD->value, IAD->len)); + + if (IAD->len >= IAD->value[0] + 1) { + PrintAndLog("\tKey index: 0x%02x", IAD->value[1]); + PrintAndLog("\tCrypto ver: 0x%02x(%03d)", IAD->value[2], IAD->value[2]); + PrintAndLog("\tCVR:", sprint_hex(&IAD->value[3], IAD->value[0] - 2)); + struct tlvdb * cvr = tlvdb_fixed(0x20, IAD->value[0] - 2, &IAD->value[3]); + TLVPrintFromTLVLev(cvr, 1); + } + } else { + PrintAndLog("WARNING: IAD not found."); + } + + } else { + PrintAndLog("ERROR AC: Application Transaction Counter (ATC) not found."); + } + } + } + // 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 && TrType == TT_MSD) { + PrintAndLog("\n--> MSD transaction."); + + PrintAndLog("* MSD dCVV path. Check dCVV"); + + const struct tlv *track2 = tlvdb_get(tlvRoot, 0x57, NULL); + if (track2) { + PrintAndLog("Track2: %s", sprint_hex(track2->value, track2->len)); + + struct tlvdb *dCVV = GetdCVVRawFromTrack2(track2); + PrintAndLog("dCVV raw data:"); + TLVPrintFromTLV(dCVV); + + if (GetCardPSVendor(AID, AIDlen) == CV_MASTERCARD) { + PrintAndLog("\n* Mastercard calculate UDOL"); + + // UDOL (9F69) + const struct tlv *UDOL = tlvdb_get(tlvRoot, 0x9F69, NULL); + // UDOL(9F69) default: 9F6A (Unpredictable number) 4 bytes + const struct tlv defUDOL = { + .tag = 0x01, + .len = 3, + .value = (uint8_t *)"\x9f\x6a\x04", + }; + if (!UDOL) + PrintAndLog("Use default UDOL."); + + 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; + } + + PrintAndLog("UDOL data[%d]: %s", udol_data_tlv->len, sprint_hex(udol_data_tlv->value, udol_data_tlv->len)); + + PrintAndLog("\n* Mastercard compute cryptographic checksum(UDOL)"); + + res = MSCComputeCryptoChecksum(true, (uint8_t *)udol_data_tlv->value, udol_data_tlv->len, buf, sizeof(buf), &len, &sw, tlvRoot); + if (res) { + PrintAndLog("ERROR Compute Crypto Checksum. APDU error %4x", sw); + return 5; + } + + if (decodeTLV) { + TLVPrintFromBuffer(buf, len); + PrintAndLog(""); + } + + } + } else { + PrintAndLog("ERROR MSD: Track2 data not found."); + } + } // DropField DropField();