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