X-Git-Url: https://git.zerfleddert.de/cgi-bin/gitweb.cgi/proxmark3-svn/blobdiff_plain/385e4b501932c96cd37bc3106b6237f34c404e0f..a31f7f899b6d62c94bc236fce4330d6bbb8996e7:/client/emv/cmdemv.c?ds=inline

diff --git a/client/emv/cmdemv.c b/client/emv/cmdemv.c
index 92ae01f9..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,7 +279,7 @@ 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");
@@ -286,14 +288,16 @@ int UsageCmdHFEMVExec(void) {
 	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;
@@ -301,15 +305,22 @@ int CmdHFEMVExec(const char *cmd) {
 	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;
@@ -352,6 +363,10 @@ int CmdHFEMVExec(const char *cmd) {
 				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;
@@ -361,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);
 
@@ -385,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
@@ -395,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
@@ -412,7 +424,7 @@ int CmdHFEMVExec(const char *cmd) {
 	
 	if (res) {	
 		PrintAndLog("Can't select AID (%d). Exit...", res);
-		return 3;
+		dreturn(3);
 	}
 	
 	if (decodeTLV)
@@ -422,25 +434,30 @@ int CmdHFEMVExec(const char *cmd) {
 	PrintAndLog("\n* Init transaction parameters.");
 
     //9F66:(Terminal Transaction Qualifiers (TTQ)) len:4
+	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 standart for contactless. just for test.
+		// 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, "\x26\x00\x00\x00"); // qVSDC
+			TLV_ADD(0x9F66, qVSDC); // qVSDC
 			break;
 		case TT_CDA:
-			TLV_ADD(0x9F66, "\x86\x80\x00\x00"); // CDA
+			TLV_ADD(0x9F66, qVSDC); // qVSDC (VISA CDA not enabled)
 			break;
 		default:
 			TLV_ADD(0x9F66, "\x26\x00\x00\x00"); // qVSDC
 			break;
 	}
-    //9F02:(Amount, Authorised (Numeric)) len:6
+	
+    //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");
@@ -459,17 +476,17 @@ int CmdHFEMVExec(const char *cmd) {
 	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));
 
@@ -477,11 +494,11 @@ int CmdHFEMVExec(const char *cmd) {
 	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);
+	//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]
@@ -567,9 +584,23 @@ int CmdHFEMVExec(const char *cmd) {
 					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;
+					}
 				}
 			}
 		}
@@ -577,6 +608,13 @@ int CmdHFEMVExec(const char *cmd) {
 		break;
 	}	
 	
+	// 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;
@@ -585,13 +623,13 @@ int CmdHFEMVExec(const char *cmd) {
 	// SDA
 	if (AIP & 0x0040) {
 		PrintAndLog("\n* SDA");
-		trSDA(AID, AIDlen, tlvRoot);
+		trSDA(tlvRoot);
 	}
 
 	// DDA
 	if (AIP & 0x0020) {
 		PrintAndLog("\n* DDA");
-		
+		trDDA(decodeTLV, tlvRoot);
 	}	
 	
 	// transaction check
@@ -644,11 +682,11 @@ int CmdHFEMVExec(const char *cmd) {
 			res = EMVGenerateChallenge(true, buf, sizeof(buf), &len, &sw, tlvRoot);
 			if (res) {
 				PrintAndLog("ERROR GetChallenge. APDU error %4x", sw);
-				return 5;
+				dreturn(6);
 			}
 			if (len < 4) {
 				PrintAndLog("ERROR GetChallenge. Wrong challenge length %d", len);
-				return 5;
+				dreturn(6);
 			}
 			
 			// ICC Dynamic Number
@@ -663,7 +701,7 @@ int CmdHFEMVExec(const char *cmd) {
 			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;
+				dreturn(6);
 			}
 			PrintAndLog("CDOL1 data[%d]: %s", cdol_data_tlv->len, sprint_hex(cdol_data_tlv->value, cdol_data_tlv->len));
 			
@@ -671,17 +709,25 @@ int CmdHFEMVExec(const char *cmd) {
 			// 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;
+				dreturn(7);
 			}
 			
 			if (decodeTLV)
 				TLVPrintFromBuffer(buf, len);
 			
-			PrintAndLog("* M/Chip transaction result:");
+			// 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) {
@@ -743,7 +789,7 @@ int CmdHFEMVExec(const char *cmd) {
 				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;
+					dreturn(8);
 				}
 
 				PrintAndLog("UDOL data[%d]: %s", udol_data_tlv->len, sprint_hex(udol_data_tlv->value, udol_data_tlv->len));
@@ -753,24 +799,27 @@ int CmdHFEMVExec(const char *cmd) {
 				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;
+					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);
 
@@ -779,6 +828,10 @@ int CmdHFEMVExec(const char *cmd) {
 	return 0;
 }
 
+int CmdHFEMVTest(const char *cmd) {
+	return ExecuteCryptoTests(true);
+}
+
 int CmdHelp(const char *Cmd);
 static command_t CommandTable[] =  {
 	{"help",	CmdHelp,		1,	"This help"},
@@ -786,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}
 };