From f267df2fb72ccc79a478d623ee0a556d21768726 Mon Sep 17 00:00:00 2001
From: merlokk <807634+merlokk@users.noreply.github.com>
Date: Fri, 28 May 2021 18:54:44 +0300
Subject: [PATCH 01/39] cipurse info sketch

---
 client/CMakeLists.txt            |   1 +
 client/Makefile                  |   2 +
 client/src/cipurse/cipursecore.c |  25 ++++++++
 client/src/cipurse/cipursecore.h |  21 +++++++
 client/src/cmdhf.c               |   2 +
 client/src/cmdhfcipurse.c        | 102 +++++++++++++++++++++++++++++++
 client/src/cmdhfcipurse.h        |  23 +++++++
 7 files changed, 176 insertions(+)
 create mode 100644 client/src/cipurse/cipursecore.c
 create mode 100644 client/src/cipurse/cipursecore.h
 create mode 100644 client/src/cmdhfcipurse.c
 create mode 100644 client/src/cmdhfcipurse.h

diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt
index 28edf7c2b..420daa1a2 100644
--- a/client/CMakeLists.txt
+++ b/client/CMakeLists.txt
@@ -242,6 +242,7 @@ set (TARGET_SOURCES
         ${PM3_ROOT}/client/src/cmdhfepa.c
         ${PM3_ROOT}/client/src/cmdhffelica.c
         ${PM3_ROOT}/client/src/cmdhffido.c
+        ${PM3_ROOT}/client/src/cmdhfcipurse.c
         ${PM3_ROOT}/client/src/cmdhficlass.c
         ${PM3_ROOT}/client/src/cmdhfjooki.c
         ${PM3_ROOT}/client/src/cmdhflegic.c
diff --git a/client/Makefile b/client/Makefile
index 696b48f40..2f5478b39 100644
--- a/client/Makefile
+++ b/client/Makefile
@@ -473,6 +473,7 @@ SRCS =  aiddesfire.c \
 		cmdhfemrtd.c \
 		cmdhffelica.c \
 		cmdhffido.c \
+		cmdhfcipurse.c \
 		cmdhficlass.c \
 		cmdhflegic.c \
 		cmdhfjooki.c \
@@ -556,6 +557,7 @@ SRCS =  aiddesfire.c \
 		fido/cose.c \
 		fido/cbortools.c \
 		fido/fidocore.c \
+		cipurse/cipursecore.c \
 		fileutils.c \
 		flash.c \
 		generator.c \
diff --git a/client/src/cipurse/cipursecore.c b/client/src/cipurse/cipursecore.c
new file mode 100644
index 000000000..8f61d843d
--- /dev/null
+++ b/client/src/cipurse/cipursecore.c
@@ -0,0 +1,25 @@
+//-----------------------------------------------------------------------------
+// Copyright (C) 2021 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.
+//-----------------------------------------------------------------------------
+// CIPURSE transport cards data and commands
+//-----------------------------------------------------------------------------
+
+#include "cipursecore.h"
+
+#include "commonutil.h"  // ARRAYLEN
+
+#include "emv/emvcore.h"
+#include "emv/emvjson.h"
+#include "ui.h"
+#include "util.h"
+
+int CIPURSESelect(bool ActivateField, bool LeaveFieldON, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
+    uint8_t data[] = {0x41, 0x44, 0x20, 0x46, 0x31};
+
+    return EMVSelect(ECC_CONTACTLESS, ActivateField, LeaveFieldON, data, sizeof(data), Result, MaxResultLen, ResultLen, sw, NULL);
+}
+
diff --git a/client/src/cipurse/cipursecore.h b/client/src/cipurse/cipursecore.h
new file mode 100644
index 000000000..7598b8343
--- /dev/null
+++ b/client/src/cipurse/cipursecore.h
@@ -0,0 +1,21 @@
+//-----------------------------------------------------------------------------
+// Copyright (C) 2021 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.
+//-----------------------------------------------------------------------------
+// CIPURSE transport cards data and commands
+//-----------------------------------------------------------------------------
+
+#ifndef __CIPURSECORE_H__
+#define __CIPURSECORE_H__
+
+#include "common.h"
+
+#include <jansson.h>
+#include "emv/apduinfo.h" // sAPDU
+
+int CIPURSESelect(bool ActivateField, bool LeaveFieldON, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);
+
+#endif /* __CIPURSECORE_H__ */
diff --git a/client/src/cmdhf.c b/client/src/cmdhf.c
index 369234b9d..94384c580 100644
--- a/client/src/cmdhf.c
+++ b/client/src/cmdhf.c
@@ -33,6 +33,7 @@
 #include "cmdhftopaz.h"     // TOPAZ
 #include "cmdhffelica.h"    // ISO18092 / FeliCa
 #include "cmdhffido.h"      // FIDO authenticators
+#include "cmdhfcipurse.h"   // CIPURSE transport cards
 #include "cmdhfthinfilm.h"  // Thinfilm
 #include "cmdhflto.h"       // LTO-CM
 #include "cmdhfcryptorf.h"  // CryptoRF
@@ -399,6 +400,7 @@ static command_t CommandTable[] = {
     {"14b",         CmdHF14B,         AlwaysAvailable, "{ ISO14443B RFIDs...                  }"},
     {"15",          CmdHF15,          AlwaysAvailable, "{ ISO15693 RFIDs...                   }"},
 //    {"cryptorf",    CmdHFCryptoRF,    AlwaysAvailable, "{ CryptoRF RFIDs...                   }"},
+    {"cipurse",     CmdHFCipurse,     AlwaysAvailable, "{ Cipurse transport Cards...          }"},
     {"epa",         CmdHFEPA,         AlwaysAvailable, "{ German Identification Card...       }"},
     {"emrtd",       CmdHFeMRTD,       AlwaysAvailable, "{ Machine Readable Travel Document... }"},
     {"felica",      CmdHFFelica,      AlwaysAvailable, "{ ISO18092 / FeliCa RFIDs...          }"},
diff --git a/client/src/cmdhfcipurse.c b/client/src/cmdhfcipurse.c
new file mode 100644
index 000000000..f519a4be9
--- /dev/null
+++ b/client/src/cmdhfcipurse.c
@@ -0,0 +1,102 @@
+//-----------------------------------------------------------------------------
+// Copyright (C) 2021 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.
+//-----------------------------------------------------------------------------
+// High frequency FIDO U2F and FIDO2 contactless authenticators
+//-----------------------------------------------------------------------------
+//
+//  JAVA implementation here:
+//
+//  https://github.com/duychuongvn/cipurse-card-core
+//-----------------------------------------------------------------------------
+
+#include "cmdhffido.h"
+#include <unistd.h>
+#include "cmdparser.h"    // command_t
+#include "commonutil.h"
+#include "comms.h"
+#include "proxmark3.h"
+#include "emv/emvcore.h"
+#include "emv/emvjson.h"
+#include "cliparser.h"
+#include "cmdhfcipurse.h"
+#include "cipurse/cipursecore.h"
+#include "ui.h"
+#include "cmdhf14a.h"
+#include "cmdtrace.h"
+#include "util.h"
+#include "fileutils.h"   // laodFileJSONroot
+
+static int CmdHelp(const char *Cmd);
+
+static int CmdHFCipurseInfo(const char *Cmd) {
+    CLIParserContext *ctx;
+    CLIParserInit(&ctx, "hf cipurse info",
+                  "Get info from cipurse tags",
+                  "hf cipurse info");
+
+    void *argtable[] = {
+        arg_param_begin,
+        arg_param_end
+    };
+    CLIExecWithReturn(ctx, Cmd, argtable, true);
+    CLIParserFree(ctx);
+
+    // info about 14a part
+    infoHF14A(false, false, false);
+
+    // CIPURSE info
+    PrintAndLogEx(INFO, "-----------" _CYAN_("CIPURSE Info") "---------------------------------");
+    SetAPDULogging(false);
+
+    uint8_t buf[APDU_RES_LEN] = {0};
+    size_t len = 0;
+    uint16_t sw = 0;
+    int res = CIPURSESelect(true, true, buf, sizeof(buf), &len, &sw);
+
+    if (res) {
+        DropField();
+        return res;
+    }
+
+    if (sw != 0x9000) {
+        if (sw)
+            PrintAndLogEx(INFO, "Not a CIPURSE card! APDU response: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
+        else
+            PrintAndLogEx(ERR, "APDU exchange error. Card returns 0x0000.");
+
+        DropField();
+        return PM3_SUCCESS;
+    }
+
+    PrintAndLogEx(INFO, "Cipurse card: " _GREEN_("OK"));
+
+
+
+    DropField();
+    return PM3_SUCCESS;
+}
+
+
+
+
+
+static command_t CommandTable[] = {
+    {"help",      CmdHelp,                   AlwaysAvailable, "This help."},
+    {"info",      CmdHFCipurseInfo,          IfPm3Iso14443a,  "Info about Cipurse tag."},
+    {NULL, NULL, 0, NULL}
+};
+
+int CmdHFCipurse(const char *Cmd) {
+    clearCommandBuffer();
+    return CmdsParse(CommandTable, Cmd);
+}
+
+int CmdHelp(const char *Cmd) {
+    (void)Cmd; // Cmd is not used so far
+    CmdsHelp(CommandTable);
+    return PM3_SUCCESS;
+}
diff --git a/client/src/cmdhfcipurse.h b/client/src/cmdhfcipurse.h
new file mode 100644
index 000000000..86d0f63be
--- /dev/null
+++ b/client/src/cmdhfcipurse.h
@@ -0,0 +1,23 @@
+//-----------------------------------------------------------------------------
+// Copyright (C) 2021 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.
+//-----------------------------------------------------------------------------
+// High frequency FIDO U2F and FIDO2 contactless authenticators
+//-----------------------------------------------------------------------------
+//
+//  JAVA implementation here:
+//
+//  https://github.com/duychuongvn/cipurse-card-core
+//-----------------------------------------------------------------------------
+
+#ifndef CMDHFCIPURSE_H__
+#define CMDHFCIPURSE_H__
+
+#include "common.h"
+
+int CmdHFCipurse(const char *Cmd);
+
+#endif

From ae6580f65b0c027257f28745c85aafc546714a04 Mon Sep 17 00:00:00 2001
From: merlokk <807634+merlokk@users.noreply.github.com>
Date: Fri, 28 May 2021 19:05:00 +0300
Subject: [PATCH 02/39] check cipurse via `hf search`

---
 client/src/cmdhf.c        | 9 +++++++++
 client/src/cmdhfcipurse.c | 9 +++++++++
 client/src/cmdhfcipurse.h | 2 ++
 3 files changed, 20 insertions(+)

diff --git a/client/src/cmdhf.c b/client/src/cmdhf.c
index 94384c580..ca4753d7c 100644
--- a/client/src/cmdhf.c
+++ b/client/src/cmdhf.c
@@ -128,6 +128,15 @@ int CmdHFSearch(const char *Cmd) {
             res = PM3_SUCCESS;
         }
     }
+    
+    PROMPT_CLEARLINE;
+    PrintAndLogEx(INPLACE, " Searching for Cipurse tag...");
+    if (IfPm3Iso14443a()) {
+        if (CheckCardCipurse()) {
+            PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("Cipurse tag") " found\n");
+            res = PM3_SUCCESS;
+        }
+    }
 
     // 14b is the longest test
     PROMPT_CLEARLINE;
diff --git a/client/src/cmdhfcipurse.c b/client/src/cmdhfcipurse.c
index f519a4be9..1a1cd7770 100644
--- a/client/src/cmdhfcipurse.c
+++ b/client/src/cmdhfcipurse.c
@@ -84,6 +84,15 @@ static int CmdHFCipurseInfo(const char *Cmd) {
 
 
 
+bool CheckCardCipurse(void) {
+    uint8_t buf[APDU_RES_LEN] = {0};
+    size_t len = 0;
+    uint16_t sw = 0;
+    int res = CIPURSESelect(true, false, buf, sizeof(buf), &len, &sw);
+
+    return (res == 0 && sw == 0x9000);
+}
+
 static command_t CommandTable[] = {
     {"help",      CmdHelp,                   AlwaysAvailable, "This help."},
     {"info",      CmdHFCipurseInfo,          IfPm3Iso14443a,  "Info about Cipurse tag."},
diff --git a/client/src/cmdhfcipurse.h b/client/src/cmdhfcipurse.h
index 86d0f63be..85e98ed60 100644
--- a/client/src/cmdhfcipurse.h
+++ b/client/src/cmdhfcipurse.h
@@ -20,4 +20,6 @@
 
 int CmdHFCipurse(const char *Cmd);
 
+bool CheckCardCipurse(void);
+
 #endif

From b1b6d3127ec87b6567739b56ad10329a42406621 Mon Sep 17 00:00:00 2001
From: merlokk <807634+merlokk@users.noreply.github.com>
Date: Fri, 28 May 2021 19:06:14 +0300
Subject: [PATCH 03/39] fix cmake

---
 client/CMakeLists.txt | 1 +
 1 file changed, 1 insertion(+)

diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt
index 420daa1a2..aab617889 100644
--- a/client/CMakeLists.txt
+++ b/client/CMakeLists.txt
@@ -211,6 +211,7 @@ set (TARGET_SOURCES
         ${PM3_ROOT}/client/src/fido/fidocore.c
         ${PM3_ROOT}/client/src/iso7816/apduinfo.c
         ${PM3_ROOT}/client/src/iso7816/iso7816core.c
+        ${PM3_ROOT}/client/src/cipurse/cipursecore.c
         ${PM3_ROOT}/client/src/loclass/cipher.c
         ${PM3_ROOT}/client/src/loclass/cipherutils.c
         ${PM3_ROOT}/client/src/loclass/elite_crack.c

From b309fe519b6f6b81b5aad44f77ea883487cf010b Mon Sep 17 00:00:00 2001
From: merlokk <807634+merlokk@users.noreply.github.com>
Date: Fri, 28 May 2021 19:57:43 +0300
Subject: [PATCH 04/39] GetChallenge works

---
 client/src/cipurse/cipursecore.c | 68 ++++++++++++++++++++++++++++++++
 client/src/cipurse/cipursecore.h |  4 ++
 client/src/cmdhfcipurse.c        | 31 +++++++++++++++
 3 files changed, 103 insertions(+)

diff --git a/client/src/cipurse/cipursecore.c b/client/src/cipurse/cipursecore.c
index 8f61d843d..f4dea46b4 100644
--- a/client/src/cipurse/cipursecore.c
+++ b/client/src/cipurse/cipursecore.c
@@ -11,15 +11,83 @@
 #include "cipursecore.h"
 
 #include "commonutil.h"  // ARRAYLEN
+#include "comms.h"       // DropField
+#include "util_posix.h"  // msleep
 
+#include "cmdhf14a.h"
 #include "emv/emvcore.h"
 #include "emv/emvjson.h"
 #include "ui.h"
 #include "util.h"
 
+static int CIPURSEExchangeEx(bool ActivateField, bool LeaveFieldON, sAPDU apdu, bool IncludeLe, uint16_t Le, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
+    uint8_t data[APDU_RES_LEN] = {0};
+
+    *ResultLen = 0;
+    if (sw) *sw = 0;
+    uint16_t isw = 0;
+    int res = 0;
+
+    if (ActivateField) {
+        DropField();
+        msleep(50);
+    }
+
+    // COMPUTE APDU
+    int datalen = 0;
+    uint16_t xle = IncludeLe ? 0x100 : 0x00;
+    if (xle == 0x100 && Le != 0)
+        xle = Le;
+    if (APDUEncodeS(&apdu, false, xle, data, &datalen)) {
+        PrintAndLogEx(ERR, "APDU encoding error.");
+        return 201;
+    }
+
+    if (GetAPDULogging())
+        PrintAndLogEx(SUCCESS, ">>>> %s", sprint_hex(data, datalen));
+
+    res = ExchangeAPDU14a(data, datalen, ActivateField, LeaveFieldON, Result, (int)MaxResultLen, (int *)ResultLen);
+    if (res) {
+        return res;
+    }
+
+    if (GetAPDULogging())
+        PrintAndLogEx(SUCCESS, "<<<< %s", sprint_hex(Result, *ResultLen));
+
+    if (*ResultLen < 2) {
+        return 200;
+    }
+
+    *ResultLen -= 2;
+    isw = Result[*ResultLen] * 0x0100 + Result[*ResultLen + 1];
+    if (sw)
+        *sw = isw;
+
+    if (isw != 0x9000) {
+        if (GetAPDULogging()) {
+            if (*sw >> 8 == 0x61) {
+                PrintAndLogEx(ERR, "APDU chaining len:%02x -->", *sw & 0xff);
+            } else {
+                PrintAndLogEx(ERR, "APDU(%02x%02x) ERROR: [%4X] %s", apdu.CLA, apdu.INS, isw, GetAPDUCodeDescription(*sw >> 8, *sw & 0xff));
+                return 5;
+            }
+        }
+    }
+
+    return PM3_SUCCESS;
+}
+
+/*static int CIPURSEExchange(sAPDU apdu, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
+    return CIPURSEExchangeEx(false, true, apdu, true, 0, Result, MaxResultLen, ResultLen, sw);
+}*/
+
 int CIPURSESelect(bool ActivateField, bool LeaveFieldON, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
     uint8_t data[] = {0x41, 0x44, 0x20, 0x46, 0x31};
 
     return EMVSelect(ECC_CONTACTLESS, ActivateField, LeaveFieldON, data, sizeof(data), Result, MaxResultLen, ResultLen, sw, NULL);
 }
 
+int CIPURSEChallenge(uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
+    return CIPURSEExchangeEx(false, true, (sAPDU) {0x00, 0x84, 0x00, 0x00, 0x00, NULL}, true, 0x16, Result, MaxResultLen, ResultLen, sw);
+}
+
diff --git a/client/src/cipurse/cipursecore.h b/client/src/cipurse/cipursecore.h
index 7598b8343..8a18011f2 100644
--- a/client/src/cipurse/cipursecore.h
+++ b/client/src/cipurse/cipursecore.h
@@ -12,10 +12,14 @@
 #define __CIPURSECORE_H__
 
 #include "common.h"
+#include "emv/apduinfo.h"
+
 
 #include <jansson.h>
 #include "emv/apduinfo.h" // sAPDU
 
 int CIPURSESelect(bool ActivateField, bool LeaveFieldON, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);
 
+int CIPURSEChallenge(uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);
+
 #endif /* __CIPURSECORE_H__ */
diff --git a/client/src/cmdhfcipurse.c b/client/src/cmdhfcipurse.c
index 1a1cd7770..a1e9176b1 100644
--- a/client/src/cmdhfcipurse.c
+++ b/client/src/cmdhfcipurse.c
@@ -80,6 +80,36 @@ static int CmdHFCipurseInfo(const char *Cmd) {
     return PM3_SUCCESS;
 }
 
+static int CmdHFCipurseAuth(const char *Cmd) {
+    uint8_t buf[APDU_RES_LEN] = {0};
+    size_t len = 0;
+    uint16_t sw = 0;
+    
+    SetAPDULogging(true);
+    
+    int res = CIPURSESelect(true, true, buf, sizeof(buf), &len, &sw);
+    if (res != 0 || sw != 0x9000) {
+        PrintAndLogEx(ERR, "Cipurse select error. Card returns 0x%04x.", sw);
+        DropField();
+        return PM3_ESOFT;
+    }
+
+    res = CIPURSEChallenge(buf, sizeof(buf), &len, &sw);
+    if (res != 0 || len != 0x16) {
+        PrintAndLogEx(ERR, "Cipurse get challenge error. Card returns 0x%04x.", sw);
+        DropField();
+        return PM3_ESOFT;
+    }
+    
+    
+    DropField();
+    return PM3_SUCCESS;
+}
+
+
+
+
+
 
 
 
@@ -96,6 +126,7 @@ bool CheckCardCipurse(void) {
 static command_t CommandTable[] = {
     {"help",      CmdHelp,                   AlwaysAvailable, "This help."},
     {"info",      CmdHFCipurseInfo,          IfPm3Iso14443a,  "Info about Cipurse tag."},
+    {"auth",      CmdHFCipurseAuth,          IfPm3Iso14443a,  "Authentication."},
     {NULL, NULL, 0, NULL}
 };
 

From ce2b22a6a7353d8506ceb0444ac96b747f384dbf Mon Sep 17 00:00:00 2001
From: merlokk <807634+merlokk@users.noreply.github.com>
Date: Fri, 28 May 2021 21:00:54 +0300
Subject: [PATCH 05/39] cipurse crypto sketch

---
 client/src/cipurse/cipursecrypto.c | 23 ++++++++++++++++
 client/src/cipurse/cipursecrypto.h | 42 ++++++++++++++++++++++++++++++
 2 files changed, 65 insertions(+)
 create mode 100644 client/src/cipurse/cipursecrypto.c
 create mode 100644 client/src/cipurse/cipursecrypto.h

diff --git a/client/src/cipurse/cipursecrypto.c b/client/src/cipurse/cipursecrypto.c
new file mode 100644
index 000000000..0f928ea1e
--- /dev/null
+++ b/client/src/cipurse/cipursecrypto.c
@@ -0,0 +1,23 @@
+//-----------------------------------------------------------------------------
+// Copyright (C) 2021 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.
+//-----------------------------------------------------------------------------
+// CIPURSE crypto primitives
+//-----------------------------------------------------------------------------
+
+#include "cipursecore.h"
+
+#include "commonutil.h"  // ARRAYLEN
+#include "comms.h"       // DropField
+#include "util_posix.h"  // msleep
+
+#include "cmdhf14a.h"
+#include "emv/emvcore.h"
+#include "emv/emvjson.h"
+#include "ui.h"
+#include "util.h"
+
+
diff --git a/client/src/cipurse/cipursecrypto.h b/client/src/cipurse/cipursecrypto.h
new file mode 100644
index 000000000..f2e0dbfa2
--- /dev/null
+++ b/client/src/cipurse/cipursecrypto.h
@@ -0,0 +1,42 @@
+//-----------------------------------------------------------------------------
+// Copyright (C) 2021 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.
+//-----------------------------------------------------------------------------
+// CIPURSE crypto primitives
+//-----------------------------------------------------------------------------
+
+#ifndef __CIPURSECRYPTO_H__
+#define __CIPURSECRYPTO_H__
+
+#include "common.h"
+
+enum CipurseChannelSecurityLevel {
+    CPSNone,
+    CPSPlain,
+    CPSMACed,
+    CPSEncrypted
+}
+
+struct CipurseSession {
+    uint8_t keyId,
+    uint8_t[16] key,
+    
+    uint8_t[16] RP,
+    uint8_t[6]  rP,
+    uint8_t[16] RT,
+    uint8_t[6]  rT,
+    
+    uint8_t[16] k0,
+    uint8_t[16] cP,
+    
+    uint8_t[16] frameKey,
+    uint8_t[16] frameKey1
+}
+
+
+
+
+#endif /* __CIPURSECRYPTO_H__ */

From 90f930659c092ebe0c2917641f3410e30a5fda4a Mon Sep 17 00:00:00 2001
From: merlokk <807634+merlokk@users.noreply.github.com>
Date: Sat, 29 May 2021 15:21:53 +0300
Subject: [PATCH 06/39] add crypto sketch

---
 client/Makefile                    |  1 +
 client/src/cipurse/cipursecrypto.c | 44 +++++++++++++++++++++++++++++-
 client/src/cipurse/cipursecrypto.h | 35 +++++++++++++++---------
 client/src/cmdhfcipurse.c          |  8 ++++++
 4 files changed, 74 insertions(+), 14 deletions(-)

diff --git a/client/Makefile b/client/Makefile
index 2f5478b39..c0f87e0a7 100644
--- a/client/Makefile
+++ b/client/Makefile
@@ -558,6 +558,7 @@ SRCS =  aiddesfire.c \
 		fido/cbortools.c \
 		fido/fidocore.c \
 		cipurse/cipursecore.c \
+		cipurse/cipursecrypto.c \
 		fileutils.c \
 		flash.c \
 		generator.c \
diff --git a/client/src/cipurse/cipursecrypto.c b/client/src/cipurse/cipursecrypto.c
index 0f928ea1e..e020676c6 100644
--- a/client/src/cipurse/cipursecrypto.c
+++ b/client/src/cipurse/cipursecrypto.c
@@ -8,7 +8,7 @@
 // CIPURSE crypto primitives
 //-----------------------------------------------------------------------------
 
-#include "cipursecore.h"
+#include "cipursecrypto.h"
 
 #include "commonutil.h"  // ARRAYLEN
 #include "comms.h"       // DropField
@@ -20,4 +20,46 @@
 #include "ui.h"
 #include "util.h"
 
+void CipurseClearContext(CipurseContext *ctx) {
+    if (ctx == NULL)
+        return;
+    
+    memset(ctx, 0, sizeof(CipurseContext));
+}
 
+void CipurseSetKey(CipurseContext *ctx, uint8_t keyId, uint8_t *key) {
+    if (ctx == NULL)
+        return;
+    
+    CipurseClearContext(ctx);
+    
+    ctx->keyId = keyId;
+    memcpy(ctx->key, key, member_size(CipurseContext, key));
+}
+
+void CipurseSetRandomFromPICC(CipurseContext *ctx, uint8_t *random) {
+    if (ctx == NULL)
+        return;
+      
+    memcpy(ctx->RP, random, member_size(CipurseContext, RP));
+    memcpy(ctx->rP, random + member_size(CipurseContext, RP), member_size(CipurseContext, rP));
+}
+
+void CipurseSetRandomHost(CipurseContext *ctx) {
+    memset(ctx->RT, 0x10, member_size(CipurseContext, RT));
+    memset(ctx->rT, 0x20, member_size(CipurseContext, rT));
+}
+
+void CipurseAuthenticateHost(CipurseContext *ctx) {
+    if (ctx == NULL)
+        return;
+    
+/*        RT = Random.nextBytes(16)
+        rT = Random.nextBytes(6)
+
+        val cP = generateK0AndGetCp(key, RP, rP, RT, rT) ?: return Pair(null, null)
+
+        return Pair(cP + RT + rT, generateCT(RT))*/
+        
+        
+}
diff --git a/client/src/cipurse/cipursecrypto.h b/client/src/cipurse/cipursecrypto.h
index f2e0dbfa2..07019f6ca 100644
--- a/client/src/cipurse/cipursecrypto.h
+++ b/client/src/cipurse/cipursecrypto.h
@@ -13,28 +13,37 @@
 
 #include "common.h"
 
+#define member_size(type, member) sizeof(((type *)0)->member)
+
 enum CipurseChannelSecurityLevel {
     CPSNone,
     CPSPlain,
     CPSMACed,
     CPSEncrypted
-}
+};
 
-struct CipurseSession {
-    uint8_t keyId,
-    uint8_t[16] key,
+typedef struct CipurseContextS {
+    uint8_t keyId;
+    uint8_t key[16];
     
-    uint8_t[16] RP,
-    uint8_t[6]  rP,
-    uint8_t[16] RT,
-    uint8_t[6]  rT,
+    uint8_t RP[16];
+    uint8_t rP[6];
+    uint8_t RT[16];
+    uint8_t rT[6];
     
-    uint8_t[16] k0,
-    uint8_t[16] cP,
+    uint8_t frameKey0[16];
+    uint8_t cP[16];
     
-    uint8_t[16] frameKey,
-    uint8_t[16] frameKey1
-}
+    uint8_t frameKey[16];
+    uint8_t frameKeyNext[16];
+} CipurseContext;
+
+void CipurseClearContext(CipurseContext *ctx);
+void CipurseSetKey(CipurseContext *ctx, uint8_t keyId, uint8_t *key);
+void CipurseSetRandomFromPICC(CipurseContext *ctx, uint8_t *random);
+void CipurseSetRandomHost(CipurseContext *ctx);
+
+void CipurseAuthenticateHost(CipurseContext *ctx);
 
 
 
diff --git a/client/src/cmdhfcipurse.c b/client/src/cmdhfcipurse.c
index a1e9176b1..1806e4be4 100644
--- a/client/src/cmdhfcipurse.c
+++ b/client/src/cmdhfcipurse.c
@@ -24,6 +24,7 @@
 #include "cliparser.h"
 #include "cmdhfcipurse.h"
 #include "cipurse/cipursecore.h"
+#include "cipurse/cipursecrypto.h"
 #include "ui.h"
 #include "cmdhf14a.h"
 #include "cmdtrace.h"
@@ -93,6 +94,10 @@ static int CmdHFCipurseAuth(const char *Cmd) {
         DropField();
         return PM3_ESOFT;
     }
+    
+    uint8_t key[] = {0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73};
+    CipurseContext ctx = {0};
+    CipurseSetKey(&ctx, 1, key);
 
     res = CIPURSEChallenge(buf, sizeof(buf), &len, &sw);
     if (res != 0 || len != 0x16) {
@@ -100,6 +105,9 @@ static int CmdHFCipurseAuth(const char *Cmd) {
         DropField();
         return PM3_ESOFT;
     }
+    CipurseSetRandomFromPICC(&ctx, buf);
+    
+    
     
     
     DropField();

From 05c8f313dda50f6097edbc0db011efcb944a91d3 Mon Sep 17 00:00:00 2001
From: merlokk <807634+merlokk@users.noreply.github.com>
Date: Sat, 29 May 2021 15:22:20 +0300
Subject: [PATCH 07/39] add auth command and some command's sketches

---
 client/src/cipurse/cipursecore.c | 4 ++++
 client/src/cipurse/cipursecore.h | 9 +++++++++
 2 files changed, 13 insertions(+)

diff --git a/client/src/cipurse/cipursecore.c b/client/src/cipurse/cipursecore.c
index f4dea46b4..45c276f2c 100644
--- a/client/src/cipurse/cipursecore.c
+++ b/client/src/cipurse/cipursecore.c
@@ -19,6 +19,7 @@
 #include "emv/emvjson.h"
 #include "ui.h"
 #include "util.h"
+#include "cipurse/cipursecrypto.h"
 
 static int CIPURSEExchangeEx(bool ActivateField, bool LeaveFieldON, sAPDU apdu, bool IncludeLe, uint16_t Le, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
     uint8_t data[APDU_RES_LEN] = {0};
@@ -91,3 +92,6 @@ int CIPURSEChallenge(uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, ui
     return CIPURSEExchangeEx(false, true, (sAPDU) {0x00, 0x84, 0x00, 0x00, 0x00, NULL}, true, 0x16, Result, MaxResultLen, ResultLen, sw);
 }
 
+int CIPURSEMutalAuthenticate(uint8_t keyIndex, uint8_t *params, uint8_t paramslen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
+    return CIPURSEExchangeEx(false, true, (sAPDU) {0x00, 0x82, 0x00, keyIndex, paramslen, params}, true, 0x10, Result, MaxResultLen, ResultLen, sw);
+}
diff --git a/client/src/cipurse/cipursecore.h b/client/src/cipurse/cipursecore.h
index 8a18011f2..47671d0c8 100644
--- a/client/src/cipurse/cipursecore.h
+++ b/client/src/cipurse/cipursecore.h
@@ -21,5 +21,14 @@
 int CIPURSESelect(bool ActivateField, bool LeaveFieldON, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);
 
 int CIPURSEChallenge(uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);
+int CIPURSEMutalAuthenticate(uint8_t keyIndex, uint8_t *params, uint8_t paramslen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);
+
+int CIPURSECreateFile(uint16_t fileID, uint8_t *fileAttr);
+int CIPURSEDeleteFile(uint16_t fileID);
+
+int CIPURSESelectFile(uint16_t fileID);
+int CIPURSEReadFileAttributes(uint8_t *data, uint16_t *datalen);
+int CIPURSEReadBinary(uint16_t offset, uint8_t *data, uint16_t *datalen);
+int CIPURSEUpdateBinary(uint16_t offset, uint8_t *data, uint16_t datalen);
 
 #endif /* __CIPURSECORE_H__ */

From f17cbfe412a2ab55e285d8f2600995b1a5715774 Mon Sep 17 00:00:00 2001
From: merlokk <807634+merlokk@users.noreply.github.com>
Date: Sat, 29 May 2021 15:51:08 +0300
Subject: [PATCH 08/39] add some consts and kvv

---
 client/src/cipurse/cipursecrypto.c | 28 ++++++++++++++++++++--------
 client/src/cipurse/cipursecrypto.h | 14 ++++++++++----
 client/src/cmdhfcipurse.c          |  4 ++++
 3 files changed, 34 insertions(+), 12 deletions(-)

diff --git a/client/src/cipurse/cipursecrypto.c b/client/src/cipurse/cipursecrypto.c
index e020676c6..16917b7aa 100644
--- a/client/src/cipurse/cipursecrypto.c
+++ b/client/src/cipurse/cipursecrypto.c
@@ -17,9 +17,26 @@
 #include "cmdhf14a.h"
 #include "emv/emvcore.h"
 #include "emv/emvjson.h"
+#include "crypto/libpcrypto.h"
 #include "ui.h"
 #include "util.h"
 
+uint8_t AESData0[CIPURSE_AES_KEY_LENGTH] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+static void CipurseCGenerateK0AndGetCp(CipurseContext *ctx) {
+
+}
+
+static void CipurseCGenerateCT(uint8_t *RT, uint8_t *CT) {
+    
+}
+
+void CipurseCGetKVV(uint8_t *key, uint8_t *kvv) {
+    uint8_t res[16] = {0};
+    aes_encode(NULL, key, AESData0, res, CIPURSE_AES_KEY_LENGTH);
+    memcpy(kvv, res, CIPURSE_KVV_LENGTH);
+}
+
 void CipurseClearContext(CipurseContext *ctx) {
     if (ctx == NULL)
         return;
@@ -54,12 +71,7 @@ void CipurseAuthenticateHost(CipurseContext *ctx) {
     if (ctx == NULL)
         return;
     
-/*        RT = Random.nextBytes(16)
-        rT = Random.nextBytes(6)
-
-        val cP = generateK0AndGetCp(key, RP, rP, RT, rT) ?: return Pair(null, null)
-
-        return Pair(cP + RT + rT, generateCT(RT))*/
-        
-        
+    CipurseSetRandomHost(ctx);
+    CipurseCGenerateK0AndGetCp(ctx);
+    CipurseCGenerateCT(ctx->RT, ctx->CT);    
 }
diff --git a/client/src/cipurse/cipursecrypto.h b/client/src/cipurse/cipursecrypto.h
index 07019f6ca..04e3d27b5 100644
--- a/client/src/cipurse/cipursecrypto.h
+++ b/client/src/cipurse/cipursecrypto.h
@@ -13,6 +13,11 @@
 
 #include "common.h"
 
+#define CIPURSE_KVV_LENGTH 4
+#define CIPURSE_AES_KEY_LENGTH 16
+#define CIPURSE_SECURITY_PARAM_N 6
+#define OSPT_MAC_LENGTH 8
+
 #define member_size(type, member) sizeof(((type *)0)->member)
 
 enum CipurseChannelSecurityLevel {
@@ -24,7 +29,7 @@ enum CipurseChannelSecurityLevel {
 
 typedef struct CipurseContextS {
     uint8_t keyId;
-    uint8_t key[16];
+    uint8_t key[CIPURSE_AES_KEY_LENGTH];
     
     uint8_t RP[16];
     uint8_t rP[6];
@@ -33,9 +38,10 @@ typedef struct CipurseContextS {
     
     uint8_t frameKey0[16];
     uint8_t cP[16];
+    uint8_t CT[16];
     
-    uint8_t frameKey[16];
-    uint8_t frameKeyNext[16];
+    uint8_t frameKey[CIPURSE_AES_KEY_LENGTH];
+    uint8_t frameKeyNext[CIPURSE_AES_KEY_LENGTH];
 } CipurseContext;
 
 void CipurseClearContext(CipurseContext *ctx);
@@ -46,6 +52,6 @@ void CipurseSetRandomHost(CipurseContext *ctx);
 void CipurseAuthenticateHost(CipurseContext *ctx);
 
 
-
+void CipurseCGetKVV(uint8_t *key, uint8_t *kvv);
 
 #endif /* __CIPURSECRYPTO_H__ */
diff --git a/client/src/cmdhfcipurse.c b/client/src/cmdhfcipurse.c
index 1806e4be4..cac5ab03c 100644
--- a/client/src/cmdhfcipurse.c
+++ b/client/src/cmdhfcipurse.c
@@ -98,6 +98,10 @@ static int CmdHFCipurseAuth(const char *Cmd) {
     uint8_t key[] = {0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73};
     CipurseContext ctx = {0};
     CipurseSetKey(&ctx, 1, key);
+    
+    uint8_t kvv[CIPURSE_KVV_LENGTH] = {0};
+    CipurseCGetKVV(key, kvv);
+    PrintAndLogEx(INFO, "Key: %s KVV: %s", sprint_hex(key, CIPURSE_AES_KEY_LENGTH), sprint_hex_inrow(kvv, CIPURSE_KVV_LENGTH));
 
     res = CIPURSEChallenge(buf, sizeof(buf), &len, &sw);
     if (res != 0 || len != 0x16) {

From fa850788224e1f7b2b4337d58623e14baf0d4c91 Mon Sep 17 00:00:00 2001
From: merlokk <807634+merlokk@users.noreply.github.com>
Date: Sat, 29 May 2021 16:09:34 +0300
Subject: [PATCH 09/39] high level of auth and some renamings

---
 client/src/cipurse/cipursecrypto.c | 25 +++++++++++++++++--------
 client/src/cipurse/cipursecrypto.h | 10 +++++-----
 client/src/cmdhfcipurse.c          | 17 ++++++++++++++---
 3 files changed, 36 insertions(+), 16 deletions(-)

diff --git a/client/src/cipurse/cipursecrypto.c b/client/src/cipurse/cipursecrypto.c
index 16917b7aa..90d6f1722 100644
--- a/client/src/cipurse/cipursecrypto.c
+++ b/client/src/cipurse/cipursecrypto.c
@@ -37,24 +37,24 @@ void CipurseCGetKVV(uint8_t *key, uint8_t *kvv) {
     memcpy(kvv, res, CIPURSE_KVV_LENGTH);
 }
 
-void CipurseClearContext(CipurseContext *ctx) {
+void CipurseCClearContext(CipurseContext *ctx) {
     if (ctx == NULL)
         return;
     
     memset(ctx, 0, sizeof(CipurseContext));
 }
 
-void CipurseSetKey(CipurseContext *ctx, uint8_t keyId, uint8_t *key) {
+void CipurseCSetKey(CipurseContext *ctx, uint8_t keyId, uint8_t *key) {
     if (ctx == NULL)
         return;
     
-    CipurseClearContext(ctx);
+    CipurseCClearContext(ctx);
     
     ctx->keyId = keyId;
     memcpy(ctx->key, key, member_size(CipurseContext, key));
 }
 
-void CipurseSetRandomFromPICC(CipurseContext *ctx, uint8_t *random) {
+void CipurseCSetRandomFromPICC(CipurseContext *ctx, uint8_t *random) {
     if (ctx == NULL)
         return;
       
@@ -62,16 +62,25 @@ void CipurseSetRandomFromPICC(CipurseContext *ctx, uint8_t *random) {
     memcpy(ctx->rP, random + member_size(CipurseContext, RP), member_size(CipurseContext, rP));
 }
 
-void CipurseSetRandomHost(CipurseContext *ctx) {
+void CipurseCSetRandomHost(CipurseContext *ctx) {
     memset(ctx->RT, 0x10, member_size(CipurseContext, RT));
     memset(ctx->rT, 0x20, member_size(CipurseContext, rT));
 }
 
-void CipurseAuthenticateHost(CipurseContext *ctx) {
+static void CipurseCFillAuthData(CipurseContext *ctx, uint8_t *authdata) {
+    memcpy(authdata, ctx->cP, member_size(CipurseContext, cP));
+    memcpy(&authdata[member_size(CipurseContext, cP)], ctx->RT, member_size(CipurseContext, RT));
+    memcpy(&authdata[member_size(CipurseContext, cP) + member_size(CipurseContext, RT)], ctx->rT, member_size(CipurseContext, rT));   
+}
+
+void CipurseCAuthenticateHost(CipurseContext *ctx, uint8_t *authdata) {
     if (ctx == NULL)
         return;
     
-    CipurseSetRandomHost(ctx);
+    CipurseCSetRandomHost(ctx);
     CipurseCGenerateK0AndGetCp(ctx);
-    CipurseCGenerateCT(ctx->RT, ctx->CT);    
+    CipurseCGenerateCT(ctx->RT, ctx->CT);
+
+    if (authdata != NULL)
+        CipurseCFillAuthData(ctx, authdata);
 }
diff --git a/client/src/cipurse/cipursecrypto.h b/client/src/cipurse/cipursecrypto.h
index 04e3d27b5..f7b330280 100644
--- a/client/src/cipurse/cipursecrypto.h
+++ b/client/src/cipurse/cipursecrypto.h
@@ -44,12 +44,12 @@ typedef struct CipurseContextS {
     uint8_t frameKeyNext[CIPURSE_AES_KEY_LENGTH];
 } CipurseContext;
 
-void CipurseClearContext(CipurseContext *ctx);
-void CipurseSetKey(CipurseContext *ctx, uint8_t keyId, uint8_t *key);
-void CipurseSetRandomFromPICC(CipurseContext *ctx, uint8_t *random);
-void CipurseSetRandomHost(CipurseContext *ctx);
+void CipurseCClearContext(CipurseContext *ctx);
+void CipurseCSetKey(CipurseContext *ctx, uint8_t keyId, uint8_t *key);
+void CipurseCSetRandomFromPICC(CipurseContext *ctx, uint8_t *random);
+void CipurseCSetRandomHost(CipurseContext *ctx);
 
-void CipurseAuthenticateHost(CipurseContext *ctx);
+void CipurseCAuthenticateHost(CipurseContext *ctx, uint8_t *authdata);
 
 
 void CipurseCGetKVV(uint8_t *key, uint8_t *kvv);
diff --git a/client/src/cmdhfcipurse.c b/client/src/cmdhfcipurse.c
index cac5ab03c..d693f2295 100644
--- a/client/src/cmdhfcipurse.c
+++ b/client/src/cmdhfcipurse.c
@@ -95,24 +95,35 @@ static int CmdHFCipurseAuth(const char *Cmd) {
         return PM3_ESOFT;
     }
     
+    uint8_t keyId = 1;
     uint8_t key[] = {0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73};
     CipurseContext ctx = {0};
-    CipurseSetKey(&ctx, 1, key);
+    CipurseCSetKey(&ctx, 1, key);
     
     uint8_t kvv[CIPURSE_KVV_LENGTH] = {0};
     CipurseCGetKVV(key, kvv);
     PrintAndLogEx(INFO, "Key: %s KVV: %s", sprint_hex(key, CIPURSE_AES_KEY_LENGTH), sprint_hex_inrow(kvv, CIPURSE_KVV_LENGTH));
 
+    // get RP, rP
     res = CIPURSEChallenge(buf, sizeof(buf), &len, &sw);
     if (res != 0 || len != 0x16) {
         PrintAndLogEx(ERR, "Cipurse get challenge error. Card returns 0x%04x.", sw);
         DropField();
         return PM3_ESOFT;
     }
-    CipurseSetRandomFromPICC(&ctx, buf);
-    
+    CipurseCSetRandomFromPICC(&ctx, buf);
     
+    // make auth data
+    uint8_t authparams[16 + 16 + 6] = {0};
+    CipurseCAuthenticateHost(&ctx, authparams);
     
+    // authenticate
+    res = CIPURSEMutalAuthenticate(keyId, authparams, sizeof(authparams), buf, sizeof(buf), &len, &sw);
+    if (res != 0 || sw != 0x9000 || len != 0x16) {
+        PrintAndLogEx(ERR, "Cipurse authentication error. Card returns 0x%04x.", sw);
+        DropField();
+        return PM3_ESOFT;
+    }
     
     DropField();
     return PM3_SUCCESS;

From 37daaa2120960f70d58f670b0e3246a5754d6f21 Mon Sep 17 00:00:00 2001
From: merlokk <807634+merlokk@users.noreply.github.com>
Date: Sat, 29 May 2021 16:20:48 +0300
Subject: [PATCH 10/39] GenerateK0AndCp sketch

---
 client/src/cipurse/cipursecrypto.c | 48 ++++++++++++++++++++++++++----
 client/src/cipurse/cipursecrypto.h |  2 +-
 2 files changed, 44 insertions(+), 6 deletions(-)

diff --git a/client/src/cipurse/cipursecrypto.c b/client/src/cipurse/cipursecrypto.c
index 90d6f1722..996d43b1e 100644
--- a/client/src/cipurse/cipursecrypto.c
+++ b/client/src/cipurse/cipursecrypto.c
@@ -23,12 +23,50 @@
 
 uint8_t AESData0[CIPURSE_AES_KEY_LENGTH] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
 
-static void CipurseCGenerateK0AndGetCp(CipurseContext *ctx) {
+static void CipurseCGenerateK0AndCp(CipurseContext *ctx) {
+    /*        // session key derivation function
+        // kP := NLM(EXT(kID), rP)
+        // k0 := AES(key=PAD2(kP) XOR PAD(rT),kID) XOR kID
+        var temp1 = CryptoUtils.extFunction(kid, CIPURSE_SECURITY_PARAM_N) ?: return null
+        val kp = CryptoUtils.computeNLM(rP, temp1) ?: return null
+        temp1 = CryptoUtils.pad2(kp) ?: return null
+        val temp2 = CryptoUtils.pad(rT) ?: return null
+        temp1 = temp1 xor temp2
 
+        // session key K0
+        k0 = AesECB.aesEncrypt(temp1, kid) ?: return null
+        k0 = k0 xor kid
+
+        // first frame key k1, function to calculate k1,
+        // k1 := AES(key = RP; k0 XOR RT) XOR (k0 XOR RT)
+        temp1 = k0 xor RT
+        val temp3: ByteArray = AesECB.aesEncrypt(RP, temp1) ?: return null
+        frameKeyi = temp3 xor temp1
+        Log.d(TAG, "frame key=${Utils.toHex(frameKeyi)}")
+
+        // function to caluclate cP := AES(key=k0, RP).
+        // terminal response
+        return AesECB.aesEncrypt(k0, RP)*/
+        
+    uint8_t temp1[CIPURSE_AES_KEY_LENGTH] = {0};
+    uint8_t temp2[CIPURSE_AES_KEY_LENGTH] = {0};
+        
+    // session key derivation function
+    // kP := NLM(EXT(kID), rP)
+    // k0 := AES(key=PAD2(kP) XOR PAD(rT),kID) XOR kID  
+        
+    // session key K0    
+        
+    // first frame key k1, function to calculate k1,
+    // k1 := AES(key = RP; k0 XOR RT) XOR (k0 XOR RT)
+        
+    // function to caluclate cP := AES(key=k0, RP).
+    // terminal response
+    aes_encode(NULL, ctx->k0, ctx->RP, ctx->Cp, CIPURSE_AES_KEY_LENGTH);
 }
 
-static void CipurseCGenerateCT(uint8_t *RT, uint8_t *CT) {
-    
+static void CipurseCGenerateCT(uint8_t *k0, uint8_t *RT, uint8_t *CT) {
+    aes_encode(NULL, k0, RT, CT, CIPURSE_AES_KEY_LENGTH);
 }
 
 void CipurseCGetKVV(uint8_t *key, uint8_t *kvv) {
@@ -78,8 +116,8 @@ void CipurseCAuthenticateHost(CipurseContext *ctx, uint8_t *authdata) {
         return;
     
     CipurseCSetRandomHost(ctx);
-    CipurseCGenerateK0AndGetCp(ctx);
-    CipurseCGenerateCT(ctx->RT, ctx->CT);
+    CipurseCGenerateK0AndCp(ctx);
+    CipurseCGenerateCT(ctx->k0, ctx->RT, ctx->CT);
 
     if (authdata != NULL)
         CipurseCFillAuthData(ctx, authdata);
diff --git a/client/src/cipurse/cipursecrypto.h b/client/src/cipurse/cipursecrypto.h
index f7b330280..c2f8c2de4 100644
--- a/client/src/cipurse/cipursecrypto.h
+++ b/client/src/cipurse/cipursecrypto.h
@@ -36,7 +36,7 @@ typedef struct CipurseContextS {
     uint8_t RT[16];
     uint8_t rT[6];
     
-    uint8_t frameKey0[16];
+    uint8_t k0[16];
     uint8_t cP[16];
     uint8_t CT[16];
     

From 1a937b33c49d1ad92fb557fe1c7d7a779a2f1556 Mon Sep 17 00:00:00 2001
From: merlokk <807634+merlokk@users.noreply.github.com>
Date: Sat, 29 May 2021 18:37:23 +0300
Subject: [PATCH 11/39] auth code done. needs tests

---
 client/src/cipurse/cipursecrypto.c | 88 +++++++++++++++++++++++++++++-
 client/src/cipurse/cipursecrypto.h | 15 ++---
 client/src/cmdhfcipurse.c          | 56 ++++++++++++++++---
 3 files changed, 140 insertions(+), 19 deletions(-)

diff --git a/client/src/cipurse/cipursecrypto.c b/client/src/cipurse/cipursecrypto.c
index 996d43b1e..9da227d58 100644
--- a/client/src/cipurse/cipursecrypto.c
+++ b/client/src/cipurse/cipursecrypto.c
@@ -23,6 +23,74 @@
 
 uint8_t AESData0[CIPURSE_AES_KEY_LENGTH] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
 
+static void bin_xor(uint8_t *d1, uint8_t *d2, size_t len) {
+    for(size_t i = 0; i < len; i++)
+        d1[i] = d1[i] ^ d2[i];
+}
+
+static void bin_ext(uint8_t *dst, size_t dstlen, uint8_t *src, size_t srclen) {
+    if (srclen > dstlen)
+        memcpy(dst, &src[srclen - dstlen], dstlen);
+    else
+        memcpy(dst, src, dstlen);
+}
+
+static void bin_pad(uint8_t *dst, size_t dstlen, uint8_t *src, size_t srclen) {
+    memset(dst, 0, dstlen);
+    if (srclen <= dstlen)
+        memcpy(&dst[dstlen - srclen], src, srclen);
+    else
+        memcpy(dst, src, dstlen);
+}
+
+static void bin_pad2(uint8_t *dst, size_t dstlen, uint8_t *src, size_t srclen) {
+    memset(dst, 0, dstlen);
+    uint8_t dbl[srclen * 2];
+    memcpy(dbl, src, srclen);
+    memcpy(&dbl[srclen], src, srclen);
+    bin_pad(dst, dstlen, dbl, srclen * 2);
+}
+
+static uint64_t rotateLeft48(uint64_t src) {
+    uint64_t dst = src << 1;
+    if (dst && 0x0001000000000000UL != 0) {
+        dst = dst | 1;
+        dst = dst & 0x0000ffffffffffffUL;
+    }
+    return dst;
+}
+
+static uint64_t computeNLM48(uint64_t x, uint64_t y) {
+    uint64_t res = 0;
+    
+    for (int i = 0; i < 48; i++) {
+        res = rotateLeft48(res);
+        if (res & 1)
+            res = res ^ CIPURSE_POLY;
+        y = rotateLeft48(y);
+        if (y & 1)
+            res = res ^ x;    
+        } 
+    return res;
+}
+
+static void computeNLM(uint8_t *res, uint8_t *x, uint8_t *y) {
+    uint64_t x64 = 0;
+    uint64_t y64 = 0;
+    
+    for (int i = 0; i < 6; i++) {
+        x64 = (x64 << 8) | x[i];
+        y64 = (y64 << 8) | y[i];
+    }
+    
+    uint64_t res64 = computeNLM48(x64, y64);
+    
+    for (int i = 0; i < 6; i++) {
+        res[5 - i] = res64 & 0xff;
+        res64 = res64 >> 8;
+    }
+}
+
 static void CipurseCGenerateK0AndCp(CipurseContext *ctx) {
     /*        // session key derivation function
         // kP := NLM(EXT(kID), rP)
@@ -50,25 +118,39 @@ static void CipurseCGenerateK0AndCp(CipurseContext *ctx) {
         
     uint8_t temp1[CIPURSE_AES_KEY_LENGTH] = {0};
     uint8_t temp2[CIPURSE_AES_KEY_LENGTH] = {0};
+    uint8_t kp[CIPURSE_SECURITY_PARAM_N] = {0};
         
     // session key derivation function
     // kP := NLM(EXT(kID), rP)
-    // k0 := AES(key=PAD2(kP) XOR PAD(rT),kID) XOR kID  
-        
+    // k0 := AES(key=PAD2(kP) XOR PAD(rT),kID) XOR kID
+    bin_ext(temp1, CIPURSE_SECURITY_PARAM_N, ctx->key, CIPURSE_AES_KEY_LENGTH);
+    computeNLM(kp, ctx->rP, temp1);  // param sizes == 6 bytes
+    bin_pad2(temp1, CIPURSE_AES_KEY_LENGTH, kp, CIPURSE_SECURITY_PARAM_N);
+    bin_pad(temp2, CIPURSE_AES_KEY_LENGTH, ctx->rT, CIPURSE_SECURITY_PARAM_N);
+    bin_xor(temp1, temp2, CIPURSE_AES_KEY_LENGTH);
+    
     // session key K0    
+    aes_encode(NULL, temp1, ctx->key, ctx->k0, CIPURSE_AES_KEY_LENGTH);
+    bin_xor(ctx->k0, ctx->key, CIPURSE_AES_KEY_LENGTH);
         
     // first frame key k1, function to calculate k1,
     // k1 := AES(key = RP; k0 XOR RT) XOR (k0 XOR RT)
+    memcpy(temp1, ctx->k0, CIPURSE_AES_KEY_LENGTH);
+    bin_xor(temp1, ctx->RT, CIPURSE_AES_KEY_LENGTH);
+    aes_encode(NULL, ctx->RP, temp1, temp2, CIPURSE_AES_KEY_LENGTH);
+    bin_xor(temp1, temp2, CIPURSE_AES_KEY_LENGTH);
+    memcpy(ctx->frameKey, temp1, CIPURSE_AES_KEY_LENGTH);
         
     // function to caluclate cP := AES(key=k0, RP).
     // terminal response
-    aes_encode(NULL, ctx->k0, ctx->RP, ctx->Cp, CIPURSE_AES_KEY_LENGTH);
+    aes_encode(NULL, ctx->k0, ctx->RP, ctx->cP, CIPURSE_AES_KEY_LENGTH);
 }
 
 static void CipurseCGenerateCT(uint8_t *k0, uint8_t *RT, uint8_t *CT) {
     aes_encode(NULL, k0, RT, CT, CIPURSE_AES_KEY_LENGTH);
 }
 
+// from: https://github.com/duychuongvn/cipurse-card-core/blob/master/src/main/java/com/github/duychuongvn/cirpusecard/core/security/securemessaging/CipurseSecureMessage.java#L68
 void CipurseCGetKVV(uint8_t *key, uint8_t *kvv) {
     uint8_t res[16] = {0};
     aes_encode(NULL, key, AESData0, res, CIPURSE_AES_KEY_LENGTH);
diff --git a/client/src/cipurse/cipursecrypto.h b/client/src/cipurse/cipursecrypto.h
index c2f8c2de4..53c171a0c 100644
--- a/client/src/cipurse/cipursecrypto.h
+++ b/client/src/cipurse/cipursecrypto.h
@@ -17,6 +17,7 @@
 #define CIPURSE_AES_KEY_LENGTH 16
 #define CIPURSE_SECURITY_PARAM_N 6
 #define OSPT_MAC_LENGTH 8
+#define CIPURSE_POLY 0x35b088cce172UL
 
 #define member_size(type, member) sizeof(((type *)0)->member)
 
@@ -31,14 +32,14 @@ typedef struct CipurseContextS {
     uint8_t keyId;
     uint8_t key[CIPURSE_AES_KEY_LENGTH];
     
-    uint8_t RP[16];
-    uint8_t rP[6];
-    uint8_t RT[16];
-    uint8_t rT[6];
+    uint8_t RP[CIPURSE_AES_KEY_LENGTH];
+    uint8_t rP[CIPURSE_SECURITY_PARAM_N];
+    uint8_t RT[CIPURSE_AES_KEY_LENGTH];
+    uint8_t rT[CIPURSE_SECURITY_PARAM_N];
     
-    uint8_t k0[16];
-    uint8_t cP[16];
-    uint8_t CT[16];
+    uint8_t k0[CIPURSE_AES_KEY_LENGTH];
+    uint8_t cP[CIPURSE_AES_KEY_LENGTH];
+    uint8_t CT[CIPURSE_AES_KEY_LENGTH];
     
     uint8_t frameKey[CIPURSE_AES_KEY_LENGTH];
     uint8_t frameKeyNext[CIPURSE_AES_KEY_LENGTH];
diff --git a/client/src/cmdhfcipurse.c b/client/src/cmdhfcipurse.c
index d693f2295..1bb3e83d5 100644
--- a/client/src/cmdhfcipurse.c
+++ b/client/src/cmdhfcipurse.c
@@ -85,8 +85,42 @@ static int CmdHFCipurseAuth(const char *Cmd) {
     uint8_t buf[APDU_RES_LEN] = {0};
     size_t len = 0;
     uint16_t sw = 0;
+    uint8_t keyId = 1;
+    uint8_t key[] = {0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73};
     
-    SetAPDULogging(true);
+    CLIParserContext *ctx;
+    CLIParserInit(&ctx, "hf 14a sim",
+                  "Simulate ISO/IEC 14443 type A tag with 4,7 or 10 byte UID",
+                  "hf 14a sim -t 1 --uid 11223344      -> MIFARE Classic 1k\n");
+
+    void *argtable[] = {
+        arg_param_begin,
+        arg_lit0("a",  "apdu",    "show APDU requests and responses"),
+        arg_lit0("v",  "verbose", "show technical data"),
+        arg_int0("n",  "keyid",   "<dec>", "key id"),
+        arg_str0("k",  "key",     "<hex>", "key for authenticate"),
+        arg_param_end
+    };
+    CLIExecWithReturn(ctx, Cmd, argtable, true);
+    
+    bool APDULogging = arg_get_lit(ctx, 1);
+    bool verbose = arg_get_lit(ctx, 2);
+    keyId = arg_get_int_def(ctx, 3, 1);
+    
+    uint8_t hdata[250] = {0};
+    int hdatalen = sizeof(hdata);    
+    CLIGetHexWithReturn(ctx, 4, hdata, &hdatalen);
+    if (hdatalen && hdatalen != 16) {
+        PrintAndLogEx(ERR, "ERROR: key length for AES128 must be 16 bytes only.");
+        CLIParserFree(ctx);
+        return PM3_EINVARG;
+    }
+    if (hdatalen)
+        memcpy(key, hdata, CIPURSE_AES_KEY_LENGTH);
+    
+    SetAPDULogging(APDULogging);
+
+    CLIParserFree(ctx);    
     
     int res = CIPURSESelect(true, true, buf, sizeof(buf), &len, &sw);
     if (res != 0 || sw != 0x9000) {
@@ -95,14 +129,13 @@ static int CmdHFCipurseAuth(const char *Cmd) {
         return PM3_ESOFT;
     }
     
-    uint8_t keyId = 1;
-    uint8_t key[] = {0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73};
-    CipurseContext ctx = {0};
-    CipurseCSetKey(&ctx, 1, key);
+    CipurseContext cpc = {0};
+    CipurseCSetKey(&cpc, 1, key);
     
     uint8_t kvv[CIPURSE_KVV_LENGTH] = {0};
     CipurseCGetKVV(key, kvv);
-    PrintAndLogEx(INFO, "Key: %s KVV: %s", sprint_hex(key, CIPURSE_AES_KEY_LENGTH), sprint_hex_inrow(kvv, CIPURSE_KVV_LENGTH));
+    if (verbose)
+        PrintAndLogEx(INFO, "Key: %s KVV: %s", sprint_hex(key, CIPURSE_AES_KEY_LENGTH), sprint_hex_inrow(kvv, CIPURSE_KVV_LENGTH));
 
     // get RP, rP
     res = CIPURSEChallenge(buf, sizeof(buf), &len, &sw);
@@ -111,16 +144,21 @@ static int CmdHFCipurseAuth(const char *Cmd) {
         DropField();
         return PM3_ESOFT;
     }
-    CipurseCSetRandomFromPICC(&ctx, buf);
+    CipurseCSetRandomFromPICC(&cpc, buf);
     
     // make auth data
     uint8_t authparams[16 + 16 + 6] = {0};
-    CipurseCAuthenticateHost(&ctx, authparams);
+    CipurseCAuthenticateHost(&cpc, authparams);
     
     // authenticate
     res = CIPURSEMutalAuthenticate(keyId, authparams, sizeof(authparams), buf, sizeof(buf), &len, &sw);
     if (res != 0 || sw != 0x9000 || len != 0x16) {
-        PrintAndLogEx(ERR, "Cipurse authentication error. Card returns 0x%04x.", sw);
+        if (sw == 0x6988)
+            PrintAndLogEx(ERR, "Cipurse authentication error. Wrong key.");
+        else if ((sw == 0x6A88))
+            PrintAndLogEx(ERR, "Cipurse authentication error. Wrong key number.");
+        else PrintAndLogEx(ERR, "Cipurse authentication error. Card returns 0x%04x.", sw);
+        
         DropField();
         return PM3_ESOFT;
     }

From 1ba960601121dde0baca55d66b2ae2d3c9f64de2 Mon Sep 17 00:00:00 2001
From: merlokk <807634+merlokk@users.noreply.github.com>
Date: Sat, 29 May 2021 19:46:51 +0300
Subject: [PATCH 12/39] authentication works

---
 client/src/cipurse/cipursecrypto.c | 34 ++++++------------------------
 client/src/cipurse/cipursecrypto.h |  1 +
 client/src/cmdhfcipurse.c          | 21 +++++++++++-------
 3 files changed, 21 insertions(+), 35 deletions(-)

diff --git a/client/src/cipurse/cipursecrypto.c b/client/src/cipurse/cipursecrypto.c
index 9da227d58..14dd8c3a5 100644
--- a/client/src/cipurse/cipursecrypto.c
+++ b/client/src/cipurse/cipursecrypto.c
@@ -53,7 +53,7 @@ static void bin_pad2(uint8_t *dst, size_t dstlen, uint8_t *src, size_t srclen) {
 
 static uint64_t rotateLeft48(uint64_t src) {
     uint64_t dst = src << 1;
-    if (dst && 0x0001000000000000UL != 0) {
+    if (dst & 0x0001000000000000UL) {
         dst = dst | 1;
         dst = dst & 0x0000ffffffffffffUL;
     }
@@ -70,7 +70,7 @@ static uint64_t computeNLM48(uint64_t x, uint64_t y) {
         y = rotateLeft48(y);
         if (y & 1)
             res = res ^ x;    
-        } 
+    }
     return res;
 }
 
@@ -91,31 +91,7 @@ static void computeNLM(uint8_t *res, uint8_t *x, uint8_t *y) {
     }
 }
 
-static void CipurseCGenerateK0AndCp(CipurseContext *ctx) {
-    /*        // session key derivation function
-        // kP := NLM(EXT(kID), rP)
-        // k0 := AES(key=PAD2(kP) XOR PAD(rT),kID) XOR kID
-        var temp1 = CryptoUtils.extFunction(kid, CIPURSE_SECURITY_PARAM_N) ?: return null
-        val kp = CryptoUtils.computeNLM(rP, temp1) ?: return null
-        temp1 = CryptoUtils.pad2(kp) ?: return null
-        val temp2 = CryptoUtils.pad(rT) ?: return null
-        temp1 = temp1 xor temp2
-
-        // session key K0
-        k0 = AesECB.aesEncrypt(temp1, kid) ?: return null
-        k0 = k0 xor kid
-
-        // first frame key k1, function to calculate k1,
-        // k1 := AES(key = RP; k0 XOR RT) XOR (k0 XOR RT)
-        temp1 = k0 xor RT
-        val temp3: ByteArray = AesECB.aesEncrypt(RP, temp1) ?: return null
-        frameKeyi = temp3 xor temp1
-        Log.d(TAG, "frame key=${Utils.toHex(frameKeyi)}")
-
-        // function to caluclate cP := AES(key=k0, RP).
-        // terminal response
-        return AesECB.aesEncrypt(k0, RP)*/
-        
+static void CipurseCGenerateK0AndCp(CipurseContext *ctx) {        
     uint8_t temp1[CIPURSE_AES_KEY_LENGTH] = {0};
     uint8_t temp2[CIPURSE_AES_KEY_LENGTH] = {0};
     uint8_t kp[CIPURSE_SECURITY_PARAM_N] = {0};
@@ -204,3 +180,7 @@ void CipurseCAuthenticateHost(CipurseContext *ctx, uint8_t *authdata) {
     if (authdata != NULL)
         CipurseCFillAuthData(ctx, authdata);
 }
+
+bool CipurseCCheckCT(CipurseContext *ctx, uint8_t *CT) {
+    return (memcmp(CT, ctx->CT, CIPURSE_AES_KEY_LENGTH) == 0);
+}
diff --git a/client/src/cipurse/cipursecrypto.h b/client/src/cipurse/cipursecrypto.h
index 53c171a0c..ca1ed29f3 100644
--- a/client/src/cipurse/cipursecrypto.h
+++ b/client/src/cipurse/cipursecrypto.h
@@ -51,6 +51,7 @@ void CipurseCSetRandomFromPICC(CipurseContext *ctx, uint8_t *random);
 void CipurseCSetRandomHost(CipurseContext *ctx);
 
 void CipurseCAuthenticateHost(CipurseContext *ctx, uint8_t *authdata);
+bool CipurseCCheckCT(CipurseContext *ctx, uint8_t *CT);
 
 
 void CipurseCGetKVV(uint8_t *key, uint8_t *kvv);
diff --git a/client/src/cmdhfcipurse.c b/client/src/cmdhfcipurse.c
index 1bb3e83d5..cea5755f4 100644
--- a/client/src/cmdhfcipurse.c
+++ b/client/src/cmdhfcipurse.c
@@ -87,7 +87,7 @@ static int CmdHFCipurseAuth(const char *Cmd) {
     uint16_t sw = 0;
     uint8_t keyId = 1;
     uint8_t key[] = {0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73};
-    
+
     CLIParserContext *ctx;
     CLIParserInit(&ctx, "hf 14a sim",
                   "Simulate ISO/IEC 14443 type A tag with 4,7 or 10 byte UID",
@@ -130,12 +130,12 @@ static int CmdHFCipurseAuth(const char *Cmd) {
     }
     
     CipurseContext cpc = {0};
-    CipurseCSetKey(&cpc, 1, key);
+    CipurseCSetKey(&cpc, keyId, key);
     
     uint8_t kvv[CIPURSE_KVV_LENGTH] = {0};
     CipurseCGetKVV(key, kvv);
     if (verbose)
-        PrintAndLogEx(INFO, "Key: %s KVV: %s", sprint_hex(key, CIPURSE_AES_KEY_LENGTH), sprint_hex_inrow(kvv, CIPURSE_KVV_LENGTH));
+        PrintAndLogEx(INFO, "Key id: %d key: %s KVV: %s", keyId, sprint_hex(key, CIPURSE_AES_KEY_LENGTH), sprint_hex_inrow(kvv, CIPURSE_KVV_LENGTH));
 
     // get RP, rP
     res = CIPURSEChallenge(buf, sizeof(buf), &len, &sw);
@@ -145,24 +145,29 @@ static int CmdHFCipurseAuth(const char *Cmd) {
         return PM3_ESOFT;
     }
     CipurseCSetRandomFromPICC(&cpc, buf);
-    
+
     // make auth data
     uint8_t authparams[16 + 16 + 6] = {0};
     CipurseCAuthenticateHost(&cpc, authparams);
     
     // authenticate
     res = CIPURSEMutalAuthenticate(keyId, authparams, sizeof(authparams), buf, sizeof(buf), &len, &sw);
-    if (res != 0 || sw != 0x9000 || len != 0x16) {
+    if (res != 0 || sw != 0x9000 || len != 16) {
         if (sw == 0x6988)
-            PrintAndLogEx(ERR, "Cipurse authentication error. Wrong key.");
+            PrintAndLogEx(ERR, "Cipurse authentication " _RED_("error") ". Wrong key.");
         else if ((sw == 0x6A88))
-            PrintAndLogEx(ERR, "Cipurse authentication error. Wrong key number.");
-        else PrintAndLogEx(ERR, "Cipurse authentication error. Card returns 0x%04x.", sw);
+            PrintAndLogEx(ERR, "Cipurse authentication " _RED_("error") ". Wrong key number.");
+        else PrintAndLogEx(ERR, "Cipurse authentication " _RED_("error") ". Card returns 0x%04x.", sw);
         
         DropField();
         return PM3_ESOFT;
     }
     
+    if (CipurseCCheckCT(&cpc, buf))
+        PrintAndLogEx(INFO, "Authentication " _GREEN_("OK"));
+    else
+        PrintAndLogEx(ERR, "Authentication " _RED_("ERROR") " card returned wrong CT");
+    
     DropField();
     return PM3_SUCCESS;
 }

From 80e8e1f8f96033cba498b44c83d4b428677d4f8d Mon Sep 17 00:00:00 2001
From: merlokk <807634+merlokk@users.noreply.github.com>
Date: Sat, 29 May 2021 20:26:31 +0300
Subject: [PATCH 13/39] add file to cmake

---
 client/CMakeLists.txt | 1 +
 1 file changed, 1 insertion(+)

diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt
index aab617889..5c826790d 100644
--- a/client/CMakeLists.txt
+++ b/client/CMakeLists.txt
@@ -211,6 +211,7 @@ set (TARGET_SOURCES
         ${PM3_ROOT}/client/src/fido/fidocore.c
         ${PM3_ROOT}/client/src/iso7816/apduinfo.c
         ${PM3_ROOT}/client/src/iso7816/iso7816core.c
+        ${PM3_ROOT}/client/src/cipurse/cipursecrypto.c
         ${PM3_ROOT}/client/src/cipurse/cipursecore.c
         ${PM3_ROOT}/client/src/loclass/cipher.c
         ${PM3_ROOT}/client/src/loclass/cipherutils.c

From da2a1f3c625c1c1cda65eea42ed56a652ce61d52 Mon Sep 17 00:00:00 2001
From: merlokk <807634+merlokk@users.noreply.github.com>
Date: Sat, 29 May 2021 20:33:20 +0300
Subject: [PATCH 14/39] add MAC sketch

---
 client/src/cipurse/cipursecore.c   |  4 ++++
 client/src/cipurse/cipursecrypto.c | 25 +++++++++++++++++++++++++
 client/src/cipurse/cipursecrypto.h |  8 +++++++-
 3 files changed, 36 insertions(+), 1 deletion(-)

diff --git a/client/src/cipurse/cipursecore.c b/client/src/cipurse/cipursecore.c
index 45c276f2c..553e5ba68 100644
--- a/client/src/cipurse/cipursecore.c
+++ b/client/src/cipurse/cipursecore.c
@@ -21,6 +21,9 @@
 #include "util.h"
 #include "cipurse/cipursecrypto.h"
 
+// context for secure channel
+CipurseContext cipurseContext;
+
 static int CIPURSEExchangeEx(bool ActivateField, bool LeaveFieldON, sAPDU apdu, bool IncludeLe, uint16_t Le, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
     uint8_t data[APDU_RES_LEN] = {0};
 
@@ -84,6 +87,7 @@ static int CIPURSEExchangeEx(bool ActivateField, bool LeaveFieldON, sAPDU apdu,
 
 int CIPURSESelect(bool ActivateField, bool LeaveFieldON, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
     uint8_t data[] = {0x41, 0x44, 0x20, 0x46, 0x31};
+    CipurseCClearContext(&cipurseContext);
 
     return EMVSelect(ECC_CONTACTLESS, ActivateField, LeaveFieldON, data, sizeof(data), Result, MaxResultLen, ResultLen, sw, NULL);
 }
diff --git a/client/src/cipurse/cipursecrypto.c b/client/src/cipurse/cipursecrypto.c
index 14dd8c3a5..29cdc994c 100644
--- a/client/src/cipurse/cipursecrypto.c
+++ b/client/src/cipurse/cipursecrypto.c
@@ -184,3 +184,28 @@ void CipurseCAuthenticateHost(CipurseContext *ctx, uint8_t *authdata) {
 bool CipurseCCheckCT(CipurseContext *ctx, uint8_t *CT) {
     return (memcmp(CT, ctx->CT, CIPURSE_AES_KEY_LENGTH) == 0);
 }
+
+void AddISO9797M2Padding(uint8_t *ddata, size_t *ddatalen, uint8_t *sdata, size_t sdatalen, size_t blocklen) {
+    *ddatalen = sdatalen + 1;
+    *ddatalen += *ddatalen % blocklen;
+    memset(ddata, 0, *ddatalen);
+    memcpy(ddata, sdata, sdatalen);
+    ddata[sdatalen] = ISO9797_M2_PAD_BYTE;
+}
+
+void CipurseCGenerateMAC(CipurseContext *ctx, uint8_t *data, size_t datalen, uint8_t *mac) {
+    
+}
+
+void CipurseCCalcMACPadded(CipurseContext *ctx, uint8_t *data, size_t datalen, uint8_t *mac) {
+    uint8_t pdata[datalen + CIPURSE_AES_KEY_LENGTH];
+    size_t pdatalen = 0;
+    AddISO9797M2Padding(pdata, &pdatalen, data, datalen, CIPURSE_AES_KEY_LENGTH);
+    CipurseCGenerateMAC(ctx, pdata, pdatalen, mac);
+}
+
+bool CipurseCCheckMACPadded(CipurseContext *ctx, uint8_t *data, size_t datalen, uint8_t *mac) {
+    uint8_t xmac[CIPURSE_MAC_LENGTH] = {0};
+    CipurseCCalcMACPadded(ctx, data, datalen, xmac);
+    return (memcmp(mac, xmac, CIPURSE_MAC_LENGTH) == 0);
+}
diff --git a/client/src/cipurse/cipursecrypto.h b/client/src/cipurse/cipursecrypto.h
index ca1ed29f3..c7fdc57e4 100644
--- a/client/src/cipurse/cipursecrypto.h
+++ b/client/src/cipurse/cipursecrypto.h
@@ -16,8 +16,9 @@
 #define CIPURSE_KVV_LENGTH 4
 #define CIPURSE_AES_KEY_LENGTH 16
 #define CIPURSE_SECURITY_PARAM_N 6
-#define OSPT_MAC_LENGTH 8
+#define CIPURSE_MAC_LENGTH 8
 #define CIPURSE_POLY 0x35b088cce172UL
+#define ISO9797_M2_PAD_BYTE 0x80
 
 #define member_size(type, member) sizeof(((type *)0)->member)
 
@@ -53,6 +54,11 @@ void CipurseCSetRandomHost(CipurseContext *ctx);
 void CipurseCAuthenticateHost(CipurseContext *ctx, uint8_t *authdata);
 bool CipurseCCheckCT(CipurseContext *ctx, uint8_t *CT);
 
+void AddISO9797M2Padding(uint8_t *ddata, size_t *ddatalen, uint8_t *sdata, size_t sdatalen, size_t blocklen);
+
+void CipurseCGenerateMAC(CipurseContext *ctx, uint8_t *data, size_t datalen, uint8_t *mac);
+void CipurseCCalcMACPadded(CipurseContext *ctx, uint8_t *data, size_t datalen, uint8_t *mac);
+bool CipurseCCheckMACPadded(CipurseContext *ctx, uint8_t *data, size_t datalen, uint8_t *mac);
 
 void CipurseCGetKVV(uint8_t *key, uint8_t *kvv);
 

From 413c5ec340927a2f91d19664954b042e9e2e1098 Mon Sep 17 00:00:00 2001
From: merlokk <807634+merlokk@users.noreply.github.com>
Date: Sat, 29 May 2021 20:56:55 +0300
Subject: [PATCH 15/39] add mac sketch

---
 client/src/cipurse/cipursecrypto.c | 27 ++++++++++++++++++++++++++-
 1 file changed, 26 insertions(+), 1 deletion(-)

diff --git a/client/src/cipurse/cipursecrypto.c b/client/src/cipurse/cipursecrypto.c
index 29cdc994c..bfd51b9f5 100644
--- a/client/src/cipurse/cipursecrypto.c
+++ b/client/src/cipurse/cipursecrypto.c
@@ -13,6 +13,7 @@
 #include "commonutil.h"  // ARRAYLEN
 #include "comms.h"       // DropField
 #include "util_posix.h"  // msleep
+#include <string.h>      // memcpy memset
 
 #include "cmdhf14a.h"
 #include "emv/emvcore.h"
@@ -193,8 +194,32 @@ void AddISO9797M2Padding(uint8_t *ddata, size_t *ddatalen, uint8_t *sdata, size_
     ddata[sdatalen] = ISO9797_M2_PAD_BYTE;
 }
 
+/* from: https://github.com/duychuongvn/cipurse-card-core/blob/master/src/main/java/com/github/duychuongvn/cirpusecard/core/security/crypto/CipurseCrypto.java#L473
+ * 
+ * Generate OSPT MAC on the given input data.
+ * Data should be already padded.
+ *  
+ * Calculation of Mi and ki+1: hx := ki , hx+1 := AES( key = hx ; Dx )
+ * XOR Dx , hx+2 := AES( key = hx+1 ; Dx+1 ) XOR Dx+1, hx+3 := AES( key =
+ * hx+2 ; Dx+2 ) XOR Dx+2, ... hy+1 := AES( key = hy ; Dy ) XOR Dy, ki+1 :=
+ * hy+1 M'i := AES( key = ki ; ki+1 ) XOR ki+1, Mi := m LS bits of M'i = (
+ * (M'i )0, (M'i )1, ..., (M'i )m-1)
+ */
 void CipurseCGenerateMAC(CipurseContext *ctx, uint8_t *data, size_t datalen, uint8_t *mac) {
-    
+    uint8_t temp[CIPURSE_AES_KEY_LENGTH] = {0};
+
+    memcpy(ctx->frameKeyNext, ctx->frameKey, CIPURSE_AES_KEY_LENGTH);
+    int i = 0;
+    while (datalen > i) {
+        aes_encode(NULL, ctx->frameKeyNext, &data[i], temp, CIPURSE_AES_KEY_LENGTH);
+        bin_xor(temp, &data[i], CIPURSE_AES_KEY_LENGTH);
+        memcpy(ctx->frameKeyNext, temp, CIPURSE_AES_KEY_LENGTH);
+        i += CIPURSE_AES_KEY_LENGTH;
+    }
+ 
+    aes_encode(NULL, ctx->frameKey, ctx->frameKeyNext, temp, CIPURSE_AES_KEY_LENGTH);
+    bin_xor(temp, ctx->frameKeyNext, CIPURSE_AES_KEY_LENGTH);
+    memcpy(mac, temp, CIPURSE_MAC_LENGTH);
 }
 
 void CipurseCCalcMACPadded(CipurseContext *ctx, uint8_t *data, size_t datalen, uint8_t *mac) {

From 283f065bc81a25c701641a3a7bef3f8e49a0c943 Mon Sep 17 00:00:00 2001
From: merlokk <807634+merlokk@users.noreply.github.com>
Date: Sat, 29 May 2021 21:23:01 +0300
Subject: [PATCH 16/39] add mac/encrypt primitives

---
 client/src/cipurse/cipursecrypto.c | 54 ++++++++++++++++++++++++++++++
 client/src/cipurse/cipursecrypto.h |  5 ++-
 2 files changed, 58 insertions(+), 1 deletion(-)

diff --git a/client/src/cipurse/cipursecrypto.c b/client/src/cipurse/cipursecrypto.c
index bfd51b9f5..d18907247 100644
--- a/client/src/cipurse/cipursecrypto.c
+++ b/client/src/cipurse/cipursecrypto.c
@@ -23,6 +23,7 @@
 #include "util.h"
 
 uint8_t AESData0[CIPURSE_AES_KEY_LENGTH] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+uint8_t QConstant[CIPURSE_AES_KEY_LENGTH] = {0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73};
 
 static void bin_xor(uint8_t *d1, uint8_t *d2, size_t len) {
     for(size_t i = 0; i < len; i++)
@@ -194,6 +195,59 @@ void AddISO9797M2Padding(uint8_t *ddata, size_t *ddatalen, uint8_t *sdata, size_
     ddata[sdatalen] = ISO9797_M2_PAD_BYTE;
 }
 
+size_t FindISO9797M2PaddingDataLen(uint8_t *data, size_t datalen) {
+    for (int i = datalen; i > 0; i--) {
+        if (data[i - 1] == 0x80)
+            return i;
+        if (data[i - 1] != 0x00)
+            return 0;
+    }
+    return 0;
+}
+
+/* from: https://github.com/duychuongvn/cipurse-card-core/blob/master/src/main/java/com/github/duychuongvn/cirpusecard/core/security/crypto/CipurseCrypto.java#L521
+ * 
+ * Encrypt/Decrypt the given data using ciphering mechanism explained the OPST.
+ * Data should be already padded.
+ *  
+ * hx-1 := ki , hx := AES( key = hx-1 ; q) XOR q, Cx := AES( key = hx ;
+ * Dx ), hx+1 := AES( key = hx ; q ) XOR q, Cx+1 := AES( key = hx+1 ;
+ * Dx+1 ), ... hy := AES( key = hy-1 ; q ) XOR q, Cy := AES( key = hy ;
+ * Dy ), ki+1 := hy
+ */
+void CipurseCEncryptDecrypt(CipurseContext *ctx, uint8_t *data, size_t datalen, uint8_t *dstdata, bool isEncrypt) {
+    uint8_t hx[CIPURSE_AES_KEY_LENGTH] = {0};
+
+    memcpy(ctx->frameKeyNext, ctx->frameKey, CIPURSE_AES_KEY_LENGTH);
+    int i = 0;
+    while (datalen > i) {
+        aes_encode(NULL, QConstant, ctx->frameKeyNext, hx, CIPURSE_AES_KEY_LENGTH);
+        bin_xor(hx, ctx->frameKeyNext, CIPURSE_AES_KEY_LENGTH);
+        
+        if (isEncrypt)
+            aes_encode(NULL, hx, &data[i], &dstdata[i], CIPURSE_AES_KEY_LENGTH);
+        else
+            aes_decode(NULL, hx, &data[i], &dstdata[i], CIPURSE_AES_KEY_LENGTH);
+        
+        memcpy(ctx->frameKeyNext, hx, CIPURSE_AES_KEY_LENGTH);
+        i += CIPURSE_AES_KEY_LENGTH;
+    }
+}
+
+void CipurseCChannelEncrypt(CipurseContext *ctx, uint8_t *data, size_t datalen, uint8_t *encdata, size_t *encdatalen) {
+    uint8_t pdata[datalen + CIPURSE_AES_KEY_LENGTH];
+    size_t pdatalen = 0;
+    AddISO9797M2Padding(pdata, &pdatalen, data, datalen, CIPURSE_AES_KEY_LENGTH);
+    
+    CipurseCEncryptDecrypt(ctx, pdata, pdatalen, encdata, true);
+    *encdatalen = pdatalen;
+}
+
+void CipurseCChannelDecrypt(CipurseContext *ctx, uint8_t *data, size_t datalen, uint8_t *plaindata, size_t *plaindatalen) {
+    CipurseCEncryptDecrypt(ctx, data, datalen, plaindata, false);
+    *plaindatalen = FindISO9797M2PaddingDataLen(plaindata, datalen);
+}
+
 /* from: https://github.com/duychuongvn/cipurse-card-core/blob/master/src/main/java/com/github/duychuongvn/cirpusecard/core/security/crypto/CipurseCrypto.java#L473
  * 
  * Generate OSPT MAC on the given input data.
diff --git a/client/src/cipurse/cipursecrypto.h b/client/src/cipurse/cipursecrypto.h
index c7fdc57e4..0ceafc5ff 100644
--- a/client/src/cipurse/cipursecrypto.h
+++ b/client/src/cipurse/cipursecrypto.h
@@ -55,11 +55,14 @@ void CipurseCAuthenticateHost(CipurseContext *ctx, uint8_t *authdata);
 bool CipurseCCheckCT(CipurseContext *ctx, uint8_t *CT);
 
 void AddISO9797M2Padding(uint8_t *ddata, size_t *ddatalen, uint8_t *sdata, size_t sdatalen, size_t blocklen);
+size_t FindISO9797M2PaddingDataLen(uint8_t *data, size_t datalen);
 
 void CipurseCGenerateMAC(CipurseContext *ctx, uint8_t *data, size_t datalen, uint8_t *mac);
 void CipurseCCalcMACPadded(CipurseContext *ctx, uint8_t *data, size_t datalen, uint8_t *mac);
 bool CipurseCCheckMACPadded(CipurseContext *ctx, uint8_t *data, size_t datalen, uint8_t *mac);
-
+void CipurseCEncryptDecrypt(CipurseContext *ctx, uint8_t *data, size_t datalen, uint8_t *dstdata, bool isEncrypt);
+void CipurseCChannelEncrypt(CipurseContext *ctx, uint8_t *data, size_t datalen, uint8_t *encdata, size_t *encdatalen);
+void CipurseCChannelDecrypt(CipurseContext *ctx, uint8_t *data, size_t datalen, uint8_t *plaindata, size_t *plaindatalen);
 void CipurseCGetKVV(uint8_t *key, uint8_t *kvv);
 
 #endif /* __CIPURSECRYPTO_H__ */

From b4ecbdb5f6805e0467a8253602ec4c9b4658df63 Mon Sep 17 00:00:00 2001
From: merlokk <807634+merlokk@users.noreply.github.com>
Date: Sat, 29 May 2021 21:28:19 +0300
Subject: [PATCH 17/39] text fix

---
 client/src/cmdhfcipurse.c | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/client/src/cmdhfcipurse.c b/client/src/cmdhfcipurse.c
index cea5755f4..8839bcda1 100644
--- a/client/src/cmdhfcipurse.c
+++ b/client/src/cmdhfcipurse.c
@@ -89,9 +89,10 @@ static int CmdHFCipurseAuth(const char *Cmd) {
     uint8_t key[] = {0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73};
 
     CLIParserContext *ctx;
-    CLIParserInit(&ctx, "hf 14a sim",
-                  "Simulate ISO/IEC 14443 type A tag with 4,7 or 10 byte UID",
-                  "hf 14a sim -t 1 --uid 11223344      -> MIFARE Classic 1k\n");
+    CLIParserInit(&ctx, "hf cipurse auth",
+                  "Authenticate with key ID and key",
+                  "hf cipurse auth      -> Authenticate with keyID=1 and key = 7373...7373\n"
+                  "hf cipurse auth -n 2 -k 65656565656565656565656565656565 -> Authenticate with key\n");
 
     void *argtable[] = {
         arg_param_begin,
@@ -111,7 +112,7 @@ static int CmdHFCipurseAuth(const char *Cmd) {
     int hdatalen = sizeof(hdata);    
     CLIGetHexWithReturn(ctx, 4, hdata, &hdatalen);
     if (hdatalen && hdatalen != 16) {
-        PrintAndLogEx(ERR, "ERROR: key length for AES128 must be 16 bytes only.");
+        PrintAndLogEx(ERR, _RED_("ERROR:") " key length for AES128 must be 16 bytes only.");
         CLIParserFree(ctx);
         return PM3_EINVARG;
     }
@@ -124,7 +125,7 @@ static int CmdHFCipurseAuth(const char *Cmd) {
     
     int res = CIPURSESelect(true, true, buf, sizeof(buf), &len, &sw);
     if (res != 0 || sw != 0x9000) {
-        PrintAndLogEx(ERR, "Cipurse select error. Card returns 0x%04x.", sw);
+        PrintAndLogEx(ERR, "Cipurse select " _RED_("error") ". Card returns 0x%04x.", sw);
         DropField();
         return PM3_ESOFT;
     }
@@ -140,7 +141,7 @@ static int CmdHFCipurseAuth(const char *Cmd) {
     // get RP, rP
     res = CIPURSEChallenge(buf, sizeof(buf), &len, &sw);
     if (res != 0 || len != 0x16) {
-        PrintAndLogEx(ERR, "Cipurse get challenge error. Card returns 0x%04x.", sw);
+        PrintAndLogEx(ERR, "Cipurse get challenge " _RED_("error") ". Card returns 0x%04x.", sw);
         DropField();
         return PM3_ESOFT;
     }

From 54e7713a9adec8386d344af098b9c72af89a89db Mon Sep 17 00:00:00 2001
From: merlokk <807634+merlokk@users.noreply.github.com>
Date: Sat, 29 May 2021 21:46:22 +0300
Subject: [PATCH 18/39] auth refactoring

---
 client/src/cipurse/cipursecore.c | 55 ++++++++++++++++++++++++++++++++
 client/src/cipurse/cipursecore.h |  2 ++
 client/src/cmdhfcipurse.c        | 43 ++++++-------------------
 3 files changed, 66 insertions(+), 34 deletions(-)

diff --git a/client/src/cipurse/cipursecore.c b/client/src/cipurse/cipursecore.c
index 553e5ba68..54f90bd52 100644
--- a/client/src/cipurse/cipursecore.c
+++ b/client/src/cipurse/cipursecore.c
@@ -99,3 +99,58 @@ int CIPURSEChallenge(uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, ui
 int CIPURSEMutalAuthenticate(uint8_t keyIndex, uint8_t *params, uint8_t paramslen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
     return CIPURSEExchangeEx(false, true, (sAPDU) {0x00, 0x82, 0x00, keyIndex, paramslen, params}, true, 0x10, Result, MaxResultLen, ResultLen, sw);
 }
+
+bool CIPURSEChannelAuthenticate(uint8_t keyIndex, uint8_t *key, bool verbose) {
+    uint8_t buf[APDU_RES_LEN] = {0};
+    size_t len = 0;
+    uint16_t sw = 0;
+
+    CipurseContext cpc = {0};
+    CipurseCSetKey(&cpc, keyIndex, key);
+    
+    // get RP, rP
+    int res = CIPURSEChallenge(buf, sizeof(buf), &len, &sw);
+    if (res != 0 || len != 0x16) {
+        if (verbose)
+            PrintAndLogEx(ERR, "Cipurse get challenge " _RED_("error") ". Card returns 0x%04x.", sw);
+        
+        return false;
+    }
+    CipurseCSetRandomFromPICC(&cpc, buf);
+
+    // make auth data
+    uint8_t authparams[16 + 16 + 6] = {0};
+    CipurseCAuthenticateHost(&cpc, authparams);
+    
+    // authenticate
+    res = CIPURSEMutalAuthenticate(keyIndex, authparams, sizeof(authparams), buf, sizeof(buf), &len, &sw);
+    if (res != 0 || sw != 0x9000 || len != 16) {
+        if (sw == 0x6988) {
+            if (verbose)
+                PrintAndLogEx(ERR, "Cipurse authentication " _RED_("error") ". Wrong key.");
+        } else if ((sw == 0x6A88)) {
+            if (verbose)
+                PrintAndLogEx(ERR, "Cipurse authentication " _RED_("error") ". Wrong key number.");
+        } else {
+            if (verbose)
+                PrintAndLogEx(ERR, "Cipurse authentication " _RED_("error") ". Card returns 0x%04x.", sw);
+        }
+        
+        CipurseCClearContext(&cipurseContext);
+        return false;
+    }
+    
+    if (CipurseCCheckCT(&cpc, buf)) {
+        if (verbose)
+            PrintAndLogEx(INFO, "Authentication " _GREEN_("OK"));
+        
+        memcpy(&cipurseContext, &cpc, sizeof(CipurseContext));
+        return true;
+    } else {
+        if (verbose)
+            PrintAndLogEx(ERR, "Authentication " _RED_("ERROR") " card returned wrong CT");
+        
+        CipurseCClearContext(&cipurseContext);
+        return false;
+    }
+}
\ No newline at end of file
diff --git a/client/src/cipurse/cipursecore.h b/client/src/cipurse/cipursecore.h
index 47671d0c8..4555b4c1f 100644
--- a/client/src/cipurse/cipursecore.h
+++ b/client/src/cipurse/cipursecore.h
@@ -31,4 +31,6 @@ int CIPURSEReadFileAttributes(uint8_t *data, uint16_t *datalen);
 int CIPURSEReadBinary(uint16_t offset, uint8_t *data, uint16_t *datalen);
 int CIPURSEUpdateBinary(uint16_t offset, uint8_t *data, uint16_t datalen);
 
+bool CIPURSEChannelAuthenticate(uint8_t keyIndex, uint8_t *key, bool verbose);
+
 #endif /* __CIPURSECORE_H__ */
diff --git a/client/src/cmdhfcipurse.c b/client/src/cmdhfcipurse.c
index 8839bcda1..39ea9cdc7 100644
--- a/client/src/cmdhfcipurse.c
+++ b/client/src/cmdhfcipurse.c
@@ -121,7 +121,7 @@ static int CmdHFCipurseAuth(const char *Cmd) {
     
     SetAPDULogging(APDULogging);
 
-    CLIParserFree(ctx);    
+    CLIParserFree(ctx);
     
     int res = CIPURSESelect(true, true, buf, sizeof(buf), &len, &sw);
     if (res != 0 || sw != 0x9000) {
@@ -129,48 +129,23 @@ static int CmdHFCipurseAuth(const char *Cmd) {
         DropField();
         return PM3_ESOFT;
     }
-    
-    CipurseContext cpc = {0};
-    CipurseCSetKey(&cpc, keyId, key);
-    
+        
     uint8_t kvv[CIPURSE_KVV_LENGTH] = {0};
     CipurseCGetKVV(key, kvv);
     if (verbose)
         PrintAndLogEx(INFO, "Key id: %d key: %s KVV: %s", keyId, sprint_hex(key, CIPURSE_AES_KEY_LENGTH), sprint_hex_inrow(kvv, CIPURSE_KVV_LENGTH));
 
-    // get RP, rP
-    res = CIPURSEChallenge(buf, sizeof(buf), &len, &sw);
-    if (res != 0 || len != 0x16) {
-        PrintAndLogEx(ERR, "Cipurse get challenge " _RED_("error") ". Card returns 0x%04x.", sw);
-        DropField();
-        return PM3_ESOFT;
-    }
-    CipurseCSetRandomFromPICC(&cpc, buf);
-
-    // make auth data
-    uint8_t authparams[16 + 16 + 6] = {0};
-    CipurseCAuthenticateHost(&cpc, authparams);
+    bool bres = CIPURSEChannelAuthenticate(keyId, key, verbose);
     
-    // authenticate
-    res = CIPURSEMutalAuthenticate(keyId, authparams, sizeof(authparams), buf, sizeof(buf), &len, &sw);
-    if (res != 0 || sw != 0x9000 || len != 16) {
-        if (sw == 0x6988)
-            PrintAndLogEx(ERR, "Cipurse authentication " _RED_("error") ". Wrong key.");
-        else if ((sw == 0x6A88))
-            PrintAndLogEx(ERR, "Cipurse authentication " _RED_("error") ". Wrong key number.");
-        else PrintAndLogEx(ERR, "Cipurse authentication " _RED_("error") ". Card returns 0x%04x.", sw);
-        
-        DropField();
-        return PM3_ESOFT;
+    if (verbose == false) {
+        if (bres)
+            PrintAndLogEx(INFO, "Authentication " _GREEN_("OK"));
+        else
+            PrintAndLogEx(ERR, "Authentication " _RED_("ERROR"));
     }
     
-    if (CipurseCCheckCT(&cpc, buf))
-        PrintAndLogEx(INFO, "Authentication " _GREEN_("OK"));
-    else
-        PrintAndLogEx(ERR, "Authentication " _RED_("ERROR") " card returned wrong CT");
-    
     DropField();
-    return PM3_SUCCESS;
+    return bres ? PM3_SUCCESS : PM3_ESOFT;
 }
 
 

From 9857094274d96f0fe7a66411d24d96980342b0dc Mon Sep 17 00:00:00 2001
From: merlokk <807634+merlokk@users.noreply.github.com>
Date: Sat, 29 May 2021 22:37:32 +0300
Subject: [PATCH 19/39] security level logic

---
 client/src/cipurse/cipursecore.c   |  6 ++++++
 client/src/cipurse/cipursecrypto.c | 22 ++++++++++++++++++++++
 client/src/cipurse/cipursecrypto.h | 12 ++++++++++--
 3 files changed, 38 insertions(+), 2 deletions(-)

diff --git a/client/src/cipurse/cipursecore.c b/client/src/cipurse/cipursecore.c
index 54f90bd52..3b905214f 100644
--- a/client/src/cipurse/cipursecore.c
+++ b/client/src/cipurse/cipursecore.c
@@ -13,6 +13,7 @@
 #include "commonutil.h"  // ARRAYLEN
 #include "comms.h"       // DropField
 #include "util_posix.h"  // msleep
+#include <string.h>      // memcpy memset
 
 #include "cmdhf14a.h"
 #include "emv/emvcore.h"
@@ -36,6 +37,10 @@ static int CIPURSEExchangeEx(bool ActivateField, bool LeaveFieldON, sAPDU apdu,
         DropField();
         msleep(50);
     }
+    
+    // long messages is not allowed
+    if (apdu.Lc > 228)
+        return 20;
 
     // COMPUTE APDU
     int datalen = 0;
@@ -144,6 +149,7 @@ bool CIPURSEChannelAuthenticate(uint8_t keyIndex, uint8_t *key, bool verbose) {
         if (verbose)
             PrintAndLogEx(INFO, "Authentication " _GREEN_("OK"));
         
+        CipurseCChannelSetSecurityLevels(&cpc, CPSMACed, CPSMACed);
         memcpy(&cipurseContext, &cpc, sizeof(CipurseContext));
         return true;
     } else {
diff --git a/client/src/cipurse/cipursecrypto.c b/client/src/cipurse/cipursecrypto.c
index d18907247..71b076515 100644
--- a/client/src/cipurse/cipursecrypto.c
+++ b/client/src/cipurse/cipursecrypto.c
@@ -25,6 +25,16 @@
 uint8_t AESData0[CIPURSE_AES_KEY_LENGTH] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
 uint8_t QConstant[CIPURSE_AES_KEY_LENGTH] = {0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73};
 
+uint8_t CipurseCSecurityLevelEnc(CipurseChannelSecurityLevel lvl) {
+    switch (lvl) {
+        case CPSNone: return 0x00;
+        case CPSPlain: return 0x00;
+        case CPSMACed: return 0x01;
+        case CPSEncrypted: return 0x02;
+        default: return 0x00;
+    }
+}
+
 static void bin_xor(uint8_t *d1, uint8_t *d2, size_t len) {
     for(size_t i = 0; i < len; i++)
         d1[i] = d1[i] ^ d2[i];
@@ -152,6 +162,11 @@ void CipurseCSetKey(CipurseContext *ctx, uint8_t keyId, uint8_t *key) {
     memcpy(ctx->key, key, member_size(CipurseContext, key));
 }
 
+void CipurseCChannelSetSecurityLevels(CipurseContext *ctx, CipurseChannelSecurityLevel req, CipurseChannelSecurityLevel resp) {
+    ctx->RequestSecurity = req;
+    ctx->ResponseSecurity = resp;
+}
+
 void CipurseCSetRandomFromPICC(CipurseContext *ctx, uint8_t *random) {
     if (ctx == NULL)
         return;
@@ -165,6 +180,13 @@ void CipurseCSetRandomHost(CipurseContext *ctx) {
     memset(ctx->rT, 0x20, member_size(CipurseContext, rT));
 }
 
+uint8_t CipurseCGetSMI(CipurseContext *ctx, bool LePresent) {
+    uint8_t res = LePresent ? 1 : 0;
+    res = res | (CipurseCSecurityLevelEnc(ctx->RequestSecurity) << 2);
+    res = res | (CipurseCSecurityLevelEnc(ctx->ResponseSecurity) << 6);
+    return res;
+}
+
 static void CipurseCFillAuthData(CipurseContext *ctx, uint8_t *authdata) {
     memcpy(authdata, ctx->cP, member_size(CipurseContext, cP));
     memcpy(&authdata[member_size(CipurseContext, cP)], ctx->RT, member_size(CipurseContext, RT));
diff --git a/client/src/cipurse/cipursecrypto.h b/client/src/cipurse/cipursecrypto.h
index 0ceafc5ff..d98f3e4fe 100644
--- a/client/src/cipurse/cipursecrypto.h
+++ b/client/src/cipurse/cipursecrypto.h
@@ -22,12 +22,12 @@
 
 #define member_size(type, member) sizeof(((type *)0)->member)
 
-enum CipurseChannelSecurityLevel {
+typedef enum {
     CPSNone,
     CPSPlain,
     CPSMACed,
     CPSEncrypted
-};
+} CipurseChannelSecurityLevel;
 
 typedef struct CipurseContextS {
     uint8_t keyId;
@@ -44,16 +44,24 @@ typedef struct CipurseContextS {
     
     uint8_t frameKey[CIPURSE_AES_KEY_LENGTH];
     uint8_t frameKeyNext[CIPURSE_AES_KEY_LENGTH];
+    
+    CipurseChannelSecurityLevel RequestSecurity;
+    CipurseChannelSecurityLevel ResponseSecurity;
 } CipurseContext;
 
+uint8_t CipurseCSecurityLevelEnc(CipurseChannelSecurityLevel lvl);
+
 void CipurseCClearContext(CipurseContext *ctx);
 void CipurseCSetKey(CipurseContext *ctx, uint8_t keyId, uint8_t *key);
 void CipurseCSetRandomFromPICC(CipurseContext *ctx, uint8_t *random);
 void CipurseCSetRandomHost(CipurseContext *ctx);
+uint8_t CipurseCGetSMI(CipurseContext *ctx, bool LePresent);
 
 void CipurseCAuthenticateHost(CipurseContext *ctx, uint8_t *authdata);
 bool CipurseCCheckCT(CipurseContext *ctx, uint8_t *CT);
 
+void CipurseCChannelSetSecurityLevels(CipurseContext *ctx, CipurseChannelSecurityLevel req, CipurseChannelSecurityLevel resp);
+
 void AddISO9797M2Padding(uint8_t *ddata, size_t *ddatalen, uint8_t *sdata, size_t sdatalen, size_t blocklen);
 size_t FindISO9797M2PaddingDataLen(uint8_t *data, size_t datalen);
 

From 99c4a3a9c0b0d5c8993f438f09d8b0a1ed8e66dc Mon Sep 17 00:00:00 2001
From: merlokk <807634+merlokk@users.noreply.github.com>
Date: Sat, 29 May 2021 22:53:31 +0300
Subject: [PATCH 20/39] fix warning

---
 client/src/cipurse/cipursecore.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/client/src/cipurse/cipursecore.c b/client/src/cipurse/cipursecore.c
index 3b905214f..4f73d73c7 100644
--- a/client/src/cipurse/cipursecore.c
+++ b/client/src/cipurse/cipursecore.c
@@ -133,7 +133,7 @@ bool CIPURSEChannelAuthenticate(uint8_t keyIndex, uint8_t *key, bool verbose) {
         if (sw == 0x6988) {
             if (verbose)
                 PrintAndLogEx(ERR, "Cipurse authentication " _RED_("error") ". Wrong key.");
-        } else if ((sw == 0x6A88)) {
+        } else if (sw == 0x6A88) {
             if (verbose)
                 PrintAndLogEx(ERR, "Cipurse authentication " _RED_("error") ". Wrong key number.");
         } else {

From 84ada9cc14890ddbb1caf7b6a0843c9904232227 Mon Sep 17 00:00:00 2001
From: merlokk <807634+merlokk@users.noreply.github.com>
Date: Wed, 2 Jun 2021 19:42:04 +0300
Subject: [PATCH 21/39] read file works

---
 client/src/cipurse/cipursecore.c   |  36 ++++++++--
 client/src/cipurse/cipursecore.h   |   7 +-
 client/src/cipurse/cipursecrypto.c |  89 +++++++++++++++++++++++++
 client/src/cipurse/cipursecrypto.h |   6 ++
 client/src/cmdhfcipurse.c          | 103 +++++++++++++++++++++++++++++
 5 files changed, 232 insertions(+), 9 deletions(-)

diff --git a/client/src/cipurse/cipursecore.c b/client/src/cipurse/cipursecore.c
index 4f73d73c7..acea27683 100644
--- a/client/src/cipurse/cipursecore.c
+++ b/client/src/cipurse/cipursecore.c
@@ -27,6 +27,8 @@ CipurseContext cipurseContext;
 
 static int CIPURSEExchangeEx(bool ActivateField, bool LeaveFieldON, sAPDU apdu, bool IncludeLe, uint16_t Le, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
     uint8_t data[APDU_RES_LEN] = {0};
+    uint8_t securedata[APDU_RES_LEN] = {0};
+    sAPDU secapdu;
 
     *ResultLen = 0;
     if (sw) *sw = 0;
@@ -47,7 +49,10 @@ static int CIPURSEExchangeEx(bool ActivateField, bool LeaveFieldON, sAPDU apdu,
     uint16_t xle = IncludeLe ? 0x100 : 0x00;
     if (xle == 0x100 && Le != 0)
         xle = Le;
-    if (APDUEncodeS(&apdu, false, xle, data, &datalen)) {
+    
+    CipurseCAPDUReqEncode(&cipurseContext, &apdu, &secapdu, securedata, IncludeLe, Le);
+    
+    if (APDUEncodeS(&secapdu, false, xle, data, &datalen)) {
         PrintAndLogEx(ERR, "APDU encoding error.");
         return 201;
     }
@@ -62,14 +67,18 @@ static int CIPURSEExchangeEx(bool ActivateField, bool LeaveFieldON, sAPDU apdu,
 
     if (GetAPDULogging())
         PrintAndLogEx(SUCCESS, "<<<< %s", sprint_hex(Result, *ResultLen));
-
+    
     if (*ResultLen < 2) {
         return 200;
     }
 
-    *ResultLen -= 2;
-    isw = Result[*ResultLen] * 0x0100 + Result[*ResultLen + 1];
-    if (sw)
+    size_t rlen = 0;
+    CipurseCAPDURespDecode(&cipurseContext, Result, *ResultLen, securedata, &rlen, &isw);
+    memcpy(Result, securedata, rlen);
+    if (ResultLen != NULL)
+        *ResultLen = rlen;
+    
+    if (sw != NULL)
         *sw = isw;
 
     if (isw != 0x9000) {
@@ -105,6 +114,20 @@ int CIPURSEMutalAuthenticate(uint8_t keyIndex, uint8_t *params, uint8_t paramsle
     return CIPURSEExchangeEx(false, true, (sAPDU) {0x00, 0x82, 0x00, keyIndex, paramslen, params}, true, 0x10, Result, MaxResultLen, ResultLen, sw);
 }
 
+int CIPURSESelectFile(uint16_t fileID, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
+    uint8_t fileIdBin[] = {fileID >> 8, fileID & 0xff};
+    return CIPURSEExchangeEx(false, true, (sAPDU) {0x00, 0xa4, 0x00, 0x00, 02, fileIdBin}, true, 0, Result, MaxResultLen, ResultLen, sw);
+}
+
+int CIPURSEReadFileAttributes(uint8_t *data, uint16_t *datalen) {
+    //CIPURSEExchangeEx(false, true, (sAPDU) {0x00, 0x82, 0x00, keyIndex, paramslen, params}, true, 0x10, Result, MaxResultLen, ResultLen, sw);
+    return 2;
+}
+
+int CIPURSEReadBinary(uint16_t offset, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
+    return CIPURSEExchangeEx(false, true, (sAPDU) {0x00, 0xb0, (offset >> 8) & 0x7f, offset & 0xff, 0, NULL}, true, 0, Result, MaxResultLen, ResultLen, sw);
+}
+
 bool CIPURSEChannelAuthenticate(uint8_t keyIndex, uint8_t *key, bool verbose) {
     uint8_t buf[APDU_RES_LEN] = {0};
     size_t len = 0;
@@ -149,7 +172,8 @@ bool CIPURSEChannelAuthenticate(uint8_t keyIndex, uint8_t *key, bool verbose) {
         if (verbose)
             PrintAndLogEx(INFO, "Authentication " _GREEN_("OK"));
         
-        CipurseCChannelSetSecurityLevels(&cpc, CPSMACed, CPSMACed);
+        //CipurseCChannelSetSecurityLevels(&cpc, CPSMACed, CPSMACed);
+        CipurseCChannelSetSecurityLevels(&cpc, CPSPlain, CPSPlain);
         memcpy(&cipurseContext, &cpc, sizeof(CipurseContext));
         return true;
     } else {
diff --git a/client/src/cipurse/cipursecore.h b/client/src/cipurse/cipursecore.h
index 4555b4c1f..7565538d0 100644
--- a/client/src/cipurse/cipursecore.h
+++ b/client/src/cipurse/cipursecore.h
@@ -14,10 +14,11 @@
 #include "common.h"
 #include "emv/apduinfo.h"
 
-
 #include <jansson.h>
 #include "emv/apduinfo.h" // sAPDU
 
+#define CIPURSE_DEFAULT_KEY {0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73}
+
 int CIPURSESelect(bool ActivateField, bool LeaveFieldON, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);
 
 int CIPURSEChallenge(uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);
@@ -26,9 +27,9 @@ int CIPURSEMutalAuthenticate(uint8_t keyIndex, uint8_t *params, uint8_t paramsle
 int CIPURSECreateFile(uint16_t fileID, uint8_t *fileAttr);
 int CIPURSEDeleteFile(uint16_t fileID);
 
-int CIPURSESelectFile(uint16_t fileID);
+int CIPURSESelectFile(uint16_t fileID, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);
 int CIPURSEReadFileAttributes(uint8_t *data, uint16_t *datalen);
-int CIPURSEReadBinary(uint16_t offset, uint8_t *data, uint16_t *datalen);
+int CIPURSEReadBinary(uint16_t offset, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);
 int CIPURSEUpdateBinary(uint16_t offset, uint8_t *data, uint16_t datalen);
 
 bool CIPURSEChannelAuthenticate(uint8_t keyIndex, uint8_t *key, bool verbose);
diff --git a/client/src/cipurse/cipursecrypto.c b/client/src/cipurse/cipursecrypto.c
index 71b076515..dd2db2c66 100644
--- a/client/src/cipurse/cipursecrypto.c
+++ b/client/src/cipurse/cipursecrypto.c
@@ -167,6 +167,10 @@ void CipurseCChannelSetSecurityLevels(CipurseContext *ctx, CipurseChannelSecurit
     ctx->ResponseSecurity = resp;
 }
 
+bool isCipurseCChannelSecuritySet(CipurseContext *ctx) {
+    return ((ctx->RequestSecurity != CPSNone) && (ctx->ResponseSecurity != CPSNone));
+}
+
 void CipurseCSetRandomFromPICC(CipurseContext *ctx, uint8_t *random) {
     if (ctx == NULL)
         return;
@@ -310,3 +314,88 @@ bool CipurseCCheckMACPadded(CipurseContext *ctx, uint8_t *data, size_t datalen,
     CipurseCCalcMACPadded(ctx, data, datalen, xmac);
     return (memcmp(mac, xmac, CIPURSE_MAC_LENGTH) == 0);
 }
+
+void CipurseCAPDUReqEncode(CipurseContext *ctx, sAPDU *srcapdu, sAPDU *dstapdu, uint8_t *dstdatabuf, bool includeLe, uint8_t Le) {
+    uint8_t mac[CIPURSE_MAC_LENGTH] = {0};
+    
+    memcpy(dstapdu, srcapdu, sizeof(sAPDU));
+    
+    if (isCipurseCChannelSecuritySet(ctx) == false)
+        return;
+    
+    dstapdu->CLA |= 0x04;
+    dstapdu->data = dstdatabuf;
+    dstapdu->data[0] = CipurseCGetSMI(ctx, includeLe);
+    dstapdu->Lc++;
+    memcpy(&dstdatabuf[1], srcapdu->data, srcapdu->Lc);
+    if (includeLe) {
+        dstapdu->data[dstapdu->Lc] = Le;
+        dstapdu->Lc++;
+    }
+
+    switch (ctx->RequestSecurity) {
+        case CPSNone:
+        break;
+        case CPSPlain: 
+            CipurseCCalcMACPadded(ctx, (uint8_t *)dstapdu, dstapdu->Lc + 5, mac);
+        break;
+        case CPSMACed:        
+            dstapdu->Lc += CIPURSE_MAC_LENGTH;
+            CipurseCCalcMACPadded(ctx, (uint8_t *)dstapdu, dstapdu->Lc + 5, mac);
+            memcpy(&dstdatabuf[dstapdu->Lc - CIPURSE_MAC_LENGTH], mac, CIPURSE_MAC_LENGTH);
+        break;
+        case CPSEncrypted:
+        break;
+        default: 
+        break;
+    }
+
+}
+
+void CipurseCAPDURespDecode(CipurseContext *ctx, uint8_t *srcdata, size_t srcdatalen, uint8_t *dstdata, size_t *dstdatalen, uint16_t *sw) {
+    if (dstdatalen != NULL)
+        *dstdatalen = 0;
+    if (sw != NULL)
+        *sw = 0;
+    
+    if (srcdatalen < 2)
+        return;
+    
+    srcdatalen -= 2;
+    uint16_t xsw = srcdata[srcdatalen] * 0x0100 + srcdata[srcdatalen + 1];
+    if (sw)
+        *sw = xsw;
+    
+    if (isCipurseCChannelSecuritySet(ctx) == false) {
+        memcpy(dstdata, srcdata, srcdatalen);
+        if (dstdatalen != NULL)
+            *dstdatalen = srcdatalen;
+        return;
+    }
+    
+    switch (ctx->RequestSecurity) {
+        case CPSNone:
+        break;
+        case CPSPlain: 
+            memcpy(dstdata, srcdata, srcdatalen);
+            if (dstdatalen != NULL)
+                *dstdatalen = srcdatalen;
+        break;
+        case CPSMACed:       
+            if (srcdatalen < CIPURSE_MAC_LENGTH)
+                return;
+            srcdatalen -= CIPURSE_MAC_LENGTH;
+            if (CipurseCCheckMACPadded(ctx, srcdata, srcdatalen, &srcdata[srcdatalen]) == false) {
+                PrintAndLogEx(WARNING, "APDU MAC is not valid!");
+            }
+            memcpy(dstdata, srcdata, srcdatalen);
+            if (dstdatalen != NULL)
+                *dstdatalen = srcdatalen;
+        break;
+        case CPSEncrypted:
+        break;
+        default: 
+        break;
+    }
+    
+}
diff --git a/client/src/cipurse/cipursecrypto.h b/client/src/cipurse/cipursecrypto.h
index d98f3e4fe..73f6116b3 100644
--- a/client/src/cipurse/cipursecrypto.h
+++ b/client/src/cipurse/cipursecrypto.h
@@ -12,6 +12,7 @@
 #define __CIPURSECRYPTO_H__
 
 #include "common.h"
+#include "emv/apduinfo.h"    // sAPDU
 
 #define CIPURSE_KVV_LENGTH 4
 #define CIPURSE_AES_KEY_LENGTH 16
@@ -61,6 +62,7 @@ void CipurseCAuthenticateHost(CipurseContext *ctx, uint8_t *authdata);
 bool CipurseCCheckCT(CipurseContext *ctx, uint8_t *CT);
 
 void CipurseCChannelSetSecurityLevels(CipurseContext *ctx, CipurseChannelSecurityLevel req, CipurseChannelSecurityLevel resp);
+bool isCipurseCChannelSecuritySet(CipurseContext *ctx);
 
 void AddISO9797M2Padding(uint8_t *ddata, size_t *ddatalen, uint8_t *sdata, size_t sdatalen, size_t blocklen);
 size_t FindISO9797M2PaddingDataLen(uint8_t *data, size_t datalen);
@@ -73,4 +75,8 @@ void CipurseCChannelEncrypt(CipurseContext *ctx, uint8_t *data, size_t datalen,
 void CipurseCChannelDecrypt(CipurseContext *ctx, uint8_t *data, size_t datalen, uint8_t *plaindata, size_t *plaindatalen);
 void CipurseCGetKVV(uint8_t *key, uint8_t *kvv);
 
+void CipurseCAPDUReqEncode(CipurseContext *ctx, sAPDU *srcapdu, sAPDU *dstapdu, uint8_t *dstdatabuf, bool includeLe, uint8_t Le);
+void CipurseCAPDURespDecode(CipurseContext *ctx, uint8_t *srcdata, size_t srcdatalen, uint8_t *dstdata, size_t *dstdatalen, uint16_t *sw);
+
+
 #endif /* __CIPURSECRYPTO_H__ */
diff --git a/client/src/cmdhfcipurse.c b/client/src/cmdhfcipurse.c
index 39ea9cdc7..d90179c29 100644
--- a/client/src/cmdhfcipurse.c
+++ b/client/src/cmdhfcipurse.c
@@ -148,6 +148,108 @@ static int CmdHFCipurseAuth(const char *Cmd) {
     return bres ? PM3_SUCCESS : PM3_ESOFT;
 }
 
+static int CmdHFCipurseReadFile(const char *Cmd) {
+    uint8_t buf[APDU_RES_LEN] = {0};
+    size_t len = 0;
+    uint16_t sw = 0;
+    uint8_t key[] = CIPURSE_DEFAULT_KEY;
+
+    CLIParserContext *ctx;
+    CLIParserInit(&ctx, "hf cipurse read",
+                  "Read file by file ID with key ID and key",
+                  "hf cipurse read -f 2ff7   -> Authenticate with keyID=1 and key = 7373...7373 and read file with id 2ff7\n"
+                  "hf cipurse auth -n 2 -k 65656565656565656565656565656565 -f 2ff7 -> Authenticate with specified key and read file\n");
+
+    void *argtable[] = {
+        arg_param_begin,
+        arg_lit0("a",  "apdu",    "show APDU requests and responses"),
+        arg_lit0("v",  "verbose", "show technical data"),
+        arg_int0("n",  "keyid",   "<dec>", "key id"),
+        arg_str0("k",  "key",     "<hex>", "key for authenticate"),
+        arg_str0("f",  "file",    "<hex>", "file ID"),
+        arg_int0("o",  "offset",  "<dec>", "offset for reading data from file"),
+        arg_param_end
+    };
+    CLIExecWithReturn(ctx, Cmd, argtable, true);
+    
+    bool APDULogging = arg_get_lit(ctx, 1);
+    bool verbose = arg_get_lit(ctx, 2);
+    uint8_t keyId = arg_get_int_def(ctx, 3, 1);
+    
+    uint8_t hdata[250] = {0};
+    int hdatalen = sizeof(hdata);    
+    CLIGetHexWithReturn(ctx, 4, hdata, &hdatalen);
+    if (hdatalen && hdatalen != 16) {
+        PrintAndLogEx(ERR, _RED_("ERROR:") " key length for AES128 must be 16 bytes only.");
+        CLIParserFree(ctx);
+        return PM3_EINVARG;
+    }
+    if (hdatalen)
+        memcpy(key, hdata, CIPURSE_AES_KEY_LENGTH);
+    
+    uint16_t fileId = 0x2ff7;
+    
+    hdatalen = sizeof(hdata);    
+    CLIGetHexWithReturn(ctx, 5, hdata, &hdatalen);
+    if (hdatalen && hdatalen != 2) {
+        PrintAndLogEx(ERR, _RED_("ERROR:") " file id length must be 2 bytes only.");
+        CLIParserFree(ctx);
+        return PM3_EINVARG;
+    }
+    if (hdatalen)
+        fileId = (hdata[0] << 8) + hdata[1];
+    
+    size_t offset = arg_get_int_def(ctx, 6, 0);
+    
+    SetAPDULogging(APDULogging);
+
+    CLIParserFree(ctx);
+    
+    int res = CIPURSESelect(true, true, buf, sizeof(buf), &len, &sw);
+    if (res != 0 || sw != 0x9000) {
+        PrintAndLogEx(ERR, "Cipurse select " _RED_("error") ". Card returns 0x%04x.", sw);
+        DropField();
+        return PM3_ESOFT;
+    }
+        
+    if (verbose)
+        PrintAndLogEx(INFO, "File id: %x offset %d key id: %d key: %s", fileId, offset, keyId, sprint_hex(key, CIPURSE_AES_KEY_LENGTH));
+
+    bool bres = CIPURSEChannelAuthenticate(keyId, key, verbose);
+    if (bres == false) {
+        if (verbose == false)
+            PrintAndLogEx(ERR, "Authentication " _RED_("ERROR"));
+        DropField();
+        return PM3_ESOFT;
+    }
+    
+    res = CIPURSESelectFile(fileId, buf, sizeof(buf), &len, &sw);
+    if (res != 0 || sw != 0x9000) {
+        if (verbose == false)
+            PrintAndLogEx(ERR, "File select " _RED_("ERROR") ". Card returns 0x%04x.", sw);
+        DropField();
+        return PM3_ESOFT;
+    }
+
+    if (verbose)
+        PrintAndLogEx(INFO, "Select file 0x%x " _GREEN_("OK"), fileId);
+    
+    res = CIPURSEReadBinary(offset, buf, sizeof(buf), &len, &sw);
+    if (res != 0 || sw != 0x9000) {
+        if (verbose == false)
+            PrintAndLogEx(ERR, "File read " _RED_("ERROR") ". Card returns 0x%04x.", sw);
+        DropField();
+        return PM3_ESOFT;
+    }
+        
+    if (len == 0)
+        PrintAndLogEx(INFO, "File id: %x is empty", fileId);
+    else
+        PrintAndLogEx(INFO, "File id: %x data[%d]: %s", fileId, len, sprint_hex(buf, len));
+    
+    DropField();
+    return PM3_SUCCESS;
+}
 
 
 
@@ -169,6 +271,7 @@ static command_t CommandTable[] = {
     {"help",      CmdHelp,                   AlwaysAvailable, "This help."},
     {"info",      CmdHFCipurseInfo,          IfPm3Iso14443a,  "Info about Cipurse tag."},
     {"auth",      CmdHFCipurseAuth,          IfPm3Iso14443a,  "Authentication."},
+    {"read",      CmdHFCipurseReadFile,      IfPm3Iso14443a,  "Read file."},
     {NULL, NULL, 0, NULL}
 };
 

From 09f33a45c7550d886262bf3bf5914b57b92e57eb Mon Sep 17 00:00:00 2001
From: merlokk <807634+merlokk@users.noreply.github.com>
Date: Wed, 2 Jun 2021 19:50:35 +0300
Subject: [PATCH 22/39] get additional info

---
 client/src/cmdhfcipurse.c | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/client/src/cmdhfcipurse.c b/client/src/cmdhfcipurse.c
index d90179c29..f4d51f633 100644
--- a/client/src/cmdhfcipurse.c
+++ b/client/src/cmdhfcipurse.c
@@ -75,7 +75,22 @@ static int CmdHFCipurseInfo(const char *Cmd) {
 
     PrintAndLogEx(INFO, "Cipurse card: " _GREEN_("OK"));
 
+    res = CIPURSESelectFile(0x2ff7, buf, sizeof(buf), &len, &sw);
+    if (res != 0 || sw != 0x9000) {
+        DropField();
+        return PM3_SUCCESS;
+    }
 
+    res = CIPURSEReadBinary(0, buf, sizeof(buf), &len, &sw);
+    if (res != 0 || sw != 0x9000) {
+        DropField();
+        return PM3_SUCCESS;
+    }
+    
+    if (len > 0) {
+        PrintAndLogEx(INFO, "Info file: " _GREEN_("OK"));
+        PrintAndLogEx(INFO, "[%d]: %s", len, sprint_hex(buf, len));
+    }
 
     DropField();
     return PM3_SUCCESS;

From 98060cf74888915192eb80cdd5180064be8c67b9 Mon Sep 17 00:00:00 2001
From: merlokk <807634+merlokk@users.noreply.github.com>
Date: Wed, 2 Jun 2021 19:59:41 +0300
Subject: [PATCH 23/39] some additional info

---
 client/src/cipurse/cipursecore.c | 13 ++++++++++++-
 client/src/cipurse/cipursecore.h |  2 ++
 client/src/cmdhfcipurse.c        |  1 +
 3 files changed, 15 insertions(+), 1 deletion(-)

diff --git a/client/src/cipurse/cipursecore.c b/client/src/cipurse/cipursecore.c
index acea27683..9b6176d97 100644
--- a/client/src/cipurse/cipursecore.c
+++ b/client/src/cipurse/cipursecore.c
@@ -183,4 +183,15 @@ bool CIPURSEChannelAuthenticate(uint8_t keyIndex, uint8_t *key, bool verbose) {
         CipurseCClearContext(&cipurseContext);
         return false;
     }
-}
\ No newline at end of file
+}
+
+void CIPURSEPrintInfoFile(uint8_t *data, size_t len) {
+    if (len < 2) {
+        PrintAndLogEx(ERR, "Info file length " _RED_("ERROR"));
+        return;
+    }
+
+    PrintAndLogEx(INFO, "------------ INFO ------------");
+    PrintAndLogEx(INFO, "CIPURSE version %d revision %d", data[0], data[1]);
+}
+
diff --git a/client/src/cipurse/cipursecore.h b/client/src/cipurse/cipursecore.h
index 7565538d0..e0f638553 100644
--- a/client/src/cipurse/cipursecore.h
+++ b/client/src/cipurse/cipursecore.h
@@ -19,6 +19,8 @@
 
 #define CIPURSE_DEFAULT_KEY {0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73}
 
+void CIPURSEPrintInfoFile(uint8_t *data, size_t len);
+
 int CIPURSESelect(bool ActivateField, bool LeaveFieldON, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);
 
 int CIPURSEChallenge(uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);
diff --git a/client/src/cmdhfcipurse.c b/client/src/cmdhfcipurse.c
index f4d51f633..a8942df5e 100644
--- a/client/src/cmdhfcipurse.c
+++ b/client/src/cmdhfcipurse.c
@@ -90,6 +90,7 @@ static int CmdHFCipurseInfo(const char *Cmd) {
     if (len > 0) {
         PrintAndLogEx(INFO, "Info file: " _GREEN_("OK"));
         PrintAndLogEx(INFO, "[%d]: %s", len, sprint_hex(buf, len));
+        CIPURSEPrintInfoFile(buf, len);
     }
 
     DropField();

From e8c6964bdf64ca3f35e5b805d81cf955fd83411c Mon Sep 17 00:00:00 2001
From: merlokk <807634+merlokk@users.noreply.github.com>
Date: Wed, 2 Jun 2021 20:07:15 +0300
Subject: [PATCH 24/39] fix small bug

---
 client/src/cipurse/cipursecrypto.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/client/src/cipurse/cipursecrypto.c b/client/src/cipurse/cipursecrypto.c
index dd2db2c66..10bbfa0ff 100644
--- a/client/src/cipurse/cipursecrypto.c
+++ b/client/src/cipurse/cipursecrypto.c
@@ -186,8 +186,8 @@ void CipurseCSetRandomHost(CipurseContext *ctx) {
 
 uint8_t CipurseCGetSMI(CipurseContext *ctx, bool LePresent) {
     uint8_t res = LePresent ? 1 : 0;
-    res = res | (CipurseCSecurityLevelEnc(ctx->RequestSecurity) << 2);
-    res = res | (CipurseCSecurityLevelEnc(ctx->ResponseSecurity) << 6);
+    res = res | (CipurseCSecurityLevelEnc(ctx->ResponseSecurity) << 2);
+    res = res | (CipurseCSecurityLevelEnc(ctx->RequestSecurity) << 6);
     return res;
 }
 

From 5caca153a5f972d49eaa50b64aebf1eacf3f884c Mon Sep 17 00:00:00 2001
From: merlokk <807634+merlokk@users.noreply.github.com>
Date: Wed, 2 Jun 2021 20:33:38 +0300
Subject: [PATCH 25/39] TX mac ok

---
 client/src/cipurse/cipursecrypto.c | 30 ++++++++++++++++++++++++------
 1 file changed, 24 insertions(+), 6 deletions(-)

diff --git a/client/src/cipurse/cipursecrypto.c b/client/src/cipurse/cipursecrypto.c
index 10bbfa0ff..e054bff27 100644
--- a/client/src/cipurse/cipursecrypto.c
+++ b/client/src/cipurse/cipursecrypto.c
@@ -215,7 +215,7 @@ bool CipurseCCheckCT(CipurseContext *ctx, uint8_t *CT) {
 
 void AddISO9797M2Padding(uint8_t *ddata, size_t *ddatalen, uint8_t *sdata, size_t sdatalen, size_t blocklen) {
     *ddatalen = sdatalen + 1;
-    *ddatalen += *ddatalen % blocklen;
+    *ddatalen += blocklen - *ddatalen % blocklen;
     memset(ddata, 0, *ddatalen);
     memcpy(ddata, sdata, sdatalen);
     ddata[sdatalen] = ISO9797_M2_PAD_BYTE;
@@ -287,7 +287,7 @@ void CipurseCChannelDecrypt(CipurseContext *ctx, uint8_t *data, size_t datalen,
  */
 void CipurseCGenerateMAC(CipurseContext *ctx, uint8_t *data, size_t datalen, uint8_t *mac) {
     uint8_t temp[CIPURSE_AES_KEY_LENGTH] = {0};
-
+PrintAndLogEx(INFO, "------[%d]: %s", datalen, sprint_hex(data, datalen));
     memcpy(ctx->frameKeyNext, ctx->frameKey, CIPURSE_AES_KEY_LENGTH);
     int i = 0;
     while (datalen > i) {
@@ -299,7 +299,8 @@ void CipurseCGenerateMAC(CipurseContext *ctx, uint8_t *data, size_t datalen, uin
  
     aes_encode(NULL, ctx->frameKey, ctx->frameKeyNext, temp, CIPURSE_AES_KEY_LENGTH);
     bin_xor(temp, ctx->frameKeyNext, CIPURSE_AES_KEY_LENGTH);
-    memcpy(mac, temp, CIPURSE_MAC_LENGTH);
+    if (mac != NULL)
+        memcpy(mac, temp, CIPURSE_MAC_LENGTH);
 }
 
 void CipurseCCalcMACPadded(CipurseContext *ctx, uint8_t *data, size_t datalen, uint8_t *mac) {
@@ -315,8 +316,23 @@ bool CipurseCCheckMACPadded(CipurseContext *ctx, uint8_t *data, size_t datalen,
     return (memcmp(mac, xmac, CIPURSE_MAC_LENGTH) == 0);
 }
 
+static void CipurseCAPDUMACEncode(CipurseContext *ctx, sAPDU *apdu, uint8_t *data, size_t *datalen) {
+    data[0] = apdu->CLA;
+    data[1] = apdu->INS;
+    data[2] = apdu->P1;
+    data[3] = apdu->P2;
+    data[4] = apdu->Lc;
+    *datalen = 5 + apdu->Lc;
+    
+    if (ctx->RequestSecurity == CPSMACed)
+        *datalen -= CIPURSE_MAC_LENGTH;
+    memcpy(&data[5], apdu->data, *datalen);
+}
+
 void CipurseCAPDUReqEncode(CipurseContext *ctx, sAPDU *srcapdu, sAPDU *dstapdu, uint8_t *dstdatabuf, bool includeLe, uint8_t Le) {
     uint8_t mac[CIPURSE_MAC_LENGTH] = {0};
+    uint8_t buf[260] = {0};
+    size_t buflen = 0;
     
     memcpy(dstapdu, srcapdu, sizeof(sAPDU));
     
@@ -337,11 +353,13 @@ void CipurseCAPDUReqEncode(CipurseContext *ctx, sAPDU *srcapdu, sAPDU *dstapdu,
         case CPSNone:
         break;
         case CPSPlain: 
-            CipurseCCalcMACPadded(ctx, (uint8_t *)dstapdu, dstapdu->Lc + 5, mac);
+            CipurseCAPDUMACEncode(ctx, dstapdu, buf, &buflen);
+            CipurseCCalcMACPadded(ctx, buf, buflen, mac);
         break;
-        case CPSMACed:        
+        case CPSMACed:
             dstapdu->Lc += CIPURSE_MAC_LENGTH;
-            CipurseCCalcMACPadded(ctx, (uint8_t *)dstapdu, dstapdu->Lc + 5, mac);
+            CipurseCAPDUMACEncode(ctx, dstapdu, buf, &buflen);
+            CipurseCCalcMACPadded(ctx, buf, buflen, mac);
             memcpy(&dstdatabuf[dstapdu->Lc - CIPURSE_MAC_LENGTH], mac, CIPURSE_MAC_LENGTH);
         break;
         case CPSEncrypted:

From eaf623af94c3c0ec2e165830341021737e7ccba4 Mon Sep 17 00:00:00 2001
From: merlokk <807634+merlokk@users.noreply.github.com>
Date: Wed, 2 Jun 2021 21:09:37 +0300
Subject: [PATCH 26/39] MAC works

---
 client/src/cipurse/cipursecore.c   | 11 +++++++----
 client/src/cipurse/cipursecore.h   |  3 +++
 client/src/cipurse/cipursecrypto.c | 22 +++++++++++++++++++---
 client/src/cmdhfcipurse.c          |  1 +
 4 files changed, 30 insertions(+), 7 deletions(-)

diff --git a/client/src/cipurse/cipursecore.c b/client/src/cipurse/cipursecore.c
index 9b6176d97..a848fc30c 100644
--- a/client/src/cipurse/cipursecore.c
+++ b/client/src/cipurse/cipursecore.c
@@ -20,7 +20,6 @@
 #include "emv/emvjson.h"
 #include "ui.h"
 #include "util.h"
-#include "cipurse/cipursecrypto.h"
 
 // context for secure channel
 CipurseContext cipurseContext;
@@ -49,7 +48,7 @@ static int CIPURSEExchangeEx(bool ActivateField, bool LeaveFieldON, sAPDU apdu,
     uint16_t xle = IncludeLe ? 0x100 : 0x00;
     if (xle == 0x100 && Le != 0)
         xle = Le;
-    
+
     CipurseCAPDUReqEncode(&cipurseContext, &apdu, &secapdu, securedata, IncludeLe, Le);
     
     if (APDUEncodeS(&secapdu, false, xle, data, &datalen)) {
@@ -172,8 +171,8 @@ bool CIPURSEChannelAuthenticate(uint8_t keyIndex, uint8_t *key, bool verbose) {
         if (verbose)
             PrintAndLogEx(INFO, "Authentication " _GREEN_("OK"));
         
-        //CipurseCChannelSetSecurityLevels(&cpc, CPSMACed, CPSMACed);
-        CipurseCChannelSetSecurityLevels(&cpc, CPSPlain, CPSPlain);
+        CipurseCChannelSetSecurityLevels(&cpc, CPSMACed, CPSMACed);
+        //CipurseCChannelSetSecurityLevels(&cpc, CPSPlain, CPSPlain);
         memcpy(&cipurseContext, &cpc, sizeof(CipurseContext));
         return true;
     } else {
@@ -185,6 +184,10 @@ bool CIPURSEChannelAuthenticate(uint8_t keyIndex, uint8_t *key, bool verbose) {
     }
 }
 
+void CIPURSECSetActChannelSecurityLevels(CipurseChannelSecurityLevel req, CipurseChannelSecurityLevel resp) {
+    CipurseCChannelSetSecurityLevels(&cipurseContext, req, resp);
+}
+
 void CIPURSEPrintInfoFile(uint8_t *data, size_t len) {
     if (len < 2) {
         PrintAndLogEx(ERR, "Info file length " _RED_("ERROR"));
diff --git a/client/src/cipurse/cipursecore.h b/client/src/cipurse/cipursecore.h
index e0f638553..ad044087e 100644
--- a/client/src/cipurse/cipursecore.h
+++ b/client/src/cipurse/cipursecore.h
@@ -16,6 +16,8 @@
 
 #include <jansson.h>
 #include "emv/apduinfo.h" // sAPDU
+#include "cipurse/cipursecrypto.h"
+
 
 #define CIPURSE_DEFAULT_KEY {0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73}
 
@@ -35,5 +37,6 @@ int CIPURSEReadBinary(uint16_t offset, uint8_t *Result, size_t MaxResultLen, siz
 int CIPURSEUpdateBinary(uint16_t offset, uint8_t *data, uint16_t datalen);
 
 bool CIPURSEChannelAuthenticate(uint8_t keyIndex, uint8_t *key, bool verbose);
+void CIPURSECSetActChannelSecurityLevels(CipurseChannelSecurityLevel req, CipurseChannelSecurityLevel resp);
 
 #endif /* __CIPURSECORE_H__ */
diff --git a/client/src/cipurse/cipursecrypto.c b/client/src/cipurse/cipursecrypto.c
index e054bff27..45081d75d 100644
--- a/client/src/cipurse/cipursecrypto.c
+++ b/client/src/cipurse/cipursecrypto.c
@@ -287,7 +287,7 @@ void CipurseCChannelDecrypt(CipurseContext *ctx, uint8_t *data, size_t datalen,
  */
 void CipurseCGenerateMAC(CipurseContext *ctx, uint8_t *data, size_t datalen, uint8_t *mac) {
     uint8_t temp[CIPURSE_AES_KEY_LENGTH] = {0};
-PrintAndLogEx(INFO, "------[%d]: %s", datalen, sprint_hex(data, datalen));
+
     memcpy(ctx->frameKeyNext, ctx->frameKey, CIPURSE_AES_KEY_LENGTH);
     int i = 0;
     while (datalen > i) {
@@ -299,6 +299,7 @@ PrintAndLogEx(INFO, "------[%d]: %s", datalen, sprint_hex(data, datalen));
  
     aes_encode(NULL, ctx->frameKey, ctx->frameKeyNext, temp, CIPURSE_AES_KEY_LENGTH);
     bin_xor(temp, ctx->frameKeyNext, CIPURSE_AES_KEY_LENGTH);
+    memcpy(ctx->frameKey, ctx->frameKeyNext, CIPURSE_AES_KEY_LENGTH);
     if (mac != NULL)
         memcpy(mac, temp, CIPURSE_MAC_LENGTH);
 }
@@ -354,7 +355,7 @@ void CipurseCAPDUReqEncode(CipurseContext *ctx, sAPDU *srcapdu, sAPDU *dstapdu,
         break;
         case CPSPlain: 
             CipurseCAPDUMACEncode(ctx, dstapdu, buf, &buflen);
-            CipurseCCalcMACPadded(ctx, buf, buflen, mac);
+            CipurseCCalcMACPadded(ctx, buf, buflen, NULL);
         break;
         case CPSMACed:
             dstapdu->Lc += CIPURSE_MAC_LENGTH;
@@ -371,6 +372,9 @@ void CipurseCAPDUReqEncode(CipurseContext *ctx, sAPDU *srcapdu, sAPDU *dstapdu,
 }
 
 void CipurseCAPDURespDecode(CipurseContext *ctx, uint8_t *srcdata, size_t srcdatalen, uint8_t *dstdata, size_t *dstdatalen, uint16_t *sw) {
+    uint8_t buf[260] = {0};
+    size_t buflen = 0;
+
     if (dstdatalen != NULL)
         *dstdatalen = 0;
     if (sw != NULL)
@@ -395,6 +399,12 @@ void CipurseCAPDURespDecode(CipurseContext *ctx, uint8_t *srcdata, size_t srcdat
         case CPSNone:
         break;
         case CPSPlain: 
+            memcpy(buf, srcdata, srcdatalen);
+            buflen = srcdatalen;
+            memcpy(&buf[buflen], &srcdata[srcdatalen], 2);
+            buflen += 2;
+            CipurseCCalcMACPadded(ctx, buf, buflen, NULL);
+            
             memcpy(dstdata, srcdata, srcdatalen);
             if (dstdatalen != NULL)
                 *dstdatalen = srcdatalen;
@@ -402,8 +412,14 @@ void CipurseCAPDURespDecode(CipurseContext *ctx, uint8_t *srcdata, size_t srcdat
         case CPSMACed:       
             if (srcdatalen < CIPURSE_MAC_LENGTH)
                 return;
+
+            buflen = srcdatalen - CIPURSE_MAC_LENGTH;
+            memcpy(buf, srcdata, buflen);
+            memcpy(&buf[buflen], &srcdata[srcdatalen], 2);
+            buflen += 2;
+
             srcdatalen -= CIPURSE_MAC_LENGTH;
-            if (CipurseCCheckMACPadded(ctx, srcdata, srcdatalen, &srcdata[srcdatalen]) == false) {
+            if (CipurseCCheckMACPadded(ctx, buf, buflen, &srcdata[srcdatalen]) == false) {
                 PrintAndLogEx(WARNING, "APDU MAC is not valid!");
             }
             memcpy(dstdata, srcdata, srcdatalen);
diff --git a/client/src/cmdhfcipurse.c b/client/src/cmdhfcipurse.c
index a8942df5e..b5be78a3a 100644
--- a/client/src/cmdhfcipurse.c
+++ b/client/src/cmdhfcipurse.c
@@ -246,6 +246,7 @@ static int CmdHFCipurseReadFile(const char *Cmd) {
         DropField();
         return PM3_ESOFT;
     }
+//CIPURSECSetActChannelSecurityLevels(CPSMACed, CPSMACed);
 
     if (verbose)
         PrintAndLogEx(INFO, "Select file 0x%x " _GREEN_("OK"), fileId);

From 6875ad1b817fe76dc4b9b3d63fb3c0c17f8307b6 Mon Sep 17 00:00:00 2001
From: merlokk <807634+merlokk@users.noreply.github.com>
Date: Wed, 2 Jun 2021 21:44:32 +0300
Subject: [PATCH 27/39] mic calculation ok

---
 client/src/cipurse/cipursecrypto.c | 55 ++++++++++++++++++++++++++++++
 client/src/cipurse/cipursecrypto.h |  2 ++
 2 files changed, 57 insertions(+)

diff --git a/client/src/cipurse/cipursecrypto.c b/client/src/cipurse/cipursecrypto.c
index 45081d75d..287f19182 100644
--- a/client/src/cipurse/cipursecrypto.c
+++ b/client/src/cipurse/cipursecrypto.c
@@ -231,6 +231,61 @@ size_t FindISO9797M2PaddingDataLen(uint8_t *data, size_t datalen) {
     return 0;
 }
 
+/*    private fun computeCRC(inputData: ByteArray): Long {
+        var initialCRC: Long = 0x6363
+        var ch: Long
+        for (i in inputData.indices) {
+            ch = (inputData[i].toInt() and 0xFF).toLong()
+            ch = ch xor (initialCRC and 0xFF)
+            ch = ch xor (ch shl 4) and 0xFF
+            initialCRC =
+                ((initialCRC shr 8 and 0x0FFFF) xor (ch shl 8 and 0x0FFFF) xor (ch shl 3 and 0x0FFFF) xor (ch shr 4 and 0x0FFFF) and 0xFFFF)
+        }
+        return initialCRC
+    }*/
+    
+static uint16_t CipurseCComputeMICCRC (uint8_t *data, size_t len) {
+    uint16_t initCRC = 0x6363;
+    for (size_t i = 0; i < len; i++) {
+        uint8_t ch = data[i] ^ initCRC;
+        ch = ch ^ ((ch << 4) & 0xff);
+        initCRC = (initCRC >> 8) ^ (ch << 8) ^ (ch << 3) ^ (ch >> 4);
+    }
+    return initCRC;
+}
+
+void CipurseCGenerateMIC(uint8_t *data, size_t datalen, uint8_t *mic) {
+    size_t plen = 0;
+    uint8_t pdata[datalen + CIPURSE_MIC_LENGTH];
+    memset(pdata, 0, sizeof(pdata));
+    
+    // 0x00 padding
+    memcpy(pdata, data, datalen);
+    plen = datalen;
+    if (datalen % CIPURSE_MIC_LENGTH)
+        plen += CIPURSE_MIC_LENGTH - datalen % CIPURSE_MIC_LENGTH;
+    
+    // crc
+    uint16_t crc1 = CipurseCComputeMICCRC(pdata, plen);
+    
+    for (size_t i = 0; i < datalen; i += 4) {
+        uint8_t tmp1 = pdata[i + 0];
+        uint8_t tmp2 = pdata[i + 1];
+        pdata[i + 0] = pdata[i + 2];
+        pdata[i + 1] = pdata[i + 3];
+        pdata[i + 2] = tmp1;
+        pdata[i + 3] = tmp2;
+    }
+    
+    uint16_t crc2 = CipurseCComputeMICCRC(pdata, plen);
+    if (mic != NULL) {
+        mic[0] = crc2 >> 8;
+        mic[1] = crc2 & 0xff;
+        mic[2] = crc1 >> 8;
+        mic[3] = crc1 & 0xff;
+    }
+}
+
 /* from: https://github.com/duychuongvn/cipurse-card-core/blob/master/src/main/java/com/github/duychuongvn/cirpusecard/core/security/crypto/CipurseCrypto.java#L521
  * 
  * Encrypt/Decrypt the given data using ciphering mechanism explained the OPST.
diff --git a/client/src/cipurse/cipursecrypto.h b/client/src/cipurse/cipursecrypto.h
index 73f6116b3..841fc8c10 100644
--- a/client/src/cipurse/cipursecrypto.h
+++ b/client/src/cipurse/cipursecrypto.h
@@ -18,6 +18,7 @@
 #define CIPURSE_AES_KEY_LENGTH 16
 #define CIPURSE_SECURITY_PARAM_N 6
 #define CIPURSE_MAC_LENGTH 8
+#define CIPURSE_MIC_LENGTH 4
 #define CIPURSE_POLY 0x35b088cce172UL
 #define ISO9797_M2_PAD_BYTE 0x80
 
@@ -70,6 +71,7 @@ size_t FindISO9797M2PaddingDataLen(uint8_t *data, size_t datalen);
 void CipurseCGenerateMAC(CipurseContext *ctx, uint8_t *data, size_t datalen, uint8_t *mac);
 void CipurseCCalcMACPadded(CipurseContext *ctx, uint8_t *data, size_t datalen, uint8_t *mac);
 bool CipurseCCheckMACPadded(CipurseContext *ctx, uint8_t *data, size_t datalen, uint8_t *mac);
+void CipurseCGenerateMIC(uint8_t *data, size_t datalen, uint8_t *mic);
 void CipurseCEncryptDecrypt(CipurseContext *ctx, uint8_t *data, size_t datalen, uint8_t *dstdata, bool isEncrypt);
 void CipurseCChannelEncrypt(CipurseContext *ctx, uint8_t *data, size_t datalen, uint8_t *encdata, size_t *encdatalen);
 void CipurseCChannelDecrypt(CipurseContext *ctx, uint8_t *data, size_t datalen, uint8_t *plaindata, size_t *plaindatalen);

From 9cfa638e4d9c480242bf5d09eb0ff62c81c9c748 Mon Sep 17 00:00:00 2001
From: merlokk <807634+merlokk@users.noreply.github.com>
Date: Thu, 3 Jun 2021 00:40:12 +0300
Subject: [PATCH 28/39] encrypted resp works

---
 client/src/cipurse/cipursecrypto.c | 51 ++++++++++++++++++++----------
 client/src/cipurse/cipursecrypto.h |  1 +
 2 files changed, 35 insertions(+), 17 deletions(-)

diff --git a/client/src/cipurse/cipursecrypto.c b/client/src/cipurse/cipursecrypto.c
index 287f19182..1775dbf0b 100644
--- a/client/src/cipurse/cipursecrypto.c
+++ b/client/src/cipurse/cipursecrypto.c
@@ -23,7 +23,7 @@
 #include "util.h"
 
 uint8_t AESData0[CIPURSE_AES_KEY_LENGTH] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
-uint8_t QConstant[CIPURSE_AES_KEY_LENGTH] = {0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73};
+uint8_t QConstant[CIPURSE_AES_KEY_LENGTH] = {0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74};
 
 uint8_t CipurseCSecurityLevelEnc(CipurseChannelSecurityLevel lvl) {
     switch (lvl) {
@@ -224,26 +224,13 @@ void AddISO9797M2Padding(uint8_t *ddata, size_t *ddatalen, uint8_t *sdata, size_
 size_t FindISO9797M2PaddingDataLen(uint8_t *data, size_t datalen) {
     for (int i = datalen; i > 0; i--) {
         if (data[i - 1] == 0x80)
-            return i;
+            return i - 1;
         if (data[i - 1] != 0x00)
             return 0;
     }
     return 0;
 }
 
-/*    private fun computeCRC(inputData: ByteArray): Long {
-        var initialCRC: Long = 0x6363
-        var ch: Long
-        for (i in inputData.indices) {
-            ch = (inputData[i].toInt() and 0xFF).toLong()
-            ch = ch xor (initialCRC and 0xFF)
-            ch = ch xor (ch shl 4) and 0xFF
-            initialCRC =
-                ((initialCRC shr 8 and 0x0FFFF) xor (ch shl 8 and 0x0FFFF) xor (ch shl 3 and 0x0FFFF) xor (ch shr 4 and 0x0FFFF) and 0xFFFF)
-        }
-        return initialCRC
-    }*/
-    
 static uint16_t CipurseCComputeMICCRC (uint8_t *data, size_t len) {
     uint16_t initCRC = 0x6363;
     for (size_t i = 0; i < len; i++) {
@@ -286,6 +273,13 @@ void CipurseCGenerateMIC(uint8_t *data, size_t datalen, uint8_t *mic) {
     }
 }
 
+bool CipurseCCheckMIC(uint8_t *data, size_t datalen, uint8_t *mic) {
+    uint8_t xmic[CIPURSE_MIC_LENGTH] = {0};
+    
+    CipurseCGenerateMIC(data, datalen, xmic);
+    return (memcmp(xmic, mic, CIPURSE_MIC_LENGTH) == 0);
+}
+
 /* from: https://github.com/duychuongvn/cipurse-card-core/blob/master/src/main/java/com/github/duychuongvn/cirpusecard/core/security/crypto/CipurseCrypto.java#L521
  * 
  * Encrypt/Decrypt the given data using ciphering mechanism explained the OPST.
@@ -298,7 +292,7 @@ void CipurseCGenerateMIC(uint8_t *data, size_t datalen, uint8_t *mic) {
  */
 void CipurseCEncryptDecrypt(CipurseContext *ctx, uint8_t *data, size_t datalen, uint8_t *dstdata, bool isEncrypt) {
     uint8_t hx[CIPURSE_AES_KEY_LENGTH] = {0};
-
+PrintAndLogEx(INFO, "----data[%d]: %s", datalen, sprint_hex(data, datalen));
     memcpy(ctx->frameKeyNext, ctx->frameKey, CIPURSE_AES_KEY_LENGTH);
     int i = 0;
     while (datalen > i) {
@@ -313,6 +307,7 @@ void CipurseCEncryptDecrypt(CipurseContext *ctx, uint8_t *data, size_t datalen,
         memcpy(ctx->frameKeyNext, hx, CIPURSE_AES_KEY_LENGTH);
         i += CIPURSE_AES_KEY_LENGTH;
     }
+    memcpy(ctx->frameKey, ctx->frameKeyNext, CIPURSE_AES_KEY_LENGTH);
 }
 
 void CipurseCChannelEncrypt(CipurseContext *ctx, uint8_t *data, size_t datalen, uint8_t *encdata, size_t *encdatalen) {
@@ -419,6 +414,9 @@ void CipurseCAPDUReqEncode(CipurseContext *ctx, sAPDU *srcapdu, sAPDU *dstapdu,
             memcpy(&dstdatabuf[dstapdu->Lc - CIPURSE_MAC_LENGTH], mac, CIPURSE_MAC_LENGTH);
         break;
         case CPSEncrypted:
+            CipurseCAPDUMACEncode(ctx, dstapdu, buf, &buflen);
+            CipurseCGenerateMIC(buf, buflen, mac);
+            PrintAndLogEx(INFO, "mic: %s", sprint_hex(mac, 4));
         break;
         default: 
         break;
@@ -429,6 +427,8 @@ void CipurseCAPDUReqEncode(CipurseContext *ctx, sAPDU *srcapdu, sAPDU *dstapdu,
 void CipurseCAPDURespDecode(CipurseContext *ctx, uint8_t *srcdata, size_t srcdatalen, uint8_t *dstdata, size_t *dstdatalen, uint16_t *sw) {
     uint8_t buf[260] = {0};
     size_t buflen = 0;
+    uint8_t micdata[260] = {0};
+    size_t micdatalen = 0;
 
     if (dstdatalen != NULL)
         *dstdatalen = 0;
@@ -450,7 +450,7 @@ void CipurseCAPDURespDecode(CipurseContext *ctx, uint8_t *srcdata, size_t srcdat
         return;
     }
     
-    switch (ctx->RequestSecurity) {
+    switch (ctx->ResponseSecurity) {
         case CPSNone:
         break;
         case CPSPlain: 
@@ -482,6 +482,23 @@ void CipurseCAPDURespDecode(CipurseContext *ctx, uint8_t *srcdata, size_t srcdat
                 *dstdatalen = srcdatalen;
         break;
         case CPSEncrypted:
+            CipurseCChannelDecrypt(ctx, srcdata, srcdatalen, buf, &buflen);
+            //PrintAndLogEx(INFO, "data plain[%d]: %s", buflen, sprint_hex(buf, buflen));
+            
+            micdatalen = buflen - 2 - CIPURSE_MIC_LENGTH;
+            memcpy(micdata, buf, buflen);
+            memcpy(&micdata[micdatalen], &buf[buflen - 2], 2);
+            micdatalen += 2;
+            
+            if (CipurseCCheckMIC(micdata, micdatalen, &buf[micdatalen - 2]) == false) {
+                PrintAndLogEx(ERR, "APDU response MIC is not valid!");
+            }
+            
+            memcpy(dstdata, buf, micdatalen - 2);
+            if (dstdatalen != NULL)
+                *dstdatalen = micdatalen - 2;
+            if (sw)
+                *sw = micdata[micdatalen - 2] * 0x0100 + micdata[micdatalen - 1];
         break;
         default: 
         break;
diff --git a/client/src/cipurse/cipursecrypto.h b/client/src/cipurse/cipursecrypto.h
index 841fc8c10..f89c72ba1 100644
--- a/client/src/cipurse/cipursecrypto.h
+++ b/client/src/cipurse/cipursecrypto.h
@@ -72,6 +72,7 @@ void CipurseCGenerateMAC(CipurseContext *ctx, uint8_t *data, size_t datalen, uin
 void CipurseCCalcMACPadded(CipurseContext *ctx, uint8_t *data, size_t datalen, uint8_t *mac);
 bool CipurseCCheckMACPadded(CipurseContext *ctx, uint8_t *data, size_t datalen, uint8_t *mac);
 void CipurseCGenerateMIC(uint8_t *data, size_t datalen, uint8_t *mic);
+bool CipurseCCheckMIC(uint8_t *data, size_t datalen, uint8_t *mic);
 void CipurseCEncryptDecrypt(CipurseContext *ctx, uint8_t *data, size_t datalen, uint8_t *dstdata, bool isEncrypt);
 void CipurseCChannelEncrypt(CipurseContext *ctx, uint8_t *data, size_t datalen, uint8_t *encdata, size_t *encdatalen);
 void CipurseCChannelDecrypt(CipurseContext *ctx, uint8_t *data, size_t datalen, uint8_t *plaindata, size_t *plaindatalen);

From a206dcc9b2911e6644a4678574a3057ae970e0b9 Mon Sep 17 00:00:00 2001
From: merlokk <807634+merlokk@users.noreply.github.com>
Date: Thu, 3 Jun 2021 12:03:32 +0300
Subject: [PATCH 29/39] clear security state when card returns error

---
 client/src/cipurse/cipursecore.c | 14 +++++++++++---
 1 file changed, 11 insertions(+), 3 deletions(-)

diff --git a/client/src/cipurse/cipursecore.c b/client/src/cipurse/cipursecore.c
index a848fc30c..ae42c5172 100644
--- a/client/src/cipurse/cipursecore.c
+++ b/client/src/cipurse/cipursecore.c
@@ -70,10 +70,17 @@ static int CIPURSEExchangeEx(bool ActivateField, bool LeaveFieldON, sAPDU apdu,
     if (*ResultLen < 2) {
         return 200;
     }
-
+    
     size_t rlen = 0;
-    CipurseCAPDURespDecode(&cipurseContext, Result, *ResultLen, securedata, &rlen, &isw);
-    memcpy(Result, securedata, rlen);
+    if (*ResultLen == 2) {
+        CipurseCClearContext(&cipurseContext);
+
+        isw = Result[0] * 0x0100 + Result[1];
+    } else {
+        CipurseCAPDURespDecode(&cipurseContext, Result, *ResultLen, securedata, &rlen, &isw);
+        memcpy(Result, securedata, rlen);
+    }
+    
     if (ResultLen != NULL)
         *ResultLen = rlen;
     
@@ -171,6 +178,7 @@ bool CIPURSEChannelAuthenticate(uint8_t keyIndex, uint8_t *key, bool verbose) {
         if (verbose)
             PrintAndLogEx(INFO, "Authentication " _GREEN_("OK"));
         
+        //CipurseCChannelSetSecurityLevels(&cpc, CPSEncrypted, CPSEncrypted);
         CipurseCChannelSetSecurityLevels(&cpc, CPSMACed, CPSMACed);
         //CipurseCChannelSetSecurityLevels(&cpc, CPSPlain, CPSPlain);
         memcpy(&cipurseContext, &cpc, sizeof(CipurseContext));

From 737ff2d46522d31881a13712e778a3ba5ca78c81 Mon Sep 17 00:00:00 2001
From: merlokk <807634+merlokk@users.noreply.github.com>
Date: Thu, 3 Jun 2021 14:39:15 +0300
Subject: [PATCH 30/39] encoding works

---
 client/src/cipurse/cipursecore.c   |  5 ++---
 client/src/cipurse/cipursecrypto.c | 32 ++++++++++++++++++++++--------
 client/src/cipurse/cipursecrypto.h |  1 +
 3 files changed, 27 insertions(+), 11 deletions(-)

diff --git a/client/src/cipurse/cipursecore.c b/client/src/cipurse/cipursecore.c
index ae42c5172..d9428241c 100644
--- a/client/src/cipurse/cipursecore.c
+++ b/client/src/cipurse/cipursecore.c
@@ -73,7 +73,8 @@ static int CIPURSEExchangeEx(bool ActivateField, bool LeaveFieldON, sAPDU apdu,
     
     size_t rlen = 0;
     if (*ResultLen == 2) {
-        CipurseCClearContext(&cipurseContext);
+        if (cipurseContext.RequestSecurity == CPSMACed || cipurseContext.RequestSecurity == CPSEncrypted)
+            CipurseCClearContext(&cipurseContext);
 
         isw = Result[0] * 0x0100 + Result[1];
     } else {
@@ -178,9 +179,7 @@ bool CIPURSEChannelAuthenticate(uint8_t keyIndex, uint8_t *key, bool verbose) {
         if (verbose)
             PrintAndLogEx(INFO, "Authentication " _GREEN_("OK"));
         
-        //CipurseCChannelSetSecurityLevels(&cpc, CPSEncrypted, CPSEncrypted);
         CipurseCChannelSetSecurityLevels(&cpc, CPSMACed, CPSMACed);
-        //CipurseCChannelSetSecurityLevels(&cpc, CPSPlain, CPSPlain);
         memcpy(&cipurseContext, &cpc, sizeof(CipurseContext));
         return true;
     } else {
diff --git a/client/src/cipurse/cipursecrypto.c b/client/src/cipurse/cipursecrypto.c
index 1775dbf0b..7e31fba07 100644
--- a/client/src/cipurse/cipursecrypto.c
+++ b/client/src/cipurse/cipursecrypto.c
@@ -292,7 +292,10 @@ bool CipurseCCheckMIC(uint8_t *data, size_t datalen, uint8_t *mic) {
  */
 void CipurseCEncryptDecrypt(CipurseContext *ctx, uint8_t *data, size_t datalen, uint8_t *dstdata, bool isEncrypt) {
     uint8_t hx[CIPURSE_AES_KEY_LENGTH] = {0};
-PrintAndLogEx(INFO, "----data[%d]: %s", datalen, sprint_hex(data, datalen));
+    
+    if (datalen == 0 || datalen % CIPURSE_AES_KEY_LENGTH != 0)
+        return;
+    
     memcpy(ctx->frameKeyNext, ctx->frameKey, CIPURSE_AES_KEY_LENGTH);
     int i = 0;
     while (datalen > i) {
@@ -367,7 +370,7 @@ bool CipurseCCheckMACPadded(CipurseContext *ctx, uint8_t *data, size_t datalen,
     return (memcmp(mac, xmac, CIPURSE_MAC_LENGTH) == 0);
 }
 
-static void CipurseCAPDUMACEncode(CipurseContext *ctx, sAPDU *apdu, uint8_t *data, size_t *datalen) {
+static void CipurseCAPDUMACEncode(CipurseContext *ctx, sAPDU *apdu, uint8_t originalLc, uint8_t *data, size_t *datalen) {
     data[0] = apdu->CLA;
     data[1] = apdu->INS;
     data[2] = apdu->P1;
@@ -375,8 +378,8 @@ static void CipurseCAPDUMACEncode(CipurseContext *ctx, sAPDU *apdu, uint8_t *dat
     data[4] = apdu->Lc;
     *datalen = 5 + apdu->Lc;
     
-    if (ctx->RequestSecurity == CPSMACed)
-        *datalen -= CIPURSE_MAC_LENGTH;
+    if (ctx->RequestSecurity == CPSMACed || ctx->RequestSecurity == CPSEncrypted)
+        *datalen = 5 + originalLc;
     memcpy(&data[5], apdu->data, *datalen);
 }
 
@@ -399,24 +402,37 @@ void CipurseCAPDUReqEncode(CipurseContext *ctx, sAPDU *srcapdu, sAPDU *dstapdu,
         dstapdu->data[dstapdu->Lc] = Le;
         dstapdu->Lc++;
     }
+    uint8_t originalLc = dstapdu->Lc;
 
     switch (ctx->RequestSecurity) {
         case CPSNone:
         break;
         case CPSPlain: 
-            CipurseCAPDUMACEncode(ctx, dstapdu, buf, &buflen);
+            CipurseCAPDUMACEncode(ctx, dstapdu, originalLc, buf, &buflen);
             CipurseCCalcMACPadded(ctx, buf, buflen, NULL);
         break;
         case CPSMACed:
             dstapdu->Lc += CIPURSE_MAC_LENGTH;
-            CipurseCAPDUMACEncode(ctx, dstapdu, buf, &buflen);
+            CipurseCAPDUMACEncode(ctx, dstapdu, originalLc, buf, &buflen);
             CipurseCCalcMACPadded(ctx, buf, buflen, mac);
             memcpy(&dstdatabuf[dstapdu->Lc - CIPURSE_MAC_LENGTH], mac, CIPURSE_MAC_LENGTH);
         break;
         case CPSEncrypted:
-            CipurseCAPDUMACEncode(ctx, dstapdu, buf, &buflen);
+            dstapdu->Lc = srcapdu->Lc + CIPURSE_MIC_LENGTH;
+            dstapdu->Lc += CIPURSE_AES_BLOCK_LENGTH - dstapdu->Lc % CIPURSE_AES_BLOCK_LENGTH + 1; // 1 - SMI
+            if (includeLe)
+                dstapdu->Lc++;
+            
+            CipurseCAPDUMACEncode(ctx, dstapdu, originalLc, buf, &buflen);
             CipurseCGenerateMIC(buf, buflen, mac);
-            PrintAndLogEx(INFO, "mic: %s", sprint_hex(mac, 4));
+            buf[0] = dstapdu->CLA;
+            buf[1] = dstapdu->INS;
+            buf[2] = dstapdu->P1;
+            buf[3] = dstapdu->P2;
+            memcpy(&buf[4], srcapdu->data, srcapdu->Lc);
+            memcpy(&buf[4 + srcapdu->Lc], mac, CIPURSE_MIC_LENGTH);
+            //PrintAndLogEx(INFO, "data plain[%d]: %s", 4 + srcapdu->Lc + CIPURSE_MIC_LENGTH, sprint_hex(buf, 4 + srcapdu->Lc + CIPURSE_MIC_LENGTH));
+            CipurseCChannelEncrypt(ctx, buf, 4 + srcapdu->Lc + CIPURSE_MIC_LENGTH, &dstdatabuf[1], &buflen);
         break;
         default: 
         break;
diff --git a/client/src/cipurse/cipursecrypto.h b/client/src/cipurse/cipursecrypto.h
index f89c72ba1..b554f54de 100644
--- a/client/src/cipurse/cipursecrypto.h
+++ b/client/src/cipurse/cipursecrypto.h
@@ -16,6 +16,7 @@
 
 #define CIPURSE_KVV_LENGTH 4
 #define CIPURSE_AES_KEY_LENGTH 16
+#define CIPURSE_AES_BLOCK_LENGTH 16
 #define CIPURSE_SECURITY_PARAM_N 6
 #define CIPURSE_MAC_LENGTH 8
 #define CIPURSE_MIC_LENGTH 4

From e5fe614a4f768d30236d22493aaee2723eed0036 Mon Sep 17 00:00:00 2001
From: merlokk <807634+merlokk@users.noreply.github.com>
Date: Thu, 3 Jun 2021 14:53:51 +0300
Subject: [PATCH 31/39] added no-authenticate option to read file

---
 client/src/cmdhfcipurse.c | 22 ++++++++++++++++------
 1 file changed, 16 insertions(+), 6 deletions(-)

diff --git a/client/src/cmdhfcipurse.c b/client/src/cmdhfcipurse.c
index b5be78a3a..9db939b66 100644
--- a/client/src/cmdhfcipurse.c
+++ b/client/src/cmdhfcipurse.c
@@ -184,6 +184,7 @@ static int CmdHFCipurseReadFile(const char *Cmd) {
         arg_str0("k",  "key",     "<hex>", "key for authenticate"),
         arg_str0("f",  "file",    "<hex>", "file ID"),
         arg_int0("o",  "offset",  "<dec>", "offset for reading data from file"),
+        arg_lit0(NULL, "noauth",  "read file without authentication"),
         arg_param_end
     };
     CLIExecWithReturn(ctx, Cmd, argtable, true);
@@ -216,6 +217,13 @@ static int CmdHFCipurseReadFile(const char *Cmd) {
         fileId = (hdata[0] << 8) + hdata[1];
     
     size_t offset = arg_get_int_def(ctx, 6, 0);
+
+    bool noAuth = arg_get_lit(ctx, 7);
+
+        //CipurseCChannelSetSecurityLevels(&cpc, CPSEncrypted, CPSEncrypted);
+        //CipurseCChannelSetSecurityLevels(&cpc, CPSMACed, CPSMACed);
+        //CipurseCChannelSetSecurityLevels(&cpc, CPSPlain, CPSPlain);
+
     
     SetAPDULogging(APDULogging);
 
@@ -231,12 +239,14 @@ static int CmdHFCipurseReadFile(const char *Cmd) {
     if (verbose)
         PrintAndLogEx(INFO, "File id: %x offset %d key id: %d key: %s", fileId, offset, keyId, sprint_hex(key, CIPURSE_AES_KEY_LENGTH));
 
-    bool bres = CIPURSEChannelAuthenticate(keyId, key, verbose);
-    if (bres == false) {
-        if (verbose == false)
-            PrintAndLogEx(ERR, "Authentication " _RED_("ERROR"));
-        DropField();
-        return PM3_ESOFT;
+    if (noAuth == false) {
+        bool bres = CIPURSEChannelAuthenticate(keyId, key, verbose);
+        if (bres == false) {
+            if (verbose == false)
+                PrintAndLogEx(ERR, "Authentication " _RED_("ERROR"));
+            DropField();
+            return PM3_ESOFT;
+        }
     }
     
     res = CIPURSESelectFile(fileId, buf, sizeof(buf), &len, &sw);

From f682a184b847ad5140a3a93b544ef5af466ad644 Mon Sep 17 00:00:00 2001
From: merlokk <807634+merlokk@users.noreply.github.com>
Date: Thu, 3 Jun 2021 15:29:46 +0300
Subject: [PATCH 32/39] set channel security levels from command line

---
 client/src/cmdhfcipurse.c | 83 +++++++++++++++++++++++++++++++--------
 1 file changed, 67 insertions(+), 16 deletions(-)

diff --git a/client/src/cmdhfcipurse.c b/client/src/cmdhfcipurse.c
index 9db939b66..dd3292457 100644
--- a/client/src/cmdhfcipurse.c
+++ b/client/src/cmdhfcipurse.c
@@ -164,6 +164,60 @@ static int CmdHFCipurseAuth(const char *Cmd) {
     return bres ? PM3_SUCCESS : PM3_ESOFT;
 }
 
+static int CLIParseKeyAndSecurityLevels(CLIParserContext *ctx, size_t keyid, size_t sreqid, size_t srespid, uint8_t *key, CipurseChannelSecurityLevel *sreq, CipurseChannelSecurityLevel *sresp) {
+    uint8_t hdata[250] = {0};
+    int hdatalen = sizeof(hdata);    
+    CLIGetHexWithReturn(ctx, keyid, hdata, &hdatalen);
+    if (hdatalen && hdatalen != 16) {
+        PrintAndLogEx(ERR, _RED_("ERROR:") " key length for AES128 must be 16 bytes only.");
+        CLIParserFree(ctx);
+        return PM3_EINVARG;
+    }
+    if (hdatalen)
+        memcpy(key, hdata, CIPURSE_AES_KEY_LENGTH);
+    
+    *sreq = CPSMACed;
+    *sresp = CPSMACed;
+
+    char cdata[250] = {0};
+    int cdatalen = sizeof(cdata);    
+    cdatalen--; // for trailer 0x00
+    CLIGetStrWithReturn(ctx, sreqid, (uint8_t *)cdata, &cdatalen);
+    if (cdatalen) {
+        str_lower(cdata);
+        if (strcmp(cdata, "plain") == 0)
+            *sreq = CPSPlain;
+        else if (strcmp(cdata, "mac") == 0)
+            *sreq = CPSMACed;
+        else if (strcmp(cdata, "enc") == 0 || strcmp(cdata, "encode") == 0 || strcmp(cdata, "encrypted") == 0)
+            *sreq = CPSEncrypted;
+        else {
+            PrintAndLogEx(ERR, _RED_("ERROR:") " security level can be only: plain|mac|encode.");
+            return PM3_EINVARG;
+        }
+    }
+
+    cdatalen = sizeof(cdata);    
+    memset(cdata, 0, cdatalen);
+    cdatalen--; // for trailer 0x00
+    CLIGetStrWithReturn(ctx, srespid, (uint8_t *)cdata, &cdatalen);
+    if (cdatalen) {
+        str_lower(cdata);
+        if (strcmp(cdata, "plain") == 0)
+            *sresp = CPSPlain;
+        else if (strcmp(cdata, "mac") == 0)
+            *sresp = CPSMACed;
+        else if (strcmp(cdata, "enc") == 0 || strcmp(cdata, "encode") == 0 || strcmp(cdata, "encrypted") == 0)
+            *sresp = CPSEncrypted;
+        else {
+            PrintAndLogEx(ERR, _RED_("ERROR:") " security level can be only: plain|mac|encode.");
+            return PM3_EINVARG;
+        }
+    }
+    
+    return PM3_SUCCESS;
+}
+
 static int CmdHFCipurseReadFile(const char *Cmd) {
     uint8_t buf[APDU_RES_LEN] = {0};
     size_t len = 0;
@@ -185,6 +239,8 @@ static int CmdHFCipurseReadFile(const char *Cmd) {
         arg_str0("f",  "file",    "<hex>", "file ID"),
         arg_int0("o",  "offset",  "<dec>", "offset for reading data from file"),
         arg_lit0(NULL, "noauth",  "read file without authentication"),
+        arg_str0(NULL, "sreq",    "<plain|mac(default)|encode>", "communication reader-PICC security level"),
+        arg_str0(NULL, "sresp",   "<plain|mac(default)|encode>", "communication PICC-reader security level"),
         arg_param_end
     };
     CLIExecWithReturn(ctx, Cmd, argtable, true);
@@ -193,20 +249,18 @@ static int CmdHFCipurseReadFile(const char *Cmd) {
     bool verbose = arg_get_lit(ctx, 2);
     uint8_t keyId = arg_get_int_def(ctx, 3, 1);
     
-    uint8_t hdata[250] = {0};
-    int hdatalen = sizeof(hdata);    
-    CLIGetHexWithReturn(ctx, 4, hdata, &hdatalen);
-    if (hdatalen && hdatalen != 16) {
-        PrintAndLogEx(ERR, _RED_("ERROR:") " key length for AES128 must be 16 bytes only.");
+    CipurseChannelSecurityLevel sreq = CPSMACed;
+    CipurseChannelSecurityLevel sresp = CPSMACed;
+    int res = CLIParseKeyAndSecurityLevels(ctx, 4, 8, 9, key, &sreq, &sresp);
+    if (res) {
         CLIParserFree(ctx);
         return PM3_EINVARG;
     }
-    if (hdatalen)
-        memcpy(key, hdata, CIPURSE_AES_KEY_LENGTH);
-    
+
     uint16_t fileId = 0x2ff7;
     
-    hdatalen = sizeof(hdata);    
+    uint8_t hdata[250] = {0};
+    int hdatalen = sizeof(hdata);    
     CLIGetHexWithReturn(ctx, 5, hdata, &hdatalen);
     if (hdatalen && hdatalen != 2) {
         PrintAndLogEx(ERR, _RED_("ERROR:") " file id length must be 2 bytes only.");
@@ -220,16 +274,11 @@ static int CmdHFCipurseReadFile(const char *Cmd) {
 
     bool noAuth = arg_get_lit(ctx, 7);
 
-        //CipurseCChannelSetSecurityLevels(&cpc, CPSEncrypted, CPSEncrypted);
-        //CipurseCChannelSetSecurityLevels(&cpc, CPSMACed, CPSMACed);
-        //CipurseCChannelSetSecurityLevels(&cpc, CPSPlain, CPSPlain);
-
-    
     SetAPDULogging(APDULogging);
 
     CLIParserFree(ctx);
     
-    int res = CIPURSESelect(true, true, buf, sizeof(buf), &len, &sw);
+    res = CIPURSESelect(true, true, buf, sizeof(buf), &len, &sw);
     if (res != 0 || sw != 0x9000) {
         PrintAndLogEx(ERR, "Cipurse select " _RED_("error") ". Card returns 0x%04x.", sw);
         DropField();
@@ -247,6 +296,9 @@ static int CmdHFCipurseReadFile(const char *Cmd) {
             DropField();
             return PM3_ESOFT;
         }
+        
+        // set channel security levels
+        CIPURSECSetActChannelSecurityLevels(sreq, sresp);
     }
     
     res = CIPURSESelectFile(fileId, buf, sizeof(buf), &len, &sw);
@@ -256,7 +308,6 @@ static int CmdHFCipurseReadFile(const char *Cmd) {
         DropField();
         return PM3_ESOFT;
     }
-//CIPURSECSetActChannelSecurityLevels(CPSMACed, CPSMACed);
 
     if (verbose)
         PrintAndLogEx(INFO, "Select file 0x%x " _GREEN_("OK"), fileId);

From 012b7e570165b75c31955f1da68760d7b5104391 Mon Sep 17 00:00:00 2001
From: merlokk <807634+merlokk@users.noreply.github.com>
Date: Thu, 3 Jun 2021 16:03:11 +0300
Subject: [PATCH 33/39] write file works

---
 client/src/cipurse/cipursecore.c |   4 +
 client/src/cipurse/cipursecore.h |   2 +-
 client/src/cmdhfcipurse.c        | 123 ++++++++++++++++++++++++++++++-
 3 files changed, 126 insertions(+), 3 deletions(-)

diff --git a/client/src/cipurse/cipursecore.c b/client/src/cipurse/cipursecore.c
index d9428241c..f36181992 100644
--- a/client/src/cipurse/cipursecore.c
+++ b/client/src/cipurse/cipursecore.c
@@ -135,6 +135,10 @@ int CIPURSEReadBinary(uint16_t offset, uint8_t *Result, size_t MaxResultLen, siz
     return CIPURSEExchangeEx(false, true, (sAPDU) {0x00, 0xb0, (offset >> 8) & 0x7f, offset & 0xff, 0, NULL}, true, 0, Result, MaxResultLen, ResultLen, sw);
 }
 
+int CIPURSEUpdateBinary(uint16_t offset, uint8_t *data, uint16_t datalen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
+    return CIPURSEExchangeEx(false, true, (sAPDU) {0x00, 0xd6, (offset >> 8) & 0x7f, offset & 0xff, datalen, data}, true, 0, Result, MaxResultLen, ResultLen, sw);
+}
+
 bool CIPURSEChannelAuthenticate(uint8_t keyIndex, uint8_t *key, bool verbose) {
     uint8_t buf[APDU_RES_LEN] = {0};
     size_t len = 0;
diff --git a/client/src/cipurse/cipursecore.h b/client/src/cipurse/cipursecore.h
index ad044087e..9ac8f4906 100644
--- a/client/src/cipurse/cipursecore.h
+++ b/client/src/cipurse/cipursecore.h
@@ -34,7 +34,7 @@ int CIPURSEDeleteFile(uint16_t fileID);
 int CIPURSESelectFile(uint16_t fileID, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);
 int CIPURSEReadFileAttributes(uint8_t *data, uint16_t *datalen);
 int CIPURSEReadBinary(uint16_t offset, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);
-int CIPURSEUpdateBinary(uint16_t offset, uint8_t *data, uint16_t datalen);
+int CIPURSEUpdateBinary(uint16_t offset, uint8_t *data, uint16_t datalen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);
 
 bool CIPURSEChannelAuthenticate(uint8_t keyIndex, uint8_t *key, bool verbose);
 void CIPURSECSetActChannelSecurityLevels(CipurseChannelSecurityLevel req, CipurseChannelSecurityLevel resp);
diff --git a/client/src/cmdhfcipurse.c b/client/src/cmdhfcipurse.c
index dd3292457..588ac9b3e 100644
--- a/client/src/cmdhfcipurse.c
+++ b/client/src/cmdhfcipurse.c
@@ -228,7 +228,7 @@ static int CmdHFCipurseReadFile(const char *Cmd) {
     CLIParserInit(&ctx, "hf cipurse read",
                   "Read file by file ID with key ID and key",
                   "hf cipurse read -f 2ff7   -> Authenticate with keyID=1 and key = 7373...7373 and read file with id 2ff7\n"
-                  "hf cipurse auth -n 2 -k 65656565656565656565656565656565 -f 2ff7 -> Authenticate with specified key and read file\n");
+                  "hf cipurse read -n 2 -k 65656565656565656565656565656565 -f 2ff7 -> Authenticate with specified key and read file\n");
 
     void *argtable[] = {
         arg_param_begin,
@@ -329,6 +329,124 @@ static int CmdHFCipurseReadFile(const char *Cmd) {
     return PM3_SUCCESS;
 }
 
+static int CmdHFCipurseWriteFile(const char *Cmd) {
+    uint8_t buf[APDU_RES_LEN] = {0};
+    size_t len = 0;
+    uint16_t sw = 0;
+    uint8_t key[] = CIPURSE_DEFAULT_KEY;
+
+    CLIParserContext *ctx;
+    CLIParserInit(&ctx, "hf cipurse write",
+                  "Write file by file ID with key ID and key",
+                  "hf cipurse write -f 2ff7   -> Authenticate with keyID=1 and key = 7373...7373 and read file with id 2ff7\n"
+                  "hf cipurse write -n 2 -k 65656565656565656565656565656565 -f 2ff7 -> Authenticate with specified key and read file\n");
+
+    void *argtable[] = {
+        arg_param_begin,
+        arg_lit0("a",  "apdu",    "show APDU requests and responses"),
+        arg_lit0("v",  "verbose", "show technical data"),
+        arg_int0("n",  "keyid",   "<dec>", "key id"),
+        arg_str0("k",  "key",     "<hex>", "key for authenticate"),
+        arg_str0("f",  "file",    "<hex>", "file ID"),
+        arg_int0("o",  "offset",  "<dec>", "offset for reading data from file"),
+        arg_lit0(NULL, "noauth",  "read file without authentication"),
+        arg_str0(NULL, "sreq",    "<plain|mac(default)|encode>", "communication reader-PICC security level"),
+        arg_str0(NULL, "sresp",   "<plain|mac(default)|encode>", "communication PICC-reader security level"),
+        arg_str0("c",  "content", "<hex>", "new file content"),
+        arg_param_end
+    };
+    CLIExecWithReturn(ctx, Cmd, argtable, true);
+    
+    bool APDULogging = arg_get_lit(ctx, 1);
+    bool verbose = arg_get_lit(ctx, 2);
+    uint8_t keyId = arg_get_int_def(ctx, 3, 1);
+    
+    CipurseChannelSecurityLevel sreq = CPSMACed;
+    CipurseChannelSecurityLevel sresp = CPSMACed;
+    int res = CLIParseKeyAndSecurityLevels(ctx, 4, 8, 9, key, &sreq, &sresp);
+    if (res) {
+        CLIParserFree(ctx);
+        return PM3_EINVARG;
+    }
+
+    uint16_t fileId = 0x2ff7;
+    
+    uint8_t hdata[250] = {0};
+    int hdatalen = sizeof(hdata);    
+    CLIGetHexWithReturn(ctx, 5, hdata, &hdatalen);
+    if (hdatalen && hdatalen != 2) {
+        PrintAndLogEx(ERR, _RED_("ERROR:") " file id length must be 2 bytes only.");
+        CLIParserFree(ctx);
+        return PM3_EINVARG;
+    }
+    if (hdatalen)
+        fileId = (hdata[0] << 8) + hdata[1];
+    
+    size_t offset = arg_get_int_def(ctx, 6, 0);
+
+    bool noAuth = arg_get_lit(ctx, 7);
+
+    hdatalen = sizeof(hdata);    
+    CLIGetHexWithReturn(ctx, 10, hdata, &hdatalen);
+    if (hdatalen == 0) {
+        PrintAndLogEx(ERR, _RED_("ERROR:") " file content length must be more 0.");
+        CLIParserFree(ctx);
+        return PM3_EINVARG;
+    }
+
+    SetAPDULogging(APDULogging);
+
+    CLIParserFree(ctx);
+    
+    res = CIPURSESelect(true, true, buf, sizeof(buf), &len, &sw);
+    if (res != 0 || sw != 0x9000) {
+        PrintAndLogEx(ERR, "Cipurse select " _RED_("error") ". Card returns 0x%04x.", sw);
+        DropField();
+        return PM3_ESOFT;
+    }
+        
+    if (verbose) {
+        PrintAndLogEx(INFO, "File id: %x offset %d key id: %d key: %s", fileId, offset, keyId, sprint_hex(key, CIPURSE_AES_KEY_LENGTH));
+        PrintAndLogEx(INFO, "data[%d]: %s", hdatalen, sprint_hex(hdata, hdatalen));
+    }
+
+    if (noAuth == false) {
+        bool bres = CIPURSEChannelAuthenticate(keyId, key, verbose);
+        if (bres == false) {
+            if (verbose == false)
+                PrintAndLogEx(ERR, "Authentication " _RED_("ERROR"));
+            DropField();
+            return PM3_ESOFT;
+        }
+        
+        // set channel security levels
+        CIPURSECSetActChannelSecurityLevels(sreq, sresp);
+    }
+    
+    res = CIPURSESelectFile(fileId, buf, sizeof(buf), &len, &sw);
+    if (res != 0 || sw != 0x9000) {
+        if (verbose == false)
+            PrintAndLogEx(ERR, "File select " _RED_("ERROR") ". Card returns 0x%04x.", sw);
+        DropField();
+        return PM3_ESOFT;
+    }
+
+    if (verbose)
+        PrintAndLogEx(INFO, "Select file 0x%x " _GREEN_("OK"), fileId);
+    
+    res = CIPURSEUpdateBinary(offset, hdata, hdatalen, buf, sizeof(buf), &len, &sw);
+    if (res != 0 || sw != 0x9000) {
+        if (verbose == false)
+            PrintAndLogEx(ERR, "File read " _RED_("ERROR") ". Card returns 0x%04x.", sw);
+        DropField();
+        return PM3_ESOFT;
+    }
+        
+    PrintAndLogEx(INFO, "File id: %x successfully written.", fileId);
+    
+    DropField();
+    return PM3_SUCCESS;
+}
 
 
 
@@ -349,7 +467,8 @@ static command_t CommandTable[] = {
     {"help",      CmdHelp,                   AlwaysAvailable, "This help."},
     {"info",      CmdHFCipurseInfo,          IfPm3Iso14443a,  "Info about Cipurse tag."},
     {"auth",      CmdHFCipurseAuth,          IfPm3Iso14443a,  "Authentication."},
-    {"read",      CmdHFCipurseReadFile,      IfPm3Iso14443a,  "Read file."},
+    {"read",      CmdHFCipurseReadFile,      IfPm3Iso14443a,  "Read binary file."},
+    {"write",     CmdHFCipurseWriteFile,     IfPm3Iso14443a,  "Write binary file."},
     {NULL, NULL, 0, NULL}
 };
 

From c5c15de7009cf32e4f4c191d0908a45aea240fa4 Mon Sep 17 00:00:00 2001
From: merlokk <807634+merlokk@users.noreply.github.com>
Date: Thu, 3 Jun 2021 17:27:25 +0300
Subject: [PATCH 34/39] read file attributes

---
 client/src/cipurse/cipursecore.c | 134 ++++++++++++++++++++++++++++++-
 client/src/cipurse/cipursecore.h |   5 +-
 client/src/cmdhfcipurse.c        | 127 ++++++++++++++++++++++++++++-
 3 files changed, 261 insertions(+), 5 deletions(-)

diff --git a/client/src/cipurse/cipursecore.c b/client/src/cipurse/cipursecore.c
index f36181992..016fa9c04 100644
--- a/client/src/cipurse/cipursecore.c
+++ b/client/src/cipurse/cipursecore.c
@@ -126,9 +126,12 @@ int CIPURSESelectFile(uint16_t fileID, uint8_t *Result, size_t MaxResultLen, siz
     return CIPURSEExchangeEx(false, true, (sAPDU) {0x00, 0xa4, 0x00, 0x00, 02, fileIdBin}, true, 0, Result, MaxResultLen, ResultLen, sw);
 }
 
-int CIPURSEReadFileAttributes(uint8_t *data, uint16_t *datalen) {
-    //CIPURSEExchangeEx(false, true, (sAPDU) {0x00, 0x82, 0x00, keyIndex, paramslen, params}, true, 0x10, Result, MaxResultLen, ResultLen, sw);
-    return 2;
+int CIPURSESelectMFFile(uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
+    return CIPURSEExchangeEx(false, true, (sAPDU) {0x00, 0xa4, 0x00, 0x00, 0, NULL}, true, 0, Result, MaxResultLen, ResultLen, sw);
+}
+
+int CIPURSEReadFileAttributes(uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
+    return CIPURSEExchangeEx(false, true, (sAPDU) {0x80, 0xce, 0x00, 0x00, 0, NULL}, true, 0, Result, MaxResultLen, ResultLen, sw);
 }
 
 int CIPURSEReadBinary(uint16_t offset, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
@@ -209,3 +212,128 @@ void CIPURSEPrintInfoFile(uint8_t *data, size_t len) {
     PrintAndLogEx(INFO, "CIPURSE version %d revision %d", data[0], data[1]);
 }
 
+static void CIPURSEPrintFileDescriptor(uint8_t desc) {
+    if (desc == 0x01)
+        PrintAndLogEx(INFO, "Binary file");
+    else if (desc == 0x11)
+        PrintAndLogEx(INFO, "Binary file with transactions");
+    else if (desc == 0x02)
+        PrintAndLogEx(INFO, "Linear record file");
+    else if (desc == 0x12)
+        PrintAndLogEx(INFO, "Linear record file with transactions");
+    else if (desc == 0x06)
+        PrintAndLogEx(INFO, "Cyclic record file");
+    else if (desc == 0x16)
+        PrintAndLogEx(INFO, "Cyclic record file with transactions");
+    else if (desc == 0x1E)
+        PrintAndLogEx(INFO, "Linear value-record file");
+    else if (desc == 0x1F)
+        PrintAndLogEx(INFO, "Linear value-record file with transactions");
+    else
+        PrintAndLogEx(INFO, "Unknown file 0x%02x", desc);
+}
+
+static void CIPURSEPrintKeyAttrib(uint8_t *attr) {
+    PrintAndLogEx(INFO, "-------- KEY ATTRIBUTES --------");
+    PrintAndLogEx(INFO, "Additional info: 0x%02x", attr[0]);
+    PrintAndLogEx(INFO, "Key length: %d", attr[1]);
+    PrintAndLogEx(INFO, "Algorithm ID: 0x%02x", attr[2]);
+    PrintAndLogEx(INFO, "Security attr: 0x%02x", attr[3]);
+    PrintAndLogEx(INFO, "KVV: 0x%02x%02x%02x", attr[4], attr[5], attr[6]);
+    PrintAndLogEx(INFO, "-------------------------------");
+}
+
+void CIPURSEPrintFileAttr(uint8_t *fileAttr, size_t len) {
+    if (len < 7) {
+        PrintAndLogEx(ERR, "Attributes length " _RED_("ERROR"));
+        return;
+    }
+    
+    PrintAndLogEx(INFO, "--------- FILE ATTRIBUTES ---------");
+    if (fileAttr[0] == 0x38) {
+        PrintAndLogEx(INFO, "Type: MF, ADF");
+        if (fileAttr[1] == 0x00) {
+            PrintAndLogEx(INFO, "Type: MF");
+        } else {
+            if ((fileAttr[1] & 0xe0) == 0x00)
+                PrintAndLogEx(INFO, "Type: Unknown");
+            if ((fileAttr[1] & 0xe0) == 0x20)
+                PrintAndLogEx(INFO, "Type: CIPURSE L");
+            if ((fileAttr[1] & 0xe0) == 0x40)
+                PrintAndLogEx(INFO, "Type: CIPURSE S");
+            if ((fileAttr[1] & 0xe0) == 0x60)
+                PrintAndLogEx(INFO, "Type: CIPURSE T");
+            if ((fileAttr[1] & 0x02) == 0x00)
+                PrintAndLogEx(INFO, "Autoselect on PxSE select OFF");
+            else
+                PrintAndLogEx(INFO, "Autoselect on PxSE select ON");
+            if ((fileAttr[1] & 0x01) == 0x00)
+                PrintAndLogEx(INFO, "PxSE select returns FCPTemplate OFF");
+            else
+                PrintAndLogEx(INFO, "PxSE select returns FCPTemplate ON");
+        }
+        
+        PrintAndLogEx(INFO, "File ID: 0x%02x%02x", fileAttr[2], fileAttr[3]);
+        
+        PrintAndLogEx(INFO, "Maximum number of custom EFs: %d", fileAttr[4]);
+        PrintAndLogEx(INFO, "Maximum number of EFs with SFID: %d", fileAttr[5]);
+        uint8_t keyNum = fileAttr[6];
+        PrintAndLogEx(INFO, "Keys assigned: %d", keyNum);
+        
+        if (len >= 9) {
+            PrintAndLogEx(INFO, "SMR entries: %02x%02x", fileAttr[7], fileAttr[8]);
+        }
+
+        if (len >= 10 + keyNum + 1) {
+            PrintAndLogEx(INFO, "ART: %s", sprint_hex(&fileAttr[9], keyNum + 1));
+        }
+        
+        if (len >= 11 + keyNum + 1 + keyNum * 7) {
+            for (int i = 0; i < keyNum; i++) {
+                PrintAndLogEx(INFO, "Key %d Attributes: %s", i, sprint_hex(&fileAttr[11 + keyNum + 1 + i * 7], 7));
+                CIPURSEPrintKeyAttrib(&fileAttr[11 + keyNum + 1 + i * 7]);
+            }
+        }
+        // MF
+        if (fileAttr[1] == 0x00) {
+            PrintAndLogEx(INFO, "Total memory size: %d", (fileAttr[len - 6] << 16) + (fileAttr[len - 1] << 5) + fileAttr[len - 4]);
+            PrintAndLogEx(INFO, "Free memory size: %d", (fileAttr[len - 3] << 16) + (fileAttr[len - 2] << 8) + fileAttr[len - 1]);
+            
+        } else {
+            int ptr = 11 + keyNum + 1 + keyNum * 7;
+            if (len > ptr)
+                PrintAndLogEx(INFO, "TLV file control: %s", sprint_hex(&fileAttr[ptr], len - ptr));
+        }
+    } else {
+        PrintAndLogEx(INFO, "Type: EF");
+        CIPURSEPrintFileDescriptor(fileAttr[0]);
+        if (fileAttr[1] == 0)
+            PrintAndLogEx(INFO, "SFI: not assigned");
+        else
+            PrintAndLogEx(INFO, "SFI: 0x%02x", fileAttr[1]);
+
+        PrintAndLogEx(INFO, "File ID: 0x%02x%02x", fileAttr[2], fileAttr[3]);
+        
+        if (fileAttr[0] == 0x01 || fileAttr[0] == 0x11)
+            PrintAndLogEx(INFO, "File size: %d", (fileAttr[4] << 8) + fileAttr[5]);
+        else
+            PrintAndLogEx(INFO, "Record num: %d record size: %d", fileAttr[4], fileAttr[5]);
+        
+        PrintAndLogEx(INFO, "Keys assigned: %d", fileAttr[6]);
+        
+        if (len >= 9) {
+            PrintAndLogEx(INFO, "SMR entries: %02x%02x", fileAttr[7], fileAttr[8]);
+        }
+        
+        if (len >= 10) {
+            PrintAndLogEx(INFO, "ART: %s", sprint_hex(&fileAttr[9], len - 9));
+            if (fileAttr[6] + 1 != len - 9)
+                PrintAndLogEx(WARNING, "ART length is wrong");
+        }
+        
+    }
+    
+    
+}
+
+
diff --git a/client/src/cipurse/cipursecore.h b/client/src/cipurse/cipursecore.h
index 9ac8f4906..715ca4c25 100644
--- a/client/src/cipurse/cipursecore.h
+++ b/client/src/cipurse/cipursecore.h
@@ -31,12 +31,15 @@ int CIPURSEMutalAuthenticate(uint8_t keyIndex, uint8_t *params, uint8_t paramsle
 int CIPURSECreateFile(uint16_t fileID, uint8_t *fileAttr);
 int CIPURSEDeleteFile(uint16_t fileID);
 
+int CIPURSESelectMFFile(uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) ;
 int CIPURSESelectFile(uint16_t fileID, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);
-int CIPURSEReadFileAttributes(uint8_t *data, uint16_t *datalen);
+int CIPURSEReadFileAttributes(uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);
 int CIPURSEReadBinary(uint16_t offset, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);
 int CIPURSEUpdateBinary(uint16_t offset, uint8_t *data, uint16_t datalen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);
 
 bool CIPURSEChannelAuthenticate(uint8_t keyIndex, uint8_t *key, bool verbose);
 void CIPURSECSetActChannelSecurityLevels(CipurseChannelSecurityLevel req, CipurseChannelSecurityLevel resp);
 
+void CIPURSEPrintFileAttr(uint8_t *fileAttr, size_t len);
+
 #endif /* __CIPURSECORE_H__ */
diff --git a/client/src/cmdhfcipurse.c b/client/src/cmdhfcipurse.c
index 588ac9b3e..040e988c9 100644
--- a/client/src/cmdhfcipurse.c
+++ b/client/src/cmdhfcipurse.c
@@ -437,7 +437,7 @@ static int CmdHFCipurseWriteFile(const char *Cmd) {
     res = CIPURSEUpdateBinary(offset, hdata, hdatalen, buf, sizeof(buf), &len, &sw);
     if (res != 0 || sw != 0x9000) {
         if (verbose == false)
-            PrintAndLogEx(ERR, "File read " _RED_("ERROR") ". Card returns 0x%04x.", sw);
+            PrintAndLogEx(ERR, "File write " _RED_("ERROR") ". Card returns 0x%04x.", sw);
         DropField();
         return PM3_ESOFT;
     }
@@ -448,6 +448,130 @@ static int CmdHFCipurseWriteFile(const char *Cmd) {
     return PM3_SUCCESS;
 }
 
+static int CmdHFCipurseReadFileAttr(const char *Cmd) {
+    uint8_t buf[APDU_RES_LEN] = {0};
+    size_t len = 0;
+    uint16_t sw = 0;
+    uint8_t key[] = CIPURSE_DEFAULT_KEY;
+
+    CLIParserContext *ctx;
+    CLIParserInit(&ctx, "hf cipurse aread",
+                  "Read file attributes by file ID with key ID and key",
+                  "hf cipurse aread -f 2ff7   -> Authenticate with keyID=1 and key = 7373...7373 and read file with id 2ff7\n"
+                  "hf cipurse aread -n 2 -k 65656565656565656565656565656565 -f 2ff7 -> Authenticate with specified key and read file\n");
+
+    void *argtable[] = {
+        arg_param_begin,
+        arg_lit0("a",  "apdu",    "show APDU requests and responses"),
+        arg_lit0("v",  "verbose", "show technical data"),
+        arg_int0("n",  "keyid",   "<dec>", "key id"),
+        arg_str0("k",  "key",     "<hex>", "key for authenticate"),
+        arg_str0("f",  "file",    "<hex>", "file ID"),
+        arg_lit0(NULL, "noauth",  "read file attributes without authentication"),
+        arg_str0(NULL, "sreq",    "<plain|mac(default)|encode>", "communication reader-PICC security level"),
+        arg_str0(NULL, "sresp",   "<plain|mac(default)|encode>", "communication PICC-reader security level"),
+        arg_lit0(NULL,  "sel-adf","show info about ADF itself"),
+        arg_lit0(NULL,  "sel-mf", "show info about master file"),
+        arg_param_end
+    };
+    CLIExecWithReturn(ctx, Cmd, argtable, true);
+    
+    bool APDULogging = arg_get_lit(ctx, 1);
+    bool verbose = arg_get_lit(ctx, 2);
+    uint8_t keyId = arg_get_int_def(ctx, 3, 1);
+    
+    CipurseChannelSecurityLevel sreq = CPSMACed;
+    CipurseChannelSecurityLevel sresp = CPSMACed;
+    int res = CLIParseKeyAndSecurityLevels(ctx, 4, 7, 8, key, &sreq, &sresp);
+    if (res) {
+        CLIParserFree(ctx);
+        return PM3_EINVARG;
+    }
+
+    uint16_t fileId = 0x2ff7;
+    
+    uint8_t hdata[250] = {0};
+    int hdatalen = sizeof(hdata);    
+    CLIGetHexWithReturn(ctx, 5, hdata, &hdatalen);
+    if (hdatalen && hdatalen != 2) {
+        PrintAndLogEx(ERR, _RED_("ERROR:") " file id length must be 2 bytes only.");
+        CLIParserFree(ctx);
+        return PM3_EINVARG;
+    }
+    if (hdatalen)
+        fileId = (hdata[0] << 8) + hdata[1];
+    
+    bool noAuth = arg_get_lit(ctx, 6);
+
+    bool seladf = arg_get_lit(ctx, 9);
+    bool selmf = arg_get_lit(ctx, 10);
+    
+    SetAPDULogging(APDULogging);
+
+    CLIParserFree(ctx);
+    
+    res = CIPURSESelect(true, true, buf, sizeof(buf), &len, &sw);
+    if (res != 0 || sw != 0x9000) {
+        PrintAndLogEx(ERR, "Cipurse select " _RED_("error") ". Card returns 0x%04x.", sw);
+        DropField();
+        return PM3_ESOFT;
+    }
+        
+    if (verbose)
+        PrintAndLogEx(INFO, "File id: %x key id: %d key: %s", fileId, keyId, sprint_hex(key, CIPURSE_AES_KEY_LENGTH));
+
+    if (noAuth == false) {
+        bool bres = CIPURSEChannelAuthenticate(keyId, key, verbose);
+        if (bres == false) {
+            if (verbose == false)
+                PrintAndLogEx(ERR, "Authentication " _RED_("ERROR"));
+            DropField();
+            return PM3_ESOFT;
+        }
+        
+        // set channel security levels
+        CIPURSECSetActChannelSecurityLevels(sreq, sresp);
+    }
+
+    if (seladf == false) {
+        if (selmf)
+            res = CIPURSESelectMFFile(buf, sizeof(buf), &len, &sw);
+        else
+            res = CIPURSESelectFile(fileId, buf, sizeof(buf), &len, &sw);
+        
+        if (res != 0 || sw != 0x9000) {
+            if (verbose == false)
+                PrintAndLogEx(ERR, "File select " _RED_("ERROR") ". Card returns 0x%04x.", sw);
+            DropField();
+            return PM3_ESOFT;
+        }
+    }
+
+    if (verbose)
+        PrintAndLogEx(INFO, "Select file 0x%x " _GREEN_("OK"), fileId);
+    
+    res = CIPURSEReadFileAttributes(buf, sizeof(buf), &len, &sw);
+    if (res != 0 || sw != 0x9000) {
+        if (verbose == false)
+            PrintAndLogEx(ERR, "File read " _RED_("ERROR") ". Card returns 0x%04x.", sw);
+        DropField();
+        return PM3_ESOFT;
+    }
+        
+    if (len == 0) {
+        PrintAndLogEx(WARNING, "File id: %x attributes is empty", fileId);
+        DropField();
+        return PM3_SUCCESS;
+    }
+    
+    if (verbose)
+        PrintAndLogEx(INFO, "File id: %x attributes[%d]: %s", fileId, len, sprint_hex(buf, len));
+    
+    CIPURSEPrintFileAttr(buf, len);
+    
+    DropField();
+    return PM3_SUCCESS;
+}
 
 
 
@@ -469,6 +593,7 @@ static command_t CommandTable[] = {
     {"auth",      CmdHFCipurseAuth,          IfPm3Iso14443a,  "Authentication."},
     {"read",      CmdHFCipurseReadFile,      IfPm3Iso14443a,  "Read binary file."},
     {"write",     CmdHFCipurseWriteFile,     IfPm3Iso14443a,  "Write binary file."},
+    {"aread",     CmdHFCipurseReadFileAttr,  IfPm3Iso14443a,  "Read file attributes."},
     {NULL, NULL, 0, NULL}
 };
 

From 2f83e872220c2b764159ad64d3fdadca8bcc0028 Mon Sep 17 00:00:00 2001
From: merlokk <807634+merlokk@users.noreply.github.com>
Date: Thu, 3 Jun 2021 18:01:13 +0300
Subject: [PATCH 35/39] some refactorings

---
 client/src/cipurse/cipursecore.c | 17 +++++++++++------
 client/src/cipurse/cipursecore.h |  4 ++--
 2 files changed, 13 insertions(+), 8 deletions(-)

diff --git a/client/src/cipurse/cipursecore.c b/client/src/cipurse/cipursecore.c
index 016fa9c04..82668ba18 100644
--- a/client/src/cipurse/cipursecore.c
+++ b/client/src/cipurse/cipursecore.c
@@ -102,9 +102,9 @@ static int CIPURSEExchangeEx(bool ActivateField, bool LeaveFieldON, sAPDU apdu,
     return PM3_SUCCESS;
 }
 
-/*static int CIPURSEExchange(sAPDU apdu, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
+static int CIPURSEExchange(sAPDU apdu, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
     return CIPURSEExchangeEx(false, true, apdu, true, 0, Result, MaxResultLen, ResultLen, sw);
-}*/
+}
 
 int CIPURSESelect(bool ActivateField, bool LeaveFieldON, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
     uint8_t data[] = {0x41, 0x44, 0x20, 0x46, 0x31};
@@ -121,21 +121,26 @@ int CIPURSEMutalAuthenticate(uint8_t keyIndex, uint8_t *params, uint8_t paramsle
     return CIPURSEExchangeEx(false, true, (sAPDU) {0x00, 0x82, 0x00, keyIndex, paramslen, params}, true, 0x10, Result, MaxResultLen, ResultLen, sw);
 }
 
+int CIPURSEDeleteFile(uint16_t fileID, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
+    uint8_t fileIdBin[] = {fileID >> 8, fileID & 0xff};
+    return CIPURSEExchangeEx(false, true, (sAPDU) {0x00, 0xe4, 0x00, 0x00, 02, fileIdBin}, false, 0, Result, MaxResultLen, ResultLen, sw);
+}
+
 int CIPURSESelectFile(uint16_t fileID, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
     uint8_t fileIdBin[] = {fileID >> 8, fileID & 0xff};
-    return CIPURSEExchangeEx(false, true, (sAPDU) {0x00, 0xa4, 0x00, 0x00, 02, fileIdBin}, true, 0, Result, MaxResultLen, ResultLen, sw);
+    return CIPURSEExchange((sAPDU) {0x00, 0xa4, 0x00, 0x00, 02, fileIdBin}, Result, MaxResultLen, ResultLen, sw);
 }
 
 int CIPURSESelectMFFile(uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
-    return CIPURSEExchangeEx(false, true, (sAPDU) {0x00, 0xa4, 0x00, 0x00, 0, NULL}, true, 0, Result, MaxResultLen, ResultLen, sw);
+    return CIPURSEExchange((sAPDU) {0x00, 0xa4, 0x00, 0x00, 0, NULL}, Result, MaxResultLen, ResultLen, sw);
 }
 
 int CIPURSEReadFileAttributes(uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
-    return CIPURSEExchangeEx(false, true, (sAPDU) {0x80, 0xce, 0x00, 0x00, 0, NULL}, true, 0, Result, MaxResultLen, ResultLen, sw);
+    return CIPURSEExchange((sAPDU) {0x80, 0xce, 0x00, 0x00, 0, NULL}, Result, MaxResultLen, ResultLen, sw);
 }
 
 int CIPURSEReadBinary(uint16_t offset, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
-    return CIPURSEExchangeEx(false, true, (sAPDU) {0x00, 0xb0, (offset >> 8) & 0x7f, offset & 0xff, 0, NULL}, true, 0, Result, MaxResultLen, ResultLen, sw);
+    return CIPURSEExchange((sAPDU) {0x00, 0xb0, (offset >> 8) & 0x7f, offset & 0xff, 0, NULL}, Result, MaxResultLen, ResultLen, sw);
 }
 
 int CIPURSEUpdateBinary(uint16_t offset, uint8_t *data, uint16_t datalen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
diff --git a/client/src/cipurse/cipursecore.h b/client/src/cipurse/cipursecore.h
index 715ca4c25..be8ff1519 100644
--- a/client/src/cipurse/cipursecore.h
+++ b/client/src/cipurse/cipursecore.h
@@ -28,8 +28,8 @@ int CIPURSESelect(bool ActivateField, bool LeaveFieldON, uint8_t *Result, size_t
 int CIPURSEChallenge(uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);
 int CIPURSEMutalAuthenticate(uint8_t keyIndex, uint8_t *params, uint8_t paramslen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);
 
-int CIPURSECreateFile(uint16_t fileID, uint8_t *fileAttr);
-int CIPURSEDeleteFile(uint16_t fileID);
+int CIPURSECreateFile(uint16_t fileID, uint8_t *attr, uint16_t attrlen, uint8_t *fileAttr, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);
+int CIPURSEDeleteFile(uint16_t fileID, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);
 
 int CIPURSESelectMFFile(uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) ;
 int CIPURSESelectFile(uint16_t fileID, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);

From 6f334ed968204c6161bcf73867c2ce2878b0563c Mon Sep 17 00:00:00 2001
From: merlokk <807634+merlokk@users.noreply.github.com>
Date: Thu, 3 Jun 2021 18:21:21 +0300
Subject: [PATCH 36/39] add create file command

---
 client/src/cipurse/cipursecore.c | 4 ++++
 client/src/cipurse/cipursecore.h | 2 +-
 2 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/client/src/cipurse/cipursecore.c b/client/src/cipurse/cipursecore.c
index 82668ba18..2ddd94554 100644
--- a/client/src/cipurse/cipursecore.c
+++ b/client/src/cipurse/cipursecore.c
@@ -121,6 +121,10 @@ int CIPURSEMutalAuthenticate(uint8_t keyIndex, uint8_t *params, uint8_t paramsle
     return CIPURSEExchangeEx(false, true, (sAPDU) {0x00, 0x82, 0x00, keyIndex, paramslen, params}, true, 0x10, Result, MaxResultLen, ResultLen, sw);
 }
 
+int CIPURSECreateFile(uint8_t *attr, uint16_t attrlen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
+    return CIPURSEExchangeEx(false, true, (sAPDU) {0x00, 0xe4, 0x00, 0x00, attrlen, attr}, false, 0, Result, MaxResultLen, ResultLen, sw);
+}
+
 int CIPURSEDeleteFile(uint16_t fileID, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
     uint8_t fileIdBin[] = {fileID >> 8, fileID & 0xff};
     return CIPURSEExchangeEx(false, true, (sAPDU) {0x00, 0xe4, 0x00, 0x00, 02, fileIdBin}, false, 0, Result, MaxResultLen, ResultLen, sw);
diff --git a/client/src/cipurse/cipursecore.h b/client/src/cipurse/cipursecore.h
index be8ff1519..e8a4112ea 100644
--- a/client/src/cipurse/cipursecore.h
+++ b/client/src/cipurse/cipursecore.h
@@ -28,7 +28,7 @@ int CIPURSESelect(bool ActivateField, bool LeaveFieldON, uint8_t *Result, size_t
 int CIPURSEChallenge(uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);
 int CIPURSEMutalAuthenticate(uint8_t keyIndex, uint8_t *params, uint8_t paramslen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);
 
-int CIPURSECreateFile(uint16_t fileID, uint8_t *attr, uint16_t attrlen, uint8_t *fileAttr, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);
+int CIPURSECreateFile(uint8_t *attr, uint16_t attrlen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);
 int CIPURSEDeleteFile(uint16_t fileID, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);
 
 int CIPURSESelectMFFile(uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) ;

From f0367ad2f7663b9389f40b07cb6ef8b3f1cde8c4 Mon Sep 17 00:00:00 2001
From: merlokk <807634+merlokk@users.noreply.github.com>
Date: Thu, 3 Jun 2021 18:21:47 +0300
Subject: [PATCH 37/39] help changes and add delete command

---
 client/src/cmdhfcipurse.c | 104 ++++++++++++++++++++++++++++++++++++--
 1 file changed, 100 insertions(+), 4 deletions(-)

diff --git a/client/src/cmdhfcipurse.c b/client/src/cmdhfcipurse.c
index 040e988c9..4379d6b02 100644
--- a/client/src/cmdhfcipurse.c
+++ b/client/src/cmdhfcipurse.c
@@ -338,8 +338,8 @@ static int CmdHFCipurseWriteFile(const char *Cmd) {
     CLIParserContext *ctx;
     CLIParserInit(&ctx, "hf cipurse write",
                   "Write file by file ID with key ID and key",
-                  "hf cipurse write -f 2ff7   -> Authenticate with keyID=1 and key = 7373...7373 and read file with id 2ff7\n"
-                  "hf cipurse write -n 2 -k 65656565656565656565656565656565 -f 2ff7 -> Authenticate with specified key and read file\n");
+                  "hf cipurse write -f 2ff7   -> Authenticate with keyID=1 and key = 7373...7373 and write file with id 2ff7\n"
+                  "hf cipurse write -n 2 -k 65656565656565656565656565656565 -f 2ff7 -> Authenticate with specified key and write file\n");
 
     void *argtable[] = {
         arg_param_begin,
@@ -457,8 +457,8 @@ static int CmdHFCipurseReadFileAttr(const char *Cmd) {
     CLIParserContext *ctx;
     CLIParserInit(&ctx, "hf cipurse aread",
                   "Read file attributes by file ID with key ID and key",
-                  "hf cipurse aread -f 2ff7   -> Authenticate with keyID=1 and key = 7373...7373 and read file with id 2ff7\n"
-                  "hf cipurse aread -n 2 -k 65656565656565656565656565656565 -f 2ff7 -> Authenticate with specified key and read file\n");
+                  "hf cipurse aread -f 2ff7   -> Authenticate with keyID=1 and key = 7373...7373 and read file attributes with id 2ff7\n"
+                  "hf cipurse aread -n 2 -k 65656565656565656565656565656565 -f 2ff7 -> Authenticate with specified key and read file attributes\n");
 
     void *argtable[] = {
         arg_param_begin,
@@ -573,6 +573,101 @@ static int CmdHFCipurseReadFileAttr(const char *Cmd) {
     return PM3_SUCCESS;
 }
 
+static int CmdHFCipurseDeleteFile(const char *Cmd) {
+    uint8_t buf[APDU_RES_LEN] = {0};
+    size_t len = 0;
+    uint16_t sw = 0;
+    uint8_t key[] = CIPURSE_DEFAULT_KEY;
+
+    CLIParserContext *ctx;
+    CLIParserInit(&ctx, "hf cipurse delete",
+                  "Read file by file ID with key ID and key",
+                  "hf cipurse delete -f 2ff7   -> Authenticate with keyID=1 and key = 7373...7373 and delete file with id 2ff7\n"
+                  "hf cipurse delete -n 2 -k 65656565656565656565656565656565 -f 2ff7 -> Authenticate with specified key and delete file\n");
+
+    void *argtable[] = {
+        arg_param_begin,
+        arg_lit0("a",  "apdu",    "show APDU requests and responses"),
+        arg_lit0("v",  "verbose", "show technical data"),
+        arg_int0("n",  "keyid",   "<dec>", "key id"),
+        arg_str0("k",  "key",     "<hex>", "key for authenticate"),
+        arg_str0("f",  "file",    "<hex>", "file ID"),
+        arg_str0(NULL, "sreq",    "<plain|mac(default)|encode>", "communication reader-PICC security level"),
+        arg_str0(NULL, "sresp",   "<plain|mac(default)|encode>", "communication PICC-reader security level"),
+        arg_param_end
+    };
+    CLIExecWithReturn(ctx, Cmd, argtable, true);
+    
+    bool APDULogging = arg_get_lit(ctx, 1);
+    bool verbose = arg_get_lit(ctx, 2);
+    uint8_t keyId = arg_get_int_def(ctx, 3, 1);
+    
+    CipurseChannelSecurityLevel sreq = CPSMACed;
+    CipurseChannelSecurityLevel sresp = CPSMACed;
+    int res = CLIParseKeyAndSecurityLevels(ctx, 4, 6, 7, key, &sreq, &sresp);
+    if (res) {
+        CLIParserFree(ctx);
+        return PM3_EINVARG;
+    }
+
+    uint16_t fileId = 0x2ff7;
+    
+    uint8_t hdata[250] = {0};
+    int hdatalen = sizeof(hdata);    
+    CLIGetHexWithReturn(ctx, 5, hdata, &hdatalen);
+    if (hdatalen && hdatalen != 2) {
+        PrintAndLogEx(ERR, _RED_("ERROR:") " file id length must be 2 bytes only.");
+        CLIParserFree(ctx);
+        return PM3_EINVARG;
+    }
+    if (hdatalen)
+        fileId = (hdata[0] << 8) + hdata[1];
+    
+    SetAPDULogging(APDULogging);
+
+    CLIParserFree(ctx);
+    
+    res = CIPURSESelect(true, true, buf, sizeof(buf), &len, &sw);
+    if (res != 0 || sw != 0x9000) {
+        PrintAndLogEx(ERR, "Cipurse select " _RED_("error") ". Card returns 0x%04x.", sw);
+        DropField();
+        return PM3_ESOFT;
+    }
+        
+    if (verbose)
+        PrintAndLogEx(INFO, "File id: %x key id: %d key: %s", fileId, keyId, sprint_hex(key, CIPURSE_AES_KEY_LENGTH));
+
+    bool bres = CIPURSEChannelAuthenticate(keyId, key, verbose);
+    if (bres == false) {
+        if (verbose == false)
+            PrintAndLogEx(ERR, "Authentication " _RED_("ERROR"));
+        DropField();
+        return PM3_ESOFT;
+    }
+    
+    // set channel security levels
+    CIPURSECSetActChannelSecurityLevels(sreq, sresp);
+    
+    res = CIPURSEDeleteFile(fileId, buf, sizeof(buf), &len, &sw);
+    if (res != 0 || sw != 0x9000) {
+        if (verbose == false)
+            PrintAndLogEx(ERR, "File select " _RED_("ERROR") ". Card returns 0x%04x.", sw);
+        DropField();
+        return PM3_ESOFT;
+    }
+
+    PrintAndLogEx(INFO, "File id: 04x deleted " _GREEN_("succesfully"), fileId);
+    
+    DropField();
+    return PM3_SUCCESS;
+}
+
+
+
+
+
+
+
 
 
 
@@ -594,6 +689,7 @@ static command_t CommandTable[] = {
     {"read",      CmdHFCipurseReadFile,      IfPm3Iso14443a,  "Read binary file."},
     {"write",     CmdHFCipurseWriteFile,     IfPm3Iso14443a,  "Write binary file."},
     {"aread",     CmdHFCipurseReadFileAttr,  IfPm3Iso14443a,  "Read file attributes."},
+    {"delete",    CmdHFCipurseDeleteFile,    IfPm3Iso14443a,  "Delete file."},
     {NULL, NULL, 0, NULL}
 };
 

From f3f3a5a270c637ce90860335d1f6372e609a60b4 Mon Sep 17 00:00:00 2001
From: merlokk <807634+merlokk@users.noreply.github.com>
Date: Thu, 3 Jun 2021 19:13:11 +0300
Subject: [PATCH 38/39] make style

---
 client/src/cipurse/cipursecore.c   |  52 +++++------
 client/src/cipurse/cipursecrypto.c | 141 +++++++++++++++--------------
 client/src/cipurse/cipursecrypto.h |   8 +-
 client/src/cmdhf.c                 |   2 +-
 client/src/cmdhfcipurse.c          | 130 +++++++++++++-------------
 5 files changed, 169 insertions(+), 164 deletions(-)

diff --git a/client/src/cipurse/cipursecore.c b/client/src/cipurse/cipursecore.c
index 2ddd94554..0fa1c6911 100644
--- a/client/src/cipurse/cipursecore.c
+++ b/client/src/cipurse/cipursecore.c
@@ -38,7 +38,7 @@ static int CIPURSEExchangeEx(bool ActivateField, bool LeaveFieldON, sAPDU apdu,
         DropField();
         msleep(50);
     }
-    
+
     // long messages is not allowed
     if (apdu.Lc > 228)
         return 20;
@@ -50,7 +50,7 @@ static int CIPURSEExchangeEx(bool ActivateField, bool LeaveFieldON, sAPDU apdu,
         xle = Le;
 
     CipurseCAPDUReqEncode(&cipurseContext, &apdu, &secapdu, securedata, IncludeLe, Le);
-    
+
     if (APDUEncodeS(&secapdu, false, xle, data, &datalen)) {
         PrintAndLogEx(ERR, "APDU encoding error.");
         return 201;
@@ -66,11 +66,11 @@ static int CIPURSEExchangeEx(bool ActivateField, bool LeaveFieldON, sAPDU apdu,
 
     if (GetAPDULogging())
         PrintAndLogEx(SUCCESS, "<<<< %s", sprint_hex(Result, *ResultLen));
-    
+
     if (*ResultLen < 2) {
         return 200;
     }
-    
+
     size_t rlen = 0;
     if (*ResultLen == 2) {
         if (cipurseContext.RequestSecurity == CPSMACed || cipurseContext.RequestSecurity == CPSEncrypted)
@@ -81,10 +81,10 @@ static int CIPURSEExchangeEx(bool ActivateField, bool LeaveFieldON, sAPDU apdu,
         CipurseCAPDURespDecode(&cipurseContext, Result, *ResultLen, securedata, &rlen, &isw);
         memcpy(Result, securedata, rlen);
     }
-    
+
     if (ResultLen != NULL)
         *ResultLen = rlen;
-    
+
     if (sw != NULL)
         *sw = isw;
 
@@ -158,13 +158,13 @@ bool CIPURSEChannelAuthenticate(uint8_t keyIndex, uint8_t *key, bool verbose) {
 
     CipurseContext cpc = {0};
     CipurseCSetKey(&cpc, keyIndex, key);
-    
+
     // get RP, rP
     int res = CIPURSEChallenge(buf, sizeof(buf), &len, &sw);
     if (res != 0 || len != 0x16) {
         if (verbose)
             PrintAndLogEx(ERR, "Cipurse get challenge " _RED_("error") ". Card returns 0x%04x.", sw);
-        
+
         return false;
     }
     CipurseCSetRandomFromPICC(&cpc, buf);
@@ -172,7 +172,7 @@ bool CIPURSEChannelAuthenticate(uint8_t keyIndex, uint8_t *key, bool verbose) {
     // make auth data
     uint8_t authparams[16 + 16 + 6] = {0};
     CipurseCAuthenticateHost(&cpc, authparams);
-    
+
     // authenticate
     res = CIPURSEMutalAuthenticate(keyIndex, authparams, sizeof(authparams), buf, sizeof(buf), &len, &sw);
     if (res != 0 || sw != 0x9000 || len != 16) {
@@ -186,22 +186,22 @@ bool CIPURSEChannelAuthenticate(uint8_t keyIndex, uint8_t *key, bool verbose) {
             if (verbose)
                 PrintAndLogEx(ERR, "Cipurse authentication " _RED_("error") ". Card returns 0x%04x.", sw);
         }
-        
+
         CipurseCClearContext(&cipurseContext);
         return false;
     }
-    
+
     if (CipurseCCheckCT(&cpc, buf)) {
         if (verbose)
             PrintAndLogEx(INFO, "Authentication " _GREEN_("OK"));
-        
+
         CipurseCChannelSetSecurityLevels(&cpc, CPSMACed, CPSMACed);
         memcpy(&cipurseContext, &cpc, sizeof(CipurseContext));
         return true;
     } else {
         if (verbose)
             PrintAndLogEx(ERR, "Authentication " _RED_("ERROR") " card returned wrong CT");
-        
+
         CipurseCClearContext(&cipurseContext);
         return false;
     }
@@ -257,7 +257,7 @@ void CIPURSEPrintFileAttr(uint8_t *fileAttr, size_t len) {
         PrintAndLogEx(ERR, "Attributes length " _RED_("ERROR"));
         return;
     }
-    
+
     PrintAndLogEx(INFO, "--------- FILE ATTRIBUTES ---------");
     if (fileAttr[0] == 0x38) {
         PrintAndLogEx(INFO, "Type: MF, ADF");
@@ -281,14 +281,14 @@ void CIPURSEPrintFileAttr(uint8_t *fileAttr, size_t len) {
             else
                 PrintAndLogEx(INFO, "PxSE select returns FCPTemplate ON");
         }
-        
+
         PrintAndLogEx(INFO, "File ID: 0x%02x%02x", fileAttr[2], fileAttr[3]);
-        
+
         PrintAndLogEx(INFO, "Maximum number of custom EFs: %d", fileAttr[4]);
         PrintAndLogEx(INFO, "Maximum number of EFs with SFID: %d", fileAttr[5]);
         uint8_t keyNum = fileAttr[6];
         PrintAndLogEx(INFO, "Keys assigned: %d", keyNum);
-        
+
         if (len >= 9) {
             PrintAndLogEx(INFO, "SMR entries: %02x%02x", fileAttr[7], fileAttr[8]);
         }
@@ -296,7 +296,7 @@ void CIPURSEPrintFileAttr(uint8_t *fileAttr, size_t len) {
         if (len >= 10 + keyNum + 1) {
             PrintAndLogEx(INFO, "ART: %s", sprint_hex(&fileAttr[9], keyNum + 1));
         }
-        
+
         if (len >= 11 + keyNum + 1 + keyNum * 7) {
             for (int i = 0; i < keyNum; i++) {
                 PrintAndLogEx(INFO, "Key %d Attributes: %s", i, sprint_hex(&fileAttr[11 + keyNum + 1 + i * 7], 7));
@@ -307,7 +307,7 @@ void CIPURSEPrintFileAttr(uint8_t *fileAttr, size_t len) {
         if (fileAttr[1] == 0x00) {
             PrintAndLogEx(INFO, "Total memory size: %d", (fileAttr[len - 6] << 16) + (fileAttr[len - 1] << 5) + fileAttr[len - 4]);
             PrintAndLogEx(INFO, "Free memory size: %d", (fileAttr[len - 3] << 16) + (fileAttr[len - 2] << 8) + fileAttr[len - 1]);
-            
+
         } else {
             int ptr = 11 + keyNum + 1 + keyNum * 7;
             if (len > ptr)
@@ -322,27 +322,27 @@ void CIPURSEPrintFileAttr(uint8_t *fileAttr, size_t len) {
             PrintAndLogEx(INFO, "SFI: 0x%02x", fileAttr[1]);
 
         PrintAndLogEx(INFO, "File ID: 0x%02x%02x", fileAttr[2], fileAttr[3]);
-        
+
         if (fileAttr[0] == 0x01 || fileAttr[0] == 0x11)
             PrintAndLogEx(INFO, "File size: %d", (fileAttr[4] << 8) + fileAttr[5]);
         else
             PrintAndLogEx(INFO, "Record num: %d record size: %d", fileAttr[4], fileAttr[5]);
-        
+
         PrintAndLogEx(INFO, "Keys assigned: %d", fileAttr[6]);
-        
+
         if (len >= 9) {
             PrintAndLogEx(INFO, "SMR entries: %02x%02x", fileAttr[7], fileAttr[8]);
         }
-        
+
         if (len >= 10) {
             PrintAndLogEx(INFO, "ART: %s", sprint_hex(&fileAttr[9], len - 9));
             if (fileAttr[6] + 1 != len - 9)
                 PrintAndLogEx(WARNING, "ART length is wrong");
         }
-        
+
     }
-    
-    
+
+
 }
 
 
diff --git a/client/src/cipurse/cipursecrypto.c b/client/src/cipurse/cipursecrypto.c
index 7e31fba07..e68adc14f 100644
--- a/client/src/cipurse/cipursecrypto.c
+++ b/client/src/cipurse/cipursecrypto.c
@@ -27,16 +27,21 @@ uint8_t QConstant[CIPURSE_AES_KEY_LENGTH] = {0x74, 0x74, 0x74, 0x74, 0x74, 0x74,
 
 uint8_t CipurseCSecurityLevelEnc(CipurseChannelSecurityLevel lvl) {
     switch (lvl) {
-        case CPSNone: return 0x00;
-        case CPSPlain: return 0x00;
-        case CPSMACed: return 0x01;
-        case CPSEncrypted: return 0x02;
-        default: return 0x00;
+        case CPSNone:
+            return 0x00;
+        case CPSPlain:
+            return 0x00;
+        case CPSMACed:
+            return 0x01;
+        case CPSEncrypted:
+            return 0x02;
+        default:
+            return 0x00;
     }
 }
 
 static void bin_xor(uint8_t *d1, uint8_t *d2, size_t len) {
-    for(size_t i = 0; i < len; i++)
+    for (size_t i = 0; i < len; i++)
         d1[i] = d1[i] ^ d2[i];
 }
 
@@ -74,14 +79,14 @@ static uint64_t rotateLeft48(uint64_t src) {
 
 static uint64_t computeNLM48(uint64_t x, uint64_t y) {
     uint64_t res = 0;
-    
+
     for (int i = 0; i < 48; i++) {
         res = rotateLeft48(res);
         if (res & 1)
             res = res ^ CIPURSE_POLY;
         y = rotateLeft48(y);
         if (y & 1)
-            res = res ^ x;    
+            res = res ^ x;
     }
     return res;
 }
@@ -89,25 +94,25 @@ static uint64_t computeNLM48(uint64_t x, uint64_t y) {
 static void computeNLM(uint8_t *res, uint8_t *x, uint8_t *y) {
     uint64_t x64 = 0;
     uint64_t y64 = 0;
-    
+
     for (int i = 0; i < 6; i++) {
         x64 = (x64 << 8) | x[i];
         y64 = (y64 << 8) | y[i];
     }
-    
+
     uint64_t res64 = computeNLM48(x64, y64);
-    
+
     for (int i = 0; i < 6; i++) {
         res[5 - i] = res64 & 0xff;
         res64 = res64 >> 8;
     }
 }
 
-static void CipurseCGenerateK0AndCp(CipurseContext *ctx) {        
+static void CipurseCGenerateK0AndCp(CipurseContext *ctx) {
     uint8_t temp1[CIPURSE_AES_KEY_LENGTH] = {0};
     uint8_t temp2[CIPURSE_AES_KEY_LENGTH] = {0};
     uint8_t kp[CIPURSE_SECURITY_PARAM_N] = {0};
-        
+
     // session key derivation function
     // kP := NLM(EXT(kID), rP)
     // k0 := AES(key=PAD2(kP) XOR PAD(rT),kID) XOR kID
@@ -116,11 +121,11 @@ static void CipurseCGenerateK0AndCp(CipurseContext *ctx) {
     bin_pad2(temp1, CIPURSE_AES_KEY_LENGTH, kp, CIPURSE_SECURITY_PARAM_N);
     bin_pad(temp2, CIPURSE_AES_KEY_LENGTH, ctx->rT, CIPURSE_SECURITY_PARAM_N);
     bin_xor(temp1, temp2, CIPURSE_AES_KEY_LENGTH);
-    
-    // session key K0    
+
+    // session key K0
     aes_encode(NULL, temp1, ctx->key, ctx->k0, CIPURSE_AES_KEY_LENGTH);
     bin_xor(ctx->k0, ctx->key, CIPURSE_AES_KEY_LENGTH);
-        
+
     // first frame key k1, function to calculate k1,
     // k1 := AES(key = RP; k0 XOR RT) XOR (k0 XOR RT)
     memcpy(temp1, ctx->k0, CIPURSE_AES_KEY_LENGTH);
@@ -128,7 +133,7 @@ static void CipurseCGenerateK0AndCp(CipurseContext *ctx) {
     aes_encode(NULL, ctx->RP, temp1, temp2, CIPURSE_AES_KEY_LENGTH);
     bin_xor(temp1, temp2, CIPURSE_AES_KEY_LENGTH);
     memcpy(ctx->frameKey, temp1, CIPURSE_AES_KEY_LENGTH);
-        
+
     // function to caluclate cP := AES(key=k0, RP).
     // terminal response
     aes_encode(NULL, ctx->k0, ctx->RP, ctx->cP, CIPURSE_AES_KEY_LENGTH);
@@ -148,16 +153,16 @@ void CipurseCGetKVV(uint8_t *key, uint8_t *kvv) {
 void CipurseCClearContext(CipurseContext *ctx) {
     if (ctx == NULL)
         return;
-    
+
     memset(ctx, 0, sizeof(CipurseContext));
 }
 
 void CipurseCSetKey(CipurseContext *ctx, uint8_t keyId, uint8_t *key) {
     if (ctx == NULL)
         return;
-    
+
     CipurseCClearContext(ctx);
-    
+
     ctx->keyId = keyId;
     memcpy(ctx->key, key, member_size(CipurseContext, key));
 }
@@ -174,7 +179,7 @@ bool isCipurseCChannelSecuritySet(CipurseContext *ctx) {
 void CipurseCSetRandomFromPICC(CipurseContext *ctx, uint8_t *random) {
     if (ctx == NULL)
         return;
-      
+
     memcpy(ctx->RP, random, member_size(CipurseContext, RP));
     memcpy(ctx->rP, random + member_size(CipurseContext, RP), member_size(CipurseContext, rP));
 }
@@ -194,13 +199,13 @@ uint8_t CipurseCGetSMI(CipurseContext *ctx, bool LePresent) {
 static void CipurseCFillAuthData(CipurseContext *ctx, uint8_t *authdata) {
     memcpy(authdata, ctx->cP, member_size(CipurseContext, cP));
     memcpy(&authdata[member_size(CipurseContext, cP)], ctx->RT, member_size(CipurseContext, RT));
-    memcpy(&authdata[member_size(CipurseContext, cP) + member_size(CipurseContext, RT)], ctx->rT, member_size(CipurseContext, rT));   
+    memcpy(&authdata[member_size(CipurseContext, cP) + member_size(CipurseContext, RT)], ctx->rT, member_size(CipurseContext, rT));
 }
 
 void CipurseCAuthenticateHost(CipurseContext *ctx, uint8_t *authdata) {
     if (ctx == NULL)
         return;
-    
+
     CipurseCSetRandomHost(ctx);
     CipurseCGenerateK0AndCp(ctx);
     CipurseCGenerateCT(ctx->k0, ctx->RT, ctx->CT);
@@ -231,7 +236,7 @@ size_t FindISO9797M2PaddingDataLen(uint8_t *data, size_t datalen) {
     return 0;
 }
 
-static uint16_t CipurseCComputeMICCRC (uint8_t *data, size_t len) {
+static uint16_t CipurseCComputeMICCRC(uint8_t *data, size_t len) {
     uint16_t initCRC = 0x6363;
     for (size_t i = 0; i < len; i++) {
         uint8_t ch = data[i] ^ initCRC;
@@ -245,16 +250,16 @@ void CipurseCGenerateMIC(uint8_t *data, size_t datalen, uint8_t *mic) {
     size_t plen = 0;
     uint8_t pdata[datalen + CIPURSE_MIC_LENGTH];
     memset(pdata, 0, sizeof(pdata));
-    
+
     // 0x00 padding
     memcpy(pdata, data, datalen);
     plen = datalen;
     if (datalen % CIPURSE_MIC_LENGTH)
         plen += CIPURSE_MIC_LENGTH - datalen % CIPURSE_MIC_LENGTH;
-    
+
     // crc
     uint16_t crc1 = CipurseCComputeMICCRC(pdata, plen);
-    
+
     for (size_t i = 0; i < datalen; i += 4) {
         uint8_t tmp1 = pdata[i + 0];
         uint8_t tmp2 = pdata[i + 1];
@@ -263,7 +268,7 @@ void CipurseCGenerateMIC(uint8_t *data, size_t datalen, uint8_t *mic) {
         pdata[i + 2] = tmp1;
         pdata[i + 3] = tmp2;
     }
-    
+
     uint16_t crc2 = CipurseCComputeMICCRC(pdata, plen);
     if (mic != NULL) {
         mic[0] = crc2 >> 8;
@@ -275,16 +280,16 @@ void CipurseCGenerateMIC(uint8_t *data, size_t datalen, uint8_t *mic) {
 
 bool CipurseCCheckMIC(uint8_t *data, size_t datalen, uint8_t *mic) {
     uint8_t xmic[CIPURSE_MIC_LENGTH] = {0};
-    
+
     CipurseCGenerateMIC(data, datalen, xmic);
     return (memcmp(xmic, mic, CIPURSE_MIC_LENGTH) == 0);
 }
 
 /* from: https://github.com/duychuongvn/cipurse-card-core/blob/master/src/main/java/com/github/duychuongvn/cirpusecard/core/security/crypto/CipurseCrypto.java#L521
- * 
+ *
  * Encrypt/Decrypt the given data using ciphering mechanism explained the OPST.
  * Data should be already padded.
- *  
+ *
  * hx-1 := ki , hx := AES( key = hx-1 ; q) XOR q, Cx := AES( key = hx ;
  * Dx ), hx+1 := AES( key = hx ; q ) XOR q, Cx+1 := AES( key = hx+1 ;
  * Dx+1 ), ... hy := AES( key = hy-1 ; q ) XOR q, Cy := AES( key = hy ;
@@ -292,21 +297,21 @@ bool CipurseCCheckMIC(uint8_t *data, size_t datalen, uint8_t *mic) {
  */
 void CipurseCEncryptDecrypt(CipurseContext *ctx, uint8_t *data, size_t datalen, uint8_t *dstdata, bool isEncrypt) {
     uint8_t hx[CIPURSE_AES_KEY_LENGTH] = {0};
-    
+
     if (datalen == 0 || datalen % CIPURSE_AES_KEY_LENGTH != 0)
         return;
-    
+
     memcpy(ctx->frameKeyNext, ctx->frameKey, CIPURSE_AES_KEY_LENGTH);
     int i = 0;
     while (datalen > i) {
         aes_encode(NULL, QConstant, ctx->frameKeyNext, hx, CIPURSE_AES_KEY_LENGTH);
         bin_xor(hx, ctx->frameKeyNext, CIPURSE_AES_KEY_LENGTH);
-        
+
         if (isEncrypt)
             aes_encode(NULL, hx, &data[i], &dstdata[i], CIPURSE_AES_KEY_LENGTH);
         else
             aes_decode(NULL, hx, &data[i], &dstdata[i], CIPURSE_AES_KEY_LENGTH);
-        
+
         memcpy(ctx->frameKeyNext, hx, CIPURSE_AES_KEY_LENGTH);
         i += CIPURSE_AES_KEY_LENGTH;
     }
@@ -317,7 +322,7 @@ void CipurseCChannelEncrypt(CipurseContext *ctx, uint8_t *data, size_t datalen,
     uint8_t pdata[datalen + CIPURSE_AES_KEY_LENGTH];
     size_t pdatalen = 0;
     AddISO9797M2Padding(pdata, &pdatalen, data, datalen, CIPURSE_AES_KEY_LENGTH);
-    
+
     CipurseCEncryptDecrypt(ctx, pdata, pdatalen, encdata, true);
     *encdatalen = pdatalen;
 }
@@ -328,10 +333,10 @@ void CipurseCChannelDecrypt(CipurseContext *ctx, uint8_t *data, size_t datalen,
 }
 
 /* from: https://github.com/duychuongvn/cipurse-card-core/blob/master/src/main/java/com/github/duychuongvn/cirpusecard/core/security/crypto/CipurseCrypto.java#L473
- * 
+ *
  * Generate OSPT MAC on the given input data.
  * Data should be already padded.
- *  
+ *
  * Calculation of Mi and ki+1: hx := ki , hx+1 := AES( key = hx ; Dx )
  * XOR Dx , hx+2 := AES( key = hx+1 ; Dx+1 ) XOR Dx+1, hx+3 := AES( key =
  * hx+2 ; Dx+2 ) XOR Dx+2, ... hy+1 := AES( key = hy ; Dy ) XOR Dy, ki+1 :=
@@ -349,7 +354,7 @@ void CipurseCGenerateMAC(CipurseContext *ctx, uint8_t *data, size_t datalen, uin
         memcpy(ctx->frameKeyNext, temp, CIPURSE_AES_KEY_LENGTH);
         i += CIPURSE_AES_KEY_LENGTH;
     }
- 
+
     aes_encode(NULL, ctx->frameKey, ctx->frameKeyNext, temp, CIPURSE_AES_KEY_LENGTH);
     bin_xor(temp, ctx->frameKeyNext, CIPURSE_AES_KEY_LENGTH);
     memcpy(ctx->frameKey, ctx->frameKeyNext, CIPURSE_AES_KEY_LENGTH);
@@ -377,7 +382,7 @@ static void CipurseCAPDUMACEncode(CipurseContext *ctx, sAPDU *apdu, uint8_t orig
     data[3] = apdu->P2;
     data[4] = apdu->Lc;
     *datalen = 5 + apdu->Lc;
-    
+
     if (ctx->RequestSecurity == CPSMACed || ctx->RequestSecurity == CPSEncrypted)
         *datalen = 5 + originalLc;
     memcpy(&data[5], apdu->data, *datalen);
@@ -387,12 +392,12 @@ void CipurseCAPDUReqEncode(CipurseContext *ctx, sAPDU *srcapdu, sAPDU *dstapdu,
     uint8_t mac[CIPURSE_MAC_LENGTH] = {0};
     uint8_t buf[260] = {0};
     size_t buflen = 0;
-    
+
     memcpy(dstapdu, srcapdu, sizeof(sAPDU));
-    
+
     if (isCipurseCChannelSecuritySet(ctx) == false)
         return;
-    
+
     dstapdu->CLA |= 0x04;
     dstapdu->data = dstdatabuf;
     dstapdu->data[0] = CipurseCGetSMI(ctx, includeLe);
@@ -406,23 +411,23 @@ void CipurseCAPDUReqEncode(CipurseContext *ctx, sAPDU *srcapdu, sAPDU *dstapdu,
 
     switch (ctx->RequestSecurity) {
         case CPSNone:
-        break;
-        case CPSPlain: 
+            break;
+        case CPSPlain:
             CipurseCAPDUMACEncode(ctx, dstapdu, originalLc, buf, &buflen);
             CipurseCCalcMACPadded(ctx, buf, buflen, NULL);
-        break;
+            break;
         case CPSMACed:
             dstapdu->Lc += CIPURSE_MAC_LENGTH;
             CipurseCAPDUMACEncode(ctx, dstapdu, originalLc, buf, &buflen);
             CipurseCCalcMACPadded(ctx, buf, buflen, mac);
             memcpy(&dstdatabuf[dstapdu->Lc - CIPURSE_MAC_LENGTH], mac, CIPURSE_MAC_LENGTH);
-        break;
+            break;
         case CPSEncrypted:
             dstapdu->Lc = srcapdu->Lc + CIPURSE_MIC_LENGTH;
             dstapdu->Lc += CIPURSE_AES_BLOCK_LENGTH - dstapdu->Lc % CIPURSE_AES_BLOCK_LENGTH + 1; // 1 - SMI
             if (includeLe)
                 dstapdu->Lc++;
-            
+
             CipurseCAPDUMACEncode(ctx, dstapdu, originalLc, buf, &buflen);
             CipurseCGenerateMIC(buf, buflen, mac);
             buf[0] = dstapdu->CLA;
@@ -433,9 +438,9 @@ void CipurseCAPDUReqEncode(CipurseContext *ctx, sAPDU *srcapdu, sAPDU *dstapdu,
             memcpy(&buf[4 + srcapdu->Lc], mac, CIPURSE_MIC_LENGTH);
             //PrintAndLogEx(INFO, "data plain[%d]: %s", 4 + srcapdu->Lc + CIPURSE_MIC_LENGTH, sprint_hex(buf, 4 + srcapdu->Lc + CIPURSE_MIC_LENGTH));
             CipurseCChannelEncrypt(ctx, buf, 4 + srcapdu->Lc + CIPURSE_MIC_LENGTH, &dstdatabuf[1], &buflen);
-        break;
-        default: 
-        break;
+            break;
+        default:
+            break;
     }
 
 }
@@ -450,37 +455,37 @@ void CipurseCAPDURespDecode(CipurseContext *ctx, uint8_t *srcdata, size_t srcdat
         *dstdatalen = 0;
     if (sw != NULL)
         *sw = 0;
-    
+
     if (srcdatalen < 2)
         return;
-    
+
     srcdatalen -= 2;
     uint16_t xsw = srcdata[srcdatalen] * 0x0100 + srcdata[srcdatalen + 1];
     if (sw)
         *sw = xsw;
-    
+
     if (isCipurseCChannelSecuritySet(ctx) == false) {
         memcpy(dstdata, srcdata, srcdatalen);
         if (dstdatalen != NULL)
             *dstdatalen = srcdatalen;
         return;
     }
-    
+
     switch (ctx->ResponseSecurity) {
         case CPSNone:
-        break;
-        case CPSPlain: 
+            break;
+        case CPSPlain:
             memcpy(buf, srcdata, srcdatalen);
             buflen = srcdatalen;
             memcpy(&buf[buflen], &srcdata[srcdatalen], 2);
             buflen += 2;
             CipurseCCalcMACPadded(ctx, buf, buflen, NULL);
-            
+
             memcpy(dstdata, srcdata, srcdatalen);
             if (dstdatalen != NULL)
                 *dstdatalen = srcdatalen;
-        break;
-        case CPSMACed:       
+            break;
+        case CPSMACed:
             if (srcdatalen < CIPURSE_MAC_LENGTH)
                 return;
 
@@ -496,28 +501,28 @@ void CipurseCAPDURespDecode(CipurseContext *ctx, uint8_t *srcdata, size_t srcdat
             memcpy(dstdata, srcdata, srcdatalen);
             if (dstdatalen != NULL)
                 *dstdatalen = srcdatalen;
-        break;
+            break;
         case CPSEncrypted:
             CipurseCChannelDecrypt(ctx, srcdata, srcdatalen, buf, &buflen);
             //PrintAndLogEx(INFO, "data plain[%d]: %s", buflen, sprint_hex(buf, buflen));
-            
+
             micdatalen = buflen - 2 - CIPURSE_MIC_LENGTH;
             memcpy(micdata, buf, buflen);
             memcpy(&micdata[micdatalen], &buf[buflen - 2], 2);
             micdatalen += 2;
-            
+
             if (CipurseCCheckMIC(micdata, micdatalen, &buf[micdatalen - 2]) == false) {
                 PrintAndLogEx(ERR, "APDU response MIC is not valid!");
             }
-            
+
             memcpy(dstdata, buf, micdatalen - 2);
             if (dstdatalen != NULL)
                 *dstdatalen = micdatalen - 2;
             if (sw)
                 *sw = micdata[micdatalen - 2] * 0x0100 + micdata[micdatalen - 1];
-        break;
-        default: 
-        break;
+            break;
+        default:
+            break;
     }
-    
+
 }
diff --git a/client/src/cipurse/cipursecrypto.h b/client/src/cipurse/cipursecrypto.h
index b554f54de..189ba7c5a 100644
--- a/client/src/cipurse/cipursecrypto.h
+++ b/client/src/cipurse/cipursecrypto.h
@@ -35,19 +35,19 @@ typedef enum {
 typedef struct CipurseContextS {
     uint8_t keyId;
     uint8_t key[CIPURSE_AES_KEY_LENGTH];
-    
+
     uint8_t RP[CIPURSE_AES_KEY_LENGTH];
     uint8_t rP[CIPURSE_SECURITY_PARAM_N];
     uint8_t RT[CIPURSE_AES_KEY_LENGTH];
     uint8_t rT[CIPURSE_SECURITY_PARAM_N];
-    
+
     uint8_t k0[CIPURSE_AES_KEY_LENGTH];
     uint8_t cP[CIPURSE_AES_KEY_LENGTH];
     uint8_t CT[CIPURSE_AES_KEY_LENGTH];
-    
+
     uint8_t frameKey[CIPURSE_AES_KEY_LENGTH];
     uint8_t frameKeyNext[CIPURSE_AES_KEY_LENGTH];
-    
+
     CipurseChannelSecurityLevel RequestSecurity;
     CipurseChannelSecurityLevel ResponseSecurity;
 } CipurseContext;
diff --git a/client/src/cmdhf.c b/client/src/cmdhf.c
index ca4753d7c..6fd9faaac 100644
--- a/client/src/cmdhf.c
+++ b/client/src/cmdhf.c
@@ -128,7 +128,7 @@ int CmdHFSearch(const char *Cmd) {
             res = PM3_SUCCESS;
         }
     }
-    
+
     PROMPT_CLEARLINE;
     PrintAndLogEx(INPLACE, " Searching for Cipurse tag...");
     if (IfPm3Iso14443a()) {
diff --git a/client/src/cmdhfcipurse.c b/client/src/cmdhfcipurse.c
index 4379d6b02..6ce7c8895 100644
--- a/client/src/cmdhfcipurse.c
+++ b/client/src/cmdhfcipurse.c
@@ -86,7 +86,7 @@ static int CmdHFCipurseInfo(const char *Cmd) {
         DropField();
         return PM3_SUCCESS;
     }
-    
+
     if (len > 0) {
         PrintAndLogEx(INFO, "Info file: " _GREEN_("OK"));
         PrintAndLogEx(INFO, "[%d]: %s", len, sprint_hex(buf, len));
@@ -119,13 +119,13 @@ static int CmdHFCipurseAuth(const char *Cmd) {
         arg_param_end
     };
     CLIExecWithReturn(ctx, Cmd, argtable, true);
-    
+
     bool APDULogging = arg_get_lit(ctx, 1);
     bool verbose = arg_get_lit(ctx, 2);
     keyId = arg_get_int_def(ctx, 3, 1);
-    
+
     uint8_t hdata[250] = {0};
-    int hdatalen = sizeof(hdata);    
+    int hdatalen = sizeof(hdata);
     CLIGetHexWithReturn(ctx, 4, hdata, &hdatalen);
     if (hdatalen && hdatalen != 16) {
         PrintAndLogEx(ERR, _RED_("ERROR:") " key length for AES128 must be 16 bytes only.");
@@ -134,39 +134,39 @@ static int CmdHFCipurseAuth(const char *Cmd) {
     }
     if (hdatalen)
         memcpy(key, hdata, CIPURSE_AES_KEY_LENGTH);
-    
+
     SetAPDULogging(APDULogging);
 
     CLIParserFree(ctx);
-    
+
     int res = CIPURSESelect(true, true, buf, sizeof(buf), &len, &sw);
     if (res != 0 || sw != 0x9000) {
         PrintAndLogEx(ERR, "Cipurse select " _RED_("error") ". Card returns 0x%04x.", sw);
         DropField();
         return PM3_ESOFT;
     }
-        
+
     uint8_t kvv[CIPURSE_KVV_LENGTH] = {0};
     CipurseCGetKVV(key, kvv);
     if (verbose)
         PrintAndLogEx(INFO, "Key id: %d key: %s KVV: %s", keyId, sprint_hex(key, CIPURSE_AES_KEY_LENGTH), sprint_hex_inrow(kvv, CIPURSE_KVV_LENGTH));
 
     bool bres = CIPURSEChannelAuthenticate(keyId, key, verbose);
-    
+
     if (verbose == false) {
         if (bres)
             PrintAndLogEx(INFO, "Authentication " _GREEN_("OK"));
         else
             PrintAndLogEx(ERR, "Authentication " _RED_("ERROR"));
     }
-    
+
     DropField();
     return bres ? PM3_SUCCESS : PM3_ESOFT;
 }
 
 static int CLIParseKeyAndSecurityLevels(CLIParserContext *ctx, size_t keyid, size_t sreqid, size_t srespid, uint8_t *key, CipurseChannelSecurityLevel *sreq, CipurseChannelSecurityLevel *sresp) {
     uint8_t hdata[250] = {0};
-    int hdatalen = sizeof(hdata);    
+    int hdatalen = sizeof(hdata);
     CLIGetHexWithReturn(ctx, keyid, hdata, &hdatalen);
     if (hdatalen && hdatalen != 16) {
         PrintAndLogEx(ERR, _RED_("ERROR:") " key length for AES128 must be 16 bytes only.");
@@ -175,12 +175,12 @@ static int CLIParseKeyAndSecurityLevels(CLIParserContext *ctx, size_t keyid, siz
     }
     if (hdatalen)
         memcpy(key, hdata, CIPURSE_AES_KEY_LENGTH);
-    
+
     *sreq = CPSMACed;
     *sresp = CPSMACed;
 
     char cdata[250] = {0};
-    int cdatalen = sizeof(cdata);    
+    int cdatalen = sizeof(cdata);
     cdatalen--; // for trailer 0x00
     CLIGetStrWithReturn(ctx, sreqid, (uint8_t *)cdata, &cdatalen);
     if (cdatalen) {
@@ -197,7 +197,7 @@ static int CLIParseKeyAndSecurityLevels(CLIParserContext *ctx, size_t keyid, siz
         }
     }
 
-    cdatalen = sizeof(cdata);    
+    cdatalen = sizeof(cdata);
     memset(cdata, 0, cdatalen);
     cdatalen--; // for trailer 0x00
     CLIGetStrWithReturn(ctx, srespid, (uint8_t *)cdata, &cdatalen);
@@ -214,7 +214,7 @@ static int CLIParseKeyAndSecurityLevels(CLIParserContext *ctx, size_t keyid, siz
             return PM3_EINVARG;
         }
     }
-    
+
     return PM3_SUCCESS;
 }
 
@@ -244,11 +244,11 @@ static int CmdHFCipurseReadFile(const char *Cmd) {
         arg_param_end
     };
     CLIExecWithReturn(ctx, Cmd, argtable, true);
-    
+
     bool APDULogging = arg_get_lit(ctx, 1);
     bool verbose = arg_get_lit(ctx, 2);
     uint8_t keyId = arg_get_int_def(ctx, 3, 1);
-    
+
     CipurseChannelSecurityLevel sreq = CPSMACed;
     CipurseChannelSecurityLevel sresp = CPSMACed;
     int res = CLIParseKeyAndSecurityLevels(ctx, 4, 8, 9, key, &sreq, &sresp);
@@ -258,9 +258,9 @@ static int CmdHFCipurseReadFile(const char *Cmd) {
     }
 
     uint16_t fileId = 0x2ff7;
-    
+
     uint8_t hdata[250] = {0};
-    int hdatalen = sizeof(hdata);    
+    int hdatalen = sizeof(hdata);
     CLIGetHexWithReturn(ctx, 5, hdata, &hdatalen);
     if (hdatalen && hdatalen != 2) {
         PrintAndLogEx(ERR, _RED_("ERROR:") " file id length must be 2 bytes only.");
@@ -269,7 +269,7 @@ static int CmdHFCipurseReadFile(const char *Cmd) {
     }
     if (hdatalen)
         fileId = (hdata[0] << 8) + hdata[1];
-    
+
     size_t offset = arg_get_int_def(ctx, 6, 0);
 
     bool noAuth = arg_get_lit(ctx, 7);
@@ -277,14 +277,14 @@ static int CmdHFCipurseReadFile(const char *Cmd) {
     SetAPDULogging(APDULogging);
 
     CLIParserFree(ctx);
-    
+
     res = CIPURSESelect(true, true, buf, sizeof(buf), &len, &sw);
     if (res != 0 || sw != 0x9000) {
         PrintAndLogEx(ERR, "Cipurse select " _RED_("error") ". Card returns 0x%04x.", sw);
         DropField();
         return PM3_ESOFT;
     }
-        
+
     if (verbose)
         PrintAndLogEx(INFO, "File id: %x offset %d key id: %d key: %s", fileId, offset, keyId, sprint_hex(key, CIPURSE_AES_KEY_LENGTH));
 
@@ -296,11 +296,11 @@ static int CmdHFCipurseReadFile(const char *Cmd) {
             DropField();
             return PM3_ESOFT;
         }
-        
+
         // set channel security levels
         CIPURSECSetActChannelSecurityLevels(sreq, sresp);
     }
-    
+
     res = CIPURSESelectFile(fileId, buf, sizeof(buf), &len, &sw);
     if (res != 0 || sw != 0x9000) {
         if (verbose == false)
@@ -311,7 +311,7 @@ static int CmdHFCipurseReadFile(const char *Cmd) {
 
     if (verbose)
         PrintAndLogEx(INFO, "Select file 0x%x " _GREEN_("OK"), fileId);
-    
+
     res = CIPURSEReadBinary(offset, buf, sizeof(buf), &len, &sw);
     if (res != 0 || sw != 0x9000) {
         if (verbose == false)
@@ -319,12 +319,12 @@ static int CmdHFCipurseReadFile(const char *Cmd) {
         DropField();
         return PM3_ESOFT;
     }
-        
+
     if (len == 0)
         PrintAndLogEx(INFO, "File id: %x is empty", fileId);
     else
         PrintAndLogEx(INFO, "File id: %x data[%d]: %s", fileId, len, sprint_hex(buf, len));
-    
+
     DropField();
     return PM3_SUCCESS;
 }
@@ -356,11 +356,11 @@ static int CmdHFCipurseWriteFile(const char *Cmd) {
         arg_param_end
     };
     CLIExecWithReturn(ctx, Cmd, argtable, true);
-    
+
     bool APDULogging = arg_get_lit(ctx, 1);
     bool verbose = arg_get_lit(ctx, 2);
     uint8_t keyId = arg_get_int_def(ctx, 3, 1);
-    
+
     CipurseChannelSecurityLevel sreq = CPSMACed;
     CipurseChannelSecurityLevel sresp = CPSMACed;
     int res = CLIParseKeyAndSecurityLevels(ctx, 4, 8, 9, key, &sreq, &sresp);
@@ -370,9 +370,9 @@ static int CmdHFCipurseWriteFile(const char *Cmd) {
     }
 
     uint16_t fileId = 0x2ff7;
-    
+
     uint8_t hdata[250] = {0};
-    int hdatalen = sizeof(hdata);    
+    int hdatalen = sizeof(hdata);
     CLIGetHexWithReturn(ctx, 5, hdata, &hdatalen);
     if (hdatalen && hdatalen != 2) {
         PrintAndLogEx(ERR, _RED_("ERROR:") " file id length must be 2 bytes only.");
@@ -381,12 +381,12 @@ static int CmdHFCipurseWriteFile(const char *Cmd) {
     }
     if (hdatalen)
         fileId = (hdata[0] << 8) + hdata[1];
-    
+
     size_t offset = arg_get_int_def(ctx, 6, 0);
 
     bool noAuth = arg_get_lit(ctx, 7);
 
-    hdatalen = sizeof(hdata);    
+    hdatalen = sizeof(hdata);
     CLIGetHexWithReturn(ctx, 10, hdata, &hdatalen);
     if (hdatalen == 0) {
         PrintAndLogEx(ERR, _RED_("ERROR:") " file content length must be more 0.");
@@ -397,14 +397,14 @@ static int CmdHFCipurseWriteFile(const char *Cmd) {
     SetAPDULogging(APDULogging);
 
     CLIParserFree(ctx);
-    
+
     res = CIPURSESelect(true, true, buf, sizeof(buf), &len, &sw);
     if (res != 0 || sw != 0x9000) {
         PrintAndLogEx(ERR, "Cipurse select " _RED_("error") ". Card returns 0x%04x.", sw);
         DropField();
         return PM3_ESOFT;
     }
-        
+
     if (verbose) {
         PrintAndLogEx(INFO, "File id: %x offset %d key id: %d key: %s", fileId, offset, keyId, sprint_hex(key, CIPURSE_AES_KEY_LENGTH));
         PrintAndLogEx(INFO, "data[%d]: %s", hdatalen, sprint_hex(hdata, hdatalen));
@@ -418,11 +418,11 @@ static int CmdHFCipurseWriteFile(const char *Cmd) {
             DropField();
             return PM3_ESOFT;
         }
-        
+
         // set channel security levels
         CIPURSECSetActChannelSecurityLevels(sreq, sresp);
     }
-    
+
     res = CIPURSESelectFile(fileId, buf, sizeof(buf), &len, &sw);
     if (res != 0 || sw != 0x9000) {
         if (verbose == false)
@@ -433,7 +433,7 @@ static int CmdHFCipurseWriteFile(const char *Cmd) {
 
     if (verbose)
         PrintAndLogEx(INFO, "Select file 0x%x " _GREEN_("OK"), fileId);
-    
+
     res = CIPURSEUpdateBinary(offset, hdata, hdatalen, buf, sizeof(buf), &len, &sw);
     if (res != 0 || sw != 0x9000) {
         if (verbose == false)
@@ -441,9 +441,9 @@ static int CmdHFCipurseWriteFile(const char *Cmd) {
         DropField();
         return PM3_ESOFT;
     }
-        
+
     PrintAndLogEx(INFO, "File id: %x successfully written.", fileId);
-    
+
     DropField();
     return PM3_SUCCESS;
 }
@@ -470,16 +470,16 @@ static int CmdHFCipurseReadFileAttr(const char *Cmd) {
         arg_lit0(NULL, "noauth",  "read file attributes without authentication"),
         arg_str0(NULL, "sreq",    "<plain|mac(default)|encode>", "communication reader-PICC security level"),
         arg_str0(NULL, "sresp",   "<plain|mac(default)|encode>", "communication PICC-reader security level"),
-        arg_lit0(NULL,  "sel-adf","show info about ADF itself"),
+        arg_lit0(NULL,  "sel-adf", "show info about ADF itself"),
         arg_lit0(NULL,  "sel-mf", "show info about master file"),
         arg_param_end
     };
     CLIExecWithReturn(ctx, Cmd, argtable, true);
-    
+
     bool APDULogging = arg_get_lit(ctx, 1);
     bool verbose = arg_get_lit(ctx, 2);
     uint8_t keyId = arg_get_int_def(ctx, 3, 1);
-    
+
     CipurseChannelSecurityLevel sreq = CPSMACed;
     CipurseChannelSecurityLevel sresp = CPSMACed;
     int res = CLIParseKeyAndSecurityLevels(ctx, 4, 7, 8, key, &sreq, &sresp);
@@ -489,9 +489,9 @@ static int CmdHFCipurseReadFileAttr(const char *Cmd) {
     }
 
     uint16_t fileId = 0x2ff7;
-    
+
     uint8_t hdata[250] = {0};
-    int hdatalen = sizeof(hdata);    
+    int hdatalen = sizeof(hdata);
     CLIGetHexWithReturn(ctx, 5, hdata, &hdatalen);
     if (hdatalen && hdatalen != 2) {
         PrintAndLogEx(ERR, _RED_("ERROR:") " file id length must be 2 bytes only.");
@@ -500,23 +500,23 @@ static int CmdHFCipurseReadFileAttr(const char *Cmd) {
     }
     if (hdatalen)
         fileId = (hdata[0] << 8) + hdata[1];
-    
+
     bool noAuth = arg_get_lit(ctx, 6);
 
     bool seladf = arg_get_lit(ctx, 9);
     bool selmf = arg_get_lit(ctx, 10);
-    
+
     SetAPDULogging(APDULogging);
 
     CLIParserFree(ctx);
-    
+
     res = CIPURSESelect(true, true, buf, sizeof(buf), &len, &sw);
     if (res != 0 || sw != 0x9000) {
         PrintAndLogEx(ERR, "Cipurse select " _RED_("error") ". Card returns 0x%04x.", sw);
         DropField();
         return PM3_ESOFT;
     }
-        
+
     if (verbose)
         PrintAndLogEx(INFO, "File id: %x key id: %d key: %s", fileId, keyId, sprint_hex(key, CIPURSE_AES_KEY_LENGTH));
 
@@ -528,7 +528,7 @@ static int CmdHFCipurseReadFileAttr(const char *Cmd) {
             DropField();
             return PM3_ESOFT;
         }
-        
+
         // set channel security levels
         CIPURSECSetActChannelSecurityLevels(sreq, sresp);
     }
@@ -538,7 +538,7 @@ static int CmdHFCipurseReadFileAttr(const char *Cmd) {
             res = CIPURSESelectMFFile(buf, sizeof(buf), &len, &sw);
         else
             res = CIPURSESelectFile(fileId, buf, sizeof(buf), &len, &sw);
-        
+
         if (res != 0 || sw != 0x9000) {
             if (verbose == false)
                 PrintAndLogEx(ERR, "File select " _RED_("ERROR") ". Card returns 0x%04x.", sw);
@@ -549,7 +549,7 @@ static int CmdHFCipurseReadFileAttr(const char *Cmd) {
 
     if (verbose)
         PrintAndLogEx(INFO, "Select file 0x%x " _GREEN_("OK"), fileId);
-    
+
     res = CIPURSEReadFileAttributes(buf, sizeof(buf), &len, &sw);
     if (res != 0 || sw != 0x9000) {
         if (verbose == false)
@@ -557,18 +557,18 @@ static int CmdHFCipurseReadFileAttr(const char *Cmd) {
         DropField();
         return PM3_ESOFT;
     }
-        
+
     if (len == 0) {
         PrintAndLogEx(WARNING, "File id: %x attributes is empty", fileId);
         DropField();
         return PM3_SUCCESS;
     }
-    
+
     if (verbose)
         PrintAndLogEx(INFO, "File id: %x attributes[%d]: %s", fileId, len, sprint_hex(buf, len));
-    
+
     CIPURSEPrintFileAttr(buf, len);
-    
+
     DropField();
     return PM3_SUCCESS;
 }
@@ -597,11 +597,11 @@ static int CmdHFCipurseDeleteFile(const char *Cmd) {
         arg_param_end
     };
     CLIExecWithReturn(ctx, Cmd, argtable, true);
-    
+
     bool APDULogging = arg_get_lit(ctx, 1);
     bool verbose = arg_get_lit(ctx, 2);
     uint8_t keyId = arg_get_int_def(ctx, 3, 1);
-    
+
     CipurseChannelSecurityLevel sreq = CPSMACed;
     CipurseChannelSecurityLevel sresp = CPSMACed;
     int res = CLIParseKeyAndSecurityLevels(ctx, 4, 6, 7, key, &sreq, &sresp);
@@ -611,9 +611,9 @@ static int CmdHFCipurseDeleteFile(const char *Cmd) {
     }
 
     uint16_t fileId = 0x2ff7;
-    
+
     uint8_t hdata[250] = {0};
-    int hdatalen = sizeof(hdata);    
+    int hdatalen = sizeof(hdata);
     CLIGetHexWithReturn(ctx, 5, hdata, &hdatalen);
     if (hdatalen && hdatalen != 2) {
         PrintAndLogEx(ERR, _RED_("ERROR:") " file id length must be 2 bytes only.");
@@ -622,18 +622,18 @@ static int CmdHFCipurseDeleteFile(const char *Cmd) {
     }
     if (hdatalen)
         fileId = (hdata[0] << 8) + hdata[1];
-    
+
     SetAPDULogging(APDULogging);
 
     CLIParserFree(ctx);
-    
+
     res = CIPURSESelect(true, true, buf, sizeof(buf), &len, &sw);
     if (res != 0 || sw != 0x9000) {
         PrintAndLogEx(ERR, "Cipurse select " _RED_("error") ". Card returns 0x%04x.", sw);
         DropField();
         return PM3_ESOFT;
     }
-        
+
     if (verbose)
         PrintAndLogEx(INFO, "File id: %x key id: %d key: %s", fileId, keyId, sprint_hex(key, CIPURSE_AES_KEY_LENGTH));
 
@@ -644,10 +644,10 @@ static int CmdHFCipurseDeleteFile(const char *Cmd) {
         DropField();
         return PM3_ESOFT;
     }
-    
+
     // set channel security levels
     CIPURSECSetActChannelSecurityLevels(sreq, sresp);
-    
+
     res = CIPURSEDeleteFile(fileId, buf, sizeof(buf), &len, &sw);
     if (res != 0 || sw != 0x9000) {
         if (verbose == false)
@@ -657,7 +657,7 @@ static int CmdHFCipurseDeleteFile(const char *Cmd) {
     }
 
     PrintAndLogEx(INFO, "File id: 04x deleted " _GREEN_("succesfully"), fileId);
-    
+
     DropField();
     return PM3_SUCCESS;
 }

From a5b5a147c9774339fa5e858ef500b80d0c63d4ab Mon Sep 17 00:00:00 2001
From: merlokk <807634+merlokk@users.noreply.github.com>
Date: Tue, 15 Jun 2021 13:36:01 +0300
Subject: [PATCH 39/39] fix libs

---
 client/src/cipurse/cipursecore.c   | 2 +-
 client/src/cipurse/cipursecore.h   | 3 +--
 client/src/cipurse/cipursecrypto.h | 2 +-
 3 files changed, 3 insertions(+), 4 deletions(-)

diff --git a/client/src/cipurse/cipursecore.c b/client/src/cipurse/cipursecore.c
index 0fa1c6911..9a6c13563 100644
--- a/client/src/cipurse/cipursecore.c
+++ b/client/src/cipurse/cipursecore.c
@@ -110,7 +110,7 @@ int CIPURSESelect(bool ActivateField, bool LeaveFieldON, uint8_t *Result, size_t
     uint8_t data[] = {0x41, 0x44, 0x20, 0x46, 0x31};
     CipurseCClearContext(&cipurseContext);
 
-    return EMVSelect(ECC_CONTACTLESS, ActivateField, LeaveFieldON, data, sizeof(data), Result, MaxResultLen, ResultLen, sw, NULL);
+    return EMVSelect(CC_CONTACTLESS, ActivateField, LeaveFieldON, data, sizeof(data), Result, MaxResultLen, ResultLen, sw, NULL);
 }
 
 int CIPURSEChallenge(uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
diff --git a/client/src/cipurse/cipursecore.h b/client/src/cipurse/cipursecore.h
index e8a4112ea..7ace29ea5 100644
--- a/client/src/cipurse/cipursecore.h
+++ b/client/src/cipurse/cipursecore.h
@@ -12,10 +12,9 @@
 #define __CIPURSECORE_H__
 
 #include "common.h"
-#include "emv/apduinfo.h"
 
 #include <jansson.h>
-#include "emv/apduinfo.h" // sAPDU
+#include "iso7816/apduinfo.h" // sAPDU
 #include "cipurse/cipursecrypto.h"
 
 
diff --git a/client/src/cipurse/cipursecrypto.h b/client/src/cipurse/cipursecrypto.h
index 189ba7c5a..7790eb600 100644
--- a/client/src/cipurse/cipursecrypto.h
+++ b/client/src/cipurse/cipursecrypto.h
@@ -12,7 +12,7 @@
 #define __CIPURSECRYPTO_H__
 
 #include "common.h"
-#include "emv/apduinfo.h"    // sAPDU
+#include "iso7816/apduinfo.h"    // sAPDU
 
 #define CIPURSE_KVV_LENGTH 4
 #define CIPURSE_AES_KEY_LENGTH 16