X-Git-Url: http://cvs.zerfleddert.de/cgi-bin/gitweb.cgi/proxmark3-svn/blobdiff_plain/3c5fce2ba7d49f3ebea05eed187a9b5ee8189803..818efbebb87ec5485fbf367021dca42514dfdee0:/client/emv/cmdemv.c

diff --git a/client/emv/cmdemv.c b/client/emv/cmdemv.c
index 3c3c1f11..c53b02af 100644
--- a/client/emv/cmdemv.c
+++ b/client/emv/cmdemv.c
@@ -8,7 +8,9 @@
 // EMV commands
 //-----------------------------------------------------------------------------
 
+#include <ctype.h>
 #include "cmdemv.h"
+#include "test/cryptotest.h"
 
 int UsageCmdHFEMVSelect(void) {
 	PrintAndLog("HELP :  Executes select applet command:\n");
@@ -68,7 +70,7 @@ int CmdHFEMVSelect(const char *cmd) {
 					return 1;
 		}
 
-		if (isxdigit(c)) {
+		if (isxdigit((unsigned char)c)) {
 			switch(param_gethex_to_eol(cmd, cmdp, data, sizeof(data), &datalen)) {
 			case 1:
 				PrintAndLog("Invalid HEX value.");
@@ -277,34 +279,48 @@ int CmdHFEMVPPSE(const char *cmd) {
 
 int UsageCmdHFEMVExec(void) {
 	PrintAndLog("HELP :  Executes EMV contactless transaction:\n");
-	PrintAndLog("Usage:  hf emv exec [-s][-a][-t]\n");
+	PrintAndLog("Usage:  hf emv exec [-s][-a][-t][-f][-v][-c][-x][-g]\n");
 	PrintAndLog("  Options:");
 	PrintAndLog("  -s       : select card");
 	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("  -g       : VISA. generate AC from GPO\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");
+	PrintAndLog(" hf emv exec -s -a -t -> execute MSD transaction");
+	PrintAndLog(" hf emv exec -s -a -t -c -> execute CDA transaction");
 	return 0;
 }
 
 #define TLV_ADD(tag, value)( tlvdb_add(tlvRoot, tlvdb_fixed(tag, sizeof(value) - 1, (const unsigned char *)value)) )
+#define dreturn(n) {free(pdol_data_tlv);tlvdb_free(tlvSelect);tlvdb_free(tlvRoot);DropField();return n;}
 
 int CmdHFEMVExec(const char *cmd) {
 	bool activateField = false;
 	bool showAPDU = false;
 	bool decodeTLV = false;
 	bool forceSearch = false;
+	enum TransactionType TrType = TT_MSD;
+	bool GenACGPO = false;
 
 	uint8_t buf[APDU_RES_LEN] = {0};
 	size_t len = 0;
 	uint16_t sw = 0;
 	uint8_t AID[APDU_AID_LEN] = {0};
 	size_t AIDlen = 0;
+	uint8_t ODAiList[4096];
+	size_t ODAiListLen = 0;
 	
 	int res;
 	
+	struct tlvdb *tlvSelect = NULL;
+	struct tlvdb *tlvRoot = NULL;
+	struct tlv *pdol_data_tlv = NULL;
+
 	if (strlen(cmd) < 1) {
 		UsageCmdHFEMVExec();
 		return 0;
@@ -317,7 +333,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 +351,22 @@ 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;
+				case 'g':
+				case 'G':
+					GenACGPO = true;
+					break;
 				default:
 					PrintAndLog("Unknown parameter '%c'", param_getchar_indx(cmd, 1, cmdp));
 					return 1;
@@ -344,7 +376,6 @@ int CmdHFEMVExec(const char *cmd) {
 
 	
 	// init applets list tree
-	struct tlvdb *tlvSelect = NULL;
 	const char *al = "Applets list";
 	tlvSelect = tlvdb_fixed(1, strlen(al), (const unsigned char *)al);
 
@@ -368,8 +399,7 @@ int CmdHFEMVExec(const char *cmd) {
 		PrintAndLog("\n* Search AID in list.");
 		SetAPDULogging(false);
 		if (EMVSearch(activateField, true, decodeTLV, tlvSelect)) {
-			tlvdb_free(tlvSelect);
-			return 2;
+			dreturn(2);
 		}
 
 		// check search and select application id
@@ -378,14 +408,13 @@ int CmdHFEMVExec(const char *cmd) {
 	}
 	
 	// Init TLV tree
-	struct tlvdb *tlvRoot = NULL;
 	const char *alr = "Root terminal TLV tree";
 	tlvRoot = tlvdb_fixed(1, strlen(alr), (const unsigned char *)alr);
 	
 	// check if we found EMV application on card
 	if (!AIDlen) {
 		PrintAndLog("Can't select AID. EMV AID not found");
-		return 2;
+		dreturn(2);
 	}
 	
 	// Select
@@ -395,20 +424,40 @@ int CmdHFEMVExec(const char *cmd) {
 	
 	if (res) {	
 		PrintAndLog("Can't select AID (%d). Exit...", res);
-		return 3;
+		dreturn(3);
 	}
 	
 	if (decodeTLV)
 		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
-    //9F02:(Amount, Authorised (Numeric)) len:6
+	char *qVSDC = "\x26\x00\x00\x00";
+	if (GenACGPO) {
+		qVSDC = "\x26\x80\x00\x00";
+	}
+	switch(TrType) {
+		case TT_MSD:
+			TLV_ADD(0x9F66, "\x86\x00\x00\x00"); // MSD
+			break;
+		// not standard for contactless. just for test.
+		case TT_VSDC:  
+			TLV_ADD(0x9F66, "\x46\x00\x00\x00"); // VSDC
+			break;
+		case TT_QVSDCMCHIP:
+			TLV_ADD(0x9F66, qVSDC); // qVSDC
+			break;
+		case TT_CDA:
+			TLV_ADD(0x9F66, qVSDC); // qVSDC (VISA CDA not enabled)
+			break;
+		default:
+			TLV_ADD(0x9F66, "\x26\x00\x00\x00"); // qVSDC
+			break;
+	}
+	
+    //9F02:(Amount, authorized (Numeric)) len:6
 	TLV_ADD(0x9F02, "\x00\x00\x00\x00\x01\x00");
     //9F1A:(Terminal Country Code) len:2
 	TLV_ADD(0x9F1A, "ru");
@@ -421,53 +470,82 @@ 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);
+	pdol_data_tlv = dol_process(tlvdb_get(tlvRoot, 0x9f38, NULL), tlvRoot, 0x83);
 	if (!pdol_data_tlv){
 		PrintAndLog("ERROR: can't create PDOL TLV.");
-		return 4;
+		dreturn(4);
 	}
 	
 	size_t pdol_data_tlv_data_len;
 	unsigned char *pdol_data_tlv_data = tlv_encode(pdol_data_tlv, &pdol_data_tlv_data_len);
 	if (!pdol_data_tlv_data) {
 		PrintAndLog("ERROR: can't create PDOL data.");
-		return 4;
+		dreturn(4);
 	}
 	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);
+	free(pdol_data_tlv_data);
+	//free(pdol_data_tlv); --- free on exit.
 	
 	if (res) {	
 		PrintAndLog("GPO error(%d): %4x. Exit...", res, sw);
-		return 5;
+		dreturn(5);
 	}
 
 	// 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) {
@@ -506,9 +584,23 @@ return 0;
 					PrintAndLog("");
 				}
 				
+				// Build Input list for Offline Data Authentication
+				// EMV 4.3 book3 10.3, page 96
 				if (SFIoffline) {
-					// here will be offline records storing...
-					// dont foget: if (sfi < 11)
+					if (SFI < 11) {
+						const unsigned char *abuf = buf;
+						size_t elmlen = len;
+						struct tlv e;
+						if (tlv_parse_tl(&abuf, &elmlen, &e)) {
+							memcpy(&ODAiList[ODAiListLen], &buf[len - elmlen], elmlen);
+							ODAiListLen += elmlen;
+						} else {
+							PrintAndLog("ERROR SFI[%02x]. Creating input list for Offline Data Authentication error.", SFI);
+						}
+					} else {
+						memcpy(&ODAiList[ODAiListLen], buf, len);
+						ODAiListLen += len;
+					}
 				}
 			}
 		}
@@ -516,13 +608,218 @@ return 0;
 		break;
 	}	
 	
-	// additional contacless EMV commands (fDDA, CDA, external authenticate)
+	// copy Input list for Offline Data Authentication
+	if (ODAiListLen) {
+		struct tlvdb *oda = tlvdb_fixed(0x21, ODAiListLen, ODAiList); // not a standard tag
+		tlvdb_add(tlvRoot, oda); 
+		PrintAndLog("* Input list for Offline Data Authentication added to TLV. len=%d \n", ODAiListLen);
+	}
+	
+	// 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(tlvRoot);
+	}
+
+	// DDA
+	if (AIP & 0x0020) {
+		PrintAndLog("\n* DDA");
+		trDDA(decodeTLV, tlvRoot);
+	}	
 	
+	// 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);
+				dreturn(6);
+			}
+			if (len < 4) {
+				PrintAndLog("ERROR GetChallenge. Wrong challenge length %d", len);
+				dreturn(6);
+			}
+			
+			// 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.");
+				dreturn(6);
+			}
+			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);
+			
+			if (res) {	
+				PrintAndLog("AC1 error(%d): %4x. Exit...", res, sw);
+				dreturn(7);
+			}
+			
+			if (decodeTLV)
+				TLVPrintFromBuffer(buf, len);
+			
+			// CDA
+			PrintAndLog("\n* CDA:");
+			struct tlvdb *ac_tlv = tlvdb_parse_multi(buf, len);
+			res = trCDA(tlvRoot, ac_tlv, pdol_data_tlv, cdol_data_tlv);
+			if (res) {	
+				PrintAndLog("CDA error (%d)", res);
+			}
+			free(ac_tlv);
+			free(cdol_data_tlv);
+			
+			PrintAndLog("\n* 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.");
+					dreturn(8);
+				}
+
+				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);
+					free(udol_data_tlv);
+					dreturn(9);
+				}
+				
+				if (decodeTLV) {
+					TLVPrintFromBuffer(buf, len);
+					PrintAndLog("");
+				}
+				free(udol_data_tlv);
+
+			}
+		} else {
+			PrintAndLog("ERROR MSD: Track2 data not found.");
+		}
+	}
+
 	// DropField
 	DropField();
 	
 	// Destroy TLV's
+	free(pdol_data_tlv);
 	tlvdb_free(tlvSelect);
 	tlvdb_free(tlvRoot);
 
@@ -531,6 +828,10 @@ return 0;
 	return 0;
 }
 
+int CmdHFEMVTest(const char *cmd) {
+	return ExecuteCryptoTests(true);
+}
+
 int CmdHelp(const char *Cmd);
 static command_t CommandTable[] =  {
 	{"help",	CmdHelp,		1,	"This help"},
@@ -538,6 +839,7 @@ static command_t CommandTable[] =  {
 	{"pse",		CmdHFEMVPPSE,	0,	"Execute PPSE. It selects 2PAY.SYS.DDF01 or 1PAY.SYS.DDF01 directory."},
 	{"search",	CmdHFEMVSearch,	0,	"Try to select all applets from applets list and print installed applets."},
 	{"select",	CmdHFEMVSelect,	0,	"Select applet."},
+	{"test",	CmdHFEMVTest,	0,	"Crypto logic test."},
 	{NULL, NULL, 0, NULL}
 };