From 10d4f823385cda5adfc8a3e0e07b761447aa5ec6 Mon Sep 17 00:00:00 2001
From: merlokk <olegmsn@gmail.com>
Date: Sat, 2 Dec 2017 21:47:30 +0200
Subject: [PATCH 1/1] transactions MSD and M/Chip, qVSDC works

---
 client/emv/cmdemv.c   | 141 +++++++++++++++++++++++++++++++++++++-----
 client/emv/emv_tags.c |  53 +++++++++++++++-
 client/emv/emvcore.c  |  32 +++++++++-
 client/emv/emvcore.h  |  19 ++++++
 4 files changed, 226 insertions(+), 19 deletions(-)

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 + <data = Nc> + 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
 
-- 
2.39.5