From 3945e1e7f423412ebe108cca669c09db102975ad Mon Sep 17 00:00:00 2001 From: merlokk <807634+merlokk@users.noreply.github.com> Date: Thu, 5 Aug 2021 19:06:53 +0300 Subject: [PATCH 01/16] added iso channel checks --- client/src/mifare/desfiresecurechan.c | 49 ++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/client/src/mifare/desfiresecurechan.c b/client/src/mifare/desfiresecurechan.c index 75ddec228..d5bc141d0 100644 --- a/client/src/mifare/desfiresecurechan.c +++ b/client/src/mifare/desfiresecurechan.c @@ -41,6 +41,7 @@ static bool CommandCanUseAnyChannel(uint8_t cmd) { } static const AllowedChannelModesS AllowedChannelModes[] = { + // D40 channel {MFDES_SELECT_APPLICATION, DACd40, DCCNative, DCMPlain}, {MFDES_CREATE_APPLICATION, DACd40, DCCNative, DCMMACed}, @@ -81,6 +82,7 @@ static const AllowedChannelModesS AllowedChannelModes[] = { {MFDES_CHANGE_KEY, DACd40, DCCNative, DCMEncryptedPlain}, {MFDES_CHANGE_KEY_EV2, DACd40, DCCNative, DCMEncryptedPlain}, + // EV1 and EV2 channel {MFDES_SELECT_APPLICATION, DACEV1, DCCNative, DCMPlain}, {MFDES_GET_KEY_VERSION, DACEV1, DCCNative, DCMMACed}, @@ -114,8 +116,26 @@ static const AllowedChannelModesS AllowedChannelModes[] = { {MFDES_CHANGE_KEY, DACEV1, DCCNative, DCMEncryptedPlain}, {MFDES_CHANGE_KEY_EV2, DACEV1, DCCNative, DCMEncryptedPlain}, + // EV2 channel separately {MFDES_AUTHENTICATE_EV2F, DACEV2, DCCNative, DCMPlain}, {MFDES_AUTHENTICATE_EV2NF, DACEV2, DCCNative, DCMPlain}, + + // ISO channel + {ISO7816_READ_BINARY, DACd40, DCCISO, DCMPlain}, + {ISO7816_UPDATE_BINARY, DACd40, DCCISO, DCMPlain}, + {ISO7816_READ_RECORDS, DACd40, DCCISO, DCMPlain}, + {ISO7816_APPEND_RECORD, DACd40, DCCISO, DCMPlain}, + + {ISO7816_READ_BINARY, DACd40, DCCISO, DCMMACed}, + {ISO7816_READ_RECORDS, DACd40, DCCISO, DCMMACed}, + + {ISO7816_READ_BINARY, DACEV1, DCCISO, DCMPlain}, + {ISO7816_UPDATE_BINARY, DACEV1, DCCISO, DCMPlain}, + {ISO7816_READ_RECORDS, DACEV1, DCCISO, DCMPlain}, + {ISO7816_APPEND_RECORD, DACEV1, DCCISO, DCMPlain}, + + {ISO7816_READ_BINARY, DACEV1, DCCISO, DCMMACed}, + {ISO7816_READ_RECORDS, DACEV1, DCCISO, DCMMACed}, }; #define CMD_HEADER_LEN_ALL 0xffff @@ -186,6 +206,25 @@ static bool DesfireEV1D40ReceiveMAC(DesfireContext *ctx, uint8_t cmd) { return false; } +static const uint8_t ISOChannelValidCmd[] = { + ISO7816_SELECT_FILE, + ISO7816_READ_BINARY, + ISO7816_UPDATE_BINARY, + ISO7816_READ_RECORDS, + ISO7816_APPEND_RECORD, + ISO7816_GET_CHALLENGE, + ISO7816_EXTERNAL_AUTHENTICATION, + ISO7816_INTERNAL_AUTHENTICATION +}; + +static bool DesfireISOChannelValidCmd(uint8_t cmd) { + for (int i = 0; i < ARRAY_LENGTH(ISOChannelValidCmd); i++) + if (ISOChannelValidCmd[i] == cmd) + return true; + + return false; +} + static void DesfireSecureChannelEncodeD40(DesfireContext *ctx, uint8_t cmd, uint8_t *srcdata, size_t srcdatalen, uint8_t *dstdata, size_t *dstdatalen) { uint8_t data[1024] = {0}; size_t rlen = 0; @@ -543,13 +582,21 @@ bool PrintChannelModeWarning(uint8_t cmd, DesfireSecureChannel secureChannel, De PrintAndLogEx(WARNING, "Communication mode can't be NONE. command: %02x", cmd); return false; } - + // no security set if (secureChannel == DACNone) return true; + if (CommandCanUseAnyChannel(cmd)) return true; + // ISO commands + if (cmdSet == DCCISO) { + bool res = DesfireISOChannelValidCmd(cmd); + if (!res) + return false; + } + bool found = false; for (int i = 0; i < ARRAY_LENGTH(AllowedChannelModes); i++) if (AllowedChannelModes[i].cmd == cmd) { From f1c48db6bd1bc364cb8febd6f04464c7b4f3ddc6 Mon Sep 17 00:00:00 2001 From: merlokk <807634+merlokk@users.noreply.github.com> Date: Thu, 5 Aug 2021 20:03:07 +0300 Subject: [PATCH 02/16] added iso commands and read sketch --- client/src/cmdhfmfdes.c | 10 +++++- client/src/mifare/desfirecore.c | 59 +++++++++++++++++++++++++++++++++ client/src/mifare/desfirecore.h | 5 +++ 3 files changed, 73 insertions(+), 1 deletion(-) diff --git a/client/src/cmdhfmfdes.c b/client/src/cmdhfmfdes.c index 62aae3bb3..a74c8ffa7 100644 --- a/client/src/cmdhfmfdes.c +++ b/client/src/cmdhfmfdes.c @@ -4922,6 +4922,11 @@ static int CmdHF14ADesClearRecordFile(const char *Cmd) { return PM3_SUCCESS; } +static int DesfileReadISOFileAndPrint(DesfireContext *dctx, uint8_t fnum, int filetype, uint32_t offset, uint32_t length, bool noauth, bool verbose) { + + + return PM3_SUCCESS; +} static int DesfileReadFileAndPrint(DesfireContext *dctx, uint8_t fnum, int filetype, uint32_t offset, uint32_t length, bool noauth, bool verbose) { int res = 0; @@ -5167,7 +5172,10 @@ static int CmdHF14ADesReadData(const char *Cmd) { return res; } - res = DesfileReadFileAndPrint(&dctx, fnum, op, offset, length, noauth, verbose); + if (dctx.cmdSet != DCCISO) + res = DesfileReadFileAndPrint(&dctx, fnum, op, offset, length, noauth, verbose); + else + res = DesfileReadISOFileAndPrint(&dctx, fnum, op, offset, length, noauth, verbose); DropField(); return res; diff --git a/client/src/mifare/desfirecore.c b/client/src/mifare/desfirecore.c index ee34f4cfe..88b0c88a8 100644 --- a/client/src/mifare/desfirecore.c +++ b/client/src/mifare/desfirecore.c @@ -2627,3 +2627,62 @@ int DesfireISOInternalAuth(DesfireContext *dctx, bool app_level, uint8_t keynum, return res; } +int DesfireISOReadBinary(DesfireContext *dctx, bool use_file_id, uint8_t fileid, uint16_t offset, uint8_t length, uint8_t *resp, size_t *resplen) { + uint8_t p1 = 0; + if (use_file_id) + p1 = 0x80 & (fileid & 0x1f); + else + p1 = (offset >> 8) & 0x7f; + uint8_t p2 = offset & 0xff; + + uint16_t sw = 0; + int res = DesfireExchangeISO(false, dctx, (sAPDU) {0x00, ISO7816_READ_BINARY, p1, p2, 0, NULL}, length, resp, resplen, &sw); + if (res == PM3_SUCCESS && sw != 0x9000) + return PM3_ESOFT; + + return res; +} + +int DesfireISOUpdateBinary(DesfireContext *dctx, bool use_file_id, uint8_t fileid, uint16_t offset, uint8_t *data, size_t datalen) { + uint8_t p1 = 0; + if (use_file_id) + p1 = 0x80 & (fileid & 0x1f); + else + p1 = (offset >> 8) & 0x7f; + uint8_t p2 = offset & 0xff; + + uint8_t resp[250] = {0}; + size_t resplen = 0; + + uint16_t sw = 0; + int res = DesfireExchangeISO(false, dctx, (sAPDU) {0x00, ISO7816_UPDATE_BINARY, p1, p2, datalen, data}, 0, resp, &resplen, &sw); + if (res == PM3_SUCCESS && sw != 0x9000) + return PM3_ESOFT; + + return res; +} + +int DesfireISOReadRecords(DesfireContext *dctx, uint8_t recordnum, bool read_all_records, uint8_t fileid, uint8_t length, uint8_t *resp, size_t *resplen) { + uint8_t p2 = ((fileid & 0x1f) << 3) | ((read_all_records) ? 0x05 : 0x04); + + uint16_t sw = 0; + int res = DesfireExchangeISO(false, dctx, (sAPDU) {0x00, ISO7816_READ_RECORDS, recordnum, p2, 0, NULL}, length, resp, resplen, &sw); + if (res == PM3_SUCCESS && sw != 0x9000) + return PM3_ESOFT; + + return res; +} + +int DesfireISOAppendRecord(DesfireContext *dctx, uint8_t fileid, uint8_t *data, size_t datalen) { + uint8_t p2 = ((fileid & 0x1f) << 3); + + uint8_t resp[250] = {0}; + size_t resplen = 0; + + uint16_t sw = 0; + int res = DesfireExchangeISO(false, dctx, (sAPDU) {0x00, ISO7816_APPEND_RECORD, 0x00, p2, datalen, data}, 0, resp, &resplen, &sw); + if (res == PM3_SUCCESS && sw != 0x9000) + return PM3_ESOFT; + + return res; +} diff --git a/client/src/mifare/desfirecore.h b/client/src/mifare/desfirecore.h index 3bf6f581b..83af91767 100644 --- a/client/src/mifare/desfirecore.h +++ b/client/src/mifare/desfirecore.h @@ -234,4 +234,9 @@ int DesfireISOGetChallenge(DesfireContext *dctx, DesfireCryptoAlgorythm keytype, int DesfireISOExternalAuth(DesfireContext *dctx, bool app_level, uint8_t keynum, DesfireCryptoAlgorythm keytype, uint8_t *data); int DesfireISOInternalAuth(DesfireContext *dctx, bool app_level, uint8_t keynum, DesfireCryptoAlgorythm keytype, uint8_t *data, uint8_t *resp, size_t *resplen); +int DesfireISOReadBinary(DesfireContext *dctx, bool use_file_id, uint8_t fileid, uint16_t offset, uint8_t length, uint8_t *resp, size_t *resplen); +int DesfireISOUpdateBinary(DesfireContext *dctx, bool use_file_id, uint8_t fileid, uint16_t offset, uint8_t *data, size_t datalen); +int DesfireISOReadRecords(DesfireContext *dctx, uint8_t recordnum, bool read_all_records, uint8_t fileid, uint8_t length, uint8_t *resp, size_t *resplen); +int DesfireISOAppendRecord(DesfireContext *dctx, uint8_t fileid, uint8_t *data, size_t datalen); + #endif // __DESFIRECORE_H From 18e2d55c57f012e26984483f9fff0cdb57eead19 Mon Sep 17 00:00:00 2001 From: merlokk <807634+merlokk@users.noreply.github.com> Date: Thu, 5 Aug 2021 20:26:27 +0300 Subject: [PATCH 03/16] remove debug & cov 354623 ) --- client/src/mifare/desfirecore.c | 1 - 1 file changed, 1 deletion(-) diff --git a/client/src/mifare/desfirecore.c b/client/src/mifare/desfirecore.c index 88b0c88a8..b5a4e1f44 100644 --- a/client/src/mifare/desfirecore.c +++ b/client/src/mifare/desfirecore.c @@ -2529,7 +2529,6 @@ int DesfireChangeKey(DesfireContext *dctx, bool change_master_key, uint8_t newke // send command uint8_t resp[257] = {0}; size_t resplen = 0; - PrintAndLogEx(SUCCESS, "Change key [%d] %s", cdatalen + 1, sprint_hex(&pckcdata[1], cdatalen + 1)); int res = DesfireChangeKeyCmd(dctx, &pckcdata[1], cdatalen + 1, resp, &resplen); // check response From c27100295a800be92d6488fb3fa9c2c7924f18dd Mon Sep 17 00:00:00 2001 From: merlokk <807634+merlokk@users.noreply.github.com> Date: Fri, 6 Aug 2021 14:52:33 +0300 Subject: [PATCH 04/16] fix iso command data --- client/src/mifare/desfirecore.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/client/src/mifare/desfirecore.c b/client/src/mifare/desfirecore.c index b5a4e1f44..17a7e0197 100644 --- a/client/src/mifare/desfirecore.c +++ b/client/src/mifare/desfirecore.c @@ -2629,13 +2629,13 @@ int DesfireISOInternalAuth(DesfireContext *dctx, bool app_level, uint8_t keynum, int DesfireISOReadBinary(DesfireContext *dctx, bool use_file_id, uint8_t fileid, uint16_t offset, uint8_t length, uint8_t *resp, size_t *resplen) { uint8_t p1 = 0; if (use_file_id) - p1 = 0x80 & (fileid & 0x1f); + p1 = 0x80 | (fileid & 0x1f); else p1 = (offset >> 8) & 0x7f; uint8_t p2 = offset & 0xff; uint16_t sw = 0; - int res = DesfireExchangeISO(false, dctx, (sAPDU) {0x00, ISO7816_READ_BINARY, p1, p2, 0, NULL}, length, resp, resplen, &sw); + int res = DesfireExchangeISO(false, dctx, (sAPDU) {0x00, ISO7816_READ_BINARY, p1, p2, 0, NULL}, (length == 0) ? APDU_INCLUDE_LE_00 : length, resp, resplen, &sw); if (res == PM3_SUCCESS && sw != 0x9000) return PM3_ESOFT; @@ -2645,7 +2645,7 @@ int DesfireISOReadBinary(DesfireContext *dctx, bool use_file_id, uint8_t fileid, int DesfireISOUpdateBinary(DesfireContext *dctx, bool use_file_id, uint8_t fileid, uint16_t offset, uint8_t *data, size_t datalen) { uint8_t p1 = 0; if (use_file_id) - p1 = 0x80 & (fileid & 0x1f); + p1 = 0x80 | (fileid & 0x1f); else p1 = (offset >> 8) & 0x7f; uint8_t p2 = offset & 0xff; @@ -2665,7 +2665,7 @@ int DesfireISOReadRecords(DesfireContext *dctx, uint8_t recordnum, bool read_all uint8_t p2 = ((fileid & 0x1f) << 3) | ((read_all_records) ? 0x05 : 0x04); uint16_t sw = 0; - int res = DesfireExchangeISO(false, dctx, (sAPDU) {0x00, ISO7816_READ_RECORDS, recordnum, p2, 0, NULL}, length, resp, resplen, &sw); + int res = DesfireExchangeISO(false, dctx, (sAPDU) {0x00, ISO7816_READ_RECORDS, recordnum, p2, 0, NULL}, (length == 0) ? APDU_INCLUDE_LE_00 : length, resp, resplen, &sw); if (res == PM3_SUCCESS && sw != 0x9000) return PM3_ESOFT; From 409fb65a3fe3294ce56152c6e8bce1ac4888eae6 Mon Sep 17 00:00:00 2001 From: merlokk <807634+merlokk@users.noreply.github.com> Date: Fri, 6 Aug 2021 14:52:58 +0300 Subject: [PATCH 05/16] add iso to read command and read bin file --- client/src/cmdhfmfdes.c | 43 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/client/src/cmdhfmfdes.c b/client/src/cmdhfmfdes.c index a74c8ffa7..a05324224 100644 --- a/client/src/cmdhfmfdes.c +++ b/client/src/cmdhfmfdes.c @@ -4924,6 +4924,42 @@ static int CmdHF14ADesClearRecordFile(const char *Cmd) { static int DesfileReadISOFileAndPrint(DesfireContext *dctx, uint8_t fnum, int filetype, uint32_t offset, uint32_t length, bool noauth, bool verbose) { + if (filetype == RFTAuto) { + PrintAndLogEx(ERR, "ISO mode needs to specify file type"); + return PM3_EINVARG; + } + + if (filetype == RFTValue) { + PrintAndLogEx(ERR, "ISO mode can't read Value file type"); + return PM3_EINVARG; + } + + if (filetype == RFTMAC) { + PrintAndLogEx(ERR, "ISO mode can't read Transaction MAC file type"); + return PM3_EINVARG; + } + + PrintAndLogEx(INFO, "------------------------------- " _CYAN_("File ISO %02x data") " -------------------------------", fnum); + + uint8_t resp[2048] = {0}; + size_t resplen = 0; + int res = 0; + + if (filetype == RFTData) { + res = DesfireISOReadBinary(dctx, true, fnum, offset, length, resp, &resplen); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Desfire ISOReadBinary command " _RED_("error") ". Result: %d", res); + DropField(); + return PM3_ESOFT; + } + + if (resplen > 0) { + PrintAndLogEx(SUCCESS, "Read %zu bytes from file 0x%02x offset %u", resplen, fnum, offset); + print_buffer_with_offset(resp, resplen, offset, true); + } else { + PrintAndLogEx(SUCCESS, "Read operation returned no data from file %d", fnum); + } + } return PM3_SUCCESS; } @@ -5090,8 +5126,13 @@ static int CmdHF14ADesReadData(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mfdes read", "Read data from file. Key needs to be provided or flag --no-auth set (depend on file settings).", + "It reads file via all command sets. \n" + "For ISO command set it can be read by specifying full 2-byte iso id or 1-byte short iso id (first byte of the full iso id). ISO id lays in the data in BIG ENDIAN format.\n" + "\n" "hf mfdes read --aid 123456 --fid 01 -> read file: app=123456, file=01, offset=0, all the data. use default channel settings from `default` command\n" - "hf mfdes read --aid 123456 --fid 01 --type record --offset 000000 --length 000001 -> read one last record from record file. use default channel settings from `default` command"); + "hf mfdes read --aid 123456 --fid 01 --type record --offset 000000 --length 000001 -> read one last record from record file. use default channel settings from `default` command\n" + "hf mfdes read --aid 123456 --fid 10 --type data -c iso -> read file via ISO channel: app=123456, short iso id=10, offset=0, all the data.\n"); + "hf mfdes read --aid 123456 --isofid 1000 --type data -c iso -> read file via ISO channel: app=123456, iso id=1000, offset=0, all the data."); void *argtable[] = { arg_param_begin, From b2ebbf3ed095f3bb99ae74a188122b532e396919 Mon Sep 17 00:00:00 2001 From: merlokk <807634+merlokk@users.noreply.github.com> Date: Fri, 6 Aug 2021 14:56:10 +0300 Subject: [PATCH 06/16] fix. remove todo) --- client/src/cmdhfmfdes.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/client/src/cmdhfmfdes.c b/client/src/cmdhfmfdes.c index a05324224..2cf38c5de 100644 --- a/client/src/cmdhfmfdes.c +++ b/client/src/cmdhfmfdes.c @@ -5131,7 +5131,7 @@ static int CmdHF14ADesReadData(const char *Cmd) { "\n" "hf mfdes read --aid 123456 --fid 01 -> read file: app=123456, file=01, offset=0, all the data. use default channel settings from `default` command\n" "hf mfdes read --aid 123456 --fid 01 --type record --offset 000000 --length 000001 -> read one last record from record file. use default channel settings from `default` command\n" - "hf mfdes read --aid 123456 --fid 10 --type data -c iso -> read file via ISO channel: app=123456, short iso id=10, offset=0, all the data.\n"); + "hf mfdes read --aid 123456 --fid 10 --type data -c iso -> read file via ISO channel: app=123456, short iso id=10, offset=0, all the data.\n" "hf mfdes read --aid 123456 --isofid 1000 --type data -c iso -> read file via ISO channel: app=123456, iso id=1000, offset=0, all the data."); void *argtable[] = { @@ -5760,7 +5760,6 @@ int CmdHFMFDes(const char *Cmd) { ISO/IEC 7816 Cmds ----------------- - 'B0' Read Binary 'D6' Update Binary 'B2' Read Records 'E2' Append Records From d7aa4feae80034810697672eb22a4a49a91e7140 Mon Sep 17 00:00:00 2001 From: merlokk <807634+merlokk@users.noreply.github.com> Date: Fri, 6 Aug 2021 19:46:49 +0300 Subject: [PATCH 07/16] iso select rework --- client/src/mifare/desfirecore.c | 93 ++++++++++++++++++++++++++++++++- client/src/mifare/desfirecore.h | 14 +++++ 2 files changed, 105 insertions(+), 2 deletions(-) diff --git a/client/src/mifare/desfirecore.c b/client/src/mifare/desfirecore.c index 17a7e0197..54b486642 100644 --- a/client/src/mifare/desfirecore.c +++ b/client/src/mifare/desfirecore.c @@ -263,6 +263,10 @@ const char *DesfireAuthErrorToStr(int error) { return "Can't select application."; case 201: return "Authentication retured no error but channel not authenticated."; + case 202: + return "Can't select application by ISO ID."; + case 203: + return "Can't select file by ISO ID."; case 301: return "ISO Get challenge error."; case 302: @@ -886,6 +890,62 @@ int DesfireSelectAndAuthenticate(DesfireContext *dctx, DesfireSecureChannel secu return DesfireSelectAndAuthenticateEx(dctx, secureChannel, aid, false, verbose); } +int DesfireSelectAndAuthenticateISO(DesfireContext *dctx, DesfireSecureChannel secureChannel, bool useaid, uint32_t aid, uint16_t isoappid, uint16_t isofileid, bool noauth, bool verbose) { + if (verbose) + DesfirePrintContext(dctx); + + int res = 0; + if (useaid) { + dctx->cmdSet = DCCNativeISO; + if (verbose) + PrintAndLogEx(INFO, "Select via " _CYAN_("native iso wrapping") " interface"); + + res = DesfireSelectAIDHex(dctx, aid, false, 0); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Desfire select " _RED_("error") "."); + return 200; + } + if (verbose) + PrintAndLogEx(INFO, "App %06x via native iso channel is " _GREEN_("selected"), aid); + + dctx->cmdSet = DCCISO; + } else { + res = DesfireSelectEx(dctx, true, ISWIsoID, isoappid, NULL); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Desfire iso application select " _RED_("error") "."); + return 202; + } + if (verbose) + PrintAndLogEx(INFO, "Application iso id %04x is " _GREEN_("selected"), isoappid); + + res = DesfireSelectEx(dctx, false, ISWIsoID, isofileid, NULL); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Desfire iso file select " _RED_("error") "."); + return 203; + } + + if (verbose) + PrintAndLogEx(INFO, "Application iso id %04x file iso id %04x is " _GREEN_("selected"), isoappid, isofileid); + } + + if (!noauth) { + res = DesfireAuthenticate(dctx, secureChannel, verbose); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Desfire authenticate " _RED_("error") ". Result: [%d] %s", res, DesfireAuthErrorToStr(res)); + return res; + } + + if (DesfireIsAuthenticated(dctx)) { + if (verbose) + PrintAndLogEx(INFO, "Desfire " _GREEN_("authenticated")); + } else { + return 201; + } + } + + return PM3_SUCCESS; +} + static int DesfireAuthenticateEV1(DesfireContext *dctx, DesfireSecureChannel secureChannel, bool verbose) { // 3 different way to authenticate AUTH (CRC16) , AUTH_ISO (CRC32) , AUTH_AES (CRC32) // 4 different crypto arg1 DES, 3DES, 3K3DES, AES @@ -2566,11 +2626,11 @@ int DesfireSetConfiguration(DesfireContext *dctx, uint8_t paramid, uint8_t *para return res; } -int DesfireISOSelect(DesfireContext *dctx, DesfireISOSelectControl cntr, uint8_t *data, uint8_t datalen, uint8_t *resp, size_t *resplen) { +int DesfireISOSelectEx(DesfireContext *dctx, bool fieldon, DesfireISOSelectControl cntr, uint8_t *data, uint8_t datalen, uint8_t *resp, size_t *resplen) { uint8_t xresp[250] = {0}; size_t xresplen = 0; uint16_t sw = 0; - int res = DesfireExchangeISO(true, dctx, (sAPDU) {0x00, ISO7816_SELECT_FILE, cntr, ((resp == NULL) ? 0x0C : 0x00), datalen, data}, APDU_INCLUDE_LE_00, xresp, &xresplen, &sw); + int res = DesfireExchangeISO(fieldon, dctx, (sAPDU) {0x00, ISO7816_SELECT_FILE, cntr, ((resp == NULL) ? 0x0C : 0x00), datalen, data}, APDU_INCLUDE_LE_00, xresp, &xresplen, &sw); if (res == PM3_SUCCESS && sw != 0x9000) return PM3_ESOFT; @@ -2585,6 +2645,10 @@ int DesfireISOSelect(DesfireContext *dctx, DesfireISOSelectControl cntr, uint8_t return res; } +int DesfireISOSelect(DesfireContext *dctx, DesfireISOSelectControl cntr, uint8_t *data, uint8_t datalen, uint8_t *resp, size_t *resplen) { + return DesfireISOSelectEx(dctx, true, cntr, data, datalen, resp, resplen); +} + int DesfireISOSelectDF(DesfireContext *dctx, char *dfname, uint8_t *resp, size_t *resplen) { return DesfireISOSelect(dctx, ISSDFName, (uint8_t *)dfname, strnlen(dfname, 16), resp, resplen); } @@ -2685,3 +2749,28 @@ int DesfireISOAppendRecord(DesfireContext *dctx, uint8_t fileid, uint8_t *data, return res; } + +int DesfireSelectEx(DesfireContext *ctx, bool fieldon, DesfireISOSelectWay way, uint32_t id, char *dfname) { + uint8_t resp[250] = {0}; + size_t resplen = 0; + + if (way == ISWMF || (way == ISWDFName && dfname == NULL)) { + return DesfireISOSelect(ctx, ISSMFDFEF, NULL, 0, resp, &resplen); + } else if (way == ISW6bAID) { + if (fieldon) + return DesfireSelectAIDHex(ctx, id, false, 0); + else + return DesfireSelectAIDHexNoFieldOn(ctx, id); + } else if (way == ISWIsoID) { + uint8_t data[2] = {0}; + Uint2byteToMemBe(data, id); + return DesfireISOSelectEx(ctx, fieldon, ISSMFDFEF, data, 2, resp, &resplen); + } else if (way == ISWDFName) { + return DesfireISOSelect(ctx, ISSMFDFEF, NULL, 0, resp, &resplen); + } + return PM3_ESOFT; +} + +int DesfireSelect(DesfireContext *ctx, DesfireISOSelectWay way, uint32_t id, char *dfname) { + return DesfireSelectEx(ctx, true, way, id, dfname); +} diff --git a/client/src/mifare/desfirecore.h b/client/src/mifare/desfirecore.h index 83af91767..a03e489eb 100644 --- a/client/src/mifare/desfirecore.h +++ b/client/src/mifare/desfirecore.h @@ -30,6 +30,14 @@ enum DesfireISOSelectControlEnum { }; typedef enum DesfireISOSelectControlEnum DesfireISOSelectControl; +enum DesfireISOSelectWayEnum { + ISW6bAID, + ISWMF, + ISWIsoID, + ISWDFName +}; +typedef enum DesfireISOSelectWayEnum DesfireISOSelectWay; + typedef struct { const uint8_t id; const char *text; @@ -160,9 +168,13 @@ int DesfireSelectAIDHex(DesfireContext *ctx, uint32_t aid1, bool select_two, uin int DesfireSelectAIDHexNoFieldOn(DesfireContext *ctx, uint32_t aid); void DesfirePrintAIDFunctions(uint32_t appid); +int DesfireSelectEx(DesfireContext *ctx, bool fieldon, DesfireISOSelectWay way, uint32_t id, char *dfname); +int DesfireSelect(DesfireContext *ctx, DesfireISOSelectWay way, uint32_t id, char *dfname); + const char *DesfireAuthErrorToStr(int error); int DesfireSelectAndAuthenticate(DesfireContext *dctx, DesfireSecureChannel secureChannel, uint32_t aid, bool verbose); int DesfireSelectAndAuthenticateEx(DesfireContext *dctx, DesfireSecureChannel secureChannel, uint32_t aid, bool noauth, bool verbose); +int DesfireSelectAndAuthenticateISO(DesfireContext *dctx, DesfireSecureChannel secureChannel, bool useaid, uint32_t aid, uint16_t isoappid, uint16_t isofileid, bool noauth, bool verbose); int DesfireAuthenticate(DesfireContext *dctx, DesfireSecureChannel secureChannel, bool verbose); void DesfireCheckAuthCommands(uint32_t appAID, char *dfname, uint8_t keyNum, AuthCommandsChk *authCmdCheck); void DesfireCheckAuthCommandsPrint(AuthCommandsChk *authCmdCheck); @@ -230,6 +242,8 @@ int DesfireUpdateRecord(DesfireContext *dctx, uint8_t fnum, uint32_t recnum, uin int DesfireISOSelectDF(DesfireContext *dctx, char *dfname, uint8_t *resp, size_t *resplen); int DesfireISOSelect(DesfireContext *dctx, DesfireISOSelectControl cntr, uint8_t *data, uint8_t datalen, uint8_t *resp, size_t *resplen); +int DesfireISOSelectFile(DesfireContext *dctx, char *appdfname, uint16_t appid, uint16_t fileid); +int DesfireISOSelectEx(DesfireContext *dctx, bool fieldon, DesfireISOSelectControl cntr, uint8_t *data, uint8_t datalen, uint8_t *resp, size_t *resplen); int DesfireISOGetChallenge(DesfireContext *dctx, DesfireCryptoAlgorythm keytype, uint8_t *resp, size_t *resplen); int DesfireISOExternalAuth(DesfireContext *dctx, bool app_level, uint8_t keynum, DesfireCryptoAlgorythm keytype, uint8_t *data); int DesfireISOInternalAuth(DesfireContext *dctx, bool app_level, uint8_t keynum, DesfireCryptoAlgorythm keytype, uint8_t *data, uint8_t *resp, size_t *resplen); From 92cb18e7d241c5d5afac60ff4ea87389ad1f8086 Mon Sep 17 00:00:00 2001 From: merlokk <807634+merlokk@users.noreply.github.com> Date: Fri, 6 Aug 2021 19:47:12 +0300 Subject: [PATCH 08/16] read binary works --- client/src/cmdhfmfdes.c | 102 +++++++++++++++++++++++++++++++++++----- 1 file changed, 90 insertions(+), 12 deletions(-) diff --git a/client/src/cmdhfmfdes.c b/client/src/cmdhfmfdes.c index 2cf38c5de..2d650e617 100644 --- a/client/src/cmdhfmfdes.c +++ b/client/src/cmdhfmfdes.c @@ -4922,7 +4922,7 @@ static int CmdHF14ADesClearRecordFile(const char *Cmd) { return PM3_SUCCESS; } -static int DesfileReadISOFileAndPrint(DesfireContext *dctx, uint8_t fnum, int filetype, uint32_t offset, uint32_t length, bool noauth, bool verbose) { +static int DesfileReadISOFileAndPrint(DesfireContext *dctx, bool select_current_file, uint8_t fnum, uint16_t fisoid, int filetype, uint32_t offset, uint32_t length, bool noauth, bool verbose) { if (filetype == RFTAuto) { PrintAndLogEx(ERR, "ISO mode needs to specify file type"); @@ -4939,14 +4939,17 @@ static int DesfileReadISOFileAndPrint(DesfireContext *dctx, uint8_t fnum, int fi return PM3_EINVARG; } - PrintAndLogEx(INFO, "------------------------------- " _CYAN_("File ISO %02x data") " -------------------------------", fnum); + if (select_current_file) + PrintAndLogEx(INFO, "------------------------------- " _CYAN_("File ISO %04x data") " -------------------------------", fisoid); + else + PrintAndLogEx(INFO, "---------------------------- " _CYAN_("File ISO short %02x data") " ----------------------------", fnum); uint8_t resp[2048] = {0}; size_t resplen = 0; int res = 0; if (filetype == RFTData) { - res = DesfireISOReadBinary(dctx, true, fnum, offset, length, resp, &resplen); + res = DesfireISOReadBinary(dctx, !select_current_file, (select_current_file) ? 0x00 : fnum, offset, length, resp, &resplen); if (res != PM3_SUCCESS) { PrintAndLogEx(ERR, "Desfire ISOReadBinary command " _RED_("error") ". Result: %d", res); DropField(); @@ -4954,10 +4957,56 @@ static int DesfileReadISOFileAndPrint(DesfireContext *dctx, uint8_t fnum, int fi } if (resplen > 0) { - PrintAndLogEx(SUCCESS, "Read %zu bytes from file 0x%02x offset %u", resplen, fnum, offset); + if (select_current_file) + PrintAndLogEx(SUCCESS, "Read %zu bytes from file 0x%04x offset %u", resplen, fisoid, offset); + else + PrintAndLogEx(SUCCESS, "Read %zu bytes from file 0x%02x offset %u", resplen, fnum, offset); print_buffer_with_offset(resp, resplen, offset, true); } else { - PrintAndLogEx(SUCCESS, "Read operation returned no data from file %d", fnum); + if (select_current_file) + PrintAndLogEx(SUCCESS, "Read operation returned no data from file %04x", fisoid); + else + PrintAndLogEx(SUCCESS, "Read operation returned no data from file %02x", fnum); + } + } + + if (filetype == RFTRecord) { + size_t reclen = 0; + res = DesfireISOReadRecords(dctx, offset, false, (select_current_file) ? 0x00 : fnum, 0, resp, &resplen); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Desfire ISOReadRecords (one record) command " _RED_("error") ". Result: %d", res); + DropField(); + return PM3_ESOFT; + } + reclen = resplen - 8; + + if (verbose) + PrintAndLogEx(INFO, "Record length %zu", reclen); + + if (length > 1) { + res = DesfireISOReadRecords(dctx, offset, true, (select_current_file) ? 0x00 : fnum, 0, resp, &resplen); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Desfire ISOReadRecords (one record) command " _RED_("error") ". Result: %d", res); + DropField(); + return PM3_ESOFT; + } + } + + if (resplen > 0) { + size_t reccount = resplen / reclen; + PrintAndLogEx(SUCCESS, "Read %zu bytes from file 0x%02x from record %d record count %zu record length %zu", resplen, fnum, offset, reccount, reclen); + if (reccount > 1) + PrintAndLogEx(SUCCESS, "Lastest record at the bottom."); + for (int i = 0; i < reccount; i++) { + if (i != 0) + PrintAndLogEx(SUCCESS, "Record %zu", reccount - (i + offset + 1)); + print_buffer_with_offset(&resp[i * reclen], reclen, offset, (i == 0)); + } + } else { + if (select_current_file) + PrintAndLogEx(SUCCESS, "Read operation returned no data from file %04x", fisoid); + else + PrintAndLogEx(SUCCESS, "Read operation returned no data from file %02x", fnum); } } @@ -5131,8 +5180,9 @@ static int CmdHF14ADesReadData(const char *Cmd) { "\n" "hf mfdes read --aid 123456 --fid 01 -> read file: app=123456, file=01, offset=0, all the data. use default channel settings from `default` command\n" "hf mfdes read --aid 123456 --fid 01 --type record --offset 000000 --length 000001 -> read one last record from record file. use default channel settings from `default` command\n" - "hf mfdes read --aid 123456 --fid 10 --type data -c iso -> read file via ISO channel: app=123456, short iso id=10, offset=0, all the data.\n" - "hf mfdes read --aid 123456 --isofid 1000 --type data -c iso -> read file via ISO channel: app=123456, iso id=1000, offset=0, all the data."); + "hf mfdes read --aid 123456 --fid 10 --type data -c iso -> read file via ISO channel: app=123456, short iso id=10, offset=0.\n" + "hf mfdes read --aid 123456 --fileisoid 1000 --type data -c iso -> read file via ISO channel: app=123456, iso id=1000, offset=0. Select via native ISO wrapper\n" + "hf mfdes read --appisoid 0102 --fileisoid 1000 --type data -c iso -> read file via ISO channel: app iso id=0102, iso id=1000, offset=0. Select via ISO commands"); void *argtable[] = { arg_param_begin, @@ -5152,6 +5202,8 @@ static int CmdHF14ADesReadData(const char *Cmd) { arg_str0(NULL, "type", "", "File Type auto/data(Standard/Backup)/value/record(linear/cyclic)/mac). Auto - check file settings and then read. Default: auto"), arg_str0("o", "offset", "", "File Offset (3 hex bytes, big endian). For records - record number (0 - lastest record). Default 0"), arg_str0("l", "length", "", "Length to read (3 hex bytes, big endian -> 000000 = Read all data). For records - records count (0 - all). Default 0."), + arg_str0(NULL, "appisoid", "", "Application ISO ID (ISO DF ID) (2 hex bytes, big endian). Works only for ISO read commands."), + arg_str0(NULL, "fileisoid", "", "File ISO ID (ISO DF ID) (2 hex bytes, big endian). Works only for ISO read commands."), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -5199,6 +5251,24 @@ static int CmdHF14ADesReadData(const char *Cmd) { return PM3_EINVARG; } + uint32_t appisoid = 0x0000; + res = arg_get_u32_hexstr_def_nlen(ctx, 17, 0x0000, &appisoid, 2, true); + bool isoidpresent = (res == 1); + if (res == 2) { + PrintAndLogEx(ERR, "Application ISO ID (for EF) must have 2 bytes length"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + + uint32_t fileisoid = 0x0000; + res = arg_get_u32_hexstr_def_nlen(ctx, 18, 0x0000, &fileisoid, 2, true); + bool fileisoidpresent = (res == 1); + if (res == 2) { + PrintAndLogEx(ERR, "File ISO ID (for DF) must have 2 bytes length"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + SetAPDULogging(APDULogging); CLIParserFree(ctx); @@ -5207,16 +5277,24 @@ static int CmdHF14ADesReadData(const char *Cmd) { return PM3_EINVARG; } - res = DesfireSelectAndAuthenticateEx(&dctx, securechann, appid, noauth, verbose); - if (res != PM3_SUCCESS) { - DropField(); - return res; + if (!isoidpresent) { + res = DesfireSelectAndAuthenticateEx(&dctx, securechann, appid, noauth, verbose); + if (res != PM3_SUCCESS) { + DropField(); + return res; + } + } else { + res = DesfireSelectAndAuthenticateISO(&dctx, securechann, (appid != 0), appid, appisoid, fileisoid, noauth, verbose); + if (res != PM3_SUCCESS) { + DropField(); + return res; + } } if (dctx.cmdSet != DCCISO) res = DesfileReadFileAndPrint(&dctx, fnum, op, offset, length, noauth, verbose); else - res = DesfileReadISOFileAndPrint(&dctx, fnum, op, offset, length, noauth, verbose); + res = DesfileReadISOFileAndPrint(&dctx, fileisoidpresent, fnum, fileisoid, op, offset, length, noauth, verbose); DropField(); return res; From 130ab34ab6283332fe706990aa27b7d69b5cc16b Mon Sep 17 00:00:00 2001 From: merlokk <807634+merlokk@users.noreply.github.com> Date: Fri, 6 Aug 2021 20:01:13 +0300 Subject: [PATCH 09/16] read record --- client/src/cmdhfmfdes.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/client/src/cmdhfmfdes.c b/client/src/cmdhfmfdes.c index 2d650e617..9a57a4c77 100644 --- a/client/src/cmdhfmfdes.c +++ b/client/src/cmdhfmfdes.c @@ -4978,12 +4978,12 @@ static int DesfileReadISOFileAndPrint(DesfireContext *dctx, bool select_current_ DropField(); return PM3_ESOFT; } - reclen = resplen - 8; + reclen = resplen; if (verbose) PrintAndLogEx(INFO, "Record length %zu", reclen); - if (length > 1) { + if (length != 1) { res = DesfireISOReadRecords(dctx, offset, true, (select_current_file) ? 0x00 : fnum, 0, resp, &resplen); if (res != PM3_SUCCESS) { PrintAndLogEx(ERR, "Desfire ISOReadRecords (one record) command " _RED_("error") ". Result: %d", res); @@ -5177,12 +5177,15 @@ static int CmdHF14ADesReadData(const char *Cmd) { "Read data from file. Key needs to be provided or flag --no-auth set (depend on file settings).", "It reads file via all command sets. \n" "For ISO command set it can be read by specifying full 2-byte iso id or 1-byte short iso id (first byte of the full iso id). ISO id lays in the data in BIG ENDIAN format.\n" + "ISO record commands: offset - record number (0-current, 1..ff-number, 1-lastest), length - if 0 - all records, if 1 - one\n" "\n" "hf mfdes read --aid 123456 --fid 01 -> read file: app=123456, file=01, offset=0, all the data. use default channel settings from `default` command\n" "hf mfdes read --aid 123456 --fid 01 --type record --offset 000000 --length 000001 -> read one last record from record file. use default channel settings from `default` command\n" "hf mfdes read --aid 123456 --fid 10 --type data -c iso -> read file via ISO channel: app=123456, short iso id=10, offset=0.\n" "hf mfdes read --aid 123456 --fileisoid 1000 --type data -c iso -> read file via ISO channel: app=123456, iso id=1000, offset=0. Select via native ISO wrapper\n" - "hf mfdes read --appisoid 0102 --fileisoid 1000 --type data -c iso -> read file via ISO channel: app iso id=0102, iso id=1000, offset=0. Select via ISO commands"); + "hf mfdes read --appisoid 0102 --fileisoid 1000 --type data -c iso -> read file via ISO channel: app iso id=0102, iso id=1000, offset=0. Select via ISO commands\n" + "hf mfdes read --appisoid 0102 --fileisoid 1100 --type record -c iso --offset 000005 --length 000001 -> get one record (number 5) from file 1100 via iso commands\n" + "hf mfdes read --appisoid 0102 --fileisoid 1100 --type record -c iso --offset 000005 --length 000000 -> get all record (from 5 to 1) from file 1100 via iso commands"); void *argtable[] = { arg_param_begin, From 1b6531f50e71e1470d744b1af0cc36f8062917ff Mon Sep 17 00:00:00 2001 From: merlokk <807634+merlokk@users.noreply.github.com> Date: Fri, 6 Aug 2021 20:02:49 +0300 Subject: [PATCH 10/16] text fix --- client/src/cmdhfmfdes.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/client/src/cmdhfmfdes.c b/client/src/cmdhfmfdes.c index 9a57a4c77..4b0ae7457 100644 --- a/client/src/cmdhfmfdes.c +++ b/client/src/cmdhfmfdes.c @@ -4994,7 +4994,10 @@ static int DesfileReadISOFileAndPrint(DesfireContext *dctx, bool select_current_ if (resplen > 0) { size_t reccount = resplen / reclen; - PrintAndLogEx(SUCCESS, "Read %zu bytes from file 0x%02x from record %d record count %zu record length %zu", resplen, fnum, offset, reccount, reclen); + if (select_current_file) + PrintAndLogEx(SUCCESS, "Read %zu bytes from file 0x%04x from record %d record count %zu record length %zu", resplen, fisoid, offset, reccount, reclen); + else + PrintAndLogEx(SUCCESS, "Read %zu bytes from file 0x%02x from record %d record count %zu record length %zu", resplen, fnum, offset, reccount, reclen); if (reccount > 1) PrintAndLogEx(SUCCESS, "Lastest record at the bottom."); for (int i = 0; i < reccount; i++) { From 0d551b3940a120c5b9a20fae08f4ff4728367e60 Mon Sep 17 00:00:00 2001 From: merlokk <807634+merlokk@users.noreply.github.com> Date: Fri, 6 Aug 2021 20:03:49 +0300 Subject: [PATCH 11/16] todo --- client/src/cmdhfmfdes.c | 1 - 1 file changed, 1 deletion(-) diff --git a/client/src/cmdhfmfdes.c b/client/src/cmdhfmfdes.c index 4b0ae7457..7d2cf0ce4 100644 --- a/client/src/cmdhfmfdes.c +++ b/client/src/cmdhfmfdes.c @@ -5845,7 +5845,6 @@ int CmdHFMFDes(const char *Cmd) { ISO/IEC 7816 Cmds ----------------- 'D6' Update Binary - 'B2' Read Records 'E2' Append Records */ From d92c8f1bff9f33487b02d04add7cb05ecabc48df Mon Sep 17 00:00:00 2001 From: merlokk <807634+merlokk@users.noreply.github.com> Date: Fri, 6 Aug 2021 20:27:04 +0300 Subject: [PATCH 12/16] iso mac calculation --- client/src/mifare/desfirecore.c | 5 +-- client/src/mifare/desfiresecurechan.c | 44 +++++++++++++++++++++++---- 2 files changed, 41 insertions(+), 8 deletions(-) diff --git a/client/src/mifare/desfirecore.c b/client/src/mifare/desfirecore.c index 54b486642..6411071d3 100644 --- a/client/src/mifare/desfirecore.c +++ b/client/src/mifare/desfirecore.c @@ -677,11 +677,12 @@ static int DesfireExchangeISONative(bool activate_field, DesfireContext *ctx, ui } static int DesfireExchangeISO(bool activate_field, DesfireContext *ctx, sAPDU apdu, uint16_t le, uint8_t *resp, size_t *resplen, uint16_t *sw) { + uint8_t r[1050] = {0}; uint32_t rlen = 0; - int res = DESFIRESendApduEx(activate_field, apdu, le, resp, 255, &rlen, sw); + int res = DESFIRESendApduEx(activate_field, apdu, le, r, 255, &rlen, sw); if (res == PM3_SUCCESS) - *resplen = rlen; + DesfireSecureChannelDecode(ctx, r, rlen, 0, resp, resplen); return res; } diff --git a/client/src/mifare/desfiresecurechan.c b/client/src/mifare/desfiresecurechan.c index d5bc141d0..11067d7fb 100644 --- a/client/src/mifare/desfiresecurechan.c +++ b/client/src/mifare/desfiresecurechan.c @@ -471,8 +471,8 @@ static void DesfireSecureChannelDecodeEV1(DesfireContext *ctx, uint8_t *srcdata, DesfireCryptoCMAC(ctx, data, *dstdatalen + 1, cmac); if (memcmp(&srcdata[*dstdatalen], cmac, DesfireGetMACLength(ctx)) != 0) { PrintAndLogEx(WARNING, "Received MAC is not match with calculated"); - PrintAndLogEx(INFO, " received MAC: %s", sprint_hex(&srcdata[*dstdatalen], desfire_get_key_block_length(ctx->keyType))); - PrintAndLogEx(INFO, " calculated MAC: %s", sprint_hex(cmac, desfire_get_key_block_length(ctx->keyType))); + PrintAndLogEx(INFO, " received MAC: %s", sprint_hex(&srcdata[*dstdatalen], DesfireGetMACLength(ctx))); + PrintAndLogEx(INFO, " calculated MAC: %s", sprint_hex(cmac, DesfireGetMACLength(ctx))); } else { if (GetAPDULogging()) PrintAndLogEx(INFO, "Received MAC OK"); @@ -521,8 +521,8 @@ static void DesfireSecureChannelDecodeEV2(DesfireContext *ctx, uint8_t *srcdata, DesfireEV2CalcCMAC(ctx, 0x00, srcdata, *dstdatalen, cmac); if (memcmp(&srcdata[*dstdatalen], cmac, DesfireGetMACLength(ctx)) != 0) { PrintAndLogEx(WARNING, "Received MAC is not match with calculated"); - PrintAndLogEx(INFO, " received MAC: %s", sprint_hex(&srcdata[*dstdatalen], desfire_get_key_block_length(ctx->keyType))); - PrintAndLogEx(INFO, " calculated MAC: %s", sprint_hex(cmac, desfire_get_key_block_length(ctx->keyType))); + PrintAndLogEx(INFO, " received MAC: %s", sprint_hex(&srcdata[*dstdatalen], DesfireGetMACLength(ctx))); + PrintAndLogEx(INFO, " calculated MAC: %s", sprint_hex(cmac, DesfireGetMACLength(ctx))); } else { if (GetAPDULogging()) PrintAndLogEx(INFO, "Received MAC OK"); @@ -538,8 +538,8 @@ static void DesfireSecureChannelDecodeEV2(DesfireContext *ctx, uint8_t *srcdata, DesfireEV2CalcCMAC(ctx, 0x00, srcdata, *dstdatalen, cmac); if (memcmp(&srcdata[*dstdatalen], cmac, DesfireGetMACLength(ctx)) != 0) { PrintAndLogEx(WARNING, "Received MAC is not match with calculated"); - PrintAndLogEx(INFO, " received MAC: %s", sprint_hex(&srcdata[*dstdatalen], desfire_get_key_block_length(ctx->keyType))); - PrintAndLogEx(INFO, " calculated MAC: %s", sprint_hex(cmac, desfire_get_key_block_length(ctx->keyType))); + PrintAndLogEx(INFO, " received MAC: %s", sprint_hex(&srcdata[*dstdatalen], DesfireGetMACLength(ctx))); + PrintAndLogEx(INFO, " calculated MAC: %s", sprint_hex(cmac, DesfireGetMACLength(ctx))); } else { if (GetAPDULogging()) PrintAndLogEx(INFO, "Received MAC OK"); @@ -559,7 +559,39 @@ static void DesfireSecureChannelDecodeEV2(DesfireContext *ctx, uint8_t *srcdata, } } +static void DesfireISODecode(DesfireContext *ctx, uint8_t *srcdata, size_t srcdatalen, uint8_t *dstdata, size_t *dstdatalen) { + memcpy(dstdata, srcdata, srcdatalen); + *dstdatalen = srcdatalen; + uint8_t data[1050] = {0}; + + if (srcdatalen < DesfireGetMACLength(ctx)) + return; + + uint8_t maclen = DesfireGetMACLength(ctx); + if (DesfireIsAuthenticated(ctx)) { + memcpy(data, srcdata, srcdatalen - maclen); + data[*dstdatalen] = 0x00; // respcode + + uint8_t cmac[DESFIRE_MAX_CRYPTO_BLOCK_SIZE] = {0}; + DesfireCryptoCMAC(ctx, data, srcdatalen - maclen + 1, cmac); + if (memcmp(&srcdata[srcdatalen - maclen], cmac, maclen) != 0) { + PrintAndLogEx(WARNING, "Received MAC is not match with calculated"); + PrintAndLogEx(INFO, " received MAC: %s", sprint_hex(&srcdata[srcdatalen - maclen], maclen)); + PrintAndLogEx(INFO, " calculated MAC: %s", sprint_hex(cmac, maclen)); + } else { + *dstdatalen = srcdatalen - maclen; + if (GetAPDULogging()) + PrintAndLogEx(INFO, "Received MAC OK"); + } + } +} + void DesfireSecureChannelDecode(DesfireContext *ctx, uint8_t *srcdata, size_t srcdatalen, uint8_t respcode, uint8_t *dstdata, size_t *dstdatalen) { + if (ctx->cmdSet == DCCISO) { + DesfireISODecode(ctx, srcdata, srcdatalen, dstdata, dstdatalen); + return; + } + switch (ctx->secureChannel) { case DACd40: DesfireSecureChannelDecodeD40(ctx, srcdata, srcdatalen, respcode, dstdata, dstdatalen); From 6c3cac9c63f203c4927ae21c1f55ee9daf0e4660 Mon Sep 17 00:00:00 2001 From: merlokk <807634+merlokk@users.noreply.github.com> Date: Fri, 6 Aug 2021 20:31:12 +0300 Subject: [PATCH 13/16] fix buffer length --- client/src/mifare/desfirecore.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/client/src/mifare/desfirecore.c b/client/src/mifare/desfirecore.c index 6411071d3..efc499cf0 100644 --- a/client/src/mifare/desfirecore.c +++ b/client/src/mifare/desfirecore.c @@ -677,12 +677,12 @@ static int DesfireExchangeISONative(bool activate_field, DesfireContext *ctx, ui } static int DesfireExchangeISO(bool activate_field, DesfireContext *ctx, sAPDU apdu, uint16_t le, uint8_t *resp, size_t *resplen, uint16_t *sw) { - uint8_t r[1050] = {0}; - uint32_t rlen = 0; - int res = DESFIRESendApduEx(activate_field, apdu, le, r, 255, &rlen, sw); + uint8_t data[1050] = {0}; + uint32_t datalen = 0; + int res = DESFIRESendApduEx(activate_field, apdu, le, data, sizeof(data), &datalen, sw); if (res == PM3_SUCCESS) - DesfireSecureChannelDecode(ctx, r, rlen, 0, resp, resplen); + DesfireSecureChannelDecode(ctx, data, datalen, 0, resp, resplen); return res; } From 39be125df31c0bb09feda2735757a489c9262699 Mon Sep 17 00:00:00 2001 From: merlokk <807634+merlokk@users.noreply.github.com> Date: Fri, 6 Aug 2021 21:38:13 +0300 Subject: [PATCH 14/16] write binary works --- client/src/cmdhfmfdes.c | 96 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 94 insertions(+), 2 deletions(-) diff --git a/client/src/cmdhfmfdes.c b/client/src/cmdhfmfdes.c index 7d2cf0ce4..3bae080d0 100644 --- a/client/src/cmdhfmfdes.c +++ b/client/src/cmdhfmfdes.c @@ -5306,6 +5306,57 @@ static int CmdHF14ADesReadData(const char *Cmd) { return res; } +static int DesfileWriteISOFile(DesfireContext *dctx, bool select_current_file, uint8_t fnum, uint16_t fisoid, int filetype, uint32_t offset, uint8_t *data, uint32_t datalen, bool verbose) { + + if (filetype == RFTAuto) { + PrintAndLogEx(ERR, "ISO mode needs to specify file type"); + return PM3_EINVARG; + } + + if (filetype == RFTValue) { + PrintAndLogEx(ERR, "ISO mode can't write Value file type"); + return PM3_EINVARG; + } + + if (filetype == RFTMAC) { + PrintAndLogEx(ERR, "ISO mode can't write Transaction MAC file type"); + return PM3_EINVARG; + } + + if (dctx->commMode != DCMPlain) { + PrintAndLogEx(ERR, "ISO mode can write only in plain mode"); + return PM3_EINVARG; + } + + int res = 0; + if (filetype == RFTData) { + res = DesfireISOUpdateBinary(dctx, !select_current_file, (select_current_file) ? 0x00 : fnum, offset, data, datalen); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Desfire ISOUpdateBinary command " _RED_("error") ". Result: %d", res); + return PM3_ESOFT; + } + + if (select_current_file) + PrintAndLogEx(INFO, "Write data file %04x " _GREEN_("success"), fisoid); + else + PrintAndLogEx(INFO, "Write data file %02x " _GREEN_("success"), fnum); + } + + if (filetype == RFTRecord) { + res = DesfireISOAppendRecord(dctx, (select_current_file) ? 0x00 : fnum, data, datalen); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Desfire WriteRecord command " _RED_("error") ". Result: %d", res); + return PM3_ESOFT; + } + if (select_current_file) + PrintAndLogEx(INFO, "Write record to file %04x " _GREEN_("success"), fisoid); + else + PrintAndLogEx(INFO, "Write record to file %02x " _GREEN_("success"), fnum); + } + + return PM3_SUCCESS; +} + static int CmdHF14ADesWriteData(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mfdes write", @@ -5341,6 +5392,8 @@ static int CmdHF14ADesWriteData(const char *Cmd) { arg_lit0(NULL, "debit", "use for value file debit operation instead of credit"), arg_lit0(NULL, "commit", "commit needs for backup file only. For the other file types and in the `auto` mode - command set it automatically."), arg_int0(NULL, "updaterec", "", "Record number for update record command. Updates record instead of write. Lastest record - 0"), + arg_str0(NULL, "appisoid", "", "Application ISO ID (ISO DF ID) (2 hex bytes, big endian). Works only for ISO read commands."), + arg_str0(NULL, "fileisoid", "", "File ISO ID (ISO DF ID) (2 hex bytes, big endian). Works only for ISO read commands."), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -5394,6 +5447,24 @@ static int CmdHF14ADesWriteData(const char *Cmd) { int updaterecno = arg_get_int_def(ctx, 19, -1); + uint32_t appisoid = 0x0000; + res = arg_get_u32_hexstr_def_nlen(ctx, 20, 0x0000, &appisoid, 2, true); + bool isoidpresent = (res == 1); + if (res == 2) { + PrintAndLogEx(ERR, "Application ISO ID (for EF) must have 2 bytes length"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + + uint32_t fileisoid = 0x0000; + res = arg_get_u32_hexstr_def_nlen(ctx, 21, 0x0000, &fileisoid, 2, true); + bool fileisoidpresent = (res == 1); + if (res == 2) { + PrintAndLogEx(ERR, "File ISO ID (for DF) must have 2 bytes length"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + SetAPDULogging(APDULogging); CLIParserFree(ctx); @@ -5402,8 +5473,29 @@ static int CmdHF14ADesWriteData(const char *Cmd) { return PM3_EINVARG; } - res = DesfireSelectAndAuthenticateEx(&dctx, securechann, appid, noauth, verbose); - if (res != PM3_SUCCESS) { + if (!isoidpresent) { + res = DesfireSelectAndAuthenticateEx(&dctx, securechann, appid, noauth, verbose); + if (res != PM3_SUCCESS) { + DropField(); + return res; + } + } else { + res = DesfireSelectAndAuthenticateISO(&dctx, securechann, (appid != 0), appid, appisoid, fileisoid, noauth, verbose); + if (res != PM3_SUCCESS) { + DropField(); + return res; + } + } + + // ISO command set + if (dctx.cmdSet == DCCISO) { + if (op == RFTRecord && updaterecno >= 0) { + PrintAndLogEx(ERR, "ISO mode can't update record. Only append."); + DropField(); + return PM3_EINVARG; + } + + res = DesfileWriteISOFile(&dctx, fileisoidpresent, fnum, fileisoid, op, offset, data, datalen, verbose); DropField(); return res; } From 53874fcd240d8c7418d2d906dd4d4182d03605a7 Mon Sep 17 00:00:00 2001 From: merlokk <807634+merlokk@users.noreply.github.com> Date: Fri, 6 Aug 2021 23:09:27 +0300 Subject: [PATCH 15/16] add help --- client/src/cmdhfmfdes.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/client/src/cmdhfmfdes.c b/client/src/cmdhfmfdes.c index 3bae080d0..0acea11bf 100644 --- a/client/src/cmdhfmfdes.c +++ b/client/src/cmdhfmfdes.c @@ -5369,7 +5369,9 @@ static int CmdHF14ADesWriteData(const char *Cmd) { "hf mfdes write --aid 123456 --fid 01 -d 01020304 -> write data to record file with `auto` type\n" "hf mfdes write --aid 123456 --fid 01 --type record -d 01020304 -> write data to record file\n" "hf mfdes write --aid 123456 --fid 01 --type record -d 01020304 --updaterec 0 -> update record in the record file. record 0 - lastest record.\n" - "hf mfdes write --aid 123456 --fid 01 --type record --offset 000000 -d 11223344 -> write record to record file. use default channel settings from `default` command"); + "hf mfdes write --aid 123456 --fid 01 --type record --offset 000000 -d 11223344 -> write record to record file. use default channel settings from `default` command\n" + "hf mfdes write --appisoid 1234 --fileisoid 1000 --type data -c iso -d 01020304 -> write data to std/backup file via iso commandset\n" + "hf mfdes write --appisoid 1234 --fileisoid 2000 --type record -c iso -d 01020304 -> aend record to record file via iso commandset"); void *argtable[] = { arg_param_begin, From ac7be1c4f476203544548e7f75afcb64f6e5a359 Mon Sep 17 00:00:00 2001 From: merlokk <807634+merlokk@users.noreply.github.com> Date: Fri, 6 Aug 2021 23:10:40 +0300 Subject: [PATCH 16/16] remove todo --- client/src/cmdhfmfdes.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/client/src/cmdhfmfdes.c b/client/src/cmdhfmfdes.c index 0acea11bf..8e1ccb5d3 100644 --- a/client/src/cmdhfmfdes.c +++ b/client/src/cmdhfmfdes.c @@ -5932,13 +5932,3 @@ int CmdHFMFDes(const char *Cmd) { clearCommandBuffer(); return CmdsParse(CommandTable, Cmd); } - -/* - ToDo: - - ISO/IEC 7816 Cmds - ----------------- - 'D6' Update Binary - 'E2' Append Records - -*/