X-Git-Url: http://cvs.zerfleddert.de/cgi-bin/gitweb.cgi/proxmark3-svn/blobdiff_plain/137dd5826ed8d69b8eb00f1a6bcdbbecf6b072e2..385c1a5ebc90f134d9856f491065476e8ef44741:/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();