From a37725facfb70e0d699f423c6d986173ef890531 Mon Sep 17 00:00:00 2001
From: Oleg Moiseenko <olegmsn@gmail.com>
Date: Fri, 9 Feb 2018 16:50:55 +0200
Subject: [PATCH 1/1] add nested auth decoding to `hf mf sniff`

---
 armsrc/mifarecmd.h   |   1 -
 armsrc/mifaresniff.c |   2 +-
 client/cmdhfmf.c     |  17 +++++-
 client/mifarehost.c  | 132 ++++++++++++++++++++++++++++++++++++++-----
 client/mifarehost.h  |   3 +-
 client/util.c        |  17 ++++++
 client/util.h        |   1 +
 common/parity.h      |   6 ++
 8 files changed, 158 insertions(+), 21 deletions(-)

diff --git a/armsrc/mifarecmd.h b/armsrc/mifarecmd.h
index 145e2989..e17fa998 100644
--- a/armsrc/mifarecmd.h
+++ b/armsrc/mifarecmd.h
@@ -16,7 +16,6 @@
 #include "proxmark3.h"
 #include "apps.h"
 #include "util.h"
-#include "string.h"
 
 #include "iso14443crc.h"
 #include "iso14443a.h"
diff --git a/armsrc/mifaresniff.c b/armsrc/mifaresniff.c
index 4e573be7..f20f2557 100644
--- a/armsrc/mifaresniff.c
+++ b/armsrc/mifaresniff.c
@@ -116,7 +116,7 @@ bool RAMFUNC MfSniffLogic(const uint8_t *data, uint16_t len, uint8_t *parity, ui
 			sniffState = SNF_CARD_CMD;
 		}	// intentionally no break;
 		case SNF_CARD_CMD:{	
-			LogTrace(data, len, 0, 0, NULL, reader);
+			LogTrace(data, len, 0, 0, parity, reader);
 			timerData = GetTickCount();
 			break;
 		}
diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c
index f5d7a5be..a2da01c9 100644
--- a/client/cmdhfmf.c
+++ b/client/cmdhfmf.c
@@ -18,6 +18,7 @@
 #include "proxmark3.h"
 #include "cmdmain.h"
 #include "cmdhfmfhard.h"
+#include "parity.h"
 #include "util.h"
 #include "util_posix.h"
 #include "usb_cmd.h"
@@ -2470,6 +2471,7 @@ int CmdHF14AMfSniff(const char *Cmd){
 	//var
 	int res = 0;
 	int len = 0;
+	int parlen = 0;
 	int blockLen = 0;
 	int pckNum = 0;
 	int num = 0;
@@ -2481,6 +2483,7 @@ int CmdHF14AMfSniff(const char *Cmd){
 	uint8_t *buf = NULL;
 	uint16_t bufsize = 0;
 	uint8_t *bufPtr = NULL;
+	uint8_t parity[16];
 
 	char ctmp = param_getchar(Cmd, 0);
 	if ( ctmp == 'h' || ctmp == 'H' ) {
@@ -2572,6 +2575,7 @@ int CmdHF14AMfSniff(const char *Cmd){
 					} else {
 						isTag = false;
 					}
+					parlen = (len - 1) / 8 + 1;
 					bufPtr += 2;
 					if ((len == 14) && (bufPtr[0] == 0xff) && (bufPtr[1] == 0xff) && (bufPtr[12] == 0xff) && (bufPtr[13] == 0xff)) {
 						memcpy(uid, bufPtr + 2, 7);
@@ -2590,15 +2594,22 @@ int CmdHF14AMfSniff(const char *Cmd){
 						if (wantDecrypt)
 							mfTraceInit(uid, atqa, sak, wantSaveToEmlFile);
 					} else {
-						PrintAndLog("%s(%d):%s", isTag ? "TAG":"RDR", num, sprint_hex(bufPtr, len));
+						oddparitybuf(bufPtr, len, parity);
+						PrintAndLog("%s(%d):%s [%s] c[%s]%c", 
+							isTag ? "TAG":"RDR", 
+							num, 
+							sprint_hex(bufPtr, len), 
+							printBitsPar(bufPtr + len, len), 
+							printBitsPar(parity, len),
+							memcmp(bufPtr + len, parity, len / 8 + 1) ? '!' : ' ');
 						if (wantLogToFile)
 							AddLogHex(logHexFileName, isTag ? "TAG: ":"RDR: ", bufPtr, len);
 						if (wantDecrypt)
-							mfTraceDecode(bufPtr, len, wantSaveToEmlFile);
+							mfTraceDecode(bufPtr, len, bufPtr[len], wantSaveToEmlFile);
 						num++;
 					}
 					bufPtr += len;
-					bufPtr += ((len-1)/8+1);	// ignore parity
+					bufPtr += parlen;	// ignore parity
 				}
 				pckNum = 0;
 			}
diff --git a/client/mifarehost.c b/client/mifarehost.c
index 471fbc42..e1ced176 100644
--- a/client/mifarehost.c
+++ b/client/mifarehost.c
@@ -20,6 +20,7 @@
 #include "usb_cmd.h"
 #include "cmdmain.h"
 #include "ui.h"
+#include "parity.h"
 #include "util.h"
 #include "iso14443crc.h"
 
@@ -582,14 +583,19 @@ struct Crypto1State *traceCrypto1 = NULL;
 
 struct Crypto1State *revstate;
 uint64_t lfsr;
+uint64_t ui64Key;
 uint32_t ks2;
 uint32_t ks3;
 
-uint32_t uid;     // serial number
-uint32_t nt;      // tag challenge
-uint32_t nr_enc;  // encrypted reader challenge
-uint32_t ar_enc;  // encrypted reader response
-uint32_t at_enc;  // encrypted tag response
+uint32_t uid;       // serial number
+uint32_t nt;        // tag challenge
+uint32_t nt_enc;    // encrypted tag challenge
+uint8_t nt_enc_par; // encrypted tag challenge parity
+uint32_t nr_enc;    // encrypted reader challenge
+uint32_t ar_enc;    // encrypted reader response
+uint8_t ar_enc_par; // encrypted reader response parity
+uint32_t at_enc;    // encrypted tag response
+uint8_t at_enc_par; // encrypted tag response parity
 
 int isTraceCardEmpty(void) {
 	return ((traceCard[0] == 0) && (traceCard[1] == 0) && (traceCard[2] == 0) && (traceCard[3] == 0));
@@ -708,8 +714,36 @@ void mf_crypto1_decrypt(struct Crypto1State *pcs, uint8_t *data, int len, bool i
 	return;
 }
 
+bool NTParityCheck(uint32_t ntx) {
+	if (
+		(oddparity8(ntx >> 8 & 0xff) ^ (ntx & 0x01) ^ ((nt_enc_par >> 5) & 0x01) ^ (nt_enc & 0x01)) ||
+		(oddparity8(ntx >> 16 & 0xff) ^ (ntx >> 8 & 0x01) ^ ((nt_enc_par >> 6) & 0x01) ^ (nt_enc >> 8 & 0x01)) ||
+		(oddparity8(ntx >> 24 & 0xff) ^ (ntx >> 16 & 0x01) ^ ((nt_enc_par >> 7) & 0x01) ^ (nt_enc >> 16 & 0x01))
+		)
+		return false;
+	
+	uint32_t ar = prng_successor(ntx, 64);
+	if (
+		(oddparity8(ar >> 8 & 0xff) ^ (ar & 0x01) ^ ((ar_enc_par >> 5) & 0x01) ^ (ar_enc & 0x01)) ||
+		(oddparity8(ar >> 16 & 0xff) ^ (ar >> 8 & 0x01) ^ ((ar_enc_par >> 6) & 0x01) ^ (ar_enc >> 8 & 0x01)) ||
+		(oddparity8(ar >> 24 & 0xff) ^ (ar >> 16 & 0x01) ^ ((ar_enc_par >> 7) & 0x01) ^ (ar_enc >> 16 & 0x01))
+		)
+		return false;
+
+	uint32_t at = prng_successor(ntx, 96);
+	if (
+		(oddparity8(ar & 0xff) ^ (at >> 24 & 0x01) ^ ((ar_enc_par >> 4) & 0x01) ^ (at_enc >> 24 & 0x01)) ||
+		(oddparity8(at >> 8 & 0xff) ^ (at & 0x01) ^ ((at_enc_par >> 5) & 0x01) ^ (at_enc & 0x01)) ||
+		(oddparity8(at >> 16 & 0xff) ^ (at >> 8 & 0x01) ^ ((at_enc_par >> 6) & 0x01) ^ (at_enc >> 8 & 0x01)) ||
+		(oddparity8(at >> 24 & 0xff) ^ (at >> 16 & 0x01) ^ ((at_enc_par >> 7) & 0x01) ^ (at_enc >> 16 & 0x01))
+		)
+		return false;
+		
+	return true;
+}
 
-int mfTraceDecode(uint8_t *data_src, int len, bool wantSaveToEmlFile) {
+
+int mfTraceDecode(uint8_t *data_src, int len, uint8_t parity, bool wantSaveToEmlFile) {
 	uint8_t data[64];
 
 	if (traceState == TRACE_ERROR) return 1;
@@ -721,7 +755,9 @@ int mfTraceDecode(uint8_t *data_src, int len, bool wantSaveToEmlFile) {
 	memcpy(data, data_src, len);
 	if ((traceCrypto1) && ((traceState == TRACE_IDLE) || (traceState > TRACE_AUTH_OK))) {
 		mf_crypto1_decrypt(traceCrypto1, data, len, 0);
-		PrintAndLog("dec> %s", sprint_hex(data, len));
+		uint8_t parity[16];
+		oddparitybuf(data, len, parity);
+		PrintAndLog("dec> %s [%s]", sprint_hex(data, len), printBitsPar(parity, len));
 		AddLogHex(logHexFileName, "dec> ", data, len);
 	}
 
@@ -810,7 +846,12 @@ int mfTraceDecode(uint8_t *data_src, int len, bool wantSaveToEmlFile) {
 	case TRACE_AUTH1:
 		if (len == 4) {
 			traceState = TRACE_AUTH2;
-			nt = bytes_to_num(data, 4);
+			if (!traceCrypto1) {
+				nt = bytes_to_num(data, 4);
+			} else {
+				nt_enc = bytes_to_num(data, 4);
+				nt_enc_par = parity;
+			}
 			return 0;
 		} else {
 			traceState = TRACE_ERROR;
@@ -824,6 +865,7 @@ int mfTraceDecode(uint8_t *data_src, int len, bool wantSaveToEmlFile) {
 
 			nr_enc = bytes_to_num(data, 4);
 			ar_enc = bytes_to_num(data + 4, 4);
+			ar_enc_par = parity << 4;
 			return 0;
 		} else {
 			traceState = TRACE_ERROR;
@@ -835,8 +877,9 @@ int mfTraceDecode(uint8_t *data_src, int len, bool wantSaveToEmlFile) {
 		if (len ==4) {
 			traceState = TRACE_IDLE;
 
+			at_enc = bytes_to_num(data, 4);
+			at_enc_par = parity;
 			if (!traceCrypto1) {
-				at_enc = bytes_to_num(data, 4);
 
 				//  decode key here)
 				ks2 = ar_enc ^ prng_successor(nt, 64);
@@ -848,16 +891,75 @@ int mfTraceDecode(uint8_t *data_src, int len, bool wantSaveToEmlFile) {
 				lfsr_rollback_word(revstate, uid ^ nt, 0);
 
 				crypto1_get_lfsr(revstate, &lfsr);
-				printf("key> %x%x\n", (unsigned int)((lfsr & 0xFFFFFFFF00000000) >> 32), (unsigned int)(lfsr & 0xFFFFFFFF));
+				crypto1_destroy(revstate);
+				ui64Key = lfsr;
+				printf("key> probable key:%x%x Prng:%s ks2:%08x ks3:%08x\n", 
+					(unsigned int)((lfsr & 0xFFFFFFFF00000000) >> 32), (unsigned int)(lfsr & 0xFFFFFFFF), 
+					validate_prng_nonce(nt) ? "WEAK": "HARDEND",
+					ks2,
+					ks3);
 				AddLogUint64(logHexFileName, "key> ", lfsr);
 			} else {
-				printf("key> nested not implemented!\n");
-				at_enc = bytes_to_num(data, 4);
+				if (validate_prng_nonce(nt)) {
+					struct Crypto1State *pcs;
+					pcs = crypto1_create(ui64Key);
+					uint32_t nt1 = crypto1_word(pcs, nt_enc ^ uid, 1) ^ nt_enc;
+					uint32_t ar = prng_successor(nt1, 64);
+					uint32_t at = prng_successor(nt1, 96);
+					printf("key> nested auth uid: %08x nt: %08x nt_parity: %s ar: %08x at: %08x\n", uid, nt1, printBitsPar(&nt_enc_par, 4), ar, at);
+					uint32_t nr1 = crypto1_word(pcs, nr_enc, 1) ^ nr_enc;
+					uint32_t ar1 = crypto1_word(pcs, 0, 0) ^ ar_enc;
+					uint32_t at1 = crypto1_word(pcs, 0, 0) ^ at_enc;
+					printf("key> the same key test. nr1: %08x ar1: %08x at1: %08x \n", nr1, ar1, at1);
+
+					if (NTParityCheck(nt1))
+						printf("key> the same key test OK. key=%x%x\n", (unsigned int)((ui64Key & 0xFFFFFFFF00000000) >> 32), (unsigned int)(ui64Key & 0xFFFFFFFF));
+					else
+						printf("key> the same key test. check nt parity error.\n");
+					
+					uint32_t ntc = prng_successor(nt, 90);
+					uint32_t ntx = 0;
+					int ntcnt = 0;
+					for (int i = 0; i < 16383; i++) {
+						ntc = prng_successor(ntc, 1);
+						if (NTParityCheck(ntc)){
+							if (!ntcnt)
+								ntx = ntc;
+							ntcnt++;
+						}						
+					}
+					if (ntcnt)
+						printf("key> nt candidate=%08x nonce distance=%d candidates count=%d\n", ntx, nonce_distance(nt, ntx), ntcnt);
+					else
+						printf("key> don't have any nt candidate( \n");
+
+					nt = ntx;
+					ks2 = ar_enc ^ prng_successor(ntx, 64);
+					ks3 = at_enc ^ prng_successor(ntx, 96);
+
+					// decode key
+					revstate = lfsr_recovery64(ks2, ks3);
+					lfsr_rollback_word(revstate, 0, 0);
+					lfsr_rollback_word(revstate, 0, 0);
+					lfsr_rollback_word(revstate, nr_enc, 1);
+					lfsr_rollback_word(revstate, uid ^ nt, 0);
+
+					crypto1_get_lfsr(revstate, &lfsr);
+					crypto1_destroy(revstate);
+					ui64Key = lfsr;
+					printf("key> probable key:%x%x  ks2:%08x ks3:%08x\n", 
+						(unsigned int)((lfsr & 0xFFFFFFFF00000000) >> 32), (unsigned int)(lfsr & 0xFFFFFFFF),
+						ks2,
+						ks3);
+					AddLogUint64(logHexFileName, "key> ", lfsr);
+				} else {				
+					printf("key> hardnested not implemented!\n");
 				
-				crypto1_destroy(traceCrypto1);
+					crypto1_destroy(traceCrypto1);
 
-				// not implemented
-				traceState = TRACE_ERROR;
+					// not implemented
+					traceState = TRACE_ERROR;
+				}
 			}
 
 			int blockShift = ((traceCurBlock & 0xFC) + 3) * 16;
diff --git a/client/mifarehost.h b/client/mifarehost.h
index 031dac1b..bef397bb 100644
--- a/client/mifarehost.h
+++ b/client/mifarehost.h
@@ -50,7 +50,7 @@ extern int mfCSetBlock(uint8_t blockNo, uint8_t *data, uint8_t *uid, bool wantWi
 extern int mfCGetBlock(uint8_t blockNo, uint8_t *data, uint8_t params);
 
 extern int mfTraceInit(uint8_t *tuid, uint8_t *atqa, uint8_t sak, bool wantSaveToEmlFile);
-extern int mfTraceDecode(uint8_t *data_src, int len, bool wantSaveToEmlFile);
+extern int mfTraceDecode(uint8_t *data_src, int len, uint8_t parity, bool wantSaveToEmlFile);
 
 extern int isTraceCardEmpty(void);
 extern int isBlockEmpty(int blockN);
@@ -61,5 +61,6 @@ extern int tryDecryptWord(uint32_t nt, uint32_t ar_enc, uint32_t at_enc, uint8_t
 
 extern int mfCIdentify();
 extern int DetectClassicPrng(void);
+extern bool validate_prng_nonce(uint32_t nonce);
 
 #endif
diff --git a/client/util.c b/client/util.c
index 7e6b4074..bbc7f2cf 100644
--- a/client/util.c
+++ b/client/util.c
@@ -356,6 +356,23 @@ char * printBits(size_t const size, void const * const ptr)
 	return buf;
 }
 
+char * printBitsPar(const uint8_t *b, size_t len) {
+	static char buf1[512] = {0};
+	static char buf2[512] = {0};
+	static char *buf;
+	if (buf != buf1)
+		buf = buf1;
+	else
+		buf = buf2;
+	memset(buf, 0x00, 512);
+
+	for (int i = 0; i < len; i++) {
+		buf[i] = ((b[i / 8] << (i % 8)) & 0x80) ? '1':'0';
+	}
+	return buf;
+}
+
+
 //  -------------------------------------------------------------------------
 //  string parameters lib
 //  -------------------------------------------------------------------------
diff --git a/client/util.h b/client/util.h
index fd7ceaff..2e64d7ca 100644
--- a/client/util.h
+++ b/client/util.h
@@ -54,6 +54,7 @@ extern uint64_t bytes_to_num(uint8_t* src, size_t len);
 extern void num_to_bytebits(uint64_t	n, size_t len, uint8_t *dest);
 extern void num_to_bytebitsLSBF(uint64_t n, size_t len, uint8_t *dest);
 extern char *printBits(size_t const size, void const * const ptr);
+extern char * printBitsPar(const uint8_t *b, size_t len);
 extern uint32_t SwapBits(uint32_t value, int nrbits);
 extern uint8_t *SwapEndian64(const uint8_t *src, const size_t len, const uint8_t blockSize);
 extern void SwapEndian64ex(const uint8_t *src, const size_t len, const uint8_t blockSize, uint8_t *dest);
diff --git a/common/parity.h b/common/parity.h
index 615fdeee..c574db55 100644
--- a/common/parity.h
+++ b/common/parity.h
@@ -13,6 +13,7 @@
 
 #include <stdint.h>
 #include <stdbool.h>
+#include "string.h"
 
 extern const uint8_t OddByteParity[256];
 
@@ -21,6 +22,11 @@ static inline bool oddparity8(const uint8_t x) {
 	return OddByteParity[x];
 }
 
+static inline void oddparitybuf(const uint8_t *x, size_t len, uint8_t *parity) {
+	memset(parity, 0x00, (len - 1) / 8 + 1);
+	for (int i = 0; i < len; i++) 
+		parity[i / 8] |= oddparity8(x[i]) << (7 - (i % 8));
+}
 
 static inline bool evenparity8(const uint8_t x) {
 	return !OddByteParity[x];
-- 
2.39.5