From ae3340a0fb04bf8f8b8c874e0859d7d0886e2db1 Mon Sep 17 00:00:00 2001
From: Oleg Moiseenko <807634+merlokk@users.noreply.github.com>
Date: Wed, 24 Oct 2018 19:58:12 +0300
Subject: [PATCH] Mfp commands (#698)

* add write perso from https://github.com/Proxmark/proxmark3/blob/master/client/scripts/mifarePlus.lua
* commit perso from https://github.com/Proxmark/proxmark3/blob/master/client/scripts/mifarePlus.lua#L184
* added errors https://github.com/Proxmark/proxmark3/blob/master/client/scripts/mifarePlus.lua#L91
* fix bug in CLIParamHexToBuf
* added init card command
* auth4 refactoring
* add changelog
---
 CHANGELOG.md                 |   1 +
 client/Makefile              |   2 +
 client/cliparser/cliparser.c |   2 +-
 client/cliparser/cliparser.h |   1 +
 client/cmdhf14a.c            |  10 +-
 client/cmdhfmf.c             | 114 +------------
 client/cmdhfmfp.c            | 302 +++++++++++++++++++++++++++++++++++
 client/mifare4.c             | 118 ++++++++++++++
 client/mifare4.h             |  30 ++++
 common/polarssl/libpcrypto.c |  44 +++++
 common/polarssl/libpcrypto.h |  20 +++
 11 files changed, 529 insertions(+), 115 deletions(-)
 create mode 100644 client/mifare4.c
 create mode 100644 client/mifare4.h
 create mode 100644 common/polarssl/libpcrypto.c
 create mode 100644 common/polarssl/libpcrypto.h

diff --git a/CHANGELOG.md b/CHANGELOG.md
index fbc40cda..5f1847e1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac
 ### Fixed
 
 ### Added
+- `hf mfp` group of commands (Merlok)
 
 ## [v3.1.0][2018-10-10]
 
diff --git a/client/Makefile b/client/Makefile
index c0a319a4..72d5080d 100644
--- a/client/Makefile
+++ b/client/Makefile
@@ -110,6 +110,7 @@ CMDSRCS = 	$(SRC_SMARTCARD) \
 			polarssl/bignum.c\
 			polarssl/rsa.c\
 			polarssl/sha1.c\
+			polarssl/libpcrypto.c\
 			cliparser/argtable3.c\
 			cliparser/cliparser.c\
 			mfkey.c\
@@ -120,6 +121,7 @@ CMDSRCS = 	$(SRC_SMARTCARD) \
 			loclass/fileutils.c\
 			whereami.c\
 			mifarehost.c\
+			mifare4.c\
 			parity.c\
 			crc.c \
 			crc16.c \
diff --git a/client/cliparser/cliparser.c b/client/cliparser/cliparser.c
index 931d68cd..95422039 100644
--- a/client/cliparser/cliparser.c
+++ b/client/cliparser/cliparser.c
@@ -156,7 +156,7 @@ int CLIParamHexToBuf(struct arg_str *argstr, uint8_t *data, int maxdatalen, int
 	
 	int ibuf = 0;
 	uint8_t buf[256] = {0};
-	int res = CLIParamStrToBuf(argstr, buf, maxdatalen, &ibuf);
+	int res = CLIParamStrToBuf(argstr, buf, maxdatalen * 2, &ibuf); // *2 because here HEX
 	if (res || !ibuf)
 		return res;
 	
diff --git a/client/cliparser/cliparser.h b/client/cliparser/cliparser.h
index 2f5ac317..c4bc7068 100644
--- a/client/cliparser/cliparser.h
+++ b/client/cliparser/cliparser.h
@@ -17,6 +17,7 @@
 
 #define arg_getsize(a) (sizeof(a) / sizeof(a[0]))
 #define arg_get_lit(n)(((struct arg_lit*)argtable[n])->count)
+#define arg_get_int_count(n)(((struct arg_int*)argtable[n])->count)
 #define arg_get_int(n)(((struct arg_int*)argtable[n])->ival[0])
 #define arg_get_str(n)((struct arg_str*)argtable[n])
 #define arg_get_str_len(n)(strlen(((struct arg_str*)argtable[n])->sval[0]))
diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c
index 31654188..326eaf50 100644
--- a/client/cmdhf14a.c
+++ b/client/cmdhf14a.c
@@ -750,6 +750,11 @@ int ExchangeRAW14a(uint8_t *datain, int datainlen, bool activateField, bool leav
         recv = resp.d.asBytes;
         int iLen = resp.arg[0];
 		
+        if(!iLen) {
+			PrintAndLog("14aRAW ERROR: No card response.");
+            return 1;
+		}
+		
 		*dataoutlen = iLen - 2;
 		if (*dataoutlen < 0)
 			*dataoutlen = 0;
@@ -766,11 +771,6 @@ int ExchangeRAW14a(uint8_t *datain, int datainlen, bool activateField, bool leav
 		
 		memcpy(dataout, &recv[2], *dataoutlen);
 		
-        if(!iLen) {
-			PrintAndLog("14aRAW ERROR: No card response.");
-            return 1;
-		}
-
 		// CRC Check
 		if (iLen == -1) {
 			PrintAndLog("14aRAW ERROR: ISO 14443A CRC error.");
diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c
index eb85b8c5..83180e7d 100644
--- a/client/cmdhfmf.c
+++ b/client/cmdhfmf.c
@@ -29,7 +29,7 @@
 #include "hardnested/hardnested_bf_core.h"
 #include "cliparser/cliparser.h"
 #include "cmdhf14a.h"
-#include <polarssl/aes.h>
+#include "mifare4.h"
 
 #define NESTED_SECTOR_RETRY     10			// how often we try mfested() until we give up
 
@@ -2637,50 +2637,12 @@ int CmdDecryptTraceCmds(const char *Cmd){
 	return tryDecryptWord(param_get32ex(Cmd,0,0,16),param_get32ex(Cmd,1,0,16),param_get32ex(Cmd,2,0,16),data,len/2);
 }
 
-int aes_encode(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *output, int length){
-	uint8_t iiv[16] = {0};
-	if (iv)
-		memcpy(iiv, iv, 16);
-	
-	aes_context aes;
-	aes_init(&aes);
-	if (aes_setkey_enc(&aes, key, 128))
-		return 1;
-	if (aes_crypt_cbc(&aes, AES_ENCRYPT, length, iiv, input, output))
-		return 2;
-	aes_free(&aes);
-
-	return 0;
-}
-
-int aes_decode(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *output, int length){
-	uint8_t iiv[16] = {0};
-	if (iv)
-		memcpy(iiv, iv, 16);
-	
-	aes_context aes;
-	aes_init(&aes);
-	if (aes_setkey_dec(&aes, key, 128))
-		return 1;
-	if (aes_crypt_cbc(&aes, AES_DECRYPT, length, iiv, input, output))
-		return 2;
-	aes_free(&aes);
-
-	return 0;
-}
-
 int CmdHF14AMfAuth4(const char *cmd) {
 	uint8_t keyn[20] = {0};
 	int keynlen = 0;
 	uint8_t key[16] = {0};
 	int keylen = 0;
-	uint8_t data[257] = {0};
-	int datalen = 0;
-	
-	uint8_t Rnd1[17] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00};
-	uint8_t Rnd2[17] = {0};
-	
-	
+
 	CLIParserInit("hf mf auth4", 
 		"Executes AES authentication command in ISO14443-4", 
 		"Usage:\n\thf mf auth4 4000 000102030405060708090a0b0c0d0e0f -> executes authentication\n"
@@ -2694,8 +2656,8 @@ int CmdHF14AMfAuth4(const char *cmd) {
 	};
 	CLIExecWithReturn(cmd, argtable, true);
 	
-	CLIGetStrWithReturn(1, keyn, &keynlen);
-	CLIGetStrWithReturn(2, key, &keylen);
+	CLIGetHexWithReturn(1, keyn, &keynlen);
+	CLIGetHexWithReturn(2, key, &keylen);
 	CLIParserFree();
 	
 	if (keynlen != 2) {
@@ -2708,73 +2670,7 @@ int CmdHF14AMfAuth4(const char *cmd) {
 		return 1;
 	}
 
-	uint8_t cmd1[] = {0x70, keyn[1], keyn[0], 0x00};
-	int res = ExchangeRAW14a(cmd1, sizeof(cmd1), true, true, data, sizeof(data), &datalen);
-	if (res) {
-		PrintAndLog("ERROR exchande raw error: %d", res);
-		DropField();
-		return 2;
-	}
-	
-	PrintAndLog("<phase1: %s", sprint_hex(data, datalen));
-		
-	if (datalen < 1) {
-		PrintAndLog("ERROR: card response length: %d", datalen);
-		DropField();
-		return 3;
-	}
-	
-	if (data[0] != 0x90) {
-		PrintAndLog("ERROR: card response error: %02x", data[2]);
-		DropField();
-		return 3;
-	}
-
-	if (datalen != 19) { // code 1b + 16b + crc 2b
-		PrintAndLog("ERROR: card response must be 19 bytes long instead of: %d", datalen);
-		DropField();
-		return 3;
-	}
-	
-    aes_decode(NULL, key, &data[1], Rnd2, 16);
-	Rnd2[16] = Rnd2[0];
-	PrintAndLog("Rnd2: %s", sprint_hex(Rnd2, 16));
-
-	uint8_t cmd2[33] = {0};
-	cmd2[0] = 0x72;
-
-	uint8_t raw[32] = {0};
-	memmove(raw, Rnd1, 16);
-	memmove(&raw[16], &Rnd2[1], 16);
-
-    aes_encode(NULL, key, raw, &cmd2[1], 32);
-	PrintAndLog(">phase2: %s", sprint_hex(cmd2, 33));
-	
-	res = ExchangeRAW14a(cmd2, sizeof(cmd2), false, false, data, sizeof(data), &datalen);
-	if (res) {
-		PrintAndLog("ERROR exchande raw error: %d", res);
-		DropField();
-		return 4;
-	}
-	
-	PrintAndLog("<phase2: %s", sprint_hex(data, datalen));
-
-    aes_decode(NULL, key, &data[1], raw, 32);
-	PrintAndLog("res: %s", sprint_hex(raw, 32));
-	
-	PrintAndLog("Rnd1`: %s", sprint_hex(&raw[4], 16));
-	if (memcmp(&raw[4], &Rnd1[1], 16)) {
-		PrintAndLog("\nERROR: Authentication FAILED. rnd not equal");
-		PrintAndLog("rnd1 reader: %s", sprint_hex(&Rnd1[1], 16));
-		PrintAndLog("rnd1   card: %s", sprint_hex(&raw[4], 16));
-		DropField();
-		return 5;
-	}
-
-	DropField();
-	PrintAndLog("\nAuthentication OK");
-	
-	return 0;
+	return MifareAuth4(NULL, keyn, key, true, false, true);
 }
 
 static command_t CommandTable[] =
diff --git a/client/cmdhfmfp.c b/client/cmdhfmfp.c
index 34abbed0..a4c53f56 100644
--- a/client/cmdhfmfp.c
+++ b/client/cmdhfmfp.c
@@ -21,9 +21,67 @@
 #include "ui.h"
 #include "cmdhf14a.h"
 #include "mifare.h"
+#include "mifare4.h"
+#include "cliparser/cliparser.h"
+#include "polarssl/libpcrypto.h"
+
+static const uint8_t DefaultKey[16] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+typedef struct {
+	uint8_t Code;
+	const char *Description;
+} PlusErrorsElm;
+
+static const PlusErrorsElm PlusErrors[] = {
+	{0xFF, ""},
+	{0x00, "Unknown error"},
+	{0x09, "Invalid block number"},
+	{0x0b, "Command code error"},
+	{0x0c, "Length error"},
+	{0x90, "OK"},
+};
+int PlusErrorsLen = sizeof(PlusErrors) / sizeof(PlusErrorsElm);
+
+const char * GetErrorDescription(uint8_t errorCode) {
+	for(int i = 0; i < PlusErrorsLen; i++)
+		if (errorCode == PlusErrors[i].Code)
+			return PlusErrors[i].Description;
+		
+	return PlusErrors[0].Description;
+}
 
 static int CmdHelp(const char *Cmd);
 
+static bool VerboseMode = false;
+void SetVerboseMode(bool verbose) {
+	VerboseMode = verbose;
+}
+
+int intExchangeRAW14aPlus(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen) {
+	if(VerboseMode)
+		PrintAndLog(">>> %s", sprint_hex(datain, datainlen));
+	
+	int res = ExchangeRAW14a(datain, datainlen, activateField, leaveSignalON, dataout, maxdataoutlen, dataoutlen);
+
+	if(VerboseMode)
+		PrintAndLog("<<< %s", sprint_hex(dataout, *dataoutlen));
+	
+	return res;
+}
+
+int MFPWritePerso(uint8_t *keyNum, uint8_t *key, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen) {
+	uint8_t rcmd[3 + 16] = {0xa8, keyNum[1], keyNum[0], 0x00};
+	memmove(&rcmd[3], key, 16);
+	
+	return intExchangeRAW14aPlus(rcmd, sizeof(rcmd), activateField, leaveSignalON, dataout, maxdataoutlen, dataoutlen);
+}
+
+int MFPCommitPerso(bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen) {
+	uint8_t rcmd[1] = {0xaa};
+	
+	return intExchangeRAW14aPlus(rcmd, sizeof(rcmd), activateField, leaveSignalON, dataout, maxdataoutlen, dataoutlen);
+}
+
 int CmdHFMFPInfo(const char *cmd) {
 	
 	if (cmd && strlen(cmd) > 0)
@@ -103,10 +161,254 @@ int CmdHFMFPInfo(const char *cmd) {
 	return 0;
 }
 
+int CmdHFMFPWritePerso(const char *cmd) {
+	uint8_t keyNum[64] = {0};
+	int keyNumLen = 0;
+	uint8_t key[64] = {0};
+	int keyLen = 0;
+
+	CLIParserInit("hf mfp wrp", 
+		"Executes Write Perso command. Can be used in SL0 mode only.", 
+		"Usage:\n\thf mfp wrp 4000 000102030405060708090a0b0c0d0e0f -> write key (00..0f) to key number 4000 \n"
+			"\thf mfp wrp 4000 -> write default key(0xff..0xff) to key number 4000");
+
+	void* argtable[] = {
+		arg_param_begin,
+		arg_lit0("vV",  "verbose", "show internal data."),
+		arg_str1(NULL,  NULL,      "<HEX key number (2b)>", NULL),
+		arg_strx0(NULL,  NULL,     "<HEX key (16b)>", NULL),
+		arg_param_end
+	};
+	CLIExecWithReturn(cmd, argtable, true);
+	
+	bool verbose = arg_get_lit(1);
+	CLIGetHexWithReturn(2, keyNum, &keyNumLen);
+	CLIGetHexWithReturn(3, key, &keyLen);
+	CLIParserFree();
+	
+	SetVerboseMode(verbose);
+	
+	if (!keyLen) {
+		memmove(key, DefaultKey, 16);
+		keyLen = 16;
+	}
+	
+	if (keyNumLen != 2) {
+		PrintAndLog("Key number length must be 2 bytes instead of: %d", keyNumLen);
+		return 1;
+	}
+	if (keyLen != 16) {
+		PrintAndLog("Key length must be 16 bytes instead of: %d", keyLen);
+		return 1;
+	}
+
+	uint8_t data[250] = {0};
+	int datalen = 0;
+
+	int res = MFPWritePerso(keyNum, key, true, false, data, sizeof(data), &datalen);
+	if (res) {
+		PrintAndLog("Exchange error: %d", res);
+		return res;
+	}
+	
+	if (datalen != 3) {
+		PrintAndLog("Command must return 3 bytes instead of: %d", datalen);
+		return 1;
+	}
+
+	if (data[0] != 0x90) {
+		PrintAndLog("Command error: %02x %s", data[0], GetErrorDescription(data[0]));
+		return 1;
+	}
+	PrintAndLog("Write OK.");
+	
+	return 0;
+}
+
+uint16_t CardAddresses[] = {0x9000, 0x9001, 0x9002, 0x9003, 0x9004, 0xA000, 0xA001, 0xA080, 0xA081, 0xC000, 0xC001};
+
+int CmdHFMFPInitPerso(const char *cmd) {
+	int res;
+	uint8_t key[256] = {0};
+	int keyLen = 0;
+	uint8_t keyNum[2] = {0};
+	uint8_t data[250] = {0};
+	int datalen = 0;
+
+	CLIParserInit("hf mfp initp", 
+		"Executes Write Perso command for all card's keys. Can be used in SL0 mode only.", 
+		"Usage:\n\thf mfp initp 000102030405060708090a0b0c0d0e0f -> fill all the keys with key (00..0f)\n"
+			"\thf mfp initp -vv -> fill all the keys with default key(0xff..0xff) and show all the data exchange");
+
+	void* argtable[] = {
+		arg_param_begin,
+		arg_litn("vV",  "verbose", 0, 2, "show internal data."),
+		arg_strx0(NULL,  NULL,      "<HEX key (16b)>", NULL),
+		arg_param_end
+	};
+	CLIExecWithReturn(cmd, argtable, true);
+	
+	bool verbose = arg_get_lit(1);
+	bool verbose2 = arg_get_lit(1) > 1;
+	CLIGetHexWithReturn(2, key, &keyLen);
+	CLIParserFree();
+
+	if (keyLen && keyLen != 16) {
+		PrintAndLog("Key length must be 16 bytes instead of: %d", keyLen);
+		return 1;
+	}
+	
+	if (!keyLen)
+		memmove(key, DefaultKey, 16);
+
+	SetVerboseMode(verbose2);
+	for (uint16_t sn = 0x4000; sn < 0x4050; sn++) {
+		keyNum[0] = sn >> 8;
+		keyNum[1] = sn & 0xff;
+		res = MFPWritePerso(keyNum, key, (sn == 0x4000), true, data, sizeof(data), &datalen);
+		if (!res && (datalen == 3) && data[0] == 0x09) {
+			PrintAndLog("2k card detected.");
+			break;
+		}
+		if (res || (datalen != 3) || data[0] != 0x90) {
+			PrintAndLog("Write error on address %04x", sn);
+			break;
+		}
+	}
+	
+	SetVerboseMode(verbose);
+	for (int i = 0; i < sizeof(CardAddresses) / 2; i++) {
+		keyNum[0] = CardAddresses[i] >> 8;
+		keyNum[1] = CardAddresses[i] & 0xff;
+		res = MFPWritePerso(keyNum, key, false, true, data, sizeof(data), &datalen);
+		if (!res && (datalen == 3) && data[0] == 0x09) {
+			PrintAndLog("Skipped[%04x]...", CardAddresses[i]);
+		} else {
+			if (res || (datalen != 3) || data[0] != 0x90) {
+				PrintAndLog("Write error on address %04x", CardAddresses[i]);
+				break;
+			}
+		}
+	}
+	
+	DropField();
+	
+	if (res)
+		return res;
+	
+	PrintAndLog("Done.");
+	
+	return 0;
+}
+
+int CmdHFMFPCommitPerso(const char *cmd) {
+	CLIParserInit("hf mfp commitp", 
+		"Executes Commit Perso command. Can be used in SL0 mode only.", 
+		"Usage:\n\thf mfp commitp ->  \n");
+
+	void* argtable[] = {
+		arg_param_begin,
+		arg_lit0("vV",  "verbose", "show internal data."),
+		arg_int0(NULL,  NULL,      "SL mode", NULL),
+		arg_param_end
+	};
+	CLIExecWithReturn(cmd, argtable, true);
+	
+	bool verbose = arg_get_lit(1);
+	CLIParserFree();
+	
+	SetVerboseMode(verbose);
+	
+	uint8_t data[250] = {0};
+	int datalen = 0;
+
+	int res = MFPCommitPerso(true, false, data, sizeof(data), &datalen);
+	if (res) {
+		PrintAndLog("Exchange error: %d", res);
+		return res;
+	}
+	
+	if (datalen != 3) {
+		PrintAndLog("Command must return 3 bytes instead of: %d", datalen);
+		return 1;
+	}
+
+	if (data[0] != 0x90) {
+		PrintAndLog("Command error: %02x %s", data[0], GetErrorDescription(data[0]));
+		return 1;
+	}
+	PrintAndLog("Switch level OK.");
+
+	return 0;
+}
+
+int CmdHFMFPAuth(const char *cmd) {
+	uint8_t keyn[250] = {0};
+	int keynlen = 0;
+	uint8_t key[250] = {0};
+	int keylen = 0;
+	
+	CLIParserInit("hf mfp auth", 
+		"Executes AES authentication command in ISO14443-4", 
+		"Usage:\n\thf mfp auth 4000 000102030405060708090a0b0c0d0e0f -> executes authentication\n"
+			"\thf mfp auth 9003 FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -v -> executes authentication and shows all the system data\n");
+
+	void* argtable[] = {
+		arg_param_begin,
+		arg_lit0("vV",  "verbose", "show internal data."),
+		arg_str1(NULL,  NULL,     "<Key Num (HEX 2 bytes)>", NULL),
+		arg_str1(NULL,  NULL,     "<Key Value (HEX 16 bytes)>", NULL),
+		arg_param_end
+	};
+	CLIExecWithReturn(cmd, argtable, true);
+	
+	bool verbose = arg_get_lit(1);
+	CLIGetHexWithReturn(2, keyn, &keynlen);
+	CLIGetHexWithReturn(3, key, &keylen);
+	CLIParserFree();
+	
+	if (keynlen != 2) {
+		PrintAndLog("ERROR: <Key Num> must be 2 bytes long instead of: %d", keynlen);
+		return 1;
+	}
+	
+	if (keylen != 16) {
+		PrintAndLog("ERROR: <Key Value> must be 16 bytes long instead of: %d", keylen);
+		return 1;
+	}
+
+	return MifareAuth4(NULL, keyn, key, true, false, verbose);
+}
+
+int CmdHFMFPRdbl(const char *cmd) {
+	//mf4Session session
+	//int res = MifareAuth4(&session, keyn, key, true, false, verbose);
+	//res = Read();
+	
+	return 0;
+}
+
+int CmdHFMFPRdsc(const char *cmd) {
+	
+	return 0;
+}
+
+int CmdHFMFPWrbl(const char *cmd) {
+	
+	return 0;
+}
+
 static command_t CommandTable[] =
 {
   {"help",             CmdHelp,					1, "This help"},
   {"info",  	       CmdHFMFPInfo,			0, "Info about Mifare Plus tag"},
+  {"wrp",	  	       CmdHFMFPWritePerso,		0, "Write Perso command"},
+  {"initp",  	       CmdHFMFPInitPerso,		0, "Fills all the card's keys"},
+  {"commitp",  	       CmdHFMFPCommitPerso,		0, "Move card to SL1 or SL3 mode"},
+  {"auth",  	       CmdHFMFPAuth,			0, "Authentication in iso1443-4"},
+//  {"rdbl",  	       CmdHFMFPRdbl,			0, "Read blocks"},
+//  {"rdsc",  	       CmdHFMFPRdsc,			0, "Read sectors"},
+//  {"wrbl",  	       CmdHFMFPWrbl,			0, "Write blocks"},
   {NULL,               NULL,					0, NULL}
 };
 
diff --git a/client/mifare4.c b/client/mifare4.c
new file mode 100644
index 00000000..3489c857
--- /dev/null
+++ b/client/mifare4.c
@@ -0,0 +1,118 @@
+//-----------------------------------------------------------------------------
+// Copyright (C) 2018 Merlok
+//
+// This code is licensed to you under the terms of the GNU GPL, version 2 or,
+// at your option, any later version. See the LICENSE.txt file for the text of
+// the license.
+//-----------------------------------------------------------------------------
+// iso14443-4 mifare commands
+//-----------------------------------------------------------------------------
+
+#include "mifare4.h"
+#include <ctype.h>
+#include <string.h>
+#include "cmdhf14a.h"
+#include "util.h"
+#include "ui.h"
+#include "polarssl/libpcrypto.h"
+
+int MifareAuth4(mf4Session *session, uint8_t *keyn, uint8_t *key, bool activateField, bool leaveSignalON, bool verbose) {
+	uint8_t data[257] = {0};
+	int datalen = 0;
+	
+	uint8_t Rnd1[17] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00};
+	uint8_t Rnd2[17] = {0};
+	
+	if (session)
+		session->Authenticated = false;	
+	
+	uint8_t cmd1[] = {0x70, keyn[1], keyn[0], 0x00};
+	int res = ExchangeRAW14a(cmd1, sizeof(cmd1), activateField, true, data, sizeof(data), &datalen);
+	if (res) {
+		PrintAndLog("ERROR exchande raw error: %d", res);
+		DropField();
+		return 2;
+	}
+	
+	if (verbose)
+		PrintAndLog("<phase1: %s", sprint_hex(data, datalen));
+		
+	if (datalen < 1) {
+		PrintAndLog("ERROR: card response length: %d", datalen);
+		DropField();
+		return 3;
+	}
+	
+	if (data[0] != 0x90) {
+		PrintAndLog("ERROR: card response error: %02x", data[2]);
+		DropField();
+		return 3;
+	}
+
+	if (datalen != 19) { // code 1b + 16b + crc 2b
+		PrintAndLog("ERROR: card response must be 19 bytes long instead of: %d", datalen);
+		DropField();
+		return 3;
+	}
+	
+    aes_decode(NULL, key, &data[1], Rnd2, 16);
+	Rnd2[16] = Rnd2[0];
+	if (verbose)
+		PrintAndLog("Rnd2: %s", sprint_hex(Rnd2, 16));
+
+	uint8_t cmd2[33] = {0};
+	cmd2[0] = 0x72;
+
+	uint8_t raw[32] = {0};
+	memmove(raw, Rnd1, 16);
+	memmove(&raw[16], &Rnd2[1], 16);
+
+	aes_encode(NULL, key, raw, &cmd2[1], 32);
+	if (verbose)
+		PrintAndLog(">phase2: %s", sprint_hex(cmd2, 33));
+	
+	res = ExchangeRAW14a(cmd2, sizeof(cmd2), false, false, data, sizeof(data), &datalen);
+	if (res) {
+		PrintAndLog("ERROR exchande raw error: %d", res);
+		DropField();
+		return 4;
+	}
+	
+	if (verbose)
+		PrintAndLog("<phase2: %s", sprint_hex(data, datalen));
+
+	aes_decode(NULL, key, &data[1], raw, 32);
+	
+	if (verbose) {
+		PrintAndLog("res: %s", sprint_hex(raw, 32));
+		PrintAndLog("Rnd1`: %s", sprint_hex(&raw[4], 16));
+	}
+
+	if (memcmp(&raw[4], &Rnd1[1], 16)) {
+		PrintAndLog("\nERROR: Authentication FAILED. rnd not equal");
+		if (verbose) {
+			PrintAndLog("rnd1 reader: %s", sprint_hex(&Rnd1[1], 16));
+			PrintAndLog("rnd1   card: %s", sprint_hex(&raw[4], 16));
+		}
+		DropField();
+		return 5;
+	}
+
+	if (!leaveSignalON)
+		DropField();
+
+	if (verbose)
+		PrintAndLog("");
+
+	if (session) {
+		session->Authenticated = true;
+		session->KeyNum = keyn[1] + (keyn[0] << 8);
+		memmove(session->Rnd1, Rnd1, 16);
+		memmove(session->Rnd2, Rnd2, 16);
+	}
+	
+	PrintAndLog("Authentication OK");
+	
+	return 0;
+}
+
diff --git a/client/mifare4.h b/client/mifare4.h
new file mode 100644
index 00000000..70711847
--- /dev/null
+++ b/client/mifare4.h
@@ -0,0 +1,30 @@
+//-----------------------------------------------------------------------------
+// Copyright (C) 2018 Merlok
+//
+// This code is licensed to you under the terms of the GNU GPL, version 2 or,
+// at your option, any later version. See the LICENSE.txt file for the text of
+// the license.
+//-----------------------------------------------------------------------------
+// iso14443-4 mifare commands
+//-----------------------------------------------------------------------------
+
+#ifndef MIFARE4_H
+#define MIFARE4_H
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stddef.h>
+
+typedef struct {
+	bool Authenticated;
+	uint16_t KeyNum;
+	uint8_t Rnd1[16];
+	uint8_t Rnd2[16];
+	
+}mf4Session;
+
+extern int MifareAuth4(mf4Session *session, uint8_t *keyn, uint8_t *key, bool activateField, bool leaveSignalON, bool verbose);
+
+
+
+#endif // mifare4.h
diff --git a/common/polarssl/libpcrypto.c b/common/polarssl/libpcrypto.c
new file mode 100644
index 00000000..032c3a18
--- /dev/null
+++ b/common/polarssl/libpcrypto.c
@@ -0,0 +1,44 @@
+//-----------------------------------------------------------------------------
+// Copyright (C) 2018 Merlok
+//
+// This code is licensed to you under the terms of the GNU GPL, version 2 or,
+// at your option, any later version. See the LICENSE.txt file for the text of
+// the license.
+//-----------------------------------------------------------------------------
+// crypto commands
+//-----------------------------------------------------------------------------
+
+#include "polarssl/libpcrypto.h"
+#include <polarssl/aes.h>
+
+int aes_encode(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *output, int length){
+	uint8_t iiv[16] = {0};
+	if (iv)
+		memcpy(iiv, iv, 16);
+	
+	aes_context aes;
+	aes_init(&aes);
+	if (aes_setkey_enc(&aes, key, 128))
+		return 1;
+	if (aes_crypt_cbc(&aes, AES_ENCRYPT, length, iiv, input, output))
+		return 2;
+	aes_free(&aes);
+
+	return 0;
+}
+
+int aes_decode(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *output, int length){
+	uint8_t iiv[16] = {0};
+	if (iv)
+		memcpy(iiv, iv, 16);
+	
+	aes_context aes;
+	aes_init(&aes);
+	if (aes_setkey_dec(&aes, key, 128))
+		return 1;
+	if (aes_crypt_cbc(&aes, AES_DECRYPT, length, iiv, input, output))
+		return 2;
+	aes_free(&aes);
+
+	return 0;
+}
\ No newline at end of file
diff --git a/common/polarssl/libpcrypto.h b/common/polarssl/libpcrypto.h
new file mode 100644
index 00000000..84732cd3
--- /dev/null
+++ b/common/polarssl/libpcrypto.h
@@ -0,0 +1,20 @@
+//-----------------------------------------------------------------------------
+// Copyright (C) 2018 Merlok
+//
+// This code is licensed to you under the terms of the GNU GPL, version 2 or,
+// at your option, any later version. See the LICENSE.txt file for the text of
+// the license.
+//-----------------------------------------------------------------------------
+// crypto commands
+//-----------------------------------------------------------------------------
+
+#ifndef LIBPCRYPTO_H
+#define LIBPCRYPTO_H
+
+#include <stdint.h>
+#include <stddef.h>
+
+extern int aes_encode(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *output, int length);
+extern int aes_decode(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *output, int length);
+
+#endif /* libpcrypto.h */
-- 
2.39.5