From c8f44306367bf9a6234652aa8b3200f4dc801c81 Mon Sep 17 00:00:00 2001 From: kormax <3392860+kormax@users.noreply.github.com> Date: Mon, 6 Apr 2026 21:04:32 +0300 Subject: [PATCH 1/2] Implement DESFire delegated app info retrieval --- client/src/cmdhfmfdes.c | 96 +++++++++++++++++++++++++++ client/src/mifare/desfirecore.c | 6 ++ client/src/mifare/desfirecore.h | 1 + client/src/mifare/desfiresecurechan.c | 3 + client/src/pm3line_vocabulary.h | 1 + 5 files changed, 107 insertions(+) diff --git a/client/src/cmdhfmfdes.c b/client/src/cmdhfmfdes.c index ace415443..79ac9e282 100644 --- a/client/src/cmdhfmfdes.c +++ b/client/src/cmdhfmfdes.c @@ -3852,6 +3852,101 @@ static int CmdHF14ADesCreateDelegateApp(const char *Cmd) { return PM3_SUCCESS; } +static int CmdHF14ADesGetDelegateAppInfo(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mfdes getdelegateappinfo", + "Get delegated application information for DAM slot (GetDelegatedInfo / 0x69).", + "By default authentication is performed with PICC key number 0x00.\n" + "Use --keyno to pick another key number, or --no-auth to skip authentication.\n" + "hf mfdes getdelegateappinfo --damslot 0001 --algo 2TDEA --key 00000000000000000000000000000000\n" + "hf mfdes getdelegateappinfo --damslot 0001 --no-auth"); + + void *argtable[] = { + arg_param_begin, + arg_lit0("a", "apdu", "Show APDU requests and responses"), + arg_lit0("v", "verbose", "Verbose output"), + arg_int0("n", "keyno", "", "Key number (default: 0 / PICC key)"), + arg_str0("t", "algo", "", "Crypt algo"), + arg_str0("k", "key", "", "Key for authenticate (HEX 8(DES), 16(2TDEA or AES) or 24(3TDEA) bytes)"), + arg_str0(NULL, "kdf", "", "Key Derivation Function (KDF)"), + arg_str0("i", "kdfi", "", "KDF input (1-31 hex bytes)"), + arg_str0("m", "cmode", "", "Communicaton mode"), + arg_str0("c", "ccset", "", "Communicaton command set"), + arg_str0(NULL, "schann", "", "Secure channel"), + arg_str0(NULL, "damslot", "", "DAM slot number (2 hex bytes, little endian on card)"), + arg_lit0(NULL, "no-auth", "Execute without authentication"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + bool APDULogging = arg_get_lit(ctx, 1); + bool verbose = arg_get_lit(ctx, 2); + bool noauth = arg_get_lit(ctx, 12); + + int keynum = arg_get_int_def(ctx, 3, 0x00); + if (keynum < 0 || keynum > 0xFF) { + CLIParserFree(ctx); + PrintAndLogEx(ERR, "Key number must be in range 0..255"); + return PM3_EINVARG; + } + + DesfireContext_t dctx = {0}; + int securechann = defaultSecureChannel; + int res = CmdDesGetSessionParameters(ctx, &dctx, 0, 4, 5, 6, 7, 8, 9, 10, 0, 0, 0, &securechann, (noauth) ? DCMPlain : DCMMACed, NULL, NULL); + if (res) { + CLIParserFree(ctx); + return res; + } + + dctx.keyNum = keynum & 0xFF; + + uint32_t damslot = 0x0000; + bool damslotpresent = false; + if (CLIGetUint32Hex(ctx, 11, 0x0000, &damslot, &damslotpresent, 2, "DAM slot number must have 2 bytes length")) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + + SetAPDULogging(APDULogging); + CLIParserFree(ctx); + + if (!damslotpresent) { + PrintAndLogEx(ERR, "DAM slot number is mandatory"); + return PM3_EINVARG; + } + + res = DesfireSelectAndAuthenticateEx(&dctx, securechann, 0x000000, noauth, verbose); + if (res != PM3_SUCCESS) { + DropField(); + return res; + } + + uint8_t resp[16] = {0}; + size_t resplen = 0; + res = DesfireGetDelegatedInfo(&dctx, damslot & 0xffff, resp, &resplen); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Desfire GetDelegatedInfo command " _RED_("error") ". Result: %d", res); + DropField(); + return PM3_ESOFT; + } + + if (verbose) + PrintAndLogEx(SUCCESS, "received data[%zu]: %s", resplen, sprint_hex(resp, resplen)); + + uint16_t quota = MemLeToUint2byte(&resp[1]); + uint16_t freeblocks = MemLeToUint2byte(&resp[3]); + uint32_t delegatedaid = MemLeToUint3byte(&resp[5]); + + PrintAndLogEx(SUCCESS, "DAM slot 0x%04x", damslot & 0xffff); + PrintAndLogEx(SUCCESS, "DAM slot ver 0x%02x", resp[0]); + PrintAndLogEx(SUCCESS, "Quota limit 0x%04x (%u blocks)", quota, (unsigned int)quota); + PrintAndLogEx(SUCCESS, "Free blocks 0x%04x (%u blocks)", freeblocks, (unsigned int)freeblocks); + PrintAndLogEx(SUCCESS, "Delegated AID 0x%06x", delegatedaid); + + DropField(); + return PM3_SUCCESS; +} + static int CmdHF14ADesDeleteApp(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mfdes deleteapp", @@ -6962,6 +7057,7 @@ static command_t CommandTable[] = { {"bruteaid", CmdHF14ADesBruteApps, IfPm3Iso14443a, "Recover AIDs by bruteforce"}, {"createapp", CmdHF14ADesCreateApp, IfPm3Iso14443a, "Create Application"}, {"createdelegateapp", CmdHF14ADesCreateDelegateApp, IfPm3Iso14443a, "Create Delegated Application"}, + {"getdelegateappinfo", CmdHF14ADesGetDelegateAppInfo, IfPm3Iso14443a, "Get Delegated Application info by DAM slot"}, {"deleteapp", CmdHF14ADesDeleteApp, IfPm3Iso14443a, "Delete Application"}, {"selectapp", CmdHF14ADesSelectApp, IfPm3Iso14443a, "Select Application ID"}, {"selectisofid", CmdHF14ADesSelectISOFID, IfPm3Iso14443a, "Select file by ISO ID"}, diff --git a/client/src/mifare/desfirecore.c b/client/src/mifare/desfirecore.c index e944eb421..51fea74a7 100644 --- a/client/src/mifare/desfirecore.c +++ b/client/src/mifare/desfirecore.c @@ -2249,6 +2249,12 @@ int DesfireCreateDelegatedApplication(DesfireContext_t *dctx, uint8_t *appdata, return PM3_SUCCESS; } +int DesfireGetDelegatedInfo(DesfireContext_t *dctx, uint16_t damslot, uint8_t *resp, size_t *resplen) { + uint8_t data[2] = {0}; + Uint2byteToMemLe(data, damslot); + return DesfireCommand(dctx, MFDES_GET_DELEGATE_INFO, data, sizeof(data), resp, resplen, 8); +} + int DesfireDeleteApplication(DesfireContext_t *dctx, uint32_t aid) { uint8_t data[3] = {0}; DesfireAIDUintToByte(aid, data); diff --git a/client/src/mifare/desfirecore.h b/client/src/mifare/desfirecore.h index 2316d78e7..047e03272 100644 --- a/client/src/mifare/desfirecore.h +++ b/client/src/mifare/desfirecore.h @@ -210,6 +210,7 @@ void DesfirePrintAppList(DesfireContext_t *dctx, PICCInfo_t *PICCInfo, AppListS int DesfireCreateApplication(DesfireContext_t *dctx, uint8_t *appdata, size_t appdatalen); int DesfireCreateDelegatedApplication(DesfireContext_t *dctx, uint8_t *appdata, size_t appdatalen, uint8_t *contdata, size_t contdatalen); +int DesfireGetDelegatedInfo(DesfireContext_t *dctx, uint16_t damslot, uint8_t *resp, size_t *resplen); int DesfireDeleteApplication(DesfireContext_t *dctx, uint32_t aid); int DesfireGetKeyVersion(DesfireContext_t *dctx, uint8_t *data, size_t len, uint8_t *resp, size_t *resplen); diff --git a/client/src/mifare/desfiresecurechan.c b/client/src/mifare/desfiresecurechan.c index a77dc7b35..056ec7ead 100644 --- a/client/src/mifare/desfiresecurechan.c +++ b/client/src/mifare/desfiresecurechan.c @@ -68,6 +68,7 @@ static const AllowedChannelModes_t AllowedChannelModes[] = { {MFDES_GET_DF_NAMES, DACd40, DCCNative, DCMMACed}, {MFDES_GET_KEY_SETTINGS, DACd40, DCCNative, DCMMACed}, {MFDES_GET_KEY_VERSION, DACd40, DCCNative, DCMMACed}, + {MFDES_GET_DELEGATE_INFO, DACd40, DCCNative, DCMMACed}, {MFDES_GET_FREE_MEMORY, DACd40, DCCNative, DCMMACed}, {MFDES_CREATE_STD_DATA_FILE, DACd40, DCCNative, DCMMACed}, {MFDES_CREATE_BACKUP_DATA_FILE, DACd40, DCCNative, DCMMACed}, @@ -107,6 +108,7 @@ static const AllowedChannelModes_t AllowedChannelModes[] = { {MFDES_SELECT_APPLICATION, DACEV1, DCCNative, DCMPlain}, {MFDES_GET_KEY_VERSION, DACEV1, DCCNative, DCMMACed}, + {MFDES_GET_DELEGATE_INFO, DACEV1, DCCNative, DCMMACed}, {MFDES_GET_FREE_MEMORY, DACEV1, DCCNative, DCMMACed}, {MFDES_CREATE_APPLICATION, DACEV1, DCCNative, DCMMACed}, {MFDES_CREATE_DELEGATE_APP, DACEV1, DCCNative, DCMMACed}, @@ -188,6 +190,7 @@ static const AllowedChannelModes_t AllowedChannelModes[] = { static const CmdHeaderLengths_t CmdHeaderLengths[] = { {MFDES_CREATE_APPLICATION, CMD_HEADER_LEN_ALL}, {MFDES_CREATE_DELEGATE_APP, CMD_HEADER_LEN_ALL}, + {MFDES_GET_DELEGATE_INFO, 2}, {MFDES_DELETE_APPLICATION, CMD_HEADER_LEN_ALL}, {MFDES_CHANGE_KEY, 1}, {MFDES_CHANGE_KEY_EV2, 2}, diff --git a/client/src/pm3line_vocabulary.h b/client/src/pm3line_vocabulary.h index e87526e35..f1783202d 100644 --- a/client/src/pm3line_vocabulary.h +++ b/client/src/pm3line_vocabulary.h @@ -496,6 +496,7 @@ const static vocabulary_t vocabulary[] = { { 0, "hf mfdes bruteaid" }, { 0, "hf mfdes createapp" }, { 0, "hf mfdes createdelegateapp" }, + { 0, "hf mfdes getdelegateappinfo" }, { 0, "hf mfdes deleteapp" }, { 0, "hf mfdes selectapp" }, { 0, "hf mfdes selectisofid" }, From 25dd37c48a97ad8f20c1560fd2e0368fdcfb5b97 Mon Sep 17 00:00:00 2001 From: kormax <3392860+kormax@users.noreply.github.com> Date: Mon, 6 Apr 2026 21:10:27 +0300 Subject: [PATCH 2/2] Update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 45ab7d88a..300a9d3f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file. This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log... ## [unreleased][unreleased] +- Added `hf mfdes getdelegateappinfo` command (@kormax) - Added `hf secc info` command to retrieve the Card Recognition Template(@antiklesys) - Added `hf mfdes createdelegateapp` command (@kormax) - Optimized `hf iclass legbrute` throughput: replaced recursive `suc()`/`output()` cipher functions with iterative loops, added 256-entry LUT for the `select()` function eliminating redundant bit arithmetic and halving key lookups per state step, switched successor state to in-place pointer update removing per-call struct copies, added `doMAC_brute()` with byte-wise LSB-first processing and direct output bit packing eliminating all bitstream struct overhead and output reversal calls per key candidate, and replaced per-iteration 64-bit modulo progress check with a countdown counter (@antiklesys)