From 2fa6c4643e62e23511ffc5c7a2f2ef52114790d5 Mon Sep 17 00:00:00 2001 From: merlokk <807634+merlokk@users.noreply.github.com> Date: Thu, 1 Jul 2021 19:53:57 +0300 Subject: [PATCH] desfire channel sketch --- client/CMakeLists.txt | 1 + client/Makefile | 1 + client/src/cmdhfmfdes.c | 117 ++++++++++++++ client/src/mifare/desfire_crypto.c | 11 ++ client/src/mifare/desfire_crypto.h | 2 + client/src/mifare/desfirecore.c | 236 +++++++++++++++++++++++++++++ client/src/mifare/desfirecore.h | 75 +++++++++ include/protocols.h | 1 + 8 files changed, 444 insertions(+) create mode 100644 client/src/mifare/desfirecore.c create mode 100644 client/src/mifare/desfirecore.h diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index a81682337..0030df688 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -226,6 +226,7 @@ set (TARGET_SOURCES ${PM3_ROOT}/client/src/mifare/mifarehost.c ${PM3_ROOT}/client/src/nfc/ndef.c ${PM3_ROOT}/client/src/mifare/desfire_crypto.c + ${PM3_ROOT}/client/src/mifare/desfirecore.c ${PM3_ROOT}/client/src/uart/uart_posix.c ${PM3_ROOT}/client/src/uart/uart_win32.c ${PM3_ROOT}/client/src/ui/overlays.ui diff --git a/client/Makefile b/client/Makefile index 9ed3feb86..ef8130c7d 100644 --- a/client/Makefile +++ b/client/Makefile @@ -572,6 +572,7 @@ SRCS = aiddesfire.c \ loclass/elite_crack.c \ loclass/ikeys.c \ mifare/desfire_crypto.c \ + mifare/desfirecore.c \ mifare/mad.c \ mifare/mfkey.c \ mifare/mifare4.c \ diff --git a/client/src/cmdhfmfdes.c b/client/src/cmdhfmfdes.c index 2608a29eb..04079eba3 100644 --- a/client/src/cmdhfmfdes.c +++ b/client/src/cmdhfmfdes.c @@ -5116,6 +5116,121 @@ static int CmdHF14aDesMAD(const char *Cmd) { } */ +static int CmdHF14ADesGetAIDs(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mfdes getaids", + "Get Application IDs list from card. Master key needs to be provided.", + "hf mfdes getaids -n 0 -t des -k 0000000000000000 -f none -> execute with default factory setup"); + + void *argtable[] = { + arg_param_begin, + arg_lit0("a", "apdu", "show APDU requests and responses"), + arg_lit0("v", "verbose", "show technical data"), + arg_int0("n", "keyno", "", "Key number"), + arg_int0("t", "algo", "", "Crypt algo: DES, 2TDEA, 3TDEA, AES"), + arg_str0("k", "key", "", "Key for authenticate (HEX 8(DES), 16(2TDEA or AES) or 24(3TDEA) bytes)"), + arg_int0("f", "kdf", "", "Key Derivation Function (KDF): None(default), AN10922, Gallagher"), + arg_str0("i", "kdfi", "", "KDF input (HEX 1-31 bytes)"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + +/* + int aidlength = 0; + uint8_t aid[3] = {0}; + CLIGetHexWithReturn(ctx, 1, aid, &aidlength); + swap24(aid); + uint8_t vkey[16] = {0}; + int vkeylen = 0; + CLIGetHexWithReturn(ctx, 2, vkey, &vkeylen); + + if (vkeylen > 0) { + if (vkeylen == 8) { + memcpy(&deskeyList[deskeyListLen], vkey, 8); + deskeyListLen++; + } else if (vkeylen == 16) { + memcpy(&aeskeyList[aeskeyListLen], vkey, 16); + aeskeyListLen++; + } else if (vkeylen == 24) { + memcpy(&k3kkeyList[k3kkeyListLen], vkey, 16); + k3kkeyListLen++; + } else { + PrintAndLogEx(ERR, "Specified key must have 8, 16 or 24 bytes length."); + CLIParserFree(ctx); + return PM3_EINVARG; + } + } + + uint8_t dict_filename[FILE_PATH_SIZE + 2] = {0}; + int dict_filenamelen = 0; + if (CLIParamStrToBuf(arg_get_str(ctx, 3), dict_filename, FILE_PATH_SIZE, &dict_filenamelen)) { + PrintAndLogEx(FAILED, "File name too long or invalid."); + CLIParserFree(ctx); + return PM3_EINVARG; + } + + bool pattern1b = arg_get_lit(ctx, 4); + bool pattern2b = arg_get_lit(ctx, 5); + + if (pattern1b && pattern2b) { + PrintAndLogEx(ERR, "Pattern search mode must be 2-byte or 1-byte only."); + CLIParserFree(ctx); + return PM3_EINVARG; + } + + if (dict_filenamelen && (pattern1b || pattern2b)) { + PrintAndLogEx(ERR, "Pattern search mode and dictionary mode can't be used in one command."); + CLIParserFree(ctx); + return PM3_EINVARG; + } + + uint32_t startPattern = 0x0000; + uint8_t vpattern[2]; + int vpatternlen = 0; + CLIGetHexWithReturn(ctx, 6, vpattern, &vpatternlen); + if (vpatternlen > 0) { + if (vpatternlen <= 2) { + startPattern = (vpattern[0] << 8) + vpattern[1]; + } else { + PrintAndLogEx(ERR, "Pattern must be 2-byte length."); + CLIParserFree(ctx); + return PM3_EINVARG; + } + if (!pattern2b) + PrintAndLogEx(WARNING, "Pattern entered, but search mode not is 2-byte search."); + } + + uint8_t jsonname[250] = {0}; + int jsonnamelen = 0; + if (CLIParamStrToBuf(arg_get_str(ctx, 7), jsonname, sizeof(jsonname), &jsonnamelen)) { + PrintAndLogEx(ERR, "Invalid json name."); + CLIParserFree(ctx); + return PM3_EINVARG; + } + jsonname[jsonnamelen] = 0; + + bool verbose = arg_get_lit(ctx, 8); + + // Get KDF input + uint8_t kdfInput[31] = {0}; + int kdfInputLen = 0; + uint8_t cmdKDFAlgo = arg_get_int_def(ctx, 9, 0); + CLIGetHexWithReturn(ctx, 10, kdfInput, &kdfInputLen); +*/ + CLIParserFree(ctx); + + + + + return PM3_SUCCESS; +} + +// {"getappnames", CmdHF14ADesGetAppNames, IfPm3Iso14443a, "Get Applications list"}, +static int CmdHF14ADesGetAppNames(const char *Cmd) { + return PM3_SUCCESS; +} + static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, {"-----------", CmdHelp, IfPm3Iso14443a, "---------------------- " _CYAN_("general") " ----------------------"}, @@ -5134,6 +5249,8 @@ static command_t CommandTable[] = { {"createaid", CmdHF14ADesCreateApp, IfPm3Iso14443a, "Create Application ID"}, {"deleteaid", CmdHF14ADesDeleteApp, IfPm3Iso14443a, "Delete Application ID"}, {"selectaid", CmdHF14ADesSelectApp, IfPm3Iso14443a, "Select Application ID"}, + {"getaids", CmdHF14ADesGetAIDs, IfPm3Iso14443a, "Get Application IDs list"}, + {"getappnames", CmdHF14ADesGetAppNames, IfPm3Iso14443a, "Get Applications list"}, {"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("Files") " -----------------------"}, {"changevalue", CmdHF14ADesChangeValue, IfPm3Iso14443a, "Write value of a value file (credit/debit/clear)"}, {"clearfile", CmdHF14ADesClearRecordFile, IfPm3Iso14443a, "Clear record File"}, diff --git a/client/src/mifare/desfire_crypto.c b/client/src/mifare/desfire_crypto.c index e3e785e6c..1767c9c47 100644 --- a/client/src/mifare/desfire_crypto.c +++ b/client/src/mifare/desfire_crypto.c @@ -51,6 +51,17 @@ static inline void update_key_schedules(desfirekey_t key) { // } } +int desfire_get_key_length(enum DESFIRE_CRYPTOALGO key_type) { + switch (key_type) { + case T_DES: return 8; + case T_3DES: return 16; + case T_3K3DES: return 24; + case T_AES: return 16; + } + return 0; +} + + /******************************************************************************/ void des_encrypt(void *out, const void *in, const void *key) { diff --git a/client/src/mifare/desfire_crypto.h b/client/src/mifare/desfire_crypto.h index ecc6fc4a5..9c67b1182 100644 --- a/client/src/mifare/desfire_crypto.h +++ b/client/src/mifare/desfire_crypto.h @@ -60,6 +60,8 @@ enum DESFIRE_CRYPTOALGO { T_AES = 0x03 }; +int desfire_get_key_length(enum DESFIRE_CRYPTOALGO key_type); + enum DESFIRE_AUTH_SCHEME { AS_LEGACY, AS_NEW diff --git a/client/src/mifare/desfirecore.c b/client/src/mifare/desfirecore.c new file mode 100644 index 000000000..dbd77e1ca --- /dev/null +++ b/client/src/mifare/desfirecore.c @@ -0,0 +1,236 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2010 Romain Tartiere. +// Copyright (C) 2014 Iceman +// 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 Desfire core functions +//----------------------------------------------------------------------------- +// Info from here and many other soursec from the internet +// https://github.com/revk/DESFireAES +// https://github.com/step21/desfire_rfid +// https://github.com/patsys/desfire-python/blob/master/Desfire/DESFire.py +//----------------------------------------------------------------------------- + +#include "desfirecore.h" +#include +#include +#include +#include "ui.h" +#include "protocols.h" +#include "cmdhf14a.h" +#include "iso7816/apduinfo.h" // APDU manipulation / errorcodes +#include "iso7816/iso7816core.h" // APDU logging +#include "util_posix.h" // msleep +#include "mifare/desfire_crypto.h" + +void DesfireClearContext(DesfireContext *ctx) { + ctx->keyNum = 0; + ctx->keyType = T_DES; + memset(ctx->key, 0, sizeof(ctx->key)); + + ctx->authChannel = DACNone; + ctx->cmdChannel = DCCNative; + ctx->commMode = DCMNone; + + memset(ctx->sessionKeyMAC, 0, sizeof(ctx->sessionKeyMAC)); + memset(ctx->sessionKeyEnc, 0, sizeof(ctx->sessionKeyEnc)); + memset(ctx->lastIV, 0, sizeof(ctx->lastIV)); + ctx->cntrTx = 0; + ctx->cntrRx = 0; + memset(ctx->TI, 0, sizeof(ctx->TI)); +} + +void DesfireSetKey(DesfireContext *ctx, uint8_t keyNum, enum DESFIRE_CRYPTOALGO keyType, uint8_t *key) { + DesfireClearContext(ctx); + + ctx->keyNum = keyNum; + ctx->keyType = keyType; + memcpy(ctx->key, key, desfire_get_key_length(keyType)); +} + +static int DESFIRESendApdu(bool activate_field, sAPDU apdu, uint8_t *result, uint32_t max_result_len, uint32_t *result_len, uint16_t *sw) { + if (result_len) *result_len = 0; + if (sw) *sw = 0; + + uint16_t isw = 0; + int res = 0; + + if (activate_field) { + DropField(); + msleep(50); + } + + uint8_t data[APDU_RES_LEN] = {0}; + + // COMPUTE APDU + int datalen = 0; + if (APDUEncodeS(&apdu, false, 0x100, data, &datalen)) { // 100 == with Le + PrintAndLogEx(ERR, "APDU encoding error."); + return PM3_EAPDU_ENCODEFAIL; + } + + if (GetAPDULogging()) + PrintAndLogEx(SUCCESS, ">>>> %s", sprint_hex(data, datalen)); + + res = ExchangeAPDU14a(data, datalen, activate_field, true, result, max_result_len, (int *)result_len); + if (res != PM3_SUCCESS) { + return res; + } + + if (GetAPDULogging()) + PrintAndLogEx(SUCCESS, "<<<< %s", sprint_hex(result, *result_len)); + + if (*result_len < 2) { + return PM3_SUCCESS; + } + + *result_len -= 2; + isw = (result[*result_len] << 8) + result[*result_len + 1]; + if (sw) + *sw = isw; + + if (isw != 0x9000 && + isw != DESFIRE_GET_ISO_STATUS(MFDES_S_OPERATION_OK) && + isw != DESFIRE_GET_ISO_STATUS(MFDES_S_SIGNATURE) && + isw != DESFIRE_GET_ISO_STATUS(MFDES_S_ADDITIONAL_FRAME) && + isw != DESFIRE_GET_ISO_STATUS(MFDES_S_NO_CHANGES)) { + if (GetAPDULogging()) { + if (isw >> 8 == 0x61) { + PrintAndLogEx(ERR, "APDU chaining len: 0x%02x -->", isw & 0xff); + } else { + PrintAndLogEx(ERR, "APDU(%02x%02x) ERROR: [0x%4X] %s", apdu.CLA, apdu.INS, isw, GetAPDUCodeDescription(isw >> 8, isw & 0xff)); + return PM3_EAPDU_FAIL; + } + } + return PM3_EAPDU_FAIL; + } + return PM3_SUCCESS; +} + +static int DesfireExchangeNative(bool activate_field, DesfireContext *ctx, uint8_t cmd, uint8_t *data, size_t datalen, uint8_t *resp, size_t *resplen, bool enable_chaining) { + + return PM3_SUCCESS; +} + +static int DesfireExchangeISO(bool activate_field, DesfireContext *ctx, uint8_t cmd, uint8_t *data, size_t datalen, uint8_t *resp, size_t *resplen, bool enable_chaining) { + if (resplen) + *resplen = 0; + + // TODO !!! + size_t splitbysize = 0; + + uint16_t sw = 0; + uint8_t buf[255 * 5] = {0x00}; + uint32_t buflen = 0; + uint32_t pos = 0; + uint32_t i = 1; + + sAPDU apdu = {0}; + apdu.CLA = MFDES_NATIVE_ISO7816_WRAP_CLA; //0x90 + apdu.INS = cmd; + apdu.Lc = datalen; + apdu.P1 = 0; + apdu.P2 = 0; + apdu.data = data; + + int res = DESFIRESendApdu(activate_field, apdu, buf, sizeof(buf), &buflen, &sw); + if (res != PM3_SUCCESS) { + //PrintAndLogEx(DEBUG, "error DESFIRESendApdu %s", GetErrorString(res, &sw)); + return res; + } + if (resp) + memcpy(resp, buf, buflen); + + pos += buflen; + if (!enable_chaining) { + if (sw == DESFIRE_GET_ISO_STATUS(MFDES_ADDITIONAL_FRAME)) { + if (resplen) + *resplen = pos; + return PM3_SUCCESS; + } + return res; + } + + while (sw == DESFIRE_GET_ISO_STATUS(MFDES_ADDITIONAL_FRAME)) { + apdu.CLA = MFDES_NATIVE_ISO7816_WRAP_CLA; //0x90 + apdu.INS = MFDES_ADDITIONAL_FRAME; //0xAF + apdu.Lc = 0; + apdu.P1 = 0; + apdu.P2 = 0; + apdu.data = NULL; + + res = DESFIRESendApdu(false, apdu, buf, sizeof(buf), &buflen, &sw); + if (res != PM3_SUCCESS) { + //PrintAndLogEx(DEBUG, "error DESFIRESendApdu %s", GetErrorString(res, &sw)); + return res; + } + + if (resp != NULL) { + if (splitbysize) { + memcpy(&resp[i * splitbysize], buf, buflen); + i += 1; + } else { + memcpy(&resp[pos], buf, buflen); + } + } + pos += buflen; + + if (sw != DESFIRE_GET_ISO_STATUS(MFDES_ADDITIONAL_FRAME)) break; + } + + if (resplen) + *resplen = (splitbysize) ? i : pos; + return PM3_SUCCESS; +} + +int DesfireExchangeEx(bool activate_field, DesfireContext *ctx, uint8_t cmd, uint8_t *data, size_t datalen, uint8_t *resp, size_t *resplen, bool enable_chaining) { + int res = PM3_SUCCESS; + + switch(ctx->cmdChannel) { + case DCCNative: + res = DesfireExchangeNative(activate_field, ctx, cmd, data, datalen, resp, resplen, enable_chaining); + break; + case DCCNativeISO: + res = DesfireExchangeISO(activate_field, ctx, cmd, data, datalen, resp, resplen, enable_chaining); + break; + case DCCISO: + return PM3_EAPDU_FAIL; + break; + } + + return res; +} + +int DesfireExchange(DesfireContext *ctx, uint8_t cmd, uint8_t *data, size_t datalen, uint8_t *resp, size_t *resplen) { + return DesfireExchangeEx(false, ctx, cmd, data, datalen, resp, resplen, true); +} + +int DesfireSelectAID(DesfireContext *ctx, uint8_t *aid1, uint8_t *aid2) { + if (aid1 == NULL) + return PM3_EINVARG; + + uint8_t data[6] = {0}; + memcpy(data, aid1, 3); + if (aid2 != NULL) + memcpy(&data[3], aid2, 3); + uint8_t resp[257] = {0}; + size_t resplen = 0; + int res = DesfireExchangeEx(true, ctx, MFDES_SELECT_APPLICATION, data, (aid2 == NULL) ? 3 : 6, resp, &resplen, true); + if (res == PM3_SUCCESS && resplen != 0) + return PM3_ECARDEXCHANGE; + return res; +} + +int DesfireSelectAIDHex(DesfireContext *ctx, uint32_t aid1, bool select_two, uint32_t aid2) { + uint8_t data[6] = {0}; + // TODO !!!! + data[0] = aid1 & 0xff; + data[1] = (aid1 >> 8) & 0xff; + data[2] = (aid1 >> 16) & 0xff; + return DesfireSelectAID(ctx, data, (select_two) ? &data[3] : NULL); +} + diff --git a/client/src/mifare/desfirecore.h b/client/src/mifare/desfirecore.h new file mode 100644 index 000000000..12acb7e8c --- /dev/null +++ b/client/src/mifare/desfirecore.h @@ -0,0 +1,75 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2010 Romain Tartiere. +// Copyright (C) 2014 Iceman +// 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 Desfire core functions +//----------------------------------------------------------------------------- + +#ifndef __DESFIRECORE_H +#define __DESFIRECORE_H + +#include "common.h" +#include "mifare/desfire_crypto.h" +#include "mifare/mifare4.h" + +#define DESF_MAX_KEY_LEN 24 + +#define DESFIRE_GET_ISO_STATUS(x) ( ((uint16_t)(0x91<<8)) + (uint16_t)x ) + +typedef enum { + DACNone, + DACd40, + DACEV1, + DACEV2 +} DesfireAuthChannel; + +typedef enum { + DCCNative, + DCCNativeISO, + DCCISO +} DesfireCommandChannel; + +typedef enum { + DCMNone, + DCMPlain, + DCMMACed, + DCMEncrypted +} DesfireCommunicationMode; + + +typedef struct DesfireContextS { + uint8_t keyNum; + enum DESFIRE_CRYPTOALGO keyType; // des,2tdea,3tdea,aes + uint8_t key[DESF_MAX_KEY_LEN]; + + // KDF finction + // KDF input + + DesfireAuthChannel authChannel; // none/d40/ev1/ev2 + DesfireCommandChannel cmdChannel; // native/nativeiso/iso + DesfireCommunicationMode commMode; // plain/mac/enc + + uint8_t sessionKeyMAC[DESF_MAX_KEY_LEN]; + uint8_t sessionKeyEnc[DESF_MAX_KEY_LEN]; // look at mifare4.h - mf4Session_t + uint8_t lastIV[DESF_MAX_KEY_LEN]; + //mf4Session_t AESSession; + uint16_t cntrTx; // for AES + uint16_t cntrRx; // for AES + uint8_t TI[4]; // for AES +} DesfireContext; + +void DesfireClearContext(DesfireContext *ctx); +void DesfireSetKey(DesfireContext *ctx, uint8_t keyNum, enum DESFIRE_CRYPTOALGO keyType, uint8_t *key); + +int DesfireSelectAID(DesfireContext *ctx, uint8_t *aid1, uint8_t *aid2); +int DesfireSelectAIDHex(DesfireContext *ctx, uint32_t aid1, bool select_two, uint32_t aid2); +int DesfireExchange(DesfireContext *ctx, uint8_t cmd, uint8_t *data, size_t datalen, uint8_t *resp, size_t *resplen); +int DesfireExchangeEx(bool activate_field, DesfireContext *ctx, uint8_t cmd, uint8_t *data, size_t datalen, uint8_t *resp, size_t *resplen, bool enable_chaining); + + +#endif // __DESFIRECORE_H diff --git a/include/protocols.h b/include/protocols.h index 4b3cf763f..66385bcb9 100644 --- a/include/protocols.h +++ b/include/protocols.h @@ -459,6 +459,7 @@ ISO 7816-4 Basic interindustry commands. For command APDU's. #define MFDES_GET_FILE_SETTINGS 0xF5 #define MFDES_FORMAT_PICC 0xFC #define MFDES_VERIFY_PC 0xFD +#define MFDES_NATIVE_ISO7816_WRAP_CLA 0x90 // MIFARE DESFire status & error codes: #define MFDES_S_OPERATION_OK 0x00